@nimara-app/mcp 0.7.0 → 0.8.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/index.js +180 -95
- package/dist/index.js.map +2 -2
- package/package.json +5 -3
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
9
9
|
// package.json
|
|
10
10
|
var package_default = {
|
|
11
11
|
name: "@nimara-app/mcp",
|
|
12
|
-
version: "0.
|
|
12
|
+
version: "0.8.0",
|
|
13
13
|
private: false,
|
|
14
14
|
mcpName: "io.github.langskip-studios/nimara",
|
|
15
15
|
description: "Nimara MCP server \u2014 AI tool integration for Claude, Codex, and others",
|
|
@@ -20,7 +20,8 @@ var package_default = {
|
|
|
20
20
|
"nimara-mcp": "dist/index.js"
|
|
21
21
|
},
|
|
22
22
|
files: [
|
|
23
|
-
"dist",
|
|
23
|
+
"dist/index.js",
|
|
24
|
+
"dist/index.js.map",
|
|
24
25
|
"README.md"
|
|
25
26
|
],
|
|
26
27
|
keywords: [
|
|
@@ -45,7 +46,8 @@ var package_default = {
|
|
|
45
46
|
dev: "node build.mjs",
|
|
46
47
|
start: "node dist/index.js",
|
|
47
48
|
typecheck: "tsc --noEmit",
|
|
48
|
-
prepublishOnly: "npm run typecheck && npm run build"
|
|
49
|
+
prepublishOnly: "npm run typecheck && npm run build",
|
|
50
|
+
test: "vitest run"
|
|
49
51
|
},
|
|
50
52
|
dependencies: {
|
|
51
53
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
@@ -53,10 +55,15 @@ var package_default = {
|
|
|
53
55
|
zod: "^3.23.0"
|
|
54
56
|
},
|
|
55
57
|
devDependencies: {
|
|
58
|
+
"@clerk/hono": "^0.1.79",
|
|
59
|
+
"@clerk/mcp-tools": "^0.6.0",
|
|
56
60
|
"@nimara/convex": "workspace:*",
|
|
57
61
|
"@types/node": "^20.0.0",
|
|
58
62
|
esbuild: "^0.23.0",
|
|
59
|
-
|
|
63
|
+
hono: "^4.13.8",
|
|
64
|
+
typescript: "^5.0.0",
|
|
65
|
+
vitest: "^2.1.9",
|
|
66
|
+
wrangler: "4.132.0"
|
|
60
67
|
},
|
|
61
68
|
engines: {
|
|
62
69
|
node: ">=20"
|
|
@@ -78,9 +85,29 @@ People are userIds from list_project_members: mention one in a comment as [@Name
|
|
|
78
85
|
|
|
79
86
|
// src/convex.ts
|
|
80
87
|
import { ConvexHttpClient } from "convex/browser";
|
|
88
|
+
import { ConvexError } from "convex/values";
|
|
81
89
|
var cachedClient = null;
|
|
90
|
+
function describeConvexError(err) {
|
|
91
|
+
if (err instanceof ConvexError) {
|
|
92
|
+
const data = err.data;
|
|
93
|
+
if (typeof data === "string" && data.trim()) return data;
|
|
94
|
+
if (typeof data === "object" && data !== null) {
|
|
95
|
+
const { code, message } = data;
|
|
96
|
+
if ((code === "user" || code === "forbidden") && typeof message === "string" && message.trim()) {
|
|
97
|
+
return message;
|
|
98
|
+
}
|
|
99
|
+
if (code === "unauthenticated") return "This token is not valid. Generate a new one in Nimara \u2192 Settings \u2192 Personal Access Tokens.";
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const raw = err instanceof Error ? err.message : String(err);
|
|
103
|
+
const requestId = /\[Request ID: ([^\]]+)\]/.exec(raw)?.[1];
|
|
104
|
+
if (requestId || err instanceof ConvexError) {
|
|
105
|
+
return requestId ? `Nimara could not complete that call (request ${requestId}). Check the arguments and try again.` : "Nimara could not complete that call. Check the arguments and try again.";
|
|
106
|
+
}
|
|
107
|
+
return `Nimara could not reach its backend: ${raw || "unknown transport error"}. Nothing is wrong with the arguments; check the server's NIMARA_CONVEX_URL and try again.`;
|
|
108
|
+
}
|
|
82
109
|
function getConvexClient() {
|
|
83
|
-
if (cachedClient) return cachedClient;
|
|
110
|
+
if (cachedClient) return wrap(cachedClient);
|
|
84
111
|
const url = process.env.NIMARA_CONVEX_URL;
|
|
85
112
|
if (!url) {
|
|
86
113
|
throw new Error(
|
|
@@ -88,9 +115,22 @@ function getConvexClient() {
|
|
|
88
115
|
);
|
|
89
116
|
}
|
|
90
117
|
cachedClient = new ConvexHttpClient(url);
|
|
91
|
-
return cachedClient;
|
|
118
|
+
return wrap(cachedClient);
|
|
92
119
|
}
|
|
93
|
-
function
|
|
120
|
+
function wrap(client) {
|
|
121
|
+
return {
|
|
122
|
+
action: async (ref, args) => {
|
|
123
|
+
try {
|
|
124
|
+
return await client.action(ref, args);
|
|
125
|
+
} catch (err) {
|
|
126
|
+
throw new Error(describeConvexError(err));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
function getToken(extra) {
|
|
132
|
+
const fromRequest = extra?.authInfo?.token;
|
|
133
|
+
if (fromRequest) return fromRequest;
|
|
94
134
|
const token = process.env.NIMARA_TOKEN;
|
|
95
135
|
if (!token) {
|
|
96
136
|
throw new Error(
|
|
@@ -122,13 +162,14 @@ function registerListOrgs(server) {
|
|
|
122
162
|
description: "List all organizations (and the personal workspace) the authenticated user belongs to. Use this first to discover org IDs needed by other tools.",
|
|
123
163
|
inputSchema: {},
|
|
124
164
|
annotations: {
|
|
165
|
+
title: "List organizations",
|
|
125
166
|
readOnlyHint: true,
|
|
126
167
|
openWorldHint: false
|
|
127
168
|
}
|
|
128
169
|
},
|
|
129
|
-
async () => {
|
|
170
|
+
async (_args, extra) => {
|
|
130
171
|
const client = getConvexClient();
|
|
131
|
-
const orgs = await client.action(api.mcp.listOrgs, { token: getToken() });
|
|
172
|
+
const orgs = await client.action(api.mcp.listOrgs, { token: getToken(extra) });
|
|
132
173
|
return {
|
|
133
174
|
content: [{ type: "text", text: JSON.stringify(orgs, null, 2) }]
|
|
134
175
|
};
|
|
@@ -145,14 +186,15 @@ function registerListProjects(server) {
|
|
|
145
186
|
description: "List projects in a given organization. Call list_orgs first to find the orgId. Each project includes `statuses` \u2014 its workflow in board order. Read that before setting a status on any item: those names are the only accepted values, and anything else is rejected.",
|
|
146
187
|
inputSchema: { orgId: z.string().describe("orgId from list_orgs") },
|
|
147
188
|
annotations: {
|
|
189
|
+
title: "List projects",
|
|
148
190
|
readOnlyHint: true,
|
|
149
191
|
openWorldHint: false
|
|
150
192
|
}
|
|
151
193
|
},
|
|
152
|
-
async ({ orgId }) => {
|
|
194
|
+
async ({ orgId }, extra) => {
|
|
153
195
|
const client = getConvexClient();
|
|
154
196
|
const projects = await client.action(api.mcp.listProjects, {
|
|
155
|
-
token: getToken(),
|
|
197
|
+
token: getToken(extra),
|
|
156
198
|
orgId
|
|
157
199
|
});
|
|
158
200
|
return {
|
|
@@ -195,6 +237,7 @@ function registerListWorkItems(server) {
|
|
|
195
237
|
)
|
|
196
238
|
},
|
|
197
239
|
annotations: {
|
|
240
|
+
title: "List work items",
|
|
198
241
|
readOnlyHint: true,
|
|
199
242
|
openWorldHint: false
|
|
200
243
|
}
|
|
@@ -208,9 +251,9 @@ function registerListWorkItems(server) {
|
|
|
208
251
|
type,
|
|
209
252
|
parentId,
|
|
210
253
|
needsReview
|
|
211
|
-
}) => {
|
|
254
|
+
}, extra) => {
|
|
212
255
|
const page = await getConvexClient().action(api.mcp.listWorkItems, {
|
|
213
|
-
token: getToken(),
|
|
256
|
+
token: getToken(extra),
|
|
214
257
|
projectId,
|
|
215
258
|
limit,
|
|
216
259
|
cursor,
|
|
@@ -251,13 +294,14 @@ function registerGetWorkItems(server) {
|
|
|
251
294
|
)
|
|
252
295
|
},
|
|
253
296
|
annotations: {
|
|
297
|
+
title: "Get work items by id",
|
|
254
298
|
readOnlyHint: true,
|
|
255
299
|
openWorldHint: false
|
|
256
300
|
}
|
|
257
301
|
},
|
|
258
|
-
async ({ displayIds, workItemIds, projectId }) => {
|
|
302
|
+
async ({ displayIds, workItemIds, projectId }, extra) => {
|
|
259
303
|
const result = await getConvexClient().action(api.mcp.getWorkItems, {
|
|
260
|
-
token: getToken(),
|
|
304
|
+
token: getToken(extra),
|
|
261
305
|
displayIds,
|
|
262
306
|
workItemIds,
|
|
263
307
|
projectId
|
|
@@ -287,11 +331,14 @@ function registerSearchWorkItems(server) {
|
|
|
287
331
|
),
|
|
288
332
|
limit: z4.number().int().min(1).max(25).optional().describe("Maximum results (default and maximum 25).")
|
|
289
333
|
},
|
|
290
|
-
annotations: {
|
|
334
|
+
annotations: {
|
|
335
|
+
title: "Search work items",
|
|
336
|
+
readOnlyHint: true
|
|
337
|
+
}
|
|
291
338
|
},
|
|
292
|
-
async ({ projectId, q, limit }) => {
|
|
339
|
+
async ({ projectId, q, limit }, extra) => {
|
|
293
340
|
const result = await getConvexClient().action(api.mcp.searchWorkItems, {
|
|
294
|
-
token: getToken(),
|
|
341
|
+
token: getToken(extra),
|
|
295
342
|
projectId,
|
|
296
343
|
q,
|
|
297
344
|
limit
|
|
@@ -318,13 +365,14 @@ function registerListAiReviewQueue(server) {
|
|
|
318
365
|
)
|
|
319
366
|
},
|
|
320
367
|
annotations: {
|
|
368
|
+
title: "List the AI review queue",
|
|
321
369
|
readOnlyHint: true,
|
|
322
370
|
openWorldHint: false
|
|
323
371
|
}
|
|
324
372
|
},
|
|
325
|
-
async ({ projectId, limit, cursor }) => {
|
|
373
|
+
async ({ projectId, limit, cursor }, extra) => {
|
|
326
374
|
const page = await getConvexClient().action(api.mcp.listAiReviewQueue, {
|
|
327
|
-
token: getToken(),
|
|
375
|
+
token: getToken(extra),
|
|
328
376
|
projectId,
|
|
329
377
|
limit,
|
|
330
378
|
cursor
|
|
@@ -347,13 +395,14 @@ function registerGetActiveBlock(server) {
|
|
|
347
395
|
projectId: z6.string().describe("projectId from list_projects")
|
|
348
396
|
},
|
|
349
397
|
annotations: {
|
|
398
|
+
title: "Get the active block",
|
|
350
399
|
readOnlyHint: true,
|
|
351
400
|
openWorldHint: false
|
|
352
401
|
}
|
|
353
402
|
},
|
|
354
|
-
async ({ projectId }) => {
|
|
403
|
+
async ({ projectId }, extra) => {
|
|
355
404
|
const block = await getConvexClient().action(api.mcp.getActiveBlock, {
|
|
356
|
-
token: getToken(),
|
|
405
|
+
token: getToken(extra),
|
|
357
406
|
projectId
|
|
358
407
|
});
|
|
359
408
|
return {
|
|
@@ -400,16 +449,17 @@ function registerCreateWorkItem(server) {
|
|
|
400
449
|
priority: z7.enum(["p0", "p1", "p2", "p3"]).optional().describe("p0 highest, p3 lowest. Defaults to p2.")
|
|
401
450
|
},
|
|
402
451
|
annotations: {
|
|
452
|
+
title: "Create a work item",
|
|
403
453
|
readOnlyHint: false,
|
|
404
454
|
destructiveHint: false,
|
|
405
455
|
idempotentHint: false,
|
|
406
456
|
openWorldHint: false
|
|
407
457
|
}
|
|
408
458
|
},
|
|
409
|
-
async (args) => {
|
|
459
|
+
async (args, extra) => {
|
|
410
460
|
const client = getConvexClient();
|
|
411
461
|
const result = await client.action(api.mcp.createWorkItem, {
|
|
412
|
-
token: getToken(),
|
|
462
|
+
token: getToken(extra),
|
|
413
463
|
...args
|
|
414
464
|
});
|
|
415
465
|
return {
|
|
@@ -432,7 +482,9 @@ function registerCreateProject(server) {
|
|
|
432
482
|
prefix: z8.string().min(1).max(8).describe(
|
|
433
483
|
"Short uppercase prefix for work item IDs (e.g. 'NIM'). Unique per org."
|
|
434
484
|
),
|
|
435
|
-
description: z8.string().optional().describe(
|
|
485
|
+
description: z8.string().max(280).optional().describe(
|
|
486
|
+
"Optional, and usually best left out. At most one or two plain sentences (280 characters) saying what the project is for \u2014 it shows behind an info button on the project page, so headings, stack notes, decisions and lineage do not belong here. Put that kind of context in a document (create_document) or the project's README instead."
|
|
487
|
+
),
|
|
436
488
|
isPrivate: z8.boolean().optional().describe(
|
|
437
489
|
"If true, only explicitly added members see it. Defaults to false."
|
|
438
490
|
),
|
|
@@ -441,16 +493,17 @@ function registerCreateProject(server) {
|
|
|
441
493
|
)
|
|
442
494
|
},
|
|
443
495
|
annotations: {
|
|
496
|
+
title: "Create a project",
|
|
444
497
|
readOnlyHint: false,
|
|
445
498
|
destructiveHint: false,
|
|
446
499
|
idempotentHint: false,
|
|
447
500
|
openWorldHint: false
|
|
448
501
|
}
|
|
449
502
|
},
|
|
450
|
-
async (args) => {
|
|
503
|
+
async (args, extra) => {
|
|
451
504
|
const client = getConvexClient();
|
|
452
505
|
const result = await client.action(api.mcp.createProject, {
|
|
453
|
-
token: getToken(),
|
|
506
|
+
token: getToken(extra),
|
|
454
507
|
...args
|
|
455
508
|
});
|
|
456
509
|
return {
|
|
@@ -526,15 +579,16 @@ function registerUpdateWorkItem(server) {
|
|
|
526
579
|
model: z9.string().optional().describe("With agentWorking: the model you are running as, e.g. claude-opus-5, so the app can say which agent is on this. Your harness is reported automatically.")
|
|
527
580
|
},
|
|
528
581
|
annotations: {
|
|
582
|
+
title: "Update a work item",
|
|
529
583
|
readOnlyHint: false,
|
|
530
584
|
destructiveHint: true,
|
|
531
585
|
idempotentHint: true,
|
|
532
586
|
openWorldHint: false
|
|
533
587
|
}
|
|
534
588
|
},
|
|
535
|
-
async ({ model, ...args }) => {
|
|
589
|
+
async ({ model, ...args }, extra) => {
|
|
536
590
|
const result = await getConvexClient().action(api.mcp.updateWorkItem, {
|
|
537
|
-
token: getToken(),
|
|
591
|
+
token: getToken(extra),
|
|
538
592
|
...args,
|
|
539
593
|
agent: agentArg(model)
|
|
540
594
|
});
|
|
@@ -563,6 +617,7 @@ function registerMoveWorkItem(server) {
|
|
|
563
617
|
)
|
|
564
618
|
},
|
|
565
619
|
annotations: {
|
|
620
|
+
title: "Move a work item to another project",
|
|
566
621
|
readOnlyHint: false,
|
|
567
622
|
// Renumbering display IDs and deleting label/milestone links are
|
|
568
623
|
// overwrites, not additions — this belongs with the tools a client
|
|
@@ -574,10 +629,10 @@ function registerMoveWorkItem(server) {
|
|
|
574
629
|
openWorldHint: false
|
|
575
630
|
}
|
|
576
631
|
},
|
|
577
|
-
async ({ workItemId, targetProjectId }) => {
|
|
632
|
+
async ({ workItemId, targetProjectId }, extra) => {
|
|
578
633
|
const result = await getConvexClient().action(
|
|
579
634
|
api.mcp.moveWorkItemToProject,
|
|
580
|
-
{ token: getToken(), workItemId, targetProjectId }
|
|
635
|
+
{ token: getToken(extra), workItemId, targetProjectId }
|
|
581
636
|
);
|
|
582
637
|
return {
|
|
583
638
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
@@ -592,26 +647,27 @@ function registerCreateDocument(server) {
|
|
|
592
647
|
server.registerTool(
|
|
593
648
|
"create_document",
|
|
594
649
|
{
|
|
595
|
-
description: "Create a markdown project
|
|
650
|
+
description: "Create a markdown document in a project: a spec, a design, a report, research. Pass workItemId to link it to the item it is about. Leave type alone unless the document is THE project brief.",
|
|
596
651
|
inputSchema: {
|
|
597
652
|
projectId: z11.string().describe("projectId from list_projects"),
|
|
598
653
|
title: z11.string().min(1),
|
|
599
|
-
type: z11.enum(["prd", "fd"]).describe(
|
|
600
|
-
"
|
|
654
|
+
type: z11.enum(["note", "prd", "fd"]).optional().describe(
|
|
655
|
+
"Defaults to 'note', which is right for almost everything. 'prd' is reserved for the project's own product requirements brief, one per project, never linked to a work item. 'fd' is an older spelling of 'note' and still accepted."
|
|
601
656
|
),
|
|
602
|
-
workItemId: z11.string().optional().describe("
|
|
657
|
+
workItemId: z11.string().optional().describe("The work item this document is about, so it shows on that item. Not allowed with type 'prd'."),
|
|
603
658
|
content: z11.string().optional().describe("Optional initial markdown content.")
|
|
604
659
|
},
|
|
605
660
|
annotations: {
|
|
661
|
+
title: "Create a document",
|
|
606
662
|
readOnlyHint: false,
|
|
607
663
|
destructiveHint: false,
|
|
608
664
|
idempotentHint: false,
|
|
609
665
|
openWorldHint: false
|
|
610
666
|
}
|
|
611
667
|
},
|
|
612
|
-
async (args) => {
|
|
668
|
+
async (args, extra) => {
|
|
613
669
|
const result = await getConvexClient().action(api.mcp.createDocument, {
|
|
614
|
-
token: getToken(),
|
|
670
|
+
token: getToken(extra),
|
|
615
671
|
...args
|
|
616
672
|
});
|
|
617
673
|
return {
|
|
@@ -636,15 +692,16 @@ function registerUpdateDocument(server) {
|
|
|
636
692
|
title: z12.string().min(1).optional().describe("New title (optional).")
|
|
637
693
|
},
|
|
638
694
|
annotations: {
|
|
695
|
+
title: "Update a document",
|
|
639
696
|
readOnlyHint: false,
|
|
640
697
|
destructiveHint: true,
|
|
641
698
|
idempotentHint: true,
|
|
642
699
|
openWorldHint: false
|
|
643
700
|
}
|
|
644
701
|
},
|
|
645
|
-
async (args) => {
|
|
702
|
+
async (args, extra) => {
|
|
646
703
|
const result = await getConvexClient().action(api.mcp.updateDocument, {
|
|
647
|
-
token: getToken(),
|
|
704
|
+
token: getToken(extra),
|
|
648
705
|
...args
|
|
649
706
|
});
|
|
650
707
|
return {
|
|
@@ -666,13 +723,14 @@ function registerListDocuments(server) {
|
|
|
666
723
|
workItemId: z13.string().optional().describe("Optional workItemId to list linked FD documents.")
|
|
667
724
|
},
|
|
668
725
|
annotations: {
|
|
726
|
+
title: "List documents",
|
|
669
727
|
readOnlyHint: true,
|
|
670
728
|
openWorldHint: false
|
|
671
729
|
}
|
|
672
730
|
},
|
|
673
|
-
async (args) => {
|
|
731
|
+
async (args, extra) => {
|
|
674
732
|
const result = await getConvexClient().action(api.mcp.listDocuments, {
|
|
675
|
-
token: getToken(),
|
|
733
|
+
token: getToken(extra),
|
|
676
734
|
...args
|
|
677
735
|
});
|
|
678
736
|
return {
|
|
@@ -705,15 +763,16 @@ function registerCreateProjectLink(server) {
|
|
|
705
763
|
category: projectLinkCategory.optional().describe("Defaults to other.")
|
|
706
764
|
},
|
|
707
765
|
annotations: {
|
|
766
|
+
title: "Add a project link",
|
|
708
767
|
readOnlyHint: false,
|
|
709
768
|
destructiveHint: false,
|
|
710
769
|
idempotentHint: false,
|
|
711
770
|
openWorldHint: false
|
|
712
771
|
}
|
|
713
772
|
},
|
|
714
|
-
async (args) => {
|
|
773
|
+
async (args, extra) => {
|
|
715
774
|
const result = await getConvexClient().action(api.mcp.createProjectLink, {
|
|
716
|
-
token: getToken(),
|
|
775
|
+
token: getToken(extra),
|
|
717
776
|
...args
|
|
718
777
|
});
|
|
719
778
|
return {
|
|
@@ -734,13 +793,14 @@ function registerListProjectLinks(server) {
|
|
|
734
793
|
projectId: z15.string().describe("projectId from list_projects")
|
|
735
794
|
},
|
|
736
795
|
annotations: {
|
|
796
|
+
title: "List project links",
|
|
737
797
|
readOnlyHint: true,
|
|
738
798
|
openWorldHint: false
|
|
739
799
|
}
|
|
740
800
|
},
|
|
741
|
-
async ({ projectId }) => {
|
|
801
|
+
async ({ projectId }, extra) => {
|
|
742
802
|
const result = await getConvexClient().action(api.mcp.listProjectLinks, {
|
|
743
|
-
token: getToken(),
|
|
803
|
+
token: getToken(extra),
|
|
744
804
|
projectId
|
|
745
805
|
});
|
|
746
806
|
return {
|
|
@@ -761,13 +821,14 @@ function registerListProjectMembers(server) {
|
|
|
761
821
|
projectId: z16.string().describe("projectId from list_projects")
|
|
762
822
|
},
|
|
763
823
|
annotations: {
|
|
824
|
+
title: "List project members",
|
|
764
825
|
readOnlyHint: true,
|
|
765
826
|
openWorldHint: false
|
|
766
827
|
}
|
|
767
828
|
},
|
|
768
|
-
async ({ projectId }) => {
|
|
829
|
+
async ({ projectId }, extra) => {
|
|
769
830
|
const result = await getConvexClient().action(api.mcp.listProjectMembers, {
|
|
770
|
-
token: getToken(),
|
|
831
|
+
token: getToken(extra),
|
|
771
832
|
projectId
|
|
772
833
|
});
|
|
773
834
|
return {
|
|
@@ -789,15 +850,16 @@ function registerUnlinkPr(server) {
|
|
|
789
850
|
prNumber: z17.number().int().positive().describe("The PR number, e.g. 354 for #354")
|
|
790
851
|
},
|
|
791
852
|
annotations: {
|
|
853
|
+
title: "Unlink a pull request",
|
|
792
854
|
readOnlyHint: false,
|
|
793
855
|
destructiveHint: true,
|
|
794
856
|
idempotentHint: true,
|
|
795
857
|
openWorldHint: false
|
|
796
858
|
}
|
|
797
859
|
},
|
|
798
|
-
async ({ workItemId, prNumber }) => {
|
|
860
|
+
async ({ workItemId, prNumber }, extra) => {
|
|
799
861
|
const result = await getConvexClient().action(api.mcp.unlinkPR, {
|
|
800
|
-
token: getToken(),
|
|
862
|
+
token: getToken(extra),
|
|
801
863
|
workItemId,
|
|
802
864
|
prNumber
|
|
803
865
|
});
|
|
@@ -819,13 +881,14 @@ function registerListScratchNotes(server) {
|
|
|
819
881
|
projectId: z18.string().describe("projectId from list_projects")
|
|
820
882
|
},
|
|
821
883
|
annotations: {
|
|
884
|
+
title: "List scratch notes",
|
|
822
885
|
readOnlyHint: true,
|
|
823
886
|
openWorldHint: false
|
|
824
887
|
}
|
|
825
888
|
},
|
|
826
|
-
async ({ projectId }) => {
|
|
889
|
+
async ({ projectId }, extra) => {
|
|
827
890
|
const notes = await getConvexClient().action(api.mcp.listScratchNotes, {
|
|
828
|
-
token: getToken(),
|
|
891
|
+
token: getToken(extra),
|
|
829
892
|
projectId
|
|
830
893
|
});
|
|
831
894
|
return {
|
|
@@ -847,15 +910,16 @@ function registerResolveScratchNote(server) {
|
|
|
847
910
|
workItemId: z19.string().optional().describe("The workItemId the note became, from create_work_item or list_work_items.")
|
|
848
911
|
},
|
|
849
912
|
annotations: {
|
|
913
|
+
title: "Resolve a scratch note",
|
|
850
914
|
readOnlyHint: false,
|
|
851
915
|
destructiveHint: false,
|
|
852
916
|
idempotentHint: true,
|
|
853
917
|
openWorldHint: false
|
|
854
918
|
}
|
|
855
919
|
},
|
|
856
|
-
async ({ noteId, workItemId }) => {
|
|
920
|
+
async ({ noteId, workItemId }, extra) => {
|
|
857
921
|
const result = await getConvexClient().action(api.mcp.resolveScratchNote, {
|
|
858
|
-
token: getToken(),
|
|
922
|
+
token: getToken(extra),
|
|
859
923
|
noteId,
|
|
860
924
|
workItemId
|
|
861
925
|
});
|
|
@@ -878,15 +942,16 @@ function registerHeartbeatWorkItem(server) {
|
|
|
878
942
|
model: z20.string().optional().describe("The model you are running as, e.g. claude-opus-5 or gpt-5, so the app can say which agent is on this. Your harness is reported automatically.")
|
|
879
943
|
},
|
|
880
944
|
annotations: {
|
|
945
|
+
title: "Keep an agent lease alive",
|
|
881
946
|
readOnlyHint: false,
|
|
882
947
|
destructiveHint: false,
|
|
883
948
|
idempotentHint: true,
|
|
884
949
|
openWorldHint: false
|
|
885
950
|
}
|
|
886
951
|
},
|
|
887
|
-
async ({ workItemId, model }) => {
|
|
952
|
+
async ({ workItemId, model }, extra) => {
|
|
888
953
|
const result = await getConvexClient().action(api.mcp.heartbeatWorkItem, {
|
|
889
|
-
token: getToken(),
|
|
954
|
+
token: getToken(extra),
|
|
890
955
|
workItemId,
|
|
891
956
|
agent: agentArg(model)
|
|
892
957
|
});
|
|
@@ -909,15 +974,16 @@ function registerWatchWorkItem(server) {
|
|
|
909
974
|
watching: z21.boolean().default(true).describe("true to watch, false to stop")
|
|
910
975
|
},
|
|
911
976
|
annotations: {
|
|
977
|
+
title: "Watch a work item",
|
|
912
978
|
readOnlyHint: false,
|
|
913
979
|
destructiveHint: false,
|
|
914
980
|
idempotentHint: true,
|
|
915
981
|
openWorldHint: false
|
|
916
982
|
}
|
|
917
983
|
},
|
|
918
|
-
async ({ workItemId, watching }) => {
|
|
984
|
+
async ({ workItemId, watching }, extra) => {
|
|
919
985
|
const result = await getConvexClient().action(api.mcp.watchWorkItem, {
|
|
920
|
-
token: getToken(),
|
|
986
|
+
token: getToken(extra),
|
|
921
987
|
workItemId,
|
|
922
988
|
watching
|
|
923
989
|
});
|
|
@@ -942,15 +1008,16 @@ function registerAddWorkItemImage(server) {
|
|
|
942
1008
|
altText: z22.string().optional().describe("Optional description/caption for the image")
|
|
943
1009
|
},
|
|
944
1010
|
annotations: {
|
|
1011
|
+
title: "Attach an image to a work item",
|
|
945
1012
|
readOnlyHint: false,
|
|
946
1013
|
destructiveHint: false,
|
|
947
1014
|
idempotentHint: false,
|
|
948
1015
|
openWorldHint: false
|
|
949
1016
|
}
|
|
950
1017
|
},
|
|
951
|
-
async (args) => {
|
|
1018
|
+
async (args, extra) => {
|
|
952
1019
|
const result = await getConvexClient().action(api.mcp.addWorkItemImage, {
|
|
953
|
-
token: getToken(),
|
|
1020
|
+
token: getToken(extra),
|
|
954
1021
|
...args
|
|
955
1022
|
});
|
|
956
1023
|
return {
|
|
@@ -971,15 +1038,16 @@ function registerListWorkItemImages(server) {
|
|
|
971
1038
|
workItemId: z23.string().describe("workItemId from list_work_items")
|
|
972
1039
|
},
|
|
973
1040
|
annotations: {
|
|
1041
|
+
title: "List work item images",
|
|
974
1042
|
readOnlyHint: true,
|
|
975
1043
|
openWorldHint: false
|
|
976
1044
|
}
|
|
977
1045
|
},
|
|
978
|
-
async ({ workItemId }) => {
|
|
1046
|
+
async ({ workItemId }, extra) => {
|
|
979
1047
|
const result = await getConvexClient().action(
|
|
980
1048
|
api.mcp.listWorkItemImages,
|
|
981
1049
|
{
|
|
982
|
-
token: getToken(),
|
|
1050
|
+
token: getToken(extra),
|
|
983
1051
|
workItemId
|
|
984
1052
|
}
|
|
985
1053
|
);
|
|
@@ -1005,17 +1073,18 @@ function registerAddWorkItemComment(server) {
|
|
|
1005
1073
|
source: z24.enum(["user", "ai"]).optional().describe('Who authored it; defaults to "ai" for MCP callers')
|
|
1006
1074
|
},
|
|
1007
1075
|
annotations: {
|
|
1076
|
+
title: "Comment on a work item",
|
|
1008
1077
|
readOnlyHint: false,
|
|
1009
1078
|
destructiveHint: false,
|
|
1010
1079
|
idempotentHint: false,
|
|
1011
1080
|
openWorldHint: false
|
|
1012
1081
|
}
|
|
1013
1082
|
},
|
|
1014
|
-
async (args) => {
|
|
1083
|
+
async (args, extra) => {
|
|
1015
1084
|
const result = await getConvexClient().action(
|
|
1016
1085
|
api.mcp.addWorkItemComment,
|
|
1017
1086
|
{
|
|
1018
|
-
token: getToken(),
|
|
1087
|
+
token: getToken(extra),
|
|
1019
1088
|
...args,
|
|
1020
1089
|
agent: agentArg()
|
|
1021
1090
|
}
|
|
@@ -1038,15 +1107,16 @@ function registerListWorkItemComments(server) {
|
|
|
1038
1107
|
workItemId: z25.string().describe("workItemId from list_work_items")
|
|
1039
1108
|
},
|
|
1040
1109
|
annotations: {
|
|
1110
|
+
title: "List work item comments",
|
|
1041
1111
|
readOnlyHint: true,
|
|
1042
1112
|
openWorldHint: false
|
|
1043
1113
|
}
|
|
1044
1114
|
},
|
|
1045
|
-
async (args) => {
|
|
1115
|
+
async (args, extra) => {
|
|
1046
1116
|
const result = await getConvexClient().action(
|
|
1047
1117
|
api.mcp.listWorkItemComments,
|
|
1048
1118
|
{
|
|
1049
|
-
token: getToken(),
|
|
1119
|
+
token: getToken(extra),
|
|
1050
1120
|
...args
|
|
1051
1121
|
}
|
|
1052
1122
|
);
|
|
@@ -1068,14 +1138,15 @@ function registerListValidations(server) {
|
|
|
1068
1138
|
projectId: z26.string().describe("projectId from list_projects")
|
|
1069
1139
|
},
|
|
1070
1140
|
annotations: {
|
|
1141
|
+
title: "List validations",
|
|
1071
1142
|
readOnlyHint: true,
|
|
1072
1143
|
openWorldHint: false
|
|
1073
1144
|
}
|
|
1074
1145
|
},
|
|
1075
|
-
async ({ projectId }) => {
|
|
1146
|
+
async ({ projectId }, extra) => {
|
|
1076
1147
|
const client = getConvexClient();
|
|
1077
1148
|
const graph = await client.action(api.mcp.listValidations, {
|
|
1078
|
-
token: getToken(),
|
|
1149
|
+
token: getToken(extra),
|
|
1079
1150
|
projectId
|
|
1080
1151
|
});
|
|
1081
1152
|
return {
|
|
@@ -1097,16 +1168,17 @@ function registerRecordValidation(server) {
|
|
|
1097
1168
|
result: z27.enum(["pass", "fail"]).describe("Outcome of running the test")
|
|
1098
1169
|
},
|
|
1099
1170
|
annotations: {
|
|
1171
|
+
title: "Record a validation result",
|
|
1100
1172
|
readOnlyHint: false,
|
|
1101
1173
|
destructiveHint: true,
|
|
1102
1174
|
idempotentHint: true,
|
|
1103
1175
|
openWorldHint: false
|
|
1104
1176
|
}
|
|
1105
1177
|
},
|
|
1106
|
-
async ({ validationTaskId, result }) => {
|
|
1178
|
+
async ({ validationTaskId, result }, extra) => {
|
|
1107
1179
|
const client = getConvexClient();
|
|
1108
1180
|
const res = await client.action(api.mcp.recordValidation, {
|
|
1109
|
-
token: getToken(),
|
|
1181
|
+
token: getToken(extra),
|
|
1110
1182
|
validationTaskId,
|
|
1111
1183
|
result
|
|
1112
1184
|
});
|
|
@@ -1128,16 +1200,17 @@ function registerMarkSystemChanged(server) {
|
|
|
1128
1200
|
systemId: z28.string().describe("systemId from list_validations")
|
|
1129
1201
|
},
|
|
1130
1202
|
annotations: {
|
|
1203
|
+
title: "Mark a system as changed",
|
|
1131
1204
|
readOnlyHint: false,
|
|
1132
1205
|
destructiveHint: false,
|
|
1133
1206
|
idempotentHint: false,
|
|
1134
1207
|
openWorldHint: false
|
|
1135
1208
|
}
|
|
1136
1209
|
},
|
|
1137
|
-
async ({ systemId }) => {
|
|
1210
|
+
async ({ systemId }, extra) => {
|
|
1138
1211
|
const client = getConvexClient();
|
|
1139
1212
|
const res = await client.action(api.mcp.markSystemChanged, {
|
|
1140
|
-
token: getToken(),
|
|
1213
|
+
token: getToken(extra),
|
|
1141
1214
|
systemId
|
|
1142
1215
|
});
|
|
1143
1216
|
return {
|
|
@@ -1160,16 +1233,17 @@ function registerCreateSystem(server) {
|
|
|
1160
1233
|
description: z29.string().optional().describe("Optional details")
|
|
1161
1234
|
},
|
|
1162
1235
|
annotations: {
|
|
1236
|
+
title: "Create a system",
|
|
1163
1237
|
readOnlyHint: false,
|
|
1164
1238
|
destructiveHint: false,
|
|
1165
1239
|
idempotentHint: false,
|
|
1166
1240
|
openWorldHint: false
|
|
1167
1241
|
}
|
|
1168
1242
|
},
|
|
1169
|
-
async (args) => {
|
|
1243
|
+
async (args, extra) => {
|
|
1170
1244
|
const client = getConvexClient();
|
|
1171
1245
|
const res = await client.action(api.mcp.createSystem, {
|
|
1172
|
-
token: getToken(),
|
|
1246
|
+
token: getToken(extra),
|
|
1173
1247
|
...args
|
|
1174
1248
|
});
|
|
1175
1249
|
return {
|
|
@@ -1198,16 +1272,17 @@ function registerCreateValidationTask(server) {
|
|
|
1198
1272
|
)
|
|
1199
1273
|
},
|
|
1200
1274
|
annotations: {
|
|
1275
|
+
title: "Create a validation task",
|
|
1201
1276
|
readOnlyHint: false,
|
|
1202
1277
|
destructiveHint: false,
|
|
1203
1278
|
idempotentHint: false,
|
|
1204
1279
|
openWorldHint: false
|
|
1205
1280
|
}
|
|
1206
1281
|
},
|
|
1207
|
-
async (args) => {
|
|
1282
|
+
async (args, extra) => {
|
|
1208
1283
|
const client = getConvexClient();
|
|
1209
1284
|
const res = await client.action(api.mcp.createValidationTask, {
|
|
1210
|
-
token: getToken(),
|
|
1285
|
+
token: getToken(extra),
|
|
1211
1286
|
...args
|
|
1212
1287
|
});
|
|
1213
1288
|
return {
|
|
@@ -1229,16 +1304,17 @@ function registerAddSystemToTask(server) {
|
|
|
1229
1304
|
systemId: z31.string().describe("systemId from list_validations")
|
|
1230
1305
|
},
|
|
1231
1306
|
annotations: {
|
|
1307
|
+
title: "Link a system to a task",
|
|
1232
1308
|
readOnlyHint: false,
|
|
1233
1309
|
destructiveHint: false,
|
|
1234
1310
|
idempotentHint: true,
|
|
1235
1311
|
openWorldHint: false
|
|
1236
1312
|
}
|
|
1237
1313
|
},
|
|
1238
|
-
async ({ validationTaskId, systemId }) => {
|
|
1314
|
+
async ({ validationTaskId, systemId }, extra) => {
|
|
1239
1315
|
const client = getConvexClient();
|
|
1240
1316
|
const res = await client.action(api.mcp.addSystemToTask, {
|
|
1241
|
-
token: getToken(),
|
|
1317
|
+
token: getToken(extra),
|
|
1242
1318
|
validationTaskId,
|
|
1243
1319
|
systemId
|
|
1244
1320
|
});
|
|
@@ -1261,16 +1337,17 @@ function registerAddValidationDependency(server) {
|
|
|
1261
1337
|
dependsOnTaskId: z32.string().describe("The dependency (upstream) task it relies on")
|
|
1262
1338
|
},
|
|
1263
1339
|
annotations: {
|
|
1340
|
+
title: "Add a validation dependency",
|
|
1264
1341
|
readOnlyHint: false,
|
|
1265
1342
|
destructiveHint: false,
|
|
1266
1343
|
idempotentHint: true,
|
|
1267
1344
|
openWorldHint: false
|
|
1268
1345
|
}
|
|
1269
1346
|
},
|
|
1270
|
-
async ({ validationTaskId, dependsOnTaskId }) => {
|
|
1347
|
+
async ({ validationTaskId, dependsOnTaskId }, extra) => {
|
|
1271
1348
|
const client = getConvexClient();
|
|
1272
1349
|
const res = await client.action(api.mcp.addValidationDependency, {
|
|
1273
|
-
token: getToken(),
|
|
1350
|
+
token: getToken(extra),
|
|
1274
1351
|
validationTaskId,
|
|
1275
1352
|
dependsOnTaskId
|
|
1276
1353
|
});
|
|
@@ -1292,13 +1369,14 @@ function registerListLabels(server) {
|
|
|
1292
1369
|
projectId: z33.string().describe("projectId from list_projects")
|
|
1293
1370
|
},
|
|
1294
1371
|
annotations: {
|
|
1372
|
+
title: "List labels",
|
|
1295
1373
|
readOnlyHint: true,
|
|
1296
1374
|
openWorldHint: false
|
|
1297
1375
|
}
|
|
1298
1376
|
},
|
|
1299
|
-
async (args) => {
|
|
1377
|
+
async (args, extra) => {
|
|
1300
1378
|
const result = await getConvexClient().action(api.mcp.listLabels, {
|
|
1301
|
-
token: getToken(),
|
|
1379
|
+
token: getToken(extra),
|
|
1302
1380
|
...args
|
|
1303
1381
|
});
|
|
1304
1382
|
return {
|
|
@@ -1321,15 +1399,16 @@ function registerCreateLabel(server) {
|
|
|
1321
1399
|
color: z34.string().regex(/^#[0-9a-fA-F]{6}$/).optional().describe("Hex color like #2563eb. Defaults to a neutral slate.")
|
|
1322
1400
|
},
|
|
1323
1401
|
annotations: {
|
|
1402
|
+
title: "Create a label",
|
|
1324
1403
|
readOnlyHint: false,
|
|
1325
1404
|
destructiveHint: false,
|
|
1326
1405
|
idempotentHint: true,
|
|
1327
1406
|
openWorldHint: false
|
|
1328
1407
|
}
|
|
1329
1408
|
},
|
|
1330
|
-
async (args) => {
|
|
1409
|
+
async (args, extra) => {
|
|
1331
1410
|
const result = await getConvexClient().action(api.mcp.createLabel, {
|
|
1332
|
-
token: getToken(),
|
|
1411
|
+
token: getToken(extra),
|
|
1333
1412
|
...args
|
|
1334
1413
|
});
|
|
1335
1414
|
return {
|
|
@@ -1351,15 +1430,16 @@ function registerAddLabelToWorkItem(server) {
|
|
|
1351
1430
|
labelId: z35.string().describe("labelId from list_labels or create_label")
|
|
1352
1431
|
},
|
|
1353
1432
|
annotations: {
|
|
1433
|
+
title: "Add a label to a work item",
|
|
1354
1434
|
readOnlyHint: false,
|
|
1355
1435
|
destructiveHint: false,
|
|
1356
1436
|
idempotentHint: true,
|
|
1357
1437
|
openWorldHint: false
|
|
1358
1438
|
}
|
|
1359
1439
|
},
|
|
1360
|
-
async (args) => {
|
|
1440
|
+
async (args, extra) => {
|
|
1361
1441
|
const result = await getConvexClient().action(api.mcp.setWorkItemLabel, {
|
|
1362
|
-
token: getToken(),
|
|
1442
|
+
token: getToken(extra),
|
|
1363
1443
|
...args,
|
|
1364
1444
|
attached: true
|
|
1365
1445
|
});
|
|
@@ -1382,15 +1462,16 @@ function registerRemoveLabelFromWorkItem(server) {
|
|
|
1382
1462
|
labelId: z36.string().describe("labelId from list_labels")
|
|
1383
1463
|
},
|
|
1384
1464
|
annotations: {
|
|
1465
|
+
title: "Remove a label from a work item",
|
|
1385
1466
|
readOnlyHint: false,
|
|
1386
1467
|
destructiveHint: true,
|
|
1387
1468
|
idempotentHint: true,
|
|
1388
1469
|
openWorldHint: false
|
|
1389
1470
|
}
|
|
1390
1471
|
},
|
|
1391
|
-
async (args) => {
|
|
1472
|
+
async (args, extra) => {
|
|
1392
1473
|
const result = await getConvexClient().action(api.mcp.setWorkItemLabel, {
|
|
1393
|
-
token: getToken(),
|
|
1474
|
+
token: getToken(extra),
|
|
1394
1475
|
...args,
|
|
1395
1476
|
attached: false
|
|
1396
1477
|
});
|
|
@@ -1412,13 +1493,14 @@ function registerListMilestones(server) {
|
|
|
1412
1493
|
projectId: z37.string().describe("projectId from list_projects")
|
|
1413
1494
|
},
|
|
1414
1495
|
annotations: {
|
|
1496
|
+
title: "List milestones",
|
|
1415
1497
|
readOnlyHint: true,
|
|
1416
1498
|
openWorldHint: false
|
|
1417
1499
|
}
|
|
1418
1500
|
},
|
|
1419
|
-
async (args) => {
|
|
1501
|
+
async (args, extra) => {
|
|
1420
1502
|
const result = await getConvexClient().action(api.mcp.listMilestones, {
|
|
1421
|
-
token: getToken(),
|
|
1503
|
+
token: getToken(extra),
|
|
1422
1504
|
...args
|
|
1423
1505
|
});
|
|
1424
1506
|
return {
|
|
@@ -1442,15 +1524,16 @@ function registerCreateMilestone(server) {
|
|
|
1442
1524
|
dueDate: z38.number().optional().describe("Optional due date as a unix timestamp in milliseconds")
|
|
1443
1525
|
},
|
|
1444
1526
|
annotations: {
|
|
1527
|
+
title: "Create a milestone",
|
|
1445
1528
|
readOnlyHint: false,
|
|
1446
1529
|
destructiveHint: false,
|
|
1447
1530
|
idempotentHint: false,
|
|
1448
1531
|
openWorldHint: false
|
|
1449
1532
|
}
|
|
1450
1533
|
},
|
|
1451
|
-
async (args) => {
|
|
1534
|
+
async (args, extra) => {
|
|
1452
1535
|
const result = await getConvexClient().action(api.mcp.createMilestone, {
|
|
1453
|
-
token: getToken(),
|
|
1536
|
+
token: getToken(extra),
|
|
1454
1537
|
...args
|
|
1455
1538
|
});
|
|
1456
1539
|
return {
|
|
@@ -1472,17 +1555,18 @@ function registerAddItemToMilestone(server) {
|
|
|
1472
1555
|
workItemId: z39.string().describe("workItemId from list_work_items")
|
|
1473
1556
|
},
|
|
1474
1557
|
annotations: {
|
|
1558
|
+
title: "Add an item to a milestone",
|
|
1475
1559
|
readOnlyHint: false,
|
|
1476
1560
|
destructiveHint: false,
|
|
1477
1561
|
idempotentHint: true,
|
|
1478
1562
|
openWorldHint: false
|
|
1479
1563
|
}
|
|
1480
1564
|
},
|
|
1481
|
-
async (args) => {
|
|
1565
|
+
async (args, extra) => {
|
|
1482
1566
|
const result = await getConvexClient().action(
|
|
1483
1567
|
api.mcp.setWorkItemMilestone,
|
|
1484
1568
|
{
|
|
1485
|
-
token: getToken(),
|
|
1569
|
+
token: getToken(extra),
|
|
1486
1570
|
...args,
|
|
1487
1571
|
attached: true
|
|
1488
1572
|
}
|
|
@@ -1506,17 +1590,18 @@ function registerRemoveItemFromMilestone(server) {
|
|
|
1506
1590
|
workItemId: z40.string().describe("workItemId from list_work_items")
|
|
1507
1591
|
},
|
|
1508
1592
|
annotations: {
|
|
1593
|
+
title: "Remove an item from a milestone",
|
|
1509
1594
|
readOnlyHint: false,
|
|
1510
1595
|
destructiveHint: true,
|
|
1511
1596
|
idempotentHint: true,
|
|
1512
1597
|
openWorldHint: false
|
|
1513
1598
|
}
|
|
1514
1599
|
},
|
|
1515
|
-
async (args) => {
|
|
1600
|
+
async (args, extra) => {
|
|
1516
1601
|
const result = await getConvexClient().action(
|
|
1517
1602
|
api.mcp.setWorkItemMilestone,
|
|
1518
1603
|
{
|
|
1519
|
-
token: getToken(),
|
|
1604
|
+
token: getToken(extra),
|
|
1520
1605
|
...args,
|
|
1521
1606
|
attached: false
|
|
1522
1607
|
}
|