@ezmodo/mcp-server 0.13.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.
Files changed (98) hide show
  1. package/README.md +305 -0
  2. package/config/development.js +20 -0
  3. package/config/endpoint-map.js +351 -0
  4. package/config/index.js +34 -0
  5. package/config/production.js +18 -0
  6. package/config/staging.js +18 -0
  7. package/handlers/access.js +141 -0
  8. package/handlers/activity.js +112 -0
  9. package/handlers/agents.js +95 -0
  10. package/handlers/ai-intelligence.js +55 -0
  11. package/handlers/attachments.js +30 -0
  12. package/handlers/catalogs.js +169 -0
  13. package/handlers/components.js +282 -0
  14. package/handlers/context-manifest.js +1150 -0
  15. package/handlers/decisions.js +114 -0
  16. package/handlers/designs.js +118 -0
  17. package/handlers/documents.js +227 -0
  18. package/handlers/entities.js +95 -0
  19. package/handlers/epics.js +190 -0
  20. package/handlers/facts.js +62 -0
  21. package/handlers/feature-flags.js +142 -0
  22. package/handlers/features.js +137 -0
  23. package/handlers/folders.js +127 -0
  24. package/handlers/git-context.js +917 -0
  25. package/handlers/github.js +72 -0
  26. package/handlers/graph.js +23 -0
  27. package/handlers/index.js +205 -0
  28. package/handlers/links.js +156 -0
  29. package/handlers/milestones.js +131 -0
  30. package/handlers/organizations.js +14 -0
  31. package/handlers/projects.js +122 -0
  32. package/handlers/recurring-tasks.js +33 -0
  33. package/handlers/tags.js +124 -0
  34. package/handlers/tasks.js +561 -0
  35. package/handlers/testing.js +116 -0
  36. package/handlers/todos.js +43 -0
  37. package/handlers/watchers.js +54 -0
  38. package/handlers/work-templates.js +32 -0
  39. package/index.js +175 -0
  40. package/lib/active-session.js +86 -0
  41. package/lib/auto-assign.js +93 -0
  42. package/lib/autolink.js +176 -0
  43. package/lib/changed-files.js +22 -0
  44. package/lib/env.js +45 -0
  45. package/lib/git-helpers.js +553 -0
  46. package/lib/git-utils.js +73 -0
  47. package/lib/http-client.js +164 -0
  48. package/lib/links-at-create.js +94 -0
  49. package/lib/local-cache.js +140 -0
  50. package/lib/logger.js +109 -0
  51. package/lib/manifest-loader.js +182 -0
  52. package/lib/manifest-query.js +686 -0
  53. package/lib/repo-config-dir.js +118 -0
  54. package/lib/version.js +10 -0
  55. package/lib/web-url.js +69 -0
  56. package/lib/worktree-tools.js +950 -0
  57. package/package.json +62 -0
  58. package/prompts/ai-workflow-automation.js +96 -0
  59. package/prompts/index.js +39 -0
  60. package/prompts/zephly-usage-guide-content.txt +631 -0
  61. package/prompts/zephly-usage-guide.js +119 -0
  62. package/tools/access-entity-types.js +28 -0
  63. package/tools/access.js +152 -0
  64. package/tools/activity.js +38 -0
  65. package/tools/agents.js +208 -0
  66. package/tools/ai-intelligence.js +111 -0
  67. package/tools/attachments.js +92 -0
  68. package/tools/catalogs.js +341 -0
  69. package/tools/components.js +249 -0
  70. package/tools/context-manifest.js +236 -0
  71. package/tools/decisions.js +168 -0
  72. package/tools/designs.js +222 -0
  73. package/tools/documents.js +287 -0
  74. package/tools/entities.js +223 -0
  75. package/tools/epics.js +267 -0
  76. package/tools/facts.js +70 -0
  77. package/tools/feature-flags.js +300 -0
  78. package/tools/features.js +246 -0
  79. package/tools/folders.js +122 -0
  80. package/tools/git-context.js +109 -0
  81. package/tools/github.js +172 -0
  82. package/tools/graph.js +70 -0
  83. package/tools/index.js +77 -0
  84. package/tools/link-params.js +93 -0
  85. package/tools/linkable-types.js +36 -0
  86. package/tools/links.js +199 -0
  87. package/tools/milestones.js +176 -0
  88. package/tools/organizations.js +23 -0
  89. package/tools/projects.js +172 -0
  90. package/tools/recurring-tasks.js +115 -0
  91. package/tools/tags.js +219 -0
  92. package/tools/task-item-schema.js +57 -0
  93. package/tools/task-type.js +33 -0
  94. package/tools/tasks.js +680 -0
  95. package/tools/testing.js +344 -0
  96. package/tools/todos.js +69 -0
  97. package/tools/watchers.js +81 -0
  98. package/tools/work-templates.js +96 -0
package/tools/tasks.js ADDED
@@ -0,0 +1,680 @@
1
+ /**
2
+ * Task Tools
3
+ * MCP tools for managing tasks
4
+ *
5
+ * Project-First Hierarchy: Tasks belong to projects (required), with optional
6
+ * component and epic grouping.
7
+ *
8
+ * NAMING (E-107): a project's type renames a task and its statuses — a sales
9
+ * project calls a task an "Activity" and calls `in_review` "Awaiting Approval".
10
+ * Read the words from `get_current_project_context().terminology` and use them
11
+ * in anything a person reads. The status VALUES sent to the API never change.
12
+ */
13
+
14
+ import { LINKS_ARRAY_SCHEMA, RELATED_ITEM_SCHEMA } from './link-params.js';
15
+ import { TASK_ITEM_PROPERTIES } from './task-item-schema.js';
16
+ import { TASK_TYPE_PROPERTY } from './task-type.js';
17
+
18
+ export const TASK_TOOLS = [
19
+ {
20
+ name: 'manage_task',
21
+ description: 'Create, update, or complete tasks. Also manages commit linking and ' +
22
+ '(re)generating the task\'s grounded "how it works" living description. ' +
23
+ 'IMPORTANT when creating: (1) Call get_context first with a keyword query matching the work topic ' +
24
+ 'to discover relevant files, patterns, and dependencies. (2) Write descriptions that explain WHY ' +
25
+ '(problem/goal), WHERE (specific files/endpoints from context), and HOW (approach using existing patterns). ' +
26
+ '(3) Write steps that reference specific file paths, not vague instructions. ' +
27
+ '(4) Every task should name the components it touches via componentIds — use get_current_project_context() to get available components.',
28
+ inputSchema: {
29
+ type: 'object',
30
+ properties: {
31
+ action: {
32
+ type: 'string',
33
+ enum: ['create', 'update', 'complete', 'defer', 'link_commit', 'unlink_commit', 'get_commits', 'generate_how_it_works', 'apply_how_it_works'],
34
+ description: 'Action to perform. "generate_how_it_works" (re)generates the task\'s ' +
35
+ 'grounded, source-attributed "how it works" living description from its reality — ' +
36
+ 'subtasks, comments, status history, commits plus a manifest pass over linked files ' +
37
+ '(requires taskId; AI-quota gated). "apply_how_it_works" (BYO-AI) persists a summary ' +
38
+ 'YOU authored: pass markdown + sources; the server validates your cited sources against ' +
39
+ 'the real grounded context (dropping fabricated ones) before saving — no server model call.',
40
+ },
41
+ // --- Identifiers (used by most actions) ---
42
+ taskId: {
43
+ type: 'string',
44
+ description: 'Task ID (required for update, complete, defer, link_commit, unlink_commit, get_commits, generate_how_it_works, apply_how_it_works)',
45
+ },
46
+ // --- apply_how_it_works (BYO-AI) ---
47
+ markdown: {
48
+ type: 'string',
49
+ description: 'Rendered markdown body for apply_how_it_works (the how-it-works YOU authored).',
50
+ },
51
+ sources: {
52
+ type: 'object',
53
+ description: 'Structured backing for apply_how_it_works: { claims: [{ text, sources: [ref...], ' +
54
+ 'confidence: "grounded"|"unverified" }], divergences: [{ intent, reality, severity }] }. Cite ' +
55
+ 'real source refs from the entity\'s grounded context; the server drops fabricated ones.',
56
+ },
57
+ projectId: {
58
+ type: 'string',
59
+ description: 'Project ID (required for create)',
60
+ },
61
+ // --- Create fields ---
62
+ title: {
63
+ type: 'string',
64
+ description: 'Task title (required for create)',
65
+ },
66
+ description: {
67
+ type: 'string',
68
+ description: 'Task description (supports markdown). Use real newlines, not literal \\n. Used by create and update. ' +
69
+ 'Writing this stays plain text; it does not create a backing document.',
70
+ },
71
+ componentId: {
72
+ type: 'string',
73
+ description: 'DEPRECATED single component. Still accepted \u2014 it is treated as a one-element componentIds \u2014 but prefer componentIds: a task touches every component its work spans, and this field can only name one of them.',
74
+ },
75
+ componentIds: {
76
+ type: 'array',
77
+ items: { type: 'string' },
78
+ description: 'Every component this task touches. Get available components from get_current_project_context(). ' +
79
+ 'On update this REPLACES the set: pass the full list, and an empty array clears it. Wins over componentId when both are given.',
80
+ },
81
+ epicId: {
82
+ type: 'string',
83
+ description: 'Epic ID to link this task to. Used by create and update (set to empty string to remove on update).',
84
+ },
85
+ priority: {
86
+ type: 'string',
87
+ enum: ['low', 'medium', 'high', 'urgent'],
88
+ description: 'Priority level. Used by create and update.',
89
+ },
90
+ taskType: {
91
+ ...TASK_TYPE_PROPERTY,
92
+ description: TASK_TYPE_PROPERTY.description +
93
+ ' Used by create and update; on update send "" to clear it back to untyped. ' +
94
+ 'This tool could not set it at all until #2545 — only the bulk create paths ' +
95
+ 'could — and the server discarded it on update until #2544, so a task\'s kind ' +
96
+ 'was effectively fixed at creation.',
97
+ },
98
+ parentTaskId: {
99
+ type: 'string',
100
+ description: 'Parent task link. Used by create. A testing task is expected to ' +
101
+ 'carry this — it is what the testing-split flow follows back to the work ' +
102
+ 'being verified.',
103
+ },
104
+ estimatedHours: {
105
+ type: 'number',
106
+ description: 'Estimated hours to complete. Used by create and update.',
107
+ },
108
+ tagIds: {
109
+ type: 'array',
110
+ items: { type: 'string' },
111
+ description: 'Tag IDs for categorization. Used by create and update; get available ' +
112
+ 'tags from get_current_project_context(). On update this REPLACES the set.',
113
+ },
114
+ assigneeType: {
115
+ type: 'string',
116
+ enum: ['human', 'ai'],
117
+ description: 'Whether assigned to human or AI agent (create only)',
118
+ },
119
+ assigneeId: {
120
+ type: 'string',
121
+ description: 'Assignee user ID or AI agent identifier (create only)',
122
+ },
123
+ assigneeName: {
124
+ type: 'string',
125
+ description: 'Assignee display name (create only)',
126
+ },
127
+ reviewers: {
128
+ type: 'array',
129
+ items: {
130
+ type: 'object',
131
+ properties: {
132
+ type: {
133
+ type: 'string',
134
+ enum: ['code', 'test'],
135
+ description: 'Reviewer type: code reviewer or test reviewer',
136
+ },
137
+ id: {
138
+ type: 'string',
139
+ description: 'User ID of the reviewer',
140
+ },
141
+ name: {
142
+ type: 'string',
143
+ description: 'Display name of the reviewer',
144
+ },
145
+ },
146
+ required: ['type', 'id', 'name'],
147
+ },
148
+ description: 'Array of reviewers (create only)',
149
+ },
150
+ knowledge: {
151
+ type: 'array',
152
+ items: {
153
+ type: 'object',
154
+ properties: {
155
+ type: {
156
+ type: 'string',
157
+ enum: ['fact', 'decision', 'context', 'reference'],
158
+ description: 'Type of knowledge item',
159
+ },
160
+ content: {
161
+ oneOf: [
162
+ { type: 'string' },
163
+ { type: 'object' },
164
+ ],
165
+ description: 'Knowledge content (string or structured data)',
166
+ },
167
+ tags: {
168
+ type: 'array',
169
+ items: { type: 'string' },
170
+ description: 'Optional tags for categorization',
171
+ },
172
+ },
173
+ required: ['type', 'content'],
174
+ },
175
+ description: 'Structured knowledge items (create only)',
176
+ },
177
+ steps: {
178
+ type: 'array',
179
+ items: { type: 'string' },
180
+ description: 'Implementation steps as strings (create only)',
181
+ },
182
+ aiWork: {
183
+ type: 'boolean',
184
+ description:
185
+ 'Whether the task is eligible for the AI agent queue. Used by create and update. ' +
186
+ 'Defaults to TRUE on create — omit it unless you specifically want a task agents ' +
187
+ 'should not pick up, in which case send false.',
188
+ },
189
+ origin: {
190
+ type: 'string',
191
+ enum: ['planned', 'discovered', 'scope-creep', 'rework', 'untracked'],
192
+ description: 'Task origin for drift classification. Used by create and update.',
193
+ },
194
+ discoveredDuringTaskId: {
195
+ type: 'string',
196
+ description: 'Task ID that was being worked on when this task was discovered. Used by create and update.',
197
+ },
198
+ linkedFiles: {
199
+ type: 'array',
200
+ items: {
201
+ type: 'object',
202
+ properties: {
203
+ path: { type: 'string', description: 'Relative file path from project root' },
204
+ source: { type: 'string', enum: ['manual', 'commit', 'mcp'], description: 'How this link was created (default: "mcp")' },
205
+ },
206
+ required: ['path'],
207
+ },
208
+ description: 'Source files this task affects. Used by create only — for update, use addLinkedFile/removeLinkedFile.',
209
+ },
210
+ changedFiles: {
211
+ type: 'array',
212
+ items: { type: 'string' },
213
+ description:
214
+ 'Repo-relative paths you touched. Simpler alternative to linkedFiles — just the paths. ' +
215
+ 'ezmodo resolves them to the components (screens/pages/areas) that own them and links ' +
216
+ 'this task to them automatically; anything it is unsure about comes back as ' +
217
+ 'linkSuggestions for you to accept or reject. Used by create and update.',
218
+ },
219
+ autolink: {
220
+ type: 'boolean',
221
+ description:
222
+ 'Default true. When no files are supplied, ezmodo searches the codebase for the task\'s ' +
223
+ 'own words and links what it finds. Set false to skip that entirely — useful for ' +
224
+ 'bookkeeping tasks that touch no code.',
225
+ },
226
+ // --- Update-only fields ---
227
+ status: {
228
+ type: 'string',
229
+ enum: ['backlog', 'todo', 'in_progress', 'in_review', 'blocked', 'completed', 'cancelled'],
230
+ description: 'Task status (update only)',
231
+ },
232
+ addReviewer: {
233
+ type: 'object',
234
+ description: 'Add a reviewer (update only)',
235
+ properties: {
236
+ type: { type: 'string', enum: ['code', 'test'] },
237
+ id: { type: 'string' },
238
+ name: { type: 'string' },
239
+ },
240
+ required: ['type', 'id', 'name'],
241
+ },
242
+ removeReviewer: {
243
+ type: 'object',
244
+ description: 'Remove a reviewer (update only)',
245
+ properties: {
246
+ type: { type: 'string', enum: ['code', 'test'] },
247
+ id: { type: 'string' },
248
+ },
249
+ required: ['type', 'id'],
250
+ },
251
+ addKnowledge: {
252
+ type: 'array',
253
+ items: {
254
+ type: 'object',
255
+ properties: {
256
+ type: {
257
+ type: 'string',
258
+ enum: ['fact', 'decision', 'context', 'reference'],
259
+ },
260
+ content: {
261
+ oneOf: [
262
+ { type: 'string' },
263
+ { type: 'object' },
264
+ ],
265
+ },
266
+ tags: {
267
+ type: 'array',
268
+ items: { type: 'string' },
269
+ },
270
+ },
271
+ required: ['type', 'content'],
272
+ },
273
+ description: 'New knowledge items to add (update only)',
274
+ },
275
+ addStep: {
276
+ type: 'string',
277
+ description: 'Add a new step (update only)',
278
+ },
279
+ toggleSteps: {
280
+ type: 'array',
281
+ items: {
282
+ type: 'object',
283
+ properties: {
284
+ stepId: { type: 'string' },
285
+ completed: { type: 'boolean' },
286
+ },
287
+ required: ['stepId', 'completed'],
288
+ },
289
+ description: 'Toggle completion for one or more steps in a single update (update only). Preferred over toggleStep.',
290
+ },
291
+ toggleStep: {
292
+ type: 'object',
293
+ description: 'Toggle a single step. Prefer toggleSteps for batch. (update only)',
294
+ properties: {
295
+ stepId: { type: 'string' },
296
+ completed: { type: 'boolean' },
297
+ },
298
+ },
299
+ updateStepContent: {
300
+ type: 'object',
301
+ description: 'Update step content (update only)',
302
+ properties: {
303
+ stepId: { type: 'string' },
304
+ content: { type: 'string' },
305
+ },
306
+ },
307
+ deleteStep: {
308
+ type: 'string',
309
+ description: 'Delete a step by ID (update only)',
310
+ },
311
+ sprintStartDate: {
312
+ type: 'string',
313
+ description: 'ISO 8601 date for sprint assignment (update only)',
314
+ },
315
+ aiAgentName: {
316
+ type: 'string',
317
+ description: 'AI agent name that worked on this task (update only)',
318
+ },
319
+ addDependency: {
320
+ type: 'string',
321
+ description: 'Add a blocking dependency by task ID. If the dependency task is not yet completed, this task will be automatically set to "blocked" status. When the dependency is completed, this task auto-unblocks. (update only)',
322
+ },
323
+ removeDependency: {
324
+ type: 'string',
325
+ description: 'Remove a blocking dependency by task ID. If this task was blocked only by this dependency, it will auto-unblock to its previous status. (update only)',
326
+ },
327
+ addRelatedTask: {
328
+ type: 'string',
329
+ description: 'Add a bidirectional related task link by task ID. Unlike dependencies, related tasks are non-blocking soft links. (update only)',
330
+ },
331
+ removeRelatedTask: {
332
+ type: 'string',
333
+ description: 'Remove a bidirectional related task link by task ID (update only)',
334
+ },
335
+ links: LINKS_ARRAY_SCHEMA,
336
+ addLinks: {
337
+ type: 'array',
338
+ items: LINKS_ARRAY_SCHEMA.items,
339
+ description: 'Add cross-entity links to an existing task (update only). ' +
340
+ 'Same shape as `links` on create.',
341
+ },
342
+ removeLinks: {
343
+ type: 'array',
344
+ items: LINKS_ARRAY_SCHEMA.items,
345
+ description: 'Remove cross-entity links from an existing task (update only).',
346
+ },
347
+ addRelatedItem: {
348
+ ...RELATED_ITEM_SCHEMA,
349
+ description: 'DEPRECATED — use addLinks. Add a cross-entity related item ' +
350
+ 'link (update only). Still works; addLinks takes several at once and ' +
351
+ 'supports linkType.',
352
+ },
353
+ removeRelatedItem: {
354
+ ...RELATED_ITEM_SCHEMA,
355
+ description: 'DEPRECATED — use removeLinks. Remove a cross-entity related ' +
356
+ 'item link (update only). Still works.',
357
+ },
358
+ addLinkedFile: {
359
+ type: 'object',
360
+ description: 'Link a source file to this task. Files can be linked to enable file-based dependency inference. (update only)',
361
+ properties: {
362
+ path: { type: 'string', description: 'Relative file path from project root (e.g., "api/internal/core/tasks/models.go")' },
363
+ source: { type: 'string', enum: ['manual', 'commit', 'mcp'], description: 'How this link was created (default: "mcp")' },
364
+ },
365
+ required: ['path'],
366
+ },
367
+ removeLinkedFile: {
368
+ type: 'string',
369
+ description: 'Remove a linked file by its path (update only)',
370
+ },
371
+ isBacklogged: {
372
+ type: 'boolean',
373
+ description: 'Move task to/from backlog (update only)',
374
+ },
375
+ backlogPriority: {
376
+ type: 'number',
377
+ description: 'Priority within backlog (update only)',
378
+ },
379
+ // --- Complete fields ---
380
+ completionNotes: {
381
+ type: 'string',
382
+ description: 'Notes about task completion (complete only)',
383
+ },
384
+ // --- Defer fields ---
385
+ reason: {
386
+ type: 'string',
387
+ description: 'Why this work is being deferred. Required for defer.',
388
+ },
389
+ stepId: {
390
+ type: 'string',
391
+ description: 'If set on defer, promotes this step on the source task into a new task ' +
392
+ 'inside the deferred epic; otherwise the whole task is reparented. Idempotent — ' +
393
+ 're-running with the same stepId returns the existing promoted task.',
394
+ },
395
+ unblockedBy: {
396
+ type: 'string',
397
+ description: 'Optional free-form note describing what would unblock this deferred work. ' +
398
+ 'Helpful for the PM review at milestone close. (defer only)',
399
+ },
400
+ targetMilestoneId: {
401
+ type: 'string',
402
+ description: 'Optional milestone override. When set, the deferred work attaches to this ' +
403
+ 'milestone\'s deferred epic instead of the source task\'s milestone. (defer only)',
404
+ },
405
+ // --- Commit linking fields ---
406
+ sha: {
407
+ type: 'string',
408
+ description: 'Full git commit hash, 40 chars (link_commit only)',
409
+ },
410
+ message: {
411
+ type: 'string',
412
+ description: 'Commit message first line (link_commit only)',
413
+ },
414
+ author: {
415
+ type: 'string',
416
+ description: 'Commit author name (link_commit only)',
417
+ },
418
+ email: {
419
+ type: 'string',
420
+ description: 'Author email (link_commit only)',
421
+ },
422
+ timestamp: {
423
+ type: 'string',
424
+ description: 'Commit timestamp ISO 8601 (link_commit only)',
425
+ },
426
+ url: {
427
+ type: 'string',
428
+ description: 'URL to commit on git provider (link_commit only)',
429
+ },
430
+ branch: {
431
+ type: 'string',
432
+ description: 'Branch name where commit was made (link_commit only)',
433
+ },
434
+ authorType: {
435
+ type: 'string',
436
+ enum: ['human', 'ai_agent'],
437
+ description: 'Whether commit authored by human or AI agent (link_commit only)',
438
+ },
439
+ files: {
440
+ type: 'array',
441
+ items: { type: 'string' },
442
+ description: 'File paths changed in commit (link_commit only). OPTIONAL — omit it and the server derives the list from the commit itself via git. Pass it only to override that, e.g. to record a subset.',
443
+ },
444
+ // --- Unlink commit fields ---
445
+ commitId: {
446
+ type: 'string',
447
+ description: 'Linked commit ID to remove, not the SHA (unlink_commit only)',
448
+ },
449
+ },
450
+ required: ['action'],
451
+ },
452
+ },
453
+ {
454
+ name: 'create_tasks',
455
+ description: 'Create MANY tasks in ONE call. Use this instead of repeated ' +
456
+ 'manage_task action:"create" whenever you are creating more than two tasks at ' +
457
+ 'once — breaking an epic into tasks is the case it exists for. One call does ' +
458
+ 'the authentication, plan check, rate-limit accounting and database connection ' +
459
+ 'setup once instead of per task; a burst of individual creates is what made the ' +
460
+ 'app unresponsive for 45 minutes on 2026-07-28. ' +
461
+ 'Put shared values (projectId, epicId, componentIds) at the TOP LEVEL and let the ' +
462
+ 'items inherit them — only override per item where a task genuinely differs. ' +
463
+ 'Each item accepts the same fields as manage_task action:"create". ' +
464
+ 'Returns a per-item result array: on partial failure retry ONLY the items marked ' +
465
+ 'failed, since the successful ones already exist and re-sending the whole batch ' +
466
+ 'would duplicate them. ' +
467
+ 'Limited to 40 tasks per call — split anything larger. ' +
468
+ 'For a single task, keep using manage_task action:"create", which also resolves ' +
469
+ 'code context and applies links.',
470
+ inputSchema: {
471
+ type: 'object',
472
+ properties: {
473
+ projectId: {
474
+ type: 'string',
475
+ description: 'Project every task belongs to (required). All tasks in one ' +
476
+ 'call must share it. Items may omit projectId and inherit this.',
477
+ },
478
+ epicId: {
479
+ type: 'string',
480
+ description: 'Epic applied to every item that does not set its own. The ' +
481
+ 'usual case for a breakdown: one epic for the whole batch.',
482
+ },
483
+ componentId: {
484
+ type: 'string',
485
+ description: 'DEPRECATED single component applied to every item that does ' +
486
+ 'not name its own. Prefer componentIds.',
487
+ },
488
+ componentIds: {
489
+ type: 'array',
490
+ items: { type: 'string' },
491
+ description: 'Components applied to every item that does not name its own. ' +
492
+ 'An item naming either form keeps its own set. ' +
493
+ 'Get available components from get_current_project_context().',
494
+ },
495
+ tasks: {
496
+ type: 'array',
497
+ description: 'The tasks to create (1-40). Each item takes the same fields ' +
498
+ 'as manage_task action:"create" (both draw on the shared item schema, so ' +
499
+ 'the two cannot drift); only title is required once the shared projectId ' +
500
+ 'is set.',
501
+ items: {
502
+ type: 'object',
503
+ properties: {
504
+ ...TASK_ITEM_PROPERTIES,
505
+ projectId: { type: 'string', description: 'Overrides the shared projectId — must match it' },
506
+ epicId: { type: 'string', description: 'Overrides the shared epicId' },
507
+ },
508
+ required: ['title'],
509
+ },
510
+ },
511
+ },
512
+ required: ['projectId', 'tasks'],
513
+ },
514
+ },
515
+ {
516
+ name: 'search_tasks',
517
+ description: 'Search for tasks with filters or semantic search. ' +
518
+ 'Use for filtered queries (by status, priority, component, etc). ' +
519
+ 'To get ALL tasks in an epic, prefer get_epic with includeTasks=true. ' +
520
+ 'Use search_tasks with epicId only when you need additional filtering ' +
521
+ '(e.g., only in_progress tasks within an epic). ' +
522
+ 'When searchText is provided, uses AI-powered semantic search to ' +
523
+ 'find tasks by meaning. Falls back to basic text matching if unavailable.',
524
+ inputSchema: {
525
+ type: 'object',
526
+ properties: {
527
+ projectId: {
528
+ type: 'string',
529
+ description: 'Filter by project ID (primary scope)',
530
+ },
531
+ componentId: {
532
+ type: 'string',
533
+ description: 'Filter by component ID (codebase area)',
534
+ },
535
+ epicId: {
536
+ type: 'string',
537
+ description: 'Filter by epic ID (feature grouping)',
538
+ },
539
+ status: {
540
+ type: 'string',
541
+ enum: ['backlog', 'todo', 'in_progress', 'in_review', 'blocked', 'completed', 'cancelled'],
542
+ description: 'Filter by status',
543
+ },
544
+ priority: {
545
+ type: 'string',
546
+ enum: ['low', 'medium', 'high', 'urgent'],
547
+ description: 'Filter by priority level',
548
+ },
549
+ assignedToAI: {
550
+ type: 'boolean',
551
+ description: 'Filter for AI-assigned tasks only',
552
+ },
553
+ isBacklogged: {
554
+ type: 'boolean',
555
+ description: 'Filter for backlogged tasks only',
556
+ },
557
+ searchText: {
558
+ type: 'string',
559
+ description: 'Natural language query for semantic search ' +
560
+ '(e.g., "fix login errors", "improve performance"). ' +
561
+ 'Automatically uses vector similarity if available.',
562
+ },
563
+ minSimilarity: {
564
+ type: 'number',
565
+ description: 'Minimum similarity score for semantic search (0-1, default: 0.3)',
566
+ },
567
+ limit: {
568
+ type: 'number',
569
+ description: 'Maximum number of results (default: 10 for semantic, 50 for basic)',
570
+ default: 50,
571
+ },
572
+ },
573
+ },
574
+ },
575
+ {
576
+ name: 'report_untracked_work',
577
+ description: 'Retroactively capture work you did WITHOUT a tracked task. Call this ' +
578
+ 'when you realize mid-conversation that you made code changes without an active ' +
579
+ 'ezmodo task — e.g. a "just fix this real quick" request, a quick bug fix, or work ' +
580
+ 'discovered while doing something else. Creates a task classified by `origin` ' +
581
+ '(default "untracked"), records the branch + changed files as evidence, links the ' +
582
+ 'discovery chain when applicable, and starts it in_progress so it becomes your ' +
583
+ 'active task (also updating the desktop app). Returns the created task so you can ' +
584
+ 'keep tracking against it. Prefer this over silently continuing untracked.',
585
+ inputSchema: {
586
+ type: 'object',
587
+ properties: {
588
+ projectId: {
589
+ type: 'string',
590
+ description: 'The project the work belongs to (required).',
591
+ },
592
+ title: {
593
+ type: 'string',
594
+ description: 'A concise title for the work that was done (required).',
595
+ },
596
+ description: {
597
+ type: 'string',
598
+ description: 'What was done and why. The branch and changed files are appended ' +
599
+ 'automatically as evidence — do not duplicate them here.',
600
+ },
601
+ origin: {
602
+ type: 'string',
603
+ enum: ['discovered', 'scope-creep', 'rework', 'untracked'],
604
+ description: 'How the work arose (drift classification). Default: "untracked". ' +
605
+ 'Use "discovered" if found while working on another task, "scope-creep" if it ' +
606
+ 'went beyond the active task\'s intended scope, "rework" if redoing prior work.',
607
+ },
608
+ discoveredDuringTaskId: {
609
+ type: 'string',
610
+ description: 'The task you were working on when this work was found. Set this ' +
611
+ 'when origin is "discovered" or "scope-creep" to link the discovery chain.',
612
+ },
613
+ branch: {
614
+ type: 'string',
615
+ description: 'The git branch the work was done on (recorded as evidence).',
616
+ },
617
+ changedFiles: {
618
+ type: 'array',
619
+ items: { type: 'string' },
620
+ description: 'Repo-relative paths of files that were changed. Recorded in the ' +
621
+ 'description and linked to the task for dependency inference.',
622
+ },
623
+ componentId: {
624
+ type: 'string',
625
+ description: 'DEPRECATED single component. Still accepted as a one-element ' +
626
+ 'componentIds, but prefer componentIds \u2014 untracked work often spans ' +
627
+ 'more than one area, which is part of why it was untracked.',
628
+ },
629
+ componentIds: {
630
+ type: 'array',
631
+ items: { type: 'string' },
632
+ description: 'Every component this work touched. Get available components ' +
633
+ 'from get_current_project_context().',
634
+ },
635
+ epicId: {
636
+ type: 'string',
637
+ description: 'Epic this work belongs under. Set it when the untracked work ' +
638
+ 'advanced an epic you already know about, so the captured task lands in ' +
639
+ 'the right place instead of floating in the backlog.',
640
+ },
641
+ featureId: {
642
+ type: 'string',
643
+ description: 'Feature (product capability) this work advanced. Recorded as a ' +
644
+ 'relates_to link on the created task, so the capability map stays honest ' +
645
+ 'even for work that was never planned.',
646
+ },
647
+ links: LINKS_ARRAY_SCHEMA,
648
+ },
649
+ required: ['projectId', 'title'],
650
+ },
651
+ },
652
+ {
653
+ name: 'get_task',
654
+ description: 'Retrieve a single task by ID or task number with full details. ' +
655
+ 'Use when you have a specific task ID or number. ' +
656
+ 'To get all tasks in an epic, use get_epic with includeTasks=true instead. ' +
657
+ 'Provide EITHER taskId OR both taskNumber and projectId. ' +
658
+ 'Responses include `descriptionDocumentId` — the id of the backing rich-description Document ' +
659
+ 'when the description has been promoted to one (E-189), otherwise omitted.',
660
+ inputSchema: {
661
+ type: 'object',
662
+ properties: {
663
+ taskId: {
664
+ type: 'string',
665
+ description: 'The document ID of the task to retrieve (e.g., "abc123").',
666
+ },
667
+ taskNumber: {
668
+ type: 'number',
669
+ description: 'The sequential task number (e.g., 42 for task ' +
670
+ '#42). Must be used with projectId. This is the ' +
671
+ 'human-friendly number displayed in the UI.',
672
+ },
673
+ projectId: {
674
+ type: 'string',
675
+ description: 'The project ID. Required when using taskNumber instead of taskId.',
676
+ },
677
+ },
678
+ },
679
+ },
680
+ ];