tododdle-mcp 2.7.1 → 3.0.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/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 tasks. Requires explicit approval.',
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('list_plans', {
209
- description: 'List bounded active or archived plans in a project.',
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('get_plan', {
225
- description: 'Get one plan in a project.',
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('create_plan', {
235
- description: 'Create a plan in a project.',
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('update_plan', {
252
- description: 'Update plan details using optimistic concurrency.',
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('move_plan', {
270
- description: 'Reorder a plan within its project using optimistic concurrency.',
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
- 'archive_plan',
286
+ 'archive_board',
287
287
  true,
288
- 'Archive a plan without changing its tasks. Requires explicit approval.',
288
+ 'Archive a board without changing its tickets. Requires explicit approval.',
289
289
  true,
290
290
  ],
291
- ['restore_plan', false, 'Restore an archived plan.', false],
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('list_sections', {
312
- description: 'List bounded sections and entry automations in a plan.',
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('get_section', {
329
- description: 'Get one plan section and its entry automations.',
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('create_section', {
343
- description: 'Create a section with optional typed entry automations.',
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('update_section', {
364
- description: 'Update a section and its entry automations using optimistic concurrency.',
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('move_section', {
386
- description: 'Reorder a section within its plan using optimistic concurrency.',
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
- 'archive_section',
403
+ 'archive_lane',
404
404
  true,
405
- 'Archive a section without changing its tasks. Requires explicit approval.',
405
+ 'Archive a lane without changing its tickets. Requires explicit approval.',
406
406
  true,
407
407
  ],
408
- ['restore_section', false, 'Restore an archived section.', false],
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 project-plan and section context for task planning.',
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('attach_file_to_task', {
563
- description: 'Upload an approved local file or HTTPS URL and attach it to a task in the same project.',
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,
@@ -586,8 +586,43 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
586
586
  openWorldHint: false,
587
587
  },
588
588
  }, async (input) => toolResult(await api.get('/api/external/work-queue', input)));
589
+ server.registerTool('list_available_work', {
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
+ inputSchema: z.object({
592
+ projectId: z.string().min(1),
593
+ status: z.enum(['TODO', 'IN_PROGRESS']).default('TODO'),
594
+ priority: prioritySchema.optional(),
595
+ search: z.string().optional(),
596
+ page: z.number().int().min(1).default(1),
597
+ limit: z.number().int().min(1).max(100).default(50),
598
+ }),
599
+ annotations: {
600
+ readOnlyHint: true,
601
+ destructiveHint: false,
602
+ idempotentHint: true,
603
+ openWorldHint: false,
604
+ },
605
+ }, async (input) => toolResult(await api.get('/api/external/work-queue/available', input)));
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
+ inputSchema: z.object({
609
+ projectId: z.string().min(1),
610
+ runId: z.string().min(1).max(200),
611
+ status: z.enum(['TODO', 'IN_PROGRESS']).default('TODO'),
612
+ priority: prioritySchema.optional(),
613
+ search: z.string().optional(),
614
+ state: z.enum(['ACTIVE', 'WAITING']).default('ACTIVE'),
615
+ leaseSeconds: z.number().int().min(60).max(3600).default(900),
616
+ }),
617
+ annotations: {
618
+ readOnlyHint: false,
619
+ destructiveHint: false,
620
+ idempotentHint: true,
621
+ openWorldHint: false,
622
+ },
623
+ }, async (body) => toolResult(await api.post('/api/external/work-queue/available', body)));
589
624
  server.registerTool('get_focus_list', {
590
- description: 'List the current user’s deliberately curated Focus tasks.',
625
+ description: 'List the current user’s deliberately curated Focus tickets.',
591
626
  inputSchema: z.object({
592
627
  projectId: z.string().optional(),
593
628
  status: statusSchema.optional(),
@@ -603,8 +638,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
603
638
  openWorldHint: false,
604
639
  },
605
640
  }, async (input) => toolResult(await api.get('/api/external/focus', input)));
606
- server.registerTool('add_task_to_focus', {
607
- description: 'Add an accessible active task to Focus. Repeated additions are idempotent.',
641
+ server.registerTool('add_ticket_to_focus', {
642
+ description: 'Add an accessible active ticket to Focus. Repeated additions are idempotent.',
608
643
  inputSchema: z.object({
609
644
  taskId: z.string().min(1),
610
645
  bucket: focusBucketSchema.default('NEXT'),
@@ -617,8 +652,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
617
652
  openWorldHint: false,
618
653
  },
619
654
  }, async ({ idempotencyKey, ...body }) => toolResult(await api.post('/api/external/focus', body, idempotencyKey || randomUUID())));
620
- server.registerTool('move_focus_task', {
621
- description: 'Move or reorder a Focus task using optimistic concurrency.',
655
+ server.registerTool('move_focus_ticket', {
656
+ description: 'Move or reorder a Focus ticket using optimistic concurrency.',
622
657
  inputSchema: z.object({
623
658
  taskId: z.string().min(1),
624
659
  bucket: focusBucketSchema,
@@ -632,8 +667,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
632
667
  openWorldHint: false,
633
668
  },
634
669
  }, async ({ taskId, ...body }) => toolResult(await api.patch(`/api/external/focus/${taskId}`, body)));
635
- server.registerTool('remove_task_from_focus', {
636
- description: 'Remove personal Focus metadata without changing the underlying task.',
670
+ server.registerTool('remove_ticket_from_focus', {
671
+ description: 'Remove personal Focus metadata without changing the underlying ticket.',
637
672
  inputSchema: z.object({ taskId: z.string().min(1) }),
638
673
  annotations: {
639
674
  readOnlyHint: false,
@@ -642,8 +677,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
642
677
  openWorldHint: false,
643
678
  },
644
679
  }, async ({ taskId }) => toolResult(await api.delete(`/api/external/focus/${taskId}`, {})));
645
- server.registerTool('list_tasks', {
646
- description: 'List bounded tasks with optional project, plan, section, assignee, status, priority, search, and archive filters.',
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.',
647
682
  inputSchema: z.object({
648
683
  projectId: z.string().min(1).optional(),
649
684
  planId: z.string().min(1).optional(),
@@ -663,8 +698,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
663
698
  openWorldHint: false,
664
699
  },
665
700
  }, async (input) => toolResult(await api.get('/api/external/tasks', input)));
666
- server.registerTool('get_task', {
667
- description: 'Get complete task context including comments and blocker information.',
701
+ server.registerTool('get_ticket', {
702
+ description: 'Get complete ticket context including comments and blocker information.',
668
703
  inputSchema: z.object({ taskId: z.string().min(1) }),
669
704
  annotations: {
670
705
  readOnlyHint: true,
@@ -673,8 +708,23 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
673
708
  openWorldHint: false,
674
709
  },
675
710
  }, async ({ taskId }) => toolResult(await api.get(`/api/external/tasks/${taskId}`)));
676
- server.registerTool('claim_task', {
677
- description: 'Claim or refresh a task for the current Agent Connection without changing its human assignee. Reuse a stable runId and refresh before the lease expires.',
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
+ inputSchema: z.object({
714
+ taskId: z.string().min(1),
715
+ runId: z.string().min(1).max(200),
716
+ state: z.enum(['ACTIVE', 'WAITING']).default('ACTIVE'),
717
+ leaseSeconds: z.number().int().min(60).max(3600).default(900),
718
+ }),
719
+ annotations: {
720
+ readOnlyHint: false,
721
+ destructiveHint: false,
722
+ idempotentHint: true,
723
+ openWorldHint: false,
724
+ },
725
+ }, async ({ taskId, ...body }) => toolResult(await api.put(`/api/external/tasks/${taskId}/claim`, body)));
726
+ server.registerTool('renew_ticket_claim', {
727
+ description: 'Renew the current Agent Connection lease for a specific ticket and stable runId before it expires.',
678
728
  inputSchema: z.object({
679
729
  taskId: z.string().min(1),
680
730
  runId: z.string().min(1).max(200),
@@ -688,8 +738,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
688
738
  openWorldHint: false,
689
739
  },
690
740
  }, async ({ taskId, ...body }) => toolResult(await api.put(`/api/external/tasks/${taskId}/claim`, body)));
691
- server.registerTool('release_task', {
692
- description: 'Release the current Agent Connection work claim without changing task status or human assignment.',
741
+ server.registerTool('release_ticket', {
742
+ description: 'Release the current Agent Connection work claim without changing ticket status or human assignment.',
693
743
  inputSchema: z.object({
694
744
  taskId: z.string().min(1),
695
745
  runId: z.string().min(1).max(200),
@@ -701,8 +751,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
701
751
  openWorldHint: false,
702
752
  },
703
753
  }, async ({ taskId, ...body }) => toolResult(await api.delete(`/api/external/tasks/${taskId}/claim`, body)));
704
- server.registerTool('create_task', {
705
- description: 'Create a task in an active project plan section.',
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.',
706
756
  inputSchema: z.object({
707
757
  projectId: z.string().min(1),
708
758
  planId: z.string().min(1),
@@ -724,8 +774,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
724
774
  openWorldHint: false,
725
775
  },
726
776
  }, async ({ idempotencyKey, ...body }) => toolResult(await api.post('/api/external/tasks', body, idempotencyKey || randomUUID())));
727
- server.registerTool('update_task', {
728
- description: 'Update editable task fields. Requires the latest updatedAt value to prevent overwriting newer changes.',
777
+ server.registerTool('update_ticket', {
778
+ description: 'Update editable ticket fields. Requires the latest updatedAt value to prevent overwriting newer changes.',
729
779
  inputSchema: z.object({
730
780
  taskId: z.string().min(1),
731
781
  expectedUpdatedAt: expectedUpdatedAtSchema,
@@ -744,8 +794,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
744
794
  openWorldHint: false,
745
795
  },
746
796
  }, async ({ taskId, ...body }) => toolResult(await api.put(`/api/external/tasks/${taskId}`, body)));
747
- server.registerTool('transition_task', {
748
- description: 'Transition a task to a canonical status.',
797
+ server.registerTool('transition_ticket', {
798
+ description: 'Transition a ticket to a canonical status.',
749
799
  inputSchema: z.object({
750
800
  taskId: z.string().min(1),
751
801
  status: statusSchema,
@@ -758,8 +808,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
758
808
  openWorldHint: false,
759
809
  },
760
810
  }, async ({ taskId, ...body }) => toolResult(await api.put(`/api/external/tasks/${taskId}`, body)));
761
- server.registerTool('move_task', {
762
- description: 'Move a task to an active section, optionally in another plan in the same project. Preview first because section actions may archive the task.',
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.',
763
813
  inputSchema: z.object({
764
814
  taskId: z.string().min(1),
765
815
  planId: z.string().min(1).optional(),
@@ -774,8 +824,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
774
824
  openWorldHint: false,
775
825
  },
776
826
  }, async ({ taskId, ...body }) => toolResult(await api.put(`/api/external/tasks/${taskId}/move`, body)));
777
- server.registerTool('preview_task_move', {
778
- description: 'Preview destination section actions, warnings, destructive effects, and hierarchy blockers before moving a task.',
827
+ server.registerTool('preview_ticket_move', {
828
+ description: 'Preview destination lane actions, warnings, destructive effects, and hierarchy blockers before moving a ticket.',
779
829
  inputSchema: z.object({
780
830
  taskId: z.string().min(1),
781
831
  planId: z.string().min(1).optional(),
@@ -789,8 +839,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
789
839
  openWorldHint: false,
790
840
  },
791
841
  }, async ({ taskId, ...body }) => toolResult(await api.post(`/api/external/tasks/${taskId}/move/preview`, body)));
792
- server.registerTool('set_task_blocker', {
793
- description: 'Set or clear the active same-project task blocking this task.',
842
+ server.registerTool('set_ticket_blocker', {
843
+ description: 'Set or clear the active same-project ticket blocking this ticket.',
794
844
  inputSchema: z.object({
795
845
  taskId: z.string().min(1),
796
846
  blockedByTaskId: z.string().nullable(),
@@ -803,8 +853,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
803
853
  openWorldHint: false,
804
854
  },
805
855
  }, async ({ taskId, ...body }) => toolResult(await api.put(`/api/external/tasks/${taskId}`, body)));
806
- server.registerTool('add_task_comment', {
807
- description: 'Add an implementation note, finding, decision, or completion evidence to a task.',
856
+ server.registerTool('add_ticket_comment', {
857
+ description: 'Add an implementation note, finding, decision, or completion evidence to a ticket.',
808
858
  inputSchema: z.object({
809
859
  taskId: z.string().min(1),
810
860
  content: z.string().min(1),
@@ -843,8 +893,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
843
893
  openWorldHint: false,
844
894
  },
845
895
  }, async ({ commentId }) => toolResult(await api.post(`/api/external/agent-inbox/${commentId}/acknowledge`, {})));
846
- server.registerTool('archive_task', {
847
- description: 'Archive a task and clear dependent blocker relationships. Requires explicit human approval.',
896
+ server.registerTool('archive_ticket', {
897
+ description: 'Archive a ticket and clear dependent blocker relationships. Requires explicit human approval.',
848
898
  inputSchema: z.object({
849
899
  taskId: z.string().min(1),
850
900
  expectedUpdatedAt: expectedUpdatedAtSchema,
@@ -889,8 +939,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
889
939
  openWorldHint: false,
890
940
  },
891
941
  }, async ({ projectId, ...query }) => toolResult(await api.get(`/api/external/projects/${projectId}/time-entries`, query)));
892
- server.registerTool('list_task_time', {
893
- description: 'List bounded time entries and totals for one task.',
942
+ server.registerTool('list_ticket_time', {
943
+ description: 'List bounded time entries and totals for one ticket.',
894
944
  inputSchema: z.object({
895
945
  projectId: z.string().min(1),
896
946
  taskId: z.string().min(1),
@@ -904,8 +954,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
904
954
  openWorldHint: false,
905
955
  },
906
956
  }, async ({ projectId, taskId, ...query }) => toolResult(await api.get(`/api/external/projects/${projectId}/tasks/${taskId}/time-entries`, query)));
907
- server.registerTool('start_task_timer', {
908
- description: 'Start a live timer on an active task. A task allows one active timer; different tasks may run concurrently.',
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.',
909
959
  inputSchema: z.object({
910
960
  projectId: z.string().min(1),
911
961
  taskId: z.string().min(1),
@@ -921,8 +971,8 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
921
971
  openWorldHint: false,
922
972
  },
923
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())));
924
- server.registerTool('stop_task_timer', {
925
- description: 'Stop an active task timer using optimistic concurrency.',
974
+ server.registerTool('stop_ticket_timer', {
975
+ description: 'Stop an active ticket timer using optimistic concurrency.',
926
976
  inputSchema: z.object({
927
977
  projectId: z.string().min(1),
928
978
  taskId: z.string().min(1),
@@ -936,9 +986,9 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
936
986
  idempotentHint: true,
937
987
  openWorldHint: false,
938
988
  },
939
- }, async ({ projectId, taskId, timeEntryId, endedAt, expectedUpdatedAt }) => toolResult(await api.post(`/api/external/projects/${projectId}/tasks/${taskId}/time-entries/${timeEntryId}/stop`, { endedAt: endedAt ?? new Date().toISOString(), expectedUpdatedAt })));
940
- server.registerTool('log_task_time', {
941
- description: 'Log a completed historical interval against a task.',
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('log_ticket_time', {
991
+ description: 'Log a completed historical interval against a ticket.',
942
992
  inputSchema: z.object({
943
993
  projectId: z.string().min(1),
944
994
  taskId: z.string().min(1),
@@ -990,7 +1040,7 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
990
1040
  },
991
1041
  }, async ({ projectId, taskId, timeEntryId, expectedUpdatedAt }) => toolResult(await api.delete(`/api/external/projects/${projectId}/tasks/${taskId}/time-entries/${timeEntryId}`, { expectedUpdatedAt })));
992
1042
  server.registerTool('get_project_brief', {
993
- description: 'Read the canonical project brief before planning or changing work.',
1043
+ description: 'Read the canonical project brief before planning or changing tickets.',
994
1044
  inputSchema: z.object({ projectId: z.string().min(1) }),
995
1045
  annotations: {
996
1046
  readOnlyHint: true,
@@ -1017,7 +1067,7 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
1017
1067
  },
1018
1068
  }, async ({ projectId, ...query }) => toolResult(await api.get(`/api/external/projects/${projectId}/artifacts`, query)));
1019
1069
  server.registerTool('get_project_artifact', {
1020
- description: 'Get complete Markdown, relationships, comments, and task links for one project artifact.',
1070
+ description: 'Get complete Markdown, relationships, comments, and ticket links for one project artifact.',
1021
1071
  inputSchema: z.object({ projectId: z.string().min(1), artifactId: z.string().min(1) }),
1022
1072
  annotations: {
1023
1073
  readOnlyHint: true,
@@ -1108,7 +1158,7 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
1108
1158
  openWorldHint: false,
1109
1159
  },
1110
1160
  }, async ({ projectId, artifactId, ...body }) => toolResult(await api.post(`/api/external/projects/${projectId}/artifacts/${artifactId}/transition`, body)));
1111
- server.registerTool('link_artifact_task', {
1161
+ server.registerTool('link_artifact_ticket', {
1112
1162
  description: 'Link same-project execution work to the context it implements, informs, evidences, or came from.',
1113
1163
  inputSchema: z.object({
1114
1164
  projectId: z.string().min(1),
@@ -1143,22 +1193,22 @@ export function createToDoddleMcpServer(api, serverOptions = {}) {
1143
1193
  openWorldHint: false,
1144
1194
  },
1145
1195
  }, async ({ projectId, artifactId, idempotencyKey, ...body }) => toolResult(await api.post(`/api/external/projects/${projectId}/artifacts/${artifactId}/comments`, body, idempotencyKey || randomUUID())));
1146
- server.registerResource('task', new ResourceTemplate('tododdle://tasks/{taskId}', { list: undefined }), {
1147
- title: 'ToDoddle task',
1148
- description: 'Complete task context by ID.',
1196
+ server.registerResource('ticket', new ResourceTemplate('tododdle://tickets/{ticketId}', { list: undefined }), {
1197
+ title: 'ToDoddle ticket',
1198
+ description: 'Complete ticket context by ID.',
1149
1199
  mimeType: 'application/json',
1150
1200
  }, async (uri, variables) => ({
1151
1201
  contents: [
1152
1202
  {
1153
1203
  uri: uri.href,
1154
1204
  mimeType: 'application/json',
1155
- text: JSON.stringify(await api.get(`/api/external/tasks/${String(variables.taskId)}`), null, 2),
1205
+ text: JSON.stringify(await api.get(`/api/external/tasks/${String(variables.ticketId)}`), null, 2),
1156
1206
  },
1157
1207
  ],
1158
1208
  }));
1159
1209
  server.registerResource('project', new ResourceTemplate('tododdle://projects/{projectId}', { list: undefined }), {
1160
1210
  title: 'ToDoddle project context',
1161
- description: 'Bounded project-plan and section context by project ID.',
1211
+ description: 'Bounded project board and lane context by project ID.',
1162
1212
  mimeType: 'application/json',
1163
1213
  }, async (uri, variables) => ({
1164
1214
  contents: [