@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
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Recurring Task Schedule Tools
3
+ * MCP tools for the E-211 recurrence engine.
4
+ *
5
+ * A recurring task schedule is a durable record of "what task to create, on what
6
+ * cadence, until when" (e.g. "every weekday at 9am for three weeks, create a
7
+ * 'check app stats' task"). A scheduled runner instantiates the task each time
8
+ * the schedule fires. The payload is either an inline task definition or a
9
+ * reference to a work template (manage_work_template).
10
+ */
11
+
12
+ import { TASK_TYPE_PROPERTY } from './task-type.js';
13
+
14
+ const RECURRENCE_SCHEMA = {
15
+ type: 'object',
16
+ description: 'The recurrence spec. Occurrences fire at atHour:atMinute local to timezone.',
17
+ properties: {
18
+ frequency: {
19
+ type: 'string',
20
+ enum: ['daily', 'weekly', 'monthly'],
21
+ description: 'Base cadence.',
22
+ },
23
+ interval: {
24
+ type: 'number',
25
+ description: 'Every N units (e.g. interval 2 + weekly = biweekly). Must be >= 1. Default 1.',
26
+ },
27
+ byWeekdays: {
28
+ type: 'array',
29
+ items: { type: 'number' },
30
+ description: 'Weekly only: weekdays to fire on, 0=Sunday .. 6=Saturday. Required for weekly.',
31
+ },
32
+ byMonthDay: {
33
+ type: 'number',
34
+ description: 'Monthly only: day-of-month 1..31 (clamped to shorter months). Required for monthly.',
35
+ },
36
+ atHour: { type: 'number', description: 'Hour of day 0..23.' },
37
+ atMinute: { type: 'number', description: 'Minute of hour 0..59. Default 0.' },
38
+ timezone: { type: 'string', description: 'IANA timezone (e.g. "America/New_York"). Default "UTC".' },
39
+ },
40
+ required: ['frequency'],
41
+ };
42
+
43
+ const INLINE_TASK_SCHEMA = {
44
+ type: 'object',
45
+ description: 'Inline task definition to create each time the schedule fires (alternative to templateId).',
46
+ properties: {
47
+ title: { type: 'string', description: 'Task title (required for an inline schedule).' },
48
+ description: { type: 'string' },
49
+ steps: { type: 'array', items: { type: 'string' } },
50
+ priority: { type: 'string', enum: ['low', 'medium', 'high', 'urgent'] },
51
+ taskType: TASK_TYPE_PROPERTY,
52
+ componentId: { type: 'string' },
53
+ epicId: { type: 'string' },
54
+ tagIds: { type: 'array', items: { type: 'string' } },
55
+ assigneeId: { type: 'string' },
56
+ assigneeName: { type: 'string' },
57
+ assigneeType: { type: 'string', enum: ['human', 'ai'] },
58
+ },
59
+ };
60
+
61
+ export const RECURRING_TASK_TOOLS = [
62
+ {
63
+ name: 'manage_recurring_task',
64
+ description: 'Create, update, pause, resume, delete, list, or get a recurring task schedule ' +
65
+ '(E-211). A schedule creates a task on a cadence (e.g. "every weekday at 9am for three weeks"). ' +
66
+ 'The payload is either an inlineTask definition OR a templateId (a work template). Returns the ' +
67
+ 'schedule including its computed nextRunAt so you can confirm when it will next fire.',
68
+ inputSchema: {
69
+ type: 'object',
70
+ properties: {
71
+ action: {
72
+ type: 'string',
73
+ enum: ['create', 'update', 'pause', 'resume', 'delete', 'list', 'get'],
74
+ description: 'Action to perform. "pause"/"resume" toggle whether the runner fires the ' +
75
+ 'schedule (resume recomputes nextRunAt from now). "list" needs organizationId ' +
76
+ '(optionally projectId); "get"/"update"/"pause"/"resume"/"delete" need scheduleId.',
77
+ },
78
+ // --- Identifiers ---
79
+ organizationId: { type: 'string', description: 'Organization ID (required for create, list).' },
80
+ projectId: {
81
+ type: 'string',
82
+ description: 'Project the created task(s) belong to (required for create; optional filter for list).',
83
+ },
84
+ scheduleId: {
85
+ type: 'string',
86
+ description: 'Schedule ID (required for get, update, pause, resume, delete).',
87
+ },
88
+ // --- Create / update fields ---
89
+ title: {
90
+ type: 'string',
91
+ description: 'Human name for the schedule (required for create), e.g. "Daily app-stats check".',
92
+ },
93
+ recurrence: RECURRENCE_SCHEMA,
94
+ startsAt: {
95
+ type: 'string',
96
+ description: 'ISO 8601 timestamp: earliest the schedule may fire (required for create).',
97
+ },
98
+ until: {
99
+ type: 'string',
100
+ description: 'ISO 8601 timestamp end bound (optional). The schedule stops after this time.',
101
+ },
102
+ count: {
103
+ type: 'number',
104
+ description: 'Max number of occurrences (optional end bound). The schedule stops after this many tasks.',
105
+ },
106
+ templateId: {
107
+ type: 'string',
108
+ description: 'A work template to instantiate each fire (mutually exclusive with inlineTask).',
109
+ },
110
+ inlineTask: INLINE_TASK_SCHEMA,
111
+ },
112
+ required: ['action'],
113
+ },
114
+ },
115
+ ];
package/tools/tags.js ADDED
@@ -0,0 +1,219 @@
1
+ /**
2
+ * Tag Tools
3
+ * MCP tools for managing tags (organization-scoped categorization)
4
+ *
5
+ * Consolidated: manage_tag (create/update/delete/merge/bulk_tag) + list_tags (get/list/find/suggest)
6
+ *
7
+ * NOTE for future sweeps: the `['task', 'epic', 'document', 'goal']` enums below
8
+ * are the TAGGABLE set, NOT the LINKABLE set. They are deliberately NOT
9
+ * `LINKABLE_TYPES` (tools/linkable-types.js) — entity_tags covers a different,
10
+ * smaller set of entities than entity_links, and widening these to the 14
11
+ * linkable types would offer agents combinations the tagging API rejects.
12
+ * Do not "fix" them to LINKABLE_TYPES.
13
+ */
14
+
15
+ export const TAG_TOOLS = [
16
+ {
17
+ name: 'manage_tag',
18
+ description: 'Create, update, delete, merge, or bulk-apply tags. ' +
19
+ 'Tags are organization-scoped categorization labels for tasks, epics, and other entities.',
20
+ inputSchema: {
21
+ type: 'object',
22
+ properties: {
23
+ action: {
24
+ type: 'string',
25
+ enum: ['create', 'update', 'delete', 'merge', 'bulk_tag'],
26
+ description: 'Action to perform',
27
+ },
28
+ // --- Identifiers ---
29
+ tagId: {
30
+ type: 'string',
31
+ description: 'Tag ID (required for update, delete)',
32
+ },
33
+ organizationId: {
34
+ type: 'string',
35
+ description: 'Organization ID (required for create, merge, bulk_tag)',
36
+ },
37
+ // --- Create fields ---
38
+ name: {
39
+ type: 'string',
40
+ description: 'Tag name (required for create, optional for update)',
41
+ },
42
+ color: {
43
+ type: 'string',
44
+ description: 'Hex color code e.g. "#FF5733" (required for create, optional for update)',
45
+ },
46
+ category: {
47
+ type: 'string',
48
+ enum: ['priority', 'status', 'type', 'team', 'customer', 'custom'],
49
+ description: 'Tag category (default: custom). Used by create and update.',
50
+ },
51
+ description: {
52
+ type: 'string',
53
+ description: 'Tag description. Used by create and update.',
54
+ },
55
+ aliases: {
56
+ type: 'array',
57
+ items: { type: 'string' },
58
+ description: 'Alternative names for search/matching. Used by create and update.',
59
+ },
60
+ pinned: {
61
+ type: 'boolean',
62
+ description: 'Whether the tag should be pinned (shown prominently). Used by create and update.',
63
+ },
64
+ // --- Update-only fields ---
65
+ hidden: {
66
+ type: 'boolean',
67
+ description: 'Whether the tag should be hidden (update only)',
68
+ },
69
+ // --- Merge fields ---
70
+ sourceTagIds: {
71
+ type: 'array',
72
+ items: { type: 'string' },
73
+ description: 'IDs of tags to merge — will be deleted after merge (merge only)',
74
+ },
75
+ targetTagId: {
76
+ type: 'string',
77
+ description: 'ID of the tag to merge into — will be kept (merge only)',
78
+ },
79
+ createAlias: {
80
+ type: 'boolean',
81
+ description: 'Add source tag names as aliases to the target tag (merge only, default: true)',
82
+ },
83
+ // --- Bulk tag fields ---
84
+ tagIds: {
85
+ type: 'array',
86
+ items: { type: 'string' },
87
+ description: 'IDs of tags to apply (bulk_tag only)',
88
+ },
89
+ entities: {
90
+ type: 'array',
91
+ items: {
92
+ type: 'object',
93
+ properties: {
94
+ entityType: {
95
+ type: 'string',
96
+ enum: ['task', 'epic', 'document', 'goal'],
97
+ description: 'Type of entity',
98
+ },
99
+ entityId: {
100
+ type: 'string',
101
+ description: 'ID of the entity',
102
+ },
103
+ },
104
+ required: ['entityType', 'entityId'],
105
+ },
106
+ description: 'List of entities to tag (bulk_tag only)',
107
+ },
108
+ operation: {
109
+ type: 'string',
110
+ enum: ['add', 'remove', 'set'],
111
+ description: 'Operation: add, remove, or set/replace all tags (bulk_tag only)',
112
+ },
113
+ },
114
+ required: ['action'],
115
+ },
116
+ },
117
+ {
118
+ name: 'list_tags',
119
+ description: 'List, get, find entities by tags, or get AI tag suggestions. ' +
120
+ 'Modes: (1) Provide tagId for single tag lookup. ' +
121
+ '(2) Provide organizationId to list tags with optional filters. ' +
122
+ '(3) Set findEntities=true with tagIds array to search entities by tags. ' +
123
+ '(4) Set suggest=true with queryText for AI-powered tag suggestions.',
124
+ inputSchema: {
125
+ type: 'object',
126
+ properties: {
127
+ // --- Single tag lookup ---
128
+ tagId: {
129
+ type: 'string',
130
+ description: 'Get a single tag by ID',
131
+ },
132
+ // --- List mode ---
133
+ organizationId: {
134
+ type: 'string',
135
+ description: 'Organization ID (required for list, find, suggest modes)',
136
+ },
137
+ category: {
138
+ type: 'string',
139
+ enum: ['priority', 'status', 'type', 'team', 'customer', 'custom'],
140
+ description: 'Filter by tag category (list mode)',
141
+ },
142
+ projectId: {
143
+ type: 'string',
144
+ description: 'Filter by project ID (list and find modes)',
145
+ },
146
+ searchQuery: {
147
+ type: 'string',
148
+ description: 'Search query to filter tags by name, description, or aliases (list mode)',
149
+ },
150
+ pinnedOnly: {
151
+ type: 'boolean',
152
+ description: 'Only return pinned tags (list mode)',
153
+ },
154
+ includeHidden: {
155
+ type: 'boolean',
156
+ description: 'Include hidden tags in results (list mode)',
157
+ },
158
+ // --- Get by name (alternative to tagId) ---
159
+ name: {
160
+ type: 'string',
161
+ description: 'Tag name to look up (used with organizationId instead of tagId)',
162
+ },
163
+ // --- Find entities mode ---
164
+ findEntities: {
165
+ type: 'boolean',
166
+ description: 'Set to true to search for entities by tags',
167
+ },
168
+ tagIds: {
169
+ type: 'array',
170
+ items: { type: 'string' },
171
+ description: 'IDs of tags to filter by (findEntities mode)',
172
+ },
173
+ operator: {
174
+ type: 'string',
175
+ enum: ['AND', 'OR'],
176
+ description: 'Match all tags (AND) or any tag (OR) (findEntities mode, default: AND)',
177
+ },
178
+ entityTypes: {
179
+ type: 'array',
180
+ items: {
181
+ type: 'string',
182
+ enum: ['task', 'epic', 'document', 'goal'],
183
+ },
184
+ description: 'Types of entities to search (findEntities mode, default: all)',
185
+ },
186
+ epicId: {
187
+ type: 'string',
188
+ description: 'Scope search to a specific epic (findEntities mode)',
189
+ },
190
+ limit: {
191
+ type: 'number',
192
+ description: 'Maximum results (findEntities default: 50, suggest default: 5)',
193
+ },
194
+ // --- Suggest mode ---
195
+ suggest: {
196
+ type: 'boolean',
197
+ description: 'Set to true for AI-powered tag suggestions',
198
+ },
199
+ queryText: {
200
+ type: 'string',
201
+ description: 'Text to find matching tags for, e.g. task title+description (suggest mode)',
202
+ },
203
+ entityType: {
204
+ type: 'string',
205
+ enum: ['task', 'epic', 'document', 'goal'],
206
+ description: 'Type of entity being tagged — for context (suggest mode)',
207
+ },
208
+ entityId: {
209
+ type: 'string',
210
+ description: 'ID of entity being tagged — for context (suggest mode)',
211
+ },
212
+ minSimilarity: {
213
+ type: 'number',
214
+ description: 'Minimum similarity threshold 0-1 (suggest mode, default: 0.3)',
215
+ },
216
+ },
217
+ },
218
+ },
219
+ ];
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Shared per-item task schema for the batch create paths.
3
+ *
4
+ * Two tools create tasks in bulk: `create_tasks` (a batch under an existing
5
+ * epic) and `manage_epic action:"create"` with a `tasks` array (an epic and its
6
+ * breakdown in one request, #2247). They accept the SAME item, and duplicating
7
+ * ~20 field descriptions across both is how the two drift apart — an agent then
8
+ * learns that a field it may pass to one is not accepted by the other.
9
+ *
10
+ * The shared part deliberately excludes projectId and epicId. Those are batch-
11
+ * level in `create_tasks` and fixed by the epic in the nested case, so each tool
12
+ * adds them (or does not) for itself.
13
+ */
14
+ import { TASK_TYPE_PROPERTY } from './task-type.js';
15
+
16
+ export const TASK_ITEM_PROPERTIES = {
17
+ title: { type: 'string', description: 'Task title (required)' },
18
+ description: {
19
+ type: 'string',
20
+ description: 'Task description (markdown). Explain WHY, WHERE and HOW, ' +
21
+ 'referencing specific file paths — same guidance as manage_task.',
22
+ },
23
+ steps: {
24
+ type: 'array',
25
+ items: { type: 'string' },
26
+ description: 'Implementation steps referencing specific files',
27
+ },
28
+ priority: { type: 'string', enum: ['low', 'medium', 'high', 'urgent'] },
29
+ status: {
30
+ type: 'string',
31
+ enum: ['backlog', 'todo', 'in_progress', 'in_review', 'blocked', 'completed'],
32
+ description: 'Initial status (defaults to todo)',
33
+ },
34
+ taskType: TASK_TYPE_PROPERTY,
35
+ componentId: { type: 'string', description: 'DEPRECATED single component; overrides the shared componentId. Prefer componentIds.' },
36
+ componentIds: {
37
+ type: 'array',
38
+ items: { type: 'string' },
39
+ description: 'Every component this task touches; overrides the shared componentId/componentIds.',
40
+ },
41
+ parentTaskId: { type: 'string', description: 'Parent task link' },
42
+ estimatedHours: { type: 'number' },
43
+ assigneeType: { type: 'string', enum: ['human', 'ai'] },
44
+ assigneeId: { type: 'string' },
45
+ assigneeName: { type: 'string' },
46
+ tagIds: { type: 'array', items: { type: 'string' } },
47
+ origin: {
48
+ type: 'string',
49
+ enum: ['planned', 'discovered', 'scope-creep', 'rework', 'untracked'],
50
+ },
51
+ aiWork: { type: 'boolean' },
52
+ changedFiles: {
53
+ type: 'array',
54
+ items: { type: 'string' },
55
+ description: 'Repo-relative paths this task touches',
56
+ },
57
+ };
@@ -0,0 +1,33 @@
1
+ /**
2
+ * The one definition of a task's kind.
3
+ *
4
+ * These four values are a Postgres enum (`task_type`, api migration 000001), so
5
+ * this list is not a convention the tools agree on — it is a constraint the
6
+ * database enforces, and a fifth value added here would be rejected on write.
7
+ *
8
+ * It lives in its own module because four unrelated tools need it and none of
9
+ * them owns it: `manage_task`, the shared batch item in `task-item-schema.js`,
10
+ * `manage_recurring_task` and `manage_work_template`. Three of those declared
11
+ * their own copy of the same four strings, and `manage_task` — the tool agents
12
+ * actually reach for — declared nothing at all, so a task's kind could be set
13
+ * in bulk but never set or changed one at a time.
14
+ */
15
+ export const TASK_TYPES = ['feature', 'bug', 'testing', 'chore'];
16
+
17
+ /**
18
+ * The property as it appears in a tool's inputSchema. `description` is worth
19
+ * carrying everywhere because task type is not decoration: it drives the
20
+ * per-area open-bug count on the project overview, the milestone freeze gates,
21
+ * and estimation weighting. An agent choosing a value should know that.
22
+ */
23
+ export const TASK_TYPE_PROPERTY = {
24
+ type: 'string',
25
+ enum: TASK_TYPES,
26
+ description:
27
+ 'The kind of work. Not cosmetic — "bug" feeds the per-area open-bug count ' +
28
+ 'on the project overview, milestone freezes gate on it (a "stabilization" ' +
29
+ 'freeze admits only bug and testing), and estimation weights past tasks of ' +
30
+ 'the same kind more heavily. Omit it and the task is stored as "feature". ' +
31
+ 'Prefer "testing" only for a task that verifies another task\'s work; it ' +
32
+ 'is expected to carry parentTaskId and drives the testing-split flow.',
33
+ };