@critical-path/mcp 0.8.0 → 0.10.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.
@@ -1,4 +1,5 @@
1
1
  import { z } from 'zod';
2
+ import { zodToJsonSchema } from 'zod-to-json-schema';
2
3
  import type { CriticalPathEngine } from '@critical-path/core';
3
4
  import type { CriticalPathClient } from '@critical-path/client';
4
5
 
@@ -9,6 +10,7 @@ export interface ToolDefinition<TParams = any, TResult = any> {
9
10
  title: string;
10
11
  description: string;
11
12
  zodSchema: z.ZodObject<any>;
13
+ /** JSON Schema shown to MCP clients. Generated from `zodSchema` by `defineTool`. */
12
14
  inputSchema: Record<string, unknown>;
13
15
  annotations?: {
14
16
  readOnlyHint?: boolean;
@@ -17,6 +19,17 @@ export interface ToolDefinition<TParams = any, TResult = any> {
17
19
  execute: (args: TParams, target: BackendContext, ambientContext?: { projectId?: string }) => Promise<TResult>;
18
20
  }
19
21
 
22
+ /**
23
+ * Declares a tool, generating its JSON Schema `inputSchema` from `zodSchema` so the schema the
24
+ * model sees and the one arguments are validated against cannot drift apart.
25
+ */
26
+ export function defineTool<TParams = any, TResult = any>(
27
+ spec: Omit<ToolDefinition<TParams, TResult>, 'inputSchema'>
28
+ ): ToolDefinition<TParams, TResult> {
29
+ const { $schema: _schema, ...inputSchema } = zodToJsonSchema(spec.zodSchema, { $refStrategy: 'none' }) as Record<string, unknown>;
30
+ return { ...spec, inputSchema };
31
+ }
32
+
20
33
  export class ToolArgumentError extends Error {
21
34
  constructor(toolName: string, details: string) {
22
35
  super(`Invalid arguments for tool "${toolName}": ${details}`);
@@ -25,11 +38,12 @@ export class ToolArgumentError extends Error {
25
38
  }
26
39
 
27
40
  /**
28
- * Validates raw tool arguments against the tool's zod schema. Unknown keys are stripped,
41
+ * Validates raw tool arguments against the tool's zod schema. Unknown keys are rejected,
29
42
  * so callers cannot pass fields the tool does not declare.
30
43
  */
31
44
  export function parseToolArgs<TParams>(tool: ToolDefinition<TParams>, args: unknown): TParams {
32
- const result = tool.zodSchema.safeParse(args ?? {});
45
+ // Strict: unknown arguments are reported to the model instead of being silently dropped.
46
+ const result = tool.zodSchema.strict().safeParse(args ?? {});
33
47
  if (!result.success) {
34
48
  const details = result.error.issues
35
49
  .map((issue) => `${issue.path.join('.') || '(root)'}: ${issue.message}`)
@@ -45,44 +59,31 @@ export async function resolveBackend(target: BackendContext) {
45
59
  return { isEngine };
46
60
  }
47
61
 
48
- export const listProjectsTool: ToolDefinition = {
62
+ export const listProjectsTool = defineTool({
49
63
  name: 'list_projects',
50
64
  title: 'List Projects',
51
65
  description: 'Retrieve all projects in the workspace.',
52
66
  zodSchema: z.object({}),
53
- inputSchema: {
54
- type: 'object',
55
- properties: {},
56
- additionalProperties: false
57
- },
58
67
  annotations: { readOnlyHint: true },
59
68
  execute: async (_args, target) => {
60
69
  return target.getProjects();
61
70
  }
62
- };
71
+ });
63
72
 
64
- export const getProjectTool: ToolDefinition<{ id: string }> = {
73
+ export const getProjectTool = defineTool<{ id: string }>({
65
74
  name: 'get_project',
66
75
  title: 'Get Project',
67
76
  description: 'Get details of a specific project by its ID.',
68
77
  zodSchema: z.object({
69
78
  id: z.string().describe('The project ID')
70
79
  }),
71
- inputSchema: {
72
- type: 'object',
73
- properties: {
74
- id: { type: 'string', description: 'The project ID' }
75
- },
76
- required: ['id'],
77
- additionalProperties: false
78
- },
79
80
  annotations: { readOnlyHint: true },
80
81
  execute: async (args, target) => {
81
82
  return target.getProject(args.id);
82
83
  }
83
- };
84
+ });
84
85
 
85
- export const createProjectTool: ToolDefinition<{ name: string; description?: string; key?: string }> = {
86
+ export const createProjectTool = defineTool<{ name: string; description?: string; key?: string }>({
86
87
  name: 'create_project',
87
88
  title: 'Create Project',
88
89
  description: 'Create a new project workspace.',
@@ -91,27 +92,17 @@ export const createProjectTool: ToolDefinition<{ name: string; description?: str
91
92
  description: z.string().optional().describe('Description of the project'),
92
93
  key: z.string().optional().describe('Short project key/prefix (e.g. "PROJ")')
93
94
  }),
94
- inputSchema: {
95
- type: 'object',
96
- properties: {
97
- name: { type: 'string', description: 'Name of the project' },
98
- description: { type: 'string', description: 'Description of the project' },
99
- key: { type: 'string', description: 'Short project key/prefix (e.g. "PROJ")' }
100
- },
101
- required: ['name'],
102
- additionalProperties: false
103
- },
104
95
  execute: async (args, target) => {
105
96
  return target.createProject(args);
106
97
  }
107
- };
98
+ });
108
99
 
109
- export const listTasksTool: ToolDefinition<{
100
+ export const listTasksTool = defineTool<{
110
101
  projectId?: string;
111
102
  status?: string;
112
103
  priority?: string;
113
104
  assigneeId?: string;
114
- }> = {
105
+ }>({
115
106
  name: 'list_tasks',
116
107
  title: 'List Tasks',
117
108
  description: 'List tasks, optionally filtered by project, status, priority, or assignee.',
@@ -121,20 +112,6 @@ export const listTasksTool: ToolDefinition<{
121
112
  priority: z.enum(['urgent', 'high', 'medium', 'low', 'none']).optional().describe('Filter by priority'),
122
113
  assigneeId: z.string().optional().describe('Filter by assignee ID')
123
114
  }),
124
- inputSchema: {
125
- type: 'object',
126
- properties: {
127
- projectId: { type: 'string', description: 'Filter by project ID' },
128
- status: { type: 'string', description: 'Filter by task status key' },
129
- priority: {
130
- type: 'string',
131
- enum: ['urgent', 'high', 'medium', 'low', 'none'],
132
- description: 'Filter by priority'
133
- },
134
- assigneeId: { type: 'string', description: 'Filter by assignee ID' }
135
- },
136
- additionalProperties: false
137
- },
138
115
  annotations: { readOnlyHint: true },
139
116
  execute: async (args, target, ambientContext) => {
140
117
  const projectId = args.projectId || ambientContext?.projectId;
@@ -150,30 +127,22 @@ export const listTasksTool: ToolDefinition<{
150
127
  }
151
128
  return tasks;
152
129
  }
153
- };
130
+ });
154
131
 
155
- export const getTaskTool: ToolDefinition<{ id: string }> = {
132
+ export const getTaskTool = defineTool<{ id: string }>({
156
133
  name: 'get_task',
157
134
  title: 'Get Task',
158
135
  description: 'Get task details by ID.',
159
136
  zodSchema: z.object({
160
137
  id: z.string().describe('The task ID')
161
138
  }),
162
- inputSchema: {
163
- type: 'object',
164
- properties: {
165
- id: { type: 'string', description: 'The task ID' }
166
- },
167
- required: ['id'],
168
- additionalProperties: false
169
- },
170
139
  annotations: { readOnlyHint: true },
171
140
  execute: async (args, target) => {
172
141
  return target.getTask(args.id);
173
142
  }
174
- };
143
+ });
175
144
 
176
- export const createTaskTool: ToolDefinition<{
145
+ export const createTaskTool = defineTool<{
177
146
  projectId?: string;
178
147
  title: string;
179
148
  description?: string;
@@ -185,7 +154,7 @@ export const createTaskTool: ToolDefinition<{
185
154
  estimatedHours?: number;
186
155
  isBlocked?: boolean;
187
156
  blockedReason?: string;
188
- }> = {
157
+ }>({
189
158
  name: 'create_task',
190
159
  title: 'Create Task',
191
160
  description: 'Create a new task within a project.',
@@ -202,24 +171,6 @@ export const createTaskTool: ToolDefinition<{
202
171
  isBlocked: z.boolean().optional().describe('Whether the task is blocked'),
203
172
  blockedReason: z.string().optional().describe('Reason why the task is blocked')
204
173
  }),
205
- inputSchema: {
206
- type: 'object',
207
- properties: {
208
- projectId: { type: 'string', description: 'Project ID (inferred from active view if omitted)' },
209
- title: { type: 'string', description: 'Title of the task' },
210
- description: { type: 'string', description: 'Detailed description' },
211
- status: { type: 'string', description: 'Status key' },
212
- priority: { type: 'string', enum: ['urgent', 'high', 'medium', 'low', 'none'] },
213
- assigneeId: { type: 'string', description: 'User ID of assignee' },
214
- dueDate: { type: 'string', description: 'Due date in ISO 8601 format' },
215
- tags: { type: 'array', items: { type: 'string' } },
216
- estimatedHours: { type: 'number' },
217
- isBlocked: { type: 'boolean', description: 'Whether the task is blocked' },
218
- blockedReason: { type: 'string', description: 'Reason why the task is blocked' }
219
- },
220
- required: ['title'],
221
- additionalProperties: false
222
- },
223
174
  execute: async (args, target, ambientContext) => {
224
175
  const projectId = args.projectId || ambientContext?.projectId;
225
176
  if (!projectId) {
@@ -240,9 +191,9 @@ export const createTaskTool: ToolDefinition<{
240
191
  };
241
192
  return target.createTask(input);
242
193
  }
243
- };
194
+ });
244
195
 
245
- export const updateTaskTool: ToolDefinition<{
196
+ export const updateTaskTool = defineTool<{
246
197
  id: string;
247
198
  title?: string;
248
199
  description?: string;
@@ -255,7 +206,7 @@ export const updateTaskTool: ToolDefinition<{
255
206
  loggedHours?: number;
256
207
  isBlocked?: boolean;
257
208
  blockedReason?: string;
258
- }> = {
209
+ }>({
259
210
  name: 'update_task',
260
211
  title: 'Update Task',
261
212
  description: 'Update fields of an existing task (e.g. title, status, priority, assignee).',
@@ -270,70 +221,36 @@ export const updateTaskTool: ToolDefinition<{
270
221
  tags: z.array(z.string()).optional(),
271
222
  estimatedHours: z.number().optional(),
272
223
  loggedHours: z.number().optional(),
273
- isBlocked: z.boolean().optional(),
274
- blockedReason: z.string().optional()
224
+ isBlocked: z.boolean().optional().describe('Whether the task is blocked'),
225
+ blockedReason: z.string().optional().describe('Reason why the task is blocked')
275
226
  }),
276
- inputSchema: {
277
- type: 'object',
278
- properties: {
279
- id: { type: 'string', description: 'Task ID' },
280
- title: { type: 'string' },
281
- description: { type: 'string' },
282
- status: { type: 'string' },
283
- priority: { type: 'string', enum: ['urgent', 'high', 'medium', 'low', 'none'] },
284
- assigneeId: { type: 'string' },
285
- dueDate: { type: 'string' },
286
- tags: { type: 'array', items: { type: 'string' } },
287
- estimatedHours: { type: 'number' },
288
- loggedHours: { type: 'number' },
289
- isBlocked: { type: 'boolean', description: 'Whether the task is blocked' },
290
- blockedReason: { type: 'string', description: 'Reason why the task is blocked' }
291
- },
292
- required: ['id'],
293
- additionalProperties: false
294
- },
295
227
  execute: async (args, target) => {
296
228
  const { id, ...updates } = args;
297
229
  return target.updateTask(id, updates);
298
230
  }
299
- };
231
+ });
300
232
 
301
- export const deleteTaskTool: ToolDefinition<{ id: string }> = {
233
+ export const deleteTaskTool = defineTool<{ id: string }>({
302
234
  name: 'delete_task',
303
235
  title: 'Delete Task',
304
236
  description: 'Delete a task by ID.',
305
237
  zodSchema: z.object({
306
238
  id: z.string().describe('The task ID to delete')
307
239
  }),
308
- inputSchema: {
309
- type: 'object',
310
- properties: {
311
- id: { type: 'string', description: 'The task ID' }
312
- },
313
- required: ['id'],
314
- additionalProperties: false
315
- },
316
240
  annotations: { requiresConfirmation: true },
317
241
  execute: async (args, target) => {
318
242
  const success = await target.deleteTask(args.id);
319
243
  return { success };
320
244
  }
321
- };
245
+ });
322
246
 
323
- export const listDeliverablesTool: ToolDefinition<{ projectId?: string }> = {
247
+ export const listDeliverablesTool = defineTool<{ projectId?: string }>({
324
248
  name: 'list_deliverables',
325
249
  title: 'List Deliverables',
326
250
  description: 'List deliverables/milestones for a project.',
327
251
  zodSchema: z.object({
328
252
  projectId: z.string().optional().describe('Project ID')
329
253
  }),
330
- inputSchema: {
331
- type: 'object',
332
- properties: {
333
- projectId: { type: 'string', description: 'Project ID' }
334
- },
335
- additionalProperties: false
336
- },
337
254
  annotations: { readOnlyHint: true },
338
255
  execute: async (args, target, ambientContext) => {
339
256
  const projectId = args.projectId || ambientContext?.projectId;
@@ -343,15 +260,15 @@ export const listDeliverablesTool: ToolDefinition<{ projectId?: string }> = {
343
260
  }
344
261
  return [];
345
262
  }
346
- };
263
+ });
347
264
 
348
- export const createDeliverableTool: ToolDefinition<{
265
+ export const createDeliverableTool = defineTool<{
349
266
  projectId?: string;
350
267
  title: string;
351
268
  description?: string;
352
269
  dueDate?: string;
353
270
  taskIds?: string[];
354
- }> = {
271
+ }>({
355
272
  name: 'create_deliverable',
356
273
  title: 'Create Deliverable',
357
274
  description: 'Create a milestone deliverable within a project.',
@@ -362,18 +279,6 @@ export const createDeliverableTool: ToolDefinition<{
362
279
  dueDate: z.string().optional(),
363
280
  taskIds: z.array(z.string()).optional()
364
281
  }),
365
- inputSchema: {
366
- type: 'object',
367
- properties: {
368
- projectId: { type: 'string' },
369
- title: { type: 'string' },
370
- description: { type: 'string' },
371
- dueDate: { type: 'string' },
372
- taskIds: { type: 'array', items: { type: 'string' } }
373
- },
374
- required: ['title'],
375
- additionalProperties: false
376
- },
377
282
  execute: async (args, target, ambientContext) => {
378
283
  const projectId = args.projectId || ambientContext?.projectId;
379
284
  if (!projectId) throw new Error('projectId is required to create a deliverable.');
@@ -385,23 +290,15 @@ export const createDeliverableTool: ToolDefinition<{
385
290
  }
386
291
  throw new Error('createDeliverable is not supported by this backend target.');
387
292
  }
388
- };
293
+ });
389
294
 
390
- export const listCommentsTool: ToolDefinition<{ taskId: string }> = {
295
+ export const listCommentsTool = defineTool<{ taskId: string }>({
391
296
  name: 'list_comments',
392
297
  title: 'List Comments',
393
298
  description: 'List comments associated with a task.',
394
299
  zodSchema: z.object({
395
300
  taskId: z.string().describe('The task ID')
396
301
  }),
397
- inputSchema: {
398
- type: 'object',
399
- properties: {
400
- taskId: { type: 'string', description: 'The task ID' }
401
- },
402
- required: ['taskId'],
403
- additionalProperties: false
404
- },
405
302
  annotations: { readOnlyHint: true },
406
303
  execute: async (args, target) => {
407
304
  if ('getComments' in target && typeof (target as any).getComments === 'function') {
@@ -409,50 +306,28 @@ export const listCommentsTool: ToolDefinition<{ taskId: string }> = {
409
306
  }
410
307
  return [];
411
308
  }
412
- };
309
+ });
413
310
 
414
- export const addCommentTool: ToolDefinition<{ taskId: string; content: string; authorId: string }> = {
311
+ export const addCommentTool = defineTool<{ taskId: string; content: string }>({
415
312
  name: 'add_comment',
416
313
  title: 'Add Comment',
417
- description: 'Add a comment to a task.',
314
+ description: 'Add a comment to a task. The comment is attributed to the identity this MCP server runs as.',
418
315
  zodSchema: z.object({
419
316
  taskId: z.string().describe('The task ID'),
420
- content: z.string().describe('Comment body/markdown'),
421
- authorId: z.string().describe('Author user ID')
317
+ content: z.string().describe('Comment body/markdown')
422
318
  }),
423
- inputSchema: {
424
- type: 'object',
425
- properties: {
426
- taskId: { type: 'string', description: 'The task ID' },
427
- content: { type: 'string', description: 'Comment body/markdown' },
428
- authorId: { type: 'string', description: 'Author user ID' }
429
- },
430
- required: ['taskId', 'content', 'authorId'],
431
- additionalProperties: false
432
- },
433
319
  execute: async (args, target) => {
434
- return (target as any).addComment({
435
- taskId: args.taskId,
436
- content: args.content,
437
- authorId: args.authorId
438
- });
320
+ return target.addComment({ taskId: args.taskId, content: args.content });
439
321
  }
440
- };
322
+ });
441
323
 
442
- export const calculateCriticalPathTool: ToolDefinition<{ projectId?: string }> = {
324
+ export const calculateCriticalPathTool = defineTool<{ projectId?: string }>({
443
325
  name: 'calculate_critical_path',
444
326
  title: 'Calculate Critical Path',
445
327
  description: 'Calculate Critical Path Method (CPM) schedule, early/late start and finish, total slack, and critical bottlenecks for a project.',
446
328
  zodSchema: z.object({
447
329
  projectId: z.string().optional().describe('Project ID (falls back to ambient context if omitted)')
448
330
  }),
449
- inputSchema: {
450
- type: 'object',
451
- properties: {
452
- projectId: { type: 'string', description: 'Project ID (falls back to ambient context if omitted)' }
453
- },
454
- additionalProperties: false
455
- },
456
331
  annotations: { readOnlyHint: true },
457
332
  execute: async (args, target, ambientContext) => {
458
333
  const projectId = args.projectId || ambientContext?.projectId;
@@ -461,14 +336,14 @@ export const calculateCriticalPathTool: ToolDefinition<{ projectId?: string }> =
461
336
  }
462
337
  return (target as any).calculateCriticalPath(projectId);
463
338
  }
464
- };
339
+ });
465
340
 
466
- export const getTimelineLadderTool: ToolDefinition<{
341
+ export const getTimelineLadderTool = defineTool<{
467
342
  projectId?: string;
468
343
  level?: 'macro' | 'standard' | 'concrete' | 'all';
469
344
  containerId?: string;
470
345
  iterationId?: string;
471
- }> = {
346
+ }>({
472
347
  name: 'get_timeline_ladder',
473
348
  title: 'Get Timeline Ladder of Abstraction',
474
349
  description: 'Retrieve Ladder of Abstraction for a project timeline across macro phase envelopes, standard Gantt tasks & CPM, and concrete deliverables/time/attachments.',
@@ -478,16 +353,6 @@ export const getTimelineLadderTool: ToolDefinition<{
478
353
  containerId: z.string().optional().describe('Optional container ID filter'),
479
354
  iterationId: z.string().optional().describe('Optional iteration/sprint ID filter')
480
355
  }),
481
- inputSchema: {
482
- type: 'object',
483
- properties: {
484
- projectId: { type: 'string', description: 'Project ID (falls back to ambient context if omitted)' },
485
- level: { type: 'string', enum: ['macro', 'standard', 'concrete', 'all'], description: 'Abstraction level' },
486
- containerId: { type: 'string', description: 'Optional container ID filter' },
487
- iterationId: { type: 'string', description: 'Optional iteration/sprint ID filter' }
488
- },
489
- additionalProperties: false
490
- },
491
356
  annotations: { readOnlyHint: true },
492
357
  execute: async (args, target, ambientContext) => {
493
358
  const projectId = args.projectId || ambientContext?.projectId;
@@ -500,72 +365,48 @@ export const getTimelineLadderTool: ToolDefinition<{
500
365
  iterationId: args.iterationId
501
366
  });
502
367
  }
503
- };
368
+ });
504
369
 
505
- export const getTaskLadderTool: ToolDefinition<{ taskId: string }> = {
370
+ export const getTaskLadderTool = defineTool<{ taskId: string }>({
506
371
  name: 'get_task_ladder',
507
372
  title: 'Get Task Ladder View',
508
373
  description: 'Retrieve multi-scale ladder view for a single task, connecting its macro phase, standard CPM timeline position, and concrete work evidence.',
509
374
  zodSchema: z.object({
510
375
  taskId: z.string().describe('The task ID')
511
376
  }),
512
- inputSchema: {
513
- type: 'object',
514
- properties: {
515
- taskId: { type: 'string', description: 'The task ID' }
516
- },
517
- required: ['taskId'],
518
- additionalProperties: false
519
- },
520
377
  annotations: { readOnlyHint: true },
521
378
  execute: async (args, target) => {
522
379
  return (target as any).getTaskLadder(args.taskId);
523
380
  }
524
- };
381
+ });
525
382
 
526
- export const getTaskMetricsTool: ToolDefinition<{ taskId: string }> = {
383
+ export const getTaskMetricsTool = defineTool<{ taskId: string }>({
527
384
  name: 'get_task_metrics',
528
385
  title: 'Get Task Metrics',
529
386
  description: 'Retrieve multi-dimensional task metrics including inferred actuals, active working duration, effort/duration/schedule variances, progress inference, and Earned Value Management (EVM) metrics.',
530
387
  zodSchema: z.object({
531
388
  taskId: z.string().describe('The task ID')
532
389
  }),
533
- inputSchema: {
534
- type: 'object',
535
- properties: {
536
- taskId: { type: 'string', description: 'The task ID' }
537
- },
538
- required: ['taskId'],
539
- additionalProperties: false
540
- },
541
390
  annotations: { readOnlyHint: true },
542
391
  execute: async (args, target) => {
543
392
  return (target as any).getTaskMetrics(args.taskId);
544
393
  }
545
- };
394
+ });
546
395
 
547
- export const getTaskProgressHistoryTool: ToolDefinition<{ taskId: string }> = {
396
+ export const getTaskProgressHistoryTool = defineTool<{ taskId: string }>({
548
397
  name: 'get_task_progress_history',
549
398
  title: 'Get Task Progress History',
550
399
  description: 'Retrieve time-series progress data points and curve shape classification (linear, s_curve, early_surge, late_rush, stalled) for a task.',
551
400
  zodSchema: z.object({
552
401
  taskId: z.string().describe('The task ID')
553
402
  }),
554
- inputSchema: {
555
- type: 'object',
556
- properties: {
557
- taskId: { type: 'string', description: 'The task ID' }
558
- },
559
- required: ['taskId'],
560
- additionalProperties: false
561
- },
562
403
  annotations: { readOnlyHint: true },
563
404
  execute: async (args, target) => {
564
405
  return (target as any).getTaskProgressHistory(args.taskId);
565
406
  }
566
- };
407
+ });
567
408
 
568
- export const getWorkloadDistributionTool: ToolDefinition<{
409
+ export const getWorkloadDistributionTool = defineTool<{
569
410
  projectId?: string;
570
411
  startDate?: string;
571
412
  endDate?: string;
@@ -573,7 +414,7 @@ export const getWorkloadDistributionTool: ToolDefinition<{
573
414
  groupBy?: 'assignee' | 'team' | 'taskType' | 'priority' | 'status';
574
415
  metric?: 'scheduled' | 'logged' | 'remaining' | 'blended';
575
416
  defaultWeeklyCapacityHours?: number;
576
- }> = {
417
+ }>({
577
418
  name: 'get_workload_distribution',
578
419
  title: 'Get Workload Distribution',
579
420
  description: 'Retrieve time-series workload and capacity distribution suitable for streamgraphs, stacked charts, and team capacity planning.',
@@ -586,19 +427,6 @@ export const getWorkloadDistributionTool: ToolDefinition<{
586
427
  metric: z.enum(['scheduled', 'logged', 'remaining', 'blended']).optional().describe('Effort metric mode'),
587
428
  defaultWeeklyCapacityHours: z.number().optional().describe('Default weekly capacity hours per person (default 40)')
588
429
  }),
589
- inputSchema: {
590
- type: 'object',
591
- properties: {
592
- projectId: { type: 'string', description: 'Project ID (optional, defaults to workspace-wide or ambient project)' },
593
- startDate: { type: 'string', description: 'Start date ISO (YYYY-MM-DD)' },
594
- endDate: { type: 'string', description: 'End date ISO (YYYY-MM-DD)' },
595
- interval: { type: 'string', enum: ['day', 'week', 'month'], description: 'Bucket interval' },
596
- groupBy: { type: 'string', enum: ['assignee', 'team', 'taskType', 'priority', 'status'], description: 'Dimension to segment by' },
597
- metric: { type: 'string', enum: ['scheduled', 'logged', 'remaining', 'blended'], description: 'Effort metric mode' },
598
- defaultWeeklyCapacityHours: { type: 'number', description: 'Default weekly capacity hours per person' }
599
- },
600
- additionalProperties: false
601
- },
602
430
  annotations: { readOnlyHint: true },
603
431
  execute: async (args, target, ambientContext) => {
604
432
  const projectId = args.projectId || ambientContext?.projectId;
@@ -611,7 +439,7 @@ export const getWorkloadDistributionTool: ToolDefinition<{
611
439
  defaultWeeklyCapacityHours: args.defaultWeeklyCapacityHours
612
440
  });
613
441
  }
614
- };
442
+ });
615
443
 
616
444
  export const ALL_TOOLS: ToolDefinition[] = [
617
445
  listProjectsTool,