exponential-mcp 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -1
- package/dist/cli.js +0 -0
- package/dist/index.js +181 -5
- package/package.json +2 -2
- package/dist/api.d.ts +0 -84
- package/dist/api.js +0 -82
package/README.md
CHANGED
|
@@ -106,12 +106,31 @@ exponential-mcp serve
|
|
|
106
106
|
|------|-------------|
|
|
107
107
|
| `get_workspaces` | List all workspaces |
|
|
108
108
|
| `get_projects` | List projects (optionally by workspace) |
|
|
109
|
-
| `get_actions` | List actions/tasks (filter by project or status) |
|
|
109
|
+
| `get_actions` | List actions/tasks (filter by project or status; no date filtering) |
|
|
110
|
+
| `get_todays_actions` | **What's on your plate now** — overdue / today / inbox, across all workspaces |
|
|
111
|
+
| `get_overdue_triage` | Why the overdue pile is that size: bulk-created cohorts vs real debt |
|
|
110
112
|
| `create_action` | Create a new task (supports natural language) |
|
|
113
|
+
| `update_action` | Rename, re-prioritise, move project, or set dates (incl. `scheduledStart`) |
|
|
114
|
+
| `defer_actions` | Amnesty: clear dates, back to the project backlog untimed |
|
|
115
|
+
| `reschedule_actions` | Move actions to a new do-date |
|
|
111
116
|
| `complete_action` | Mark an action as done |
|
|
112
117
|
| `get_goals` | List OKRs with progress |
|
|
113
118
|
| `search` | Search across everything |
|
|
114
119
|
|
|
120
|
+
### Asking about the day
|
|
121
|
+
|
|
122
|
+
Use **`get_todays_actions`**, not `get_actions`, for anything about today,
|
|
123
|
+
priorities, or what the user is behind on. `get_actions` has no date filtering
|
|
124
|
+
at all, so it cannot distinguish overdue work from anything else.
|
|
125
|
+
|
|
126
|
+
When there is a lot of overdue work, follow up with **`get_overdue_triage`**
|
|
127
|
+
before proposing what to do. A large overdue count is usually a few bulk writes
|
|
128
|
+
— a generated project plan stamped every row with one timestamp — not a large
|
|
129
|
+
number of missed commitments. Those are **cohorts**, and the honest disposition
|
|
130
|
+
is `defer_actions` (amnesty); `reschedule_actions` would just re-inflict the
|
|
131
|
+
same pile tomorrow. Individually-dated **loose** actions are the ones that
|
|
132
|
+
deserve a real decision.
|
|
133
|
+
|
|
115
134
|
## Development
|
|
116
135
|
|
|
117
136
|
```bash
|
package/dist/cli.js
CHANGED
|
File without changes
|
package/dist/index.js
CHANGED
|
@@ -46,6 +46,22 @@ function loadClientConfig() {
|
|
|
46
46
|
}
|
|
47
47
|
return { token, apiUrl };
|
|
48
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* `undefined` = leave the field alone, `null` = clear it, otherwise a Date.
|
|
51
|
+
* MCP arguments arrive as JSON, so an explicit clear can be either a real null
|
|
52
|
+
* or the string "null" depending on how the model emits it.
|
|
53
|
+
*/
|
|
54
|
+
function parseDateArg(raw) {
|
|
55
|
+
if (raw === undefined)
|
|
56
|
+
return undefined;
|
|
57
|
+
if (raw === null || raw === 'null')
|
|
58
|
+
return null;
|
|
59
|
+
const parsed = new Date(raw);
|
|
60
|
+
if (isNaN(parsed.getTime())) {
|
|
61
|
+
throw new Error(`Invalid date "${String(raw)}". Use an ISO datetime, or null to clear.`);
|
|
62
|
+
}
|
|
63
|
+
return parsed;
|
|
64
|
+
}
|
|
49
65
|
// Tool definitions
|
|
50
66
|
const TOOLS = [
|
|
51
67
|
{
|
|
@@ -63,7 +79,7 @@ const TOOLS = [
|
|
|
63
79
|
},
|
|
64
80
|
{
|
|
65
81
|
name: 'get_actions',
|
|
66
|
-
description: 'List actions/tasks
|
|
82
|
+
description: 'List actions/tasks, optionally filtered by project or status. This is a flat list with no date filtering — to answer "what should I work on today" or "what am I behind on", use get_todays_actions instead.',
|
|
67
83
|
inputSchema: {
|
|
68
84
|
type: 'object',
|
|
69
85
|
properties: {
|
|
@@ -79,6 +95,97 @@ const TOOLS = [
|
|
|
79
95
|
}
|
|
80
96
|
}
|
|
81
97
|
},
|
|
98
|
+
{
|
|
99
|
+
name: 'get_todays_actions',
|
|
100
|
+
description: "What is on the user's plate right now, split into overdue / today / inbox. This is the same set the /today page renders, across all workspaces. Use this FIRST for any question about today, this week, priorities, what to work on, or what the user is behind on — it is the only tool that surfaces overdue work. Returns action IDs, so pair it with update_action, defer_actions, or reschedule_actions to act on what it finds.",
|
|
101
|
+
inputSchema: {
|
|
102
|
+
type: 'object',
|
|
103
|
+
properties: {
|
|
104
|
+
workspaceId: {
|
|
105
|
+
type: 'string',
|
|
106
|
+
description: 'Optional workspace ID. Omit to span all workspaces (usually what you want).'
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
name: 'get_overdue_triage',
|
|
113
|
+
description: 'Explain WHY the overdue pile is the size it is, before proposing what to do about it. Splits overdue actions into "cohorts" — groups sharing one exact timestamp, the fingerprint of a bulk write like a generated project plan, which were almost certainly never individually due — and "loose" individually-dated actions, which are real missed commitments. Use this whenever the user has a lot of overdue work: recommend defer_actions (amnesty) for cohorts and a real decision for loose items. Rescheduling a cohort just re-inflicts the pile tomorrow.',
|
|
114
|
+
inputSchema: {
|
|
115
|
+
type: 'object',
|
|
116
|
+
properties: {
|
|
117
|
+
workspaceId: {
|
|
118
|
+
type: 'string',
|
|
119
|
+
description: 'Optional workspace ID. Omit to span all workspaces.'
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
name: 'update_action',
|
|
126
|
+
description: 'Update an action: rename, re-prioritise, move project, change status, or set its dates. scheduledStart is the "do date" — when the user plans to work on it — and it is what /today partitions on, taking precedence over dueDate. To move something out of the overdue bucket you must set scheduledStart; changing dueDate alone will not do it.',
|
|
127
|
+
inputSchema: {
|
|
128
|
+
type: 'object',
|
|
129
|
+
properties: {
|
|
130
|
+
id: { type: 'string', description: 'Action ID' },
|
|
131
|
+
name: { type: 'string', description: 'New name' },
|
|
132
|
+
description: { type: 'string', description: 'New description' },
|
|
133
|
+
projectId: { type: 'string', description: 'Move to this project ID' },
|
|
134
|
+
status: {
|
|
135
|
+
type: 'string',
|
|
136
|
+
enum: ['ACTIVE', 'COMPLETED', 'CANCELLED'],
|
|
137
|
+
description: 'New status'
|
|
138
|
+
},
|
|
139
|
+
dueDate: {
|
|
140
|
+
type: 'string',
|
|
141
|
+
description: 'Deadline as an ISO datetime, or null to clear'
|
|
142
|
+
},
|
|
143
|
+
scheduledStart: {
|
|
144
|
+
type: 'string',
|
|
145
|
+
description: 'Do-date as an ISO datetime (e.g. 2026-08-05T09:00:00Z), or null to clear'
|
|
146
|
+
},
|
|
147
|
+
scheduledEnd: {
|
|
148
|
+
type: 'string',
|
|
149
|
+
description: 'End of the time block as an ISO datetime, or null to clear'
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
required: ['id']
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
name: 'defer_actions',
|
|
157
|
+
description: 'Amnesty: clear the dates on these actions so they fall back to their project backlog untimed. Use for work that was never really due on the date it carries — most often a bulk-created cohort from get_overdue_triage. The actions stay ACTIVE and are not deleted or archived; they simply stop counting as overdue. Prefer this over reschedule_actions when the dates were never a real commitment.',
|
|
158
|
+
inputSchema: {
|
|
159
|
+
type: 'object',
|
|
160
|
+
properties: {
|
|
161
|
+
actionIds: {
|
|
162
|
+
type: 'array',
|
|
163
|
+
items: { type: 'string' },
|
|
164
|
+
description: 'Action IDs to defer'
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
required: ['actionIds']
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
name: 'reschedule_actions',
|
|
172
|
+
description: 'Move actions to a new do-date, for work that genuinely is still due, just later. Sets scheduledStart, pushing dueDate forward only where it would otherwise fall before it. If the actions were bulk-created and never individually due, use defer_actions instead — rescheduling them only re-inflicts the same pile tomorrow.',
|
|
173
|
+
inputSchema: {
|
|
174
|
+
type: 'object',
|
|
175
|
+
properties: {
|
|
176
|
+
actionIds: {
|
|
177
|
+
type: 'array',
|
|
178
|
+
items: { type: 'string' },
|
|
179
|
+
description: 'Action IDs to reschedule'
|
|
180
|
+
},
|
|
181
|
+
date: {
|
|
182
|
+
type: 'string',
|
|
183
|
+
description: 'New do-date as an ISO datetime (e.g. 2026-08-05T09:00:00Z)'
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
required: ['actionIds', 'date']
|
|
187
|
+
}
|
|
188
|
+
},
|
|
82
189
|
{
|
|
83
190
|
name: 'create_action',
|
|
84
191
|
description: 'Create a new action/task. Supports natural language with dates and project names.',
|
|
@@ -216,10 +323,34 @@ async function main() {
|
|
|
216
323
|
],
|
|
217
324
|
};
|
|
218
325
|
}
|
|
326
|
+
case 'get_todays_actions': {
|
|
327
|
+
const todays = await client.actions.getTodaysActions(args?.workspaceId);
|
|
328
|
+
return {
|
|
329
|
+
content: [
|
|
330
|
+
{
|
|
331
|
+
type: 'text',
|
|
332
|
+
text: JSON.stringify(todays, null, 2),
|
|
333
|
+
},
|
|
334
|
+
],
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
case 'get_overdue_triage': {
|
|
338
|
+
const triage = await client.actions.getOverdueTriage(args?.workspaceId);
|
|
339
|
+
return {
|
|
340
|
+
content: [
|
|
341
|
+
{
|
|
342
|
+
type: 'text',
|
|
343
|
+
text: JSON.stringify(triage, null, 2),
|
|
344
|
+
},
|
|
345
|
+
],
|
|
346
|
+
};
|
|
347
|
+
}
|
|
219
348
|
case 'create_action': {
|
|
220
|
-
//
|
|
349
|
+
// quickCreate's input field is `name`, not `text` -- it parses natural
|
|
350
|
+
// language out of the name itself (dates, project names) when
|
|
351
|
+
// parseNaturalLanguage is on, which it is by default.
|
|
221
352
|
const action = await trpcClient.action.quickCreate.mutate({
|
|
222
|
-
|
|
353
|
+
name: args?.text,
|
|
223
354
|
});
|
|
224
355
|
return {
|
|
225
356
|
content: [
|
|
@@ -230,9 +361,54 @@ async function main() {
|
|
|
230
361
|
],
|
|
231
362
|
};
|
|
232
363
|
}
|
|
364
|
+
case 'update_action': {
|
|
365
|
+
const action = await client.actions.update({
|
|
366
|
+
id: args?.id,
|
|
367
|
+
name: args?.name,
|
|
368
|
+
description: args?.description,
|
|
369
|
+
projectId: args?.projectId,
|
|
370
|
+
status: args?.status,
|
|
371
|
+
dueDate: parseDateArg(args?.dueDate),
|
|
372
|
+
scheduledStart: parseDateArg(args?.scheduledStart),
|
|
373
|
+
scheduledEnd: parseDateArg(args?.scheduledEnd),
|
|
374
|
+
});
|
|
375
|
+
return {
|
|
376
|
+
content: [
|
|
377
|
+
{
|
|
378
|
+
type: 'text',
|
|
379
|
+
text: `Updated: ${action.name} (ID: ${action.id})`,
|
|
380
|
+
},
|
|
381
|
+
],
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
case 'defer_actions': {
|
|
385
|
+
const result = await client.actions.bulkDefer(args?.actionIds);
|
|
386
|
+
return {
|
|
387
|
+
content: [
|
|
388
|
+
{
|
|
389
|
+
type: 'text',
|
|
390
|
+
text: result.message,
|
|
391
|
+
},
|
|
392
|
+
],
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
case 'reschedule_actions': {
|
|
396
|
+
const when = new Date(args?.date);
|
|
397
|
+
if (isNaN(when.getTime())) {
|
|
398
|
+
throw new Error(`Invalid date "${String(args?.date)}". Use an ISO datetime.`);
|
|
399
|
+
}
|
|
400
|
+
const result = await client.actions.bulkReschedule(args?.actionIds, when);
|
|
401
|
+
return {
|
|
402
|
+
content: [
|
|
403
|
+
{
|
|
404
|
+
type: 'text',
|
|
405
|
+
text: `Rescheduled ${result.count} action${result.count === 1 ? '' : 's'} to ${when.toISOString()}`,
|
|
406
|
+
},
|
|
407
|
+
],
|
|
408
|
+
};
|
|
409
|
+
}
|
|
233
410
|
case 'complete_action': {
|
|
234
|
-
|
|
235
|
-
const action = await trpcClient.action.update.mutate({
|
|
411
|
+
const action = await client.actions.update({
|
|
236
412
|
id: args?.id,
|
|
237
413
|
status: 'COMPLETED',
|
|
238
414
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "exponential-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "MCP server for Exponential - connect Claude to your projects, actions, and goals",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
27
27
|
"commander": "^12.0.0",
|
|
28
|
-
"exponential-sdk": "^1.
|
|
28
|
+
"exponential-sdk": "^1.10.0",
|
|
29
29
|
"zod": "^3.22.0"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
package/dist/api.d.ts
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Exponential API Client
|
|
3
|
-
* Handles authenticated requests to the Exponential API
|
|
4
|
-
*/
|
|
5
|
-
export interface ExponentialConfig {
|
|
6
|
-
apiKey: string;
|
|
7
|
-
baseUrl?: string;
|
|
8
|
-
}
|
|
9
|
-
export interface Project {
|
|
10
|
-
id: string;
|
|
11
|
-
name: string;
|
|
12
|
-
description?: string;
|
|
13
|
-
status: string;
|
|
14
|
-
priority: string;
|
|
15
|
-
progress: number;
|
|
16
|
-
}
|
|
17
|
-
export interface Action {
|
|
18
|
-
id: string;
|
|
19
|
-
name: string;
|
|
20
|
-
description?: string;
|
|
21
|
-
status: string;
|
|
22
|
-
priority: string;
|
|
23
|
-
dueDate?: string;
|
|
24
|
-
projectId?: string;
|
|
25
|
-
project?: {
|
|
26
|
-
name: string;
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
export interface Goal {
|
|
30
|
-
id: number;
|
|
31
|
-
title: string;
|
|
32
|
-
description?: string;
|
|
33
|
-
period?: string;
|
|
34
|
-
keyResults: KeyResult[];
|
|
35
|
-
}
|
|
36
|
-
export interface KeyResult {
|
|
37
|
-
id: string;
|
|
38
|
-
title: string;
|
|
39
|
-
targetValue?: number;
|
|
40
|
-
currentValue?: number;
|
|
41
|
-
status: string;
|
|
42
|
-
}
|
|
43
|
-
export interface Workspace {
|
|
44
|
-
id: string;
|
|
45
|
-
name: string;
|
|
46
|
-
slug: string;
|
|
47
|
-
}
|
|
48
|
-
export declare class ExponentialAPI {
|
|
49
|
-
private apiKey;
|
|
50
|
-
private baseUrl;
|
|
51
|
-
constructor(config: ExponentialConfig);
|
|
52
|
-
private request;
|
|
53
|
-
private trpcQuery;
|
|
54
|
-
private trpcMutation;
|
|
55
|
-
getWorkspaces(): Promise<Workspace[]>;
|
|
56
|
-
getProjects(workspaceId?: string): Promise<Project[]>;
|
|
57
|
-
getProject(id: string): Promise<Project>;
|
|
58
|
-
getActions(params?: {
|
|
59
|
-
projectId?: string;
|
|
60
|
-
status?: string;
|
|
61
|
-
}): Promise<Action[]>;
|
|
62
|
-
createAction(data: {
|
|
63
|
-
name: string;
|
|
64
|
-
description?: string;
|
|
65
|
-
projectId?: string;
|
|
66
|
-
priority?: string;
|
|
67
|
-
dueDate?: string;
|
|
68
|
-
}): Promise<Action>;
|
|
69
|
-
updateAction(id: string, data: {
|
|
70
|
-
name?: string;
|
|
71
|
-
status?: string;
|
|
72
|
-
priority?: string;
|
|
73
|
-
dueDate?: string;
|
|
74
|
-
}): Promise<Action>;
|
|
75
|
-
completeAction(id: string): Promise<Action>;
|
|
76
|
-
getGoals(workspaceId?: string): Promise<Goal[]>;
|
|
77
|
-
getGoal(id: number): Promise<Goal>;
|
|
78
|
-
quickCreateAction(text: string): Promise<Action>;
|
|
79
|
-
search(query: string): Promise<{
|
|
80
|
-
projects: Project[];
|
|
81
|
-
actions: Action[];
|
|
82
|
-
goals: Goal[];
|
|
83
|
-
}>;
|
|
84
|
-
}
|
package/dist/api.js
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Exponential API Client
|
|
3
|
-
* Handles authenticated requests to the Exponential API
|
|
4
|
-
*/
|
|
5
|
-
const DEFAULT_BASE_URL = 'https://www.exponential.im';
|
|
6
|
-
export class ExponentialAPI {
|
|
7
|
-
apiKey;
|
|
8
|
-
baseUrl;
|
|
9
|
-
constructor(config) {
|
|
10
|
-
this.apiKey = config.apiKey;
|
|
11
|
-
this.baseUrl = config.baseUrl || DEFAULT_BASE_URL;
|
|
12
|
-
}
|
|
13
|
-
async request(endpoint, options = {}) {
|
|
14
|
-
const url = `${this.baseUrl}${endpoint}`;
|
|
15
|
-
const response = await fetch(url, {
|
|
16
|
-
...options,
|
|
17
|
-
headers: {
|
|
18
|
-
'Authorization': `Bearer ${this.apiKey}`,
|
|
19
|
-
'Content-Type': 'application/json',
|
|
20
|
-
...options.headers,
|
|
21
|
-
},
|
|
22
|
-
});
|
|
23
|
-
if (!response.ok) {
|
|
24
|
-
const error = await response.text();
|
|
25
|
-
throw new Error(`Exponential API error (${response.status}): ${error}`);
|
|
26
|
-
}
|
|
27
|
-
return response.json();
|
|
28
|
-
}
|
|
29
|
-
// tRPC-style calls
|
|
30
|
-
async trpcQuery(procedure, input) {
|
|
31
|
-
const inputStr = input ? encodeURIComponent(JSON.stringify(input)) : '';
|
|
32
|
-
const url = `/api/trpc/${procedure}${inputStr ? `?input=${inputStr}` : ''}`;
|
|
33
|
-
return this.request(url).then(r => r.result.data);
|
|
34
|
-
}
|
|
35
|
-
async trpcMutation(procedure, input) {
|
|
36
|
-
const url = `/api/trpc/${procedure}`;
|
|
37
|
-
return this.request(url, {
|
|
38
|
-
method: 'POST',
|
|
39
|
-
body: JSON.stringify(input),
|
|
40
|
-
}).then(r => r.result.data);
|
|
41
|
-
}
|
|
42
|
-
// === Workspace ===
|
|
43
|
-
async getWorkspaces() {
|
|
44
|
-
return this.trpcQuery('workspace.list');
|
|
45
|
-
}
|
|
46
|
-
// === Projects ===
|
|
47
|
-
async getProjects(workspaceId) {
|
|
48
|
-
return this.trpcQuery('project.list', { workspaceId });
|
|
49
|
-
}
|
|
50
|
-
async getProject(id) {
|
|
51
|
-
return this.trpcQuery('project.get', { id });
|
|
52
|
-
}
|
|
53
|
-
// === Actions ===
|
|
54
|
-
async getActions(params) {
|
|
55
|
-
return this.trpcQuery('action.list', params);
|
|
56
|
-
}
|
|
57
|
-
async createAction(data) {
|
|
58
|
-
return this.trpcMutation('action.create', data);
|
|
59
|
-
}
|
|
60
|
-
async updateAction(id, data) {
|
|
61
|
-
return this.trpcMutation('action.update', { id, ...data });
|
|
62
|
-
}
|
|
63
|
-
async completeAction(id) {
|
|
64
|
-
return this.updateAction(id, { status: 'COMPLETED' });
|
|
65
|
-
}
|
|
66
|
-
// === Goals (OKRs) ===
|
|
67
|
-
async getGoals(workspaceId) {
|
|
68
|
-
return this.trpcQuery('goal.list', { workspaceId });
|
|
69
|
-
}
|
|
70
|
-
async getGoal(id) {
|
|
71
|
-
return this.trpcQuery('goal.get', { id });
|
|
72
|
-
}
|
|
73
|
-
// === Quick Actions ===
|
|
74
|
-
async quickCreateAction(text) {
|
|
75
|
-
// Uses natural language parsing (dates, project names, etc.)
|
|
76
|
-
return this.trpcMutation('action.quickCreate', { text });
|
|
77
|
-
}
|
|
78
|
-
// === Search ===
|
|
79
|
-
async search(query) {
|
|
80
|
-
return this.trpcQuery('search.global', { query });
|
|
81
|
-
}
|
|
82
|
-
}
|