exponential-mcp 0.4.0 → 0.5.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/dist/cli.js CHANGED
File without changes
package/dist/index.js CHANGED
@@ -260,13 +260,54 @@ const TOOLS = [
260
260
  },
261
261
  {
262
262
  name: 'get_goals',
263
- description: 'List OKRs (Objectives and Key Results) with their progress',
263
+ description: "List objectives (goals). Each objective carries its linked projects, which is how work ladders up to an OKR: every action has a projectId. Objectives have INTEGER ids — key results, which are separate, have CUIDs (see get_key_results). Use tree: true for the annual → quarterly cascade.",
264
264
  inputSchema: {
265
265
  type: 'object',
266
266
  properties: {
267
267
  workspaceId: {
268
268
  type: 'string',
269
- description: 'Optional workspace ID'
269
+ description: 'Optional workspace ID. With it the list is workspace-wide (every member\'s objectives); without it you get your own.'
270
+ },
271
+ period: {
272
+ type: 'string',
273
+ description: 'Optional period filter, e.g. "Q3-2026" or "Annual-2026"'
274
+ },
275
+ status: {
276
+ type: 'string',
277
+ description: 'Optional status: planned, active, completed, archived, on-hold'
278
+ },
279
+ tree: {
280
+ type: 'boolean',
281
+ description: 'Return objectives nested parent → child (up to 5 levels), each with its projects and key results, instead of a flat list'
282
+ }
283
+ }
284
+ }
285
+ },
286
+ {
287
+ name: 'get_key_results',
288
+ description: "List key results — the measurable half of an OKR. By default groups them under their objectives (the richest read: one call for 'how is the quarter going'). Pass flat: true for a bare list. Key result ids are CUIDs; the goalId tying one to its objective is an integer.",
289
+ inputSchema: {
290
+ type: 'object',
291
+ properties: {
292
+ workspaceId: {
293
+ type: 'string',
294
+ description: "Optional workspace ID. With it the list is workspace-wide (every member's key results); without it you get your own."
295
+ },
296
+ period: {
297
+ type: 'string',
298
+ description: 'Optional period filter, e.g. "Q3-2026"'
299
+ },
300
+ goalId: {
301
+ type: 'number',
302
+ description: 'Optional objective ID (an integer). Only applies with flat: true.'
303
+ },
304
+ status: {
305
+ type: 'string',
306
+ description: 'Optional status: not-started, on-track, at-risk, off-track, achieved. Only applies with flat: true.'
307
+ },
308
+ flat: {
309
+ type: 'boolean',
310
+ description: 'Return a flat list of key results rather than grouping them by objective'
270
311
  }
271
312
  }
272
313
  }
@@ -465,10 +506,17 @@ async function main() {
465
506
  ],
466
507
  };
467
508
  }
509
+ // Goes through the SDK's GoalsApi rather than a raw tRPC path. The old
510
+ // implementation called `goal.list`, which does not exist — the tool
511
+ // hard-errored with `No procedure found on path "goal.list"` on every
512
+ // invocation.
468
513
  case 'get_goals': {
469
- const goals = await trpcClient.goal.list.query({
470
- workspaceId: args?.workspaceId,
471
- });
514
+ const workspaceId = args?.workspaceId;
515
+ const period = args?.period;
516
+ const status = args?.status;
517
+ const goals = args?.tree
518
+ ? await client.goals.tree({ workspaceId, status })
519
+ : await client.goals.list({ workspaceId, period, status });
472
520
  return {
473
521
  content: [
474
522
  {
@@ -478,6 +526,26 @@ async function main() {
478
526
  ],
479
527
  };
480
528
  }
529
+ case 'get_key_results': {
530
+ const workspaceId = args?.workspaceId;
531
+ const period = args?.period;
532
+ const keyResults = args?.flat
533
+ ? await client.goals.keyResults.list({
534
+ workspaceId,
535
+ period,
536
+ goalId: args?.goalId,
537
+ status: args?.status,
538
+ })
539
+ : await client.goals.keyResults.byObjective({ workspaceId, period });
540
+ return {
541
+ content: [
542
+ {
543
+ type: 'text',
544
+ text: JSON.stringify(keyResults, null, 2),
545
+ },
546
+ ],
547
+ };
548
+ }
481
549
  case 'search': {
482
550
  const results = await trpcClient.search.global.query({
483
551
  query: args?.query,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "exponential-mcp",
3
- "version": "0.4.0",
3
+ "version": "0.5.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": {
@@ -26,7 +26,7 @@
26
26
  "dependencies": {
27
27
  "@modelcontextprotocol/sdk": "^1.30.0",
28
28
  "commander": "^12.0.0",
29
- "exponential-sdk": "^1.11.0",
29
+ "exponential-sdk": "^1.12.0",
30
30
  "zod": "^3.22.0"
31
31
  },
32
32
  "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
- }