tododdle-mcp 2.8.0 → 3.0.1
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/.codex-plugin/plugin.json +4 -4
- package/README.md +50 -40
- package/config/external-api-parity.json +44 -42
- package/dist/api-client.d.ts.map +1 -1
- package/dist/api-client.js +12 -3
- package/dist/api-client.js.map +1 -1
- package/dist/compatibility.d.ts +10 -0
- package/dist/compatibility.d.ts.map +1 -0
- package/dist/compatibility.js +17 -0
- package/dist/compatibility.js.map +1 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +82 -82
- package/dist/server.js.map +1 -1
- package/examples/AGENTS.tododdle.md +7 -5
- package/package.json +1 -1
- package/skills/tododdle-workflow/SKILL.md +35 -19
- package/skills/tododdle-workflow/agents/openai.yaml +1 -1
package/dist/server.js
CHANGED
|
@@ -186,7 +186,7 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
|
|
|
186
186
|
[
|
|
187
187
|
'archive_project',
|
|
188
188
|
true,
|
|
189
|
-
'Archive a project without deleting its
|
|
189
|
+
'Archive a project without deleting its tickets. Requires explicit approval.',
|
|
190
190
|
true,
|
|
191
191
|
],
|
|
192
192
|
['restore_project', false, 'Restore an archived project.', false],
|
|
@@ -205,8 +205,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
|
|
|
205
205
|
},
|
|
206
206
|
}, async ({ projectId, expectedUpdatedAt }) => toolResult(await api.put(`/api/external/projects/${projectId}`, { archived, expectedUpdatedAt })));
|
|
207
207
|
}
|
|
208
|
-
server.registerTool('
|
|
209
|
-
description: 'List bounded active or archived
|
|
208
|
+
server.registerTool('list_boards', {
|
|
209
|
+
description: 'List bounded active or archived boards in a project.',
|
|
210
210
|
inputSchema: z.object({
|
|
211
211
|
projectId: z.string().min(1),
|
|
212
212
|
search: z.string().optional(),
|
|
@@ -221,8 +221,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
|
|
|
221
221
|
openWorldHint: false,
|
|
222
222
|
},
|
|
223
223
|
}, async ({ projectId, ...query }) => toolResult(await api.get(`/api/external/projects/${projectId}/plans`, query)));
|
|
224
|
-
server.registerTool('
|
|
225
|
-
description: 'Get one
|
|
224
|
+
server.registerTool('get_board', {
|
|
225
|
+
description: 'Get one board in a project.',
|
|
226
226
|
inputSchema: z.object({ projectId: z.string().min(1), planId: z.string().min(1) }),
|
|
227
227
|
annotations: {
|
|
228
228
|
readOnlyHint: true,
|
|
@@ -231,8 +231,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
|
|
|
231
231
|
openWorldHint: false,
|
|
232
232
|
},
|
|
233
233
|
}, async ({ projectId, planId }) => toolResult(await api.get(`/api/external/projects/${projectId}/plans/${planId}`)));
|
|
234
|
-
server.registerTool('
|
|
235
|
-
description: 'Create a
|
|
234
|
+
server.registerTool('create_board', {
|
|
235
|
+
description: 'Create a board in a project.',
|
|
236
236
|
inputSchema: z.object({
|
|
237
237
|
projectId: z.string().min(1),
|
|
238
238
|
name: z.string().min(1).max(500),
|
|
@@ -248,8 +248,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
|
|
|
248
248
|
openWorldHint: false,
|
|
249
249
|
},
|
|
250
250
|
}, async ({ projectId, idempotencyKey, ...body }) => toolResult(await api.post(`/api/external/projects/${projectId}/plans`, body, idempotencyKey)));
|
|
251
|
-
server.registerTool('
|
|
252
|
-
description: 'Update
|
|
251
|
+
server.registerTool('update_board', {
|
|
252
|
+
description: 'Update board details using optimistic concurrency.',
|
|
253
253
|
inputSchema: z.object({
|
|
254
254
|
projectId: z.string().min(1),
|
|
255
255
|
planId: z.string().min(1),
|
|
@@ -266,8 +266,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
|
|
|
266
266
|
openWorldHint: false,
|
|
267
267
|
},
|
|
268
268
|
}, async ({ projectId, planId, ...body }) => toolResult(await api.put(`/api/external/projects/${projectId}/plans/${planId}`, body)));
|
|
269
|
-
server.registerTool('
|
|
270
|
-
description: 'Reorder a
|
|
269
|
+
server.registerTool('move_board', {
|
|
270
|
+
description: 'Reorder a board within its project using optimistic concurrency.',
|
|
271
271
|
inputSchema: z.object({
|
|
272
272
|
projectId: z.string().min(1),
|
|
273
273
|
planId: z.string().min(1),
|
|
@@ -283,12 +283,12 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
|
|
|
283
283
|
}, async ({ projectId, planId, ...body }) => toolResult(await api.put(`/api/external/projects/${projectId}/plans/${planId}`, body)));
|
|
284
284
|
for (const [name, archived, description, destructiveHint] of [
|
|
285
285
|
[
|
|
286
|
-
'
|
|
286
|
+
'archive_board',
|
|
287
287
|
true,
|
|
288
|
-
'Archive a
|
|
288
|
+
'Archive a board without changing its tickets. Requires explicit approval.',
|
|
289
289
|
true,
|
|
290
290
|
],
|
|
291
|
-
['
|
|
291
|
+
['restore_board', false, 'Restore an archived board.', false],
|
|
292
292
|
]) {
|
|
293
293
|
server.registerTool(name, {
|
|
294
294
|
description,
|
|
@@ -308,8 +308,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
|
|
|
308
308
|
expectedUpdatedAt,
|
|
309
309
|
})));
|
|
310
310
|
}
|
|
311
|
-
server.registerTool('
|
|
312
|
-
description: 'List bounded
|
|
311
|
+
server.registerTool('list_lanes', {
|
|
312
|
+
description: 'List bounded lanes and entry automations in a board.',
|
|
313
313
|
inputSchema: z.object({
|
|
314
314
|
projectId: z.string().min(1),
|
|
315
315
|
planId: z.string().min(1),
|
|
@@ -325,8 +325,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
|
|
|
325
325
|
openWorldHint: false,
|
|
326
326
|
},
|
|
327
327
|
}, async ({ projectId, planId, ...query }) => toolResult(await api.get(`/api/external/projects/${projectId}/plans/${planId}/sections`, query)));
|
|
328
|
-
server.registerTool('
|
|
329
|
-
description: 'Get one
|
|
328
|
+
server.registerTool('get_lane', {
|
|
329
|
+
description: 'Get one board lane and its entry automations.',
|
|
330
330
|
inputSchema: z.object({
|
|
331
331
|
projectId: z.string().min(1),
|
|
332
332
|
planId: z.string().min(1),
|
|
@@ -339,8 +339,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
|
|
|
339
339
|
openWorldHint: false,
|
|
340
340
|
},
|
|
341
341
|
}, async ({ projectId, planId, sectionId }) => toolResult(await api.get(`/api/external/projects/${projectId}/plans/${planId}/sections/${sectionId}`)));
|
|
342
|
-
server.registerTool('
|
|
343
|
-
description: 'Create a
|
|
342
|
+
server.registerTool('create_lane', {
|
|
343
|
+
description: 'Create a lane with optional typed entry automations.',
|
|
344
344
|
inputSchema: z.object({
|
|
345
345
|
projectId: z.string().min(1),
|
|
346
346
|
planId: z.string().min(1),
|
|
@@ -360,8 +360,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
|
|
|
360
360
|
openWorldHint: false,
|
|
361
361
|
},
|
|
362
362
|
}, async ({ projectId, planId, idempotencyKey, ...body }) => toolResult(await api.post(`/api/external/projects/${projectId}/plans/${planId}/sections`, body, idempotencyKey)));
|
|
363
|
-
server.registerTool('
|
|
364
|
-
description: 'Update a
|
|
363
|
+
server.registerTool('update_lane', {
|
|
364
|
+
description: 'Update a lane and its entry automations using optimistic concurrency.',
|
|
365
365
|
inputSchema: z.object({
|
|
366
366
|
projectId: z.string().min(1),
|
|
367
367
|
planId: z.string().min(1),
|
|
@@ -382,8 +382,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
|
|
|
382
382
|
openWorldHint: false,
|
|
383
383
|
},
|
|
384
384
|
}, async ({ projectId, planId, sectionId, ...body }) => toolResult(await api.put(`/api/external/projects/${projectId}/plans/${planId}/sections/${sectionId}`, body)));
|
|
385
|
-
server.registerTool('
|
|
386
|
-
description: 'Reorder a
|
|
385
|
+
server.registerTool('move_lane', {
|
|
386
|
+
description: 'Reorder a lane within its board using optimistic concurrency.',
|
|
387
387
|
inputSchema: z.object({
|
|
388
388
|
projectId: z.string().min(1),
|
|
389
389
|
planId: z.string().min(1),
|
|
@@ -400,12 +400,12 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
|
|
|
400
400
|
}, async ({ projectId, planId, sectionId, ...body }) => toolResult(await api.put(`/api/external/projects/${projectId}/plans/${planId}/sections/${sectionId}`, body)));
|
|
401
401
|
for (const [name, archived, description, destructiveHint] of [
|
|
402
402
|
[
|
|
403
|
-
'
|
|
403
|
+
'archive_lane',
|
|
404
404
|
true,
|
|
405
|
-
'Archive a
|
|
405
|
+
'Archive a lane without changing its tickets. Requires explicit approval.',
|
|
406
406
|
true,
|
|
407
407
|
],
|
|
408
|
-
['
|
|
408
|
+
['restore_lane', false, 'Restore an archived lane.', false],
|
|
409
409
|
]) {
|
|
410
410
|
server.registerTool(name, {
|
|
411
411
|
description,
|
|
@@ -540,7 +540,7 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
|
|
|
540
540
|
},
|
|
541
541
|
}, async ({ noteId, expectedUpdatedAt }) => toolResult(await api.delete(`/api/external/notes/${noteId}`, { expectedUpdatedAt })));
|
|
542
542
|
server.registerTool('get_project_context', {
|
|
543
|
-
description: 'Get bounded
|
|
543
|
+
description: 'Get bounded board and lane context for ticket planning.',
|
|
544
544
|
inputSchema: z.object({ projectId: z.string().min(1) }),
|
|
545
545
|
annotations: {
|
|
546
546
|
readOnlyHint: true,
|
|
@@ -559,8 +559,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
|
|
|
559
559
|
openWorldHint: true,
|
|
560
560
|
},
|
|
561
561
|
}, async (input) => toolResult(await uploadDocument(api, options, input)));
|
|
562
|
-
server.registerTool('
|
|
563
|
-
description: 'Upload an approved local file or HTTPS URL and attach it to a
|
|
562
|
+
server.registerTool('attach_file_to_ticket', {
|
|
563
|
+
description: 'Upload an approved local file or HTTPS URL and attach it to a ticket in the same project.',
|
|
564
564
|
inputSchema: taskUploadInputSchema,
|
|
565
565
|
annotations: {
|
|
566
566
|
readOnlyHint: false,
|
|
@@ -587,7 +587,7 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
|
|
|
587
587
|
},
|
|
588
588
|
}, async (input) => toolResult(await api.get('/api/external/work-queue', input)));
|
|
589
589
|
server.registerTool('list_available_work', {
|
|
590
|
-
description: 'List bounded, unblocked, currently unclaimed
|
|
590
|
+
description: 'List bounded, unblocked, currently unclaimed tickets eligible for an Agent Connection in one project, ordered by priority, due date, and project position.',
|
|
591
591
|
inputSchema: z.object({
|
|
592
592
|
projectId: z.string().min(1),
|
|
593
593
|
status: z.enum(['TODO', 'IN_PROGRESS']).default('TODO'),
|
|
@@ -603,8 +603,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
|
|
|
603
603
|
openWorldHint: false,
|
|
604
604
|
},
|
|
605
605
|
}, async (input) => toolResult(await api.get('/api/external/work-queue/available', input)));
|
|
606
|
-
server.registerTool('
|
|
607
|
-
description: 'Atomically claim the highest-ranked eligible
|
|
606
|
+
server.registerTool('claim_next_ticket', {
|
|
607
|
+
description: 'Atomically claim the highest-ranked eligible ticket in one project. Reuse a unique stable runId to replay the same live claim safely.',
|
|
608
608
|
inputSchema: z.object({
|
|
609
609
|
projectId: z.string().min(1),
|
|
610
610
|
runId: z.string().min(1).max(200),
|
|
@@ -622,7 +622,7 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
|
|
|
622
622
|
},
|
|
623
623
|
}, async (body) => toolResult(await api.post('/api/external/work-queue/available', body)));
|
|
624
624
|
server.registerTool('get_focus_list', {
|
|
625
|
-
description: 'List the current user’s deliberately curated Focus
|
|
625
|
+
description: 'List the current user’s deliberately curated Focus tickets.',
|
|
626
626
|
inputSchema: z.object({
|
|
627
627
|
projectId: z.string().optional(),
|
|
628
628
|
status: statusSchema.optional(),
|
|
@@ -638,8 +638,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
|
|
|
638
638
|
openWorldHint: false,
|
|
639
639
|
},
|
|
640
640
|
}, async (input) => toolResult(await api.get('/api/external/focus', input)));
|
|
641
|
-
server.registerTool('
|
|
642
|
-
description: 'Add an accessible active
|
|
641
|
+
server.registerTool('add_ticket_to_focus', {
|
|
642
|
+
description: 'Add an accessible active ticket to Focus. Repeated additions are idempotent.',
|
|
643
643
|
inputSchema: z.object({
|
|
644
644
|
taskId: z.string().min(1),
|
|
645
645
|
bucket: focusBucketSchema.default('NEXT'),
|
|
@@ -652,8 +652,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
|
|
|
652
652
|
openWorldHint: false,
|
|
653
653
|
},
|
|
654
654
|
}, async ({ idempotencyKey, ...body }) => toolResult(await api.post('/api/external/focus', body, idempotencyKey || randomUUID())));
|
|
655
|
-
server.registerTool('
|
|
656
|
-
description: 'Move or reorder a Focus
|
|
655
|
+
server.registerTool('move_focus_ticket', {
|
|
656
|
+
description: 'Move or reorder a Focus ticket using optimistic concurrency.',
|
|
657
657
|
inputSchema: z.object({
|
|
658
658
|
taskId: z.string().min(1),
|
|
659
659
|
bucket: focusBucketSchema,
|
|
@@ -667,8 +667,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
|
|
|
667
667
|
openWorldHint: false,
|
|
668
668
|
},
|
|
669
669
|
}, async ({ taskId, ...body }) => toolResult(await api.patch(`/api/external/focus/${taskId}`, body)));
|
|
670
|
-
server.registerTool('
|
|
671
|
-
description: 'Remove personal Focus metadata without changing the underlying
|
|
670
|
+
server.registerTool('remove_ticket_from_focus', {
|
|
671
|
+
description: 'Remove personal Focus metadata without changing the underlying ticket.',
|
|
672
672
|
inputSchema: z.object({ taskId: z.string().min(1) }),
|
|
673
673
|
annotations: {
|
|
674
674
|
readOnlyHint: false,
|
|
@@ -677,8 +677,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
|
|
|
677
677
|
openWorldHint: false,
|
|
678
678
|
},
|
|
679
679
|
}, async ({ taskId }) => toolResult(await api.delete(`/api/external/focus/${taskId}`, {})));
|
|
680
|
-
server.registerTool('
|
|
681
|
-
description: 'List bounded
|
|
680
|
+
server.registerTool('list_tickets', {
|
|
681
|
+
description: 'List bounded tickets with optional project, board, lane, assignee, status, priority, search, and archive filters. The stable planId and sectionId fields identify the board and lane.',
|
|
682
682
|
inputSchema: z.object({
|
|
683
683
|
projectId: z.string().min(1).optional(),
|
|
684
684
|
planId: z.string().min(1).optional(),
|
|
@@ -698,8 +698,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
|
|
|
698
698
|
openWorldHint: false,
|
|
699
699
|
},
|
|
700
700
|
}, async (input) => toolResult(await api.get('/api/external/tasks', input)));
|
|
701
|
-
server.registerTool('
|
|
702
|
-
description: 'Get complete
|
|
701
|
+
server.registerTool('get_ticket', {
|
|
702
|
+
description: 'Get complete ticket context including comments and blocker information.',
|
|
703
703
|
inputSchema: z.object({ taskId: z.string().min(1) }),
|
|
704
704
|
annotations: {
|
|
705
705
|
readOnlyHint: true,
|
|
@@ -708,8 +708,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
|
|
|
708
708
|
openWorldHint: false,
|
|
709
709
|
},
|
|
710
710
|
}, async ({ taskId }) => toolResult(await api.get(`/api/external/tasks/${taskId}`)));
|
|
711
|
-
server.registerTool('
|
|
712
|
-
description: 'Claim or refresh a
|
|
711
|
+
server.registerTool('claim_ticket', {
|
|
712
|
+
description: 'Claim or refresh a ticket for the current Agent Connection without changing its human assignee. Reuse a stable runId and refresh before the lease expires.',
|
|
713
713
|
inputSchema: z.object({
|
|
714
714
|
taskId: z.string().min(1),
|
|
715
715
|
runId: z.string().min(1).max(200),
|
|
@@ -723,8 +723,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
|
|
|
723
723
|
openWorldHint: false,
|
|
724
724
|
},
|
|
725
725
|
}, async ({ taskId, ...body }) => toolResult(await api.put(`/api/external/tasks/${taskId}/claim`, body)));
|
|
726
|
-
server.registerTool('
|
|
727
|
-
description: 'Renew the current Agent Connection lease for a specific
|
|
726
|
+
server.registerTool('renew_ticket_claim', {
|
|
727
|
+
description: 'Renew the current Agent Connection lease for a specific ticket and stable runId before it expires.',
|
|
728
728
|
inputSchema: z.object({
|
|
729
729
|
taskId: z.string().min(1),
|
|
730
730
|
runId: z.string().min(1).max(200),
|
|
@@ -738,8 +738,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
|
|
|
738
738
|
openWorldHint: false,
|
|
739
739
|
},
|
|
740
740
|
}, async ({ taskId, ...body }) => toolResult(await api.put(`/api/external/tasks/${taskId}/claim`, body)));
|
|
741
|
-
server.registerTool('
|
|
742
|
-
description: 'Release the current Agent Connection work claim without changing
|
|
741
|
+
server.registerTool('release_ticket', {
|
|
742
|
+
description: 'Release the current Agent Connection work claim without changing ticket status or human assignment.',
|
|
743
743
|
inputSchema: z.object({
|
|
744
744
|
taskId: z.string().min(1),
|
|
745
745
|
runId: z.string().min(1).max(200),
|
|
@@ -751,8 +751,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
|
|
|
751
751
|
openWorldHint: false,
|
|
752
752
|
},
|
|
753
753
|
}, async ({ taskId, ...body }) => toolResult(await api.delete(`/api/external/tasks/${taskId}/claim`, body)));
|
|
754
|
-
server.registerTool('
|
|
755
|
-
description: 'Create a
|
|
754
|
+
server.registerTool('create_ticket', {
|
|
755
|
+
description: 'Create a ticket in an active board lane. The stable planId and sectionId fields identify the board and lane.',
|
|
756
756
|
inputSchema: z.object({
|
|
757
757
|
projectId: z.string().min(1),
|
|
758
758
|
planId: z.string().min(1),
|
|
@@ -774,8 +774,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
|
|
|
774
774
|
openWorldHint: false,
|
|
775
775
|
},
|
|
776
776
|
}, async ({ idempotencyKey, ...body }) => toolResult(await api.post('/api/external/tasks', body, idempotencyKey || randomUUID())));
|
|
777
|
-
server.registerTool('
|
|
778
|
-
description: 'Update editable
|
|
777
|
+
server.registerTool('update_ticket', {
|
|
778
|
+
description: 'Update editable ticket fields. Requires the latest updatedAt value to prevent overwriting newer changes.',
|
|
779
779
|
inputSchema: z.object({
|
|
780
780
|
taskId: z.string().min(1),
|
|
781
781
|
expectedUpdatedAt: expectedUpdatedAtSchema,
|
|
@@ -794,8 +794,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
|
|
|
794
794
|
openWorldHint: false,
|
|
795
795
|
},
|
|
796
796
|
}, async ({ taskId, ...body }) => toolResult(await api.put(`/api/external/tasks/${taskId}`, body)));
|
|
797
|
-
server.registerTool('
|
|
798
|
-
description: 'Transition a
|
|
797
|
+
server.registerTool('transition_ticket', {
|
|
798
|
+
description: 'Transition a ticket to a canonical status.',
|
|
799
799
|
inputSchema: z.object({
|
|
800
800
|
taskId: z.string().min(1),
|
|
801
801
|
status: statusSchema,
|
|
@@ -808,8 +808,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
|
|
|
808
808
|
openWorldHint: false,
|
|
809
809
|
},
|
|
810
810
|
}, async ({ taskId, ...body }) => toolResult(await api.put(`/api/external/tasks/${taskId}`, body)));
|
|
811
|
-
server.registerTool('
|
|
812
|
-
description: 'Move a
|
|
811
|
+
server.registerTool('move_ticket', {
|
|
812
|
+
description: 'Move a ticket to an active lane, optionally on another board in the same project. Preview first because lane actions may archive the ticket.',
|
|
813
813
|
inputSchema: z.object({
|
|
814
814
|
taskId: z.string().min(1),
|
|
815
815
|
planId: z.string().min(1).optional(),
|
|
@@ -824,8 +824,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
|
|
|
824
824
|
openWorldHint: false,
|
|
825
825
|
},
|
|
826
826
|
}, async ({ taskId, ...body }) => toolResult(await api.put(`/api/external/tasks/${taskId}/move`, body)));
|
|
827
|
-
server.registerTool('
|
|
828
|
-
description: 'Preview destination
|
|
827
|
+
server.registerTool('preview_ticket_move', {
|
|
828
|
+
description: 'Preview destination lane actions, warnings, destructive effects, and hierarchy blockers before moving a ticket.',
|
|
829
829
|
inputSchema: z.object({
|
|
830
830
|
taskId: z.string().min(1),
|
|
831
831
|
planId: z.string().min(1).optional(),
|
|
@@ -839,8 +839,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
|
|
|
839
839
|
openWorldHint: false,
|
|
840
840
|
},
|
|
841
841
|
}, async ({ taskId, ...body }) => toolResult(await api.post(`/api/external/tasks/${taskId}/move/preview`, body)));
|
|
842
|
-
server.registerTool('
|
|
843
|
-
description: 'Set or clear the active same-project
|
|
842
|
+
server.registerTool('set_ticket_blocker', {
|
|
843
|
+
description: 'Set or clear the active same-project ticket blocking this ticket.',
|
|
844
844
|
inputSchema: z.object({
|
|
845
845
|
taskId: z.string().min(1),
|
|
846
846
|
blockedByTaskId: z.string().nullable(),
|
|
@@ -853,8 +853,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
|
|
|
853
853
|
openWorldHint: false,
|
|
854
854
|
},
|
|
855
855
|
}, async ({ taskId, ...body }) => toolResult(await api.put(`/api/external/tasks/${taskId}`, body)));
|
|
856
|
-
server.registerTool('
|
|
857
|
-
description: 'Add an implementation note, finding, decision, or completion evidence to a
|
|
856
|
+
server.registerTool('add_ticket_comment', {
|
|
857
|
+
description: 'Add an implementation note, finding, decision, or completion evidence to a ticket.',
|
|
858
858
|
inputSchema: z.object({
|
|
859
859
|
taskId: z.string().min(1),
|
|
860
860
|
content: z.string().min(1),
|
|
@@ -893,8 +893,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
|
|
|
893
893
|
openWorldHint: false,
|
|
894
894
|
},
|
|
895
895
|
}, async ({ commentId }) => toolResult(await api.post(`/api/external/agent-inbox/${commentId}/acknowledge`, {})));
|
|
896
|
-
server.registerTool('
|
|
897
|
-
description: 'Archive a
|
|
896
|
+
server.registerTool('archive_ticket', {
|
|
897
|
+
description: 'Archive a ticket and clear dependent blocker relationships. Requires explicit human approval.',
|
|
898
898
|
inputSchema: z.object({
|
|
899
899
|
taskId: z.string().min(1),
|
|
900
900
|
expectedUpdatedAt: expectedUpdatedAtSchema,
|
|
@@ -939,8 +939,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
|
|
|
939
939
|
openWorldHint: false,
|
|
940
940
|
},
|
|
941
941
|
}, async ({ projectId, ...query }) => toolResult(await api.get(`/api/external/projects/${projectId}/time-entries`, query)));
|
|
942
|
-
server.registerTool('
|
|
943
|
-
description: 'List bounded time entries and totals for one
|
|
942
|
+
server.registerTool('list_ticket_time', {
|
|
943
|
+
description: 'List bounded time entries and totals for one ticket.',
|
|
944
944
|
inputSchema: z.object({
|
|
945
945
|
projectId: z.string().min(1),
|
|
946
946
|
taskId: z.string().min(1),
|
|
@@ -954,8 +954,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
|
|
|
954
954
|
openWorldHint: false,
|
|
955
955
|
},
|
|
956
956
|
}, async ({ projectId, taskId, ...query }) => toolResult(await api.get(`/api/external/projects/${projectId}/tasks/${taskId}/time-entries`, query)));
|
|
957
|
-
server.registerTool('
|
|
958
|
-
description: 'Start a live timer on an active
|
|
957
|
+
server.registerTool('start_ticket_timer', {
|
|
958
|
+
description: 'Start a live timer on an active ticket. A ticket allows one active timer; different tickets may run concurrently.',
|
|
959
959
|
inputSchema: z.object({
|
|
960
960
|
projectId: z.string().min(1),
|
|
961
961
|
taskId: z.string().min(1),
|
|
@@ -971,8 +971,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
|
|
|
971
971
|
openWorldHint: false,
|
|
972
972
|
},
|
|
973
973
|
}, async ({ projectId, taskId, idempotencyKey, startedAt, ...body }) => toolResult(await api.post(`/api/external/projects/${projectId}/tasks/${taskId}/time-entries`, { ...body, startedAt: startedAt ?? new Date().toISOString() }, idempotencyKey || randomUUID())));
|
|
974
|
-
server.registerTool('
|
|
975
|
-
description: 'Stop an active
|
|
974
|
+
server.registerTool('stop_ticket_timer', {
|
|
975
|
+
description: 'Stop an active ticket timer using optimistic concurrency.',
|
|
976
976
|
inputSchema: z.object({
|
|
977
977
|
projectId: z.string().min(1),
|
|
978
978
|
taskId: z.string().min(1),
|
|
@@ -987,8 +987,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
|
|
|
987
987
|
openWorldHint: false,
|
|
988
988
|
},
|
|
989
989
|
}, async ({ projectId, taskId, timeEntryId, endedAt, expectedUpdatedAt }) => toolResult(await api.post(`/api/external/projects/${projectId}/tasks/${taskId}/time-entries/${timeEntryId}/stop`, { ...(endedAt ? { endedAt } : {}), expectedUpdatedAt })));
|
|
990
|
-
server.registerTool('
|
|
991
|
-
description: 'Log a completed historical interval against a
|
|
990
|
+
server.registerTool('log_ticket_time', {
|
|
991
|
+
description: 'Log a completed historical interval against a ticket.',
|
|
992
992
|
inputSchema: z.object({
|
|
993
993
|
projectId: z.string().min(1),
|
|
994
994
|
taskId: z.string().min(1),
|
|
@@ -1040,7 +1040,7 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
|
|
|
1040
1040
|
},
|
|
1041
1041
|
}, async ({ projectId, taskId, timeEntryId, expectedUpdatedAt }) => toolResult(await api.delete(`/api/external/projects/${projectId}/tasks/${taskId}/time-entries/${timeEntryId}`, { expectedUpdatedAt })));
|
|
1042
1042
|
server.registerTool('get_project_brief', {
|
|
1043
|
-
description: 'Read the canonical project brief before planning or changing
|
|
1043
|
+
description: 'Read the canonical project brief before planning or changing tickets.',
|
|
1044
1044
|
inputSchema: z.object({ projectId: z.string().min(1) }),
|
|
1045
1045
|
annotations: {
|
|
1046
1046
|
readOnlyHint: true,
|
|
@@ -1067,7 +1067,7 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
|
|
|
1067
1067
|
},
|
|
1068
1068
|
}, async ({ projectId, ...query }) => toolResult(await api.get(`/api/external/projects/${projectId}/artifacts`, query)));
|
|
1069
1069
|
server.registerTool('get_project_artifact', {
|
|
1070
|
-
description: 'Get complete Markdown, relationships, comments, and
|
|
1070
|
+
description: 'Get complete Markdown, relationships, comments, and ticket links for one project artifact.',
|
|
1071
1071
|
inputSchema: z.object({ projectId: z.string().min(1), artifactId: z.string().min(1) }),
|
|
1072
1072
|
annotations: {
|
|
1073
1073
|
readOnlyHint: true,
|
|
@@ -1158,7 +1158,7 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
|
|
|
1158
1158
|
openWorldHint: false,
|
|
1159
1159
|
},
|
|
1160
1160
|
}, async ({ projectId, artifactId, ...body }) => toolResult(await api.post(`/api/external/projects/${projectId}/artifacts/${artifactId}/transition`, body)));
|
|
1161
|
-
server.registerTool('
|
|
1161
|
+
server.registerTool('link_artifact_ticket', {
|
|
1162
1162
|
description: 'Link same-project execution work to the context it implements, informs, evidences, or came from.',
|
|
1163
1163
|
inputSchema: z.object({
|
|
1164
1164
|
projectId: z.string().min(1),
|
|
@@ -1193,22 +1193,22 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
|
|
|
1193
1193
|
openWorldHint: false,
|
|
1194
1194
|
},
|
|
1195
1195
|
}, async ({ projectId, artifactId, idempotencyKey, ...body }) => toolResult(await api.post(`/api/external/projects/${projectId}/artifacts/${artifactId}/comments`, body, idempotencyKey || randomUUID())));
|
|
1196
|
-
server.registerResource('
|
|
1197
|
-
title: 'ToDoddle
|
|
1198
|
-
description: 'Complete
|
|
1196
|
+
server.registerResource('ticket', new ResourceTemplate('tododdle://tickets/{ticketId}', { list: undefined }), {
|
|
1197
|
+
title: 'ToDoddle ticket',
|
|
1198
|
+
description: 'Complete ticket context by ID.',
|
|
1199
1199
|
mimeType: 'application/json',
|
|
1200
1200
|
}, async (uri, variables) => ({
|
|
1201
1201
|
contents: [
|
|
1202
1202
|
{
|
|
1203
1203
|
uri: uri.href,
|
|
1204
1204
|
mimeType: 'application/json',
|
|
1205
|
-
text: JSON.stringify(await api.get(`/api/external/tasks/${String(variables.
|
|
1205
|
+
text: JSON.stringify(await api.get(`/api/external/tasks/${String(variables.ticketId)}`), null, 2),
|
|
1206
1206
|
},
|
|
1207
1207
|
],
|
|
1208
1208
|
}));
|
|
1209
1209
|
server.registerResource('project', new ResourceTemplate('tododdle://projects/{projectId}', { list: undefined }), {
|
|
1210
1210
|
title: 'ToDoddle project context',
|
|
1211
|
-
description: 'Bounded project
|
|
1211
|
+
description: 'Bounded project board and lane context by project ID.',
|
|
1212
1212
|
mimeType: 'application/json',
|
|
1213
1213
|
}, async (uri, variables) => ({
|
|
1214
1214
|
contents: [
|