@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/links.js ADDED
@@ -0,0 +1,199 @@
1
+ /**
2
+ * Links Tools
3
+ *
4
+ * Polymorphic link management — exposes the unified entity_links surface
5
+ * (Phase 3 of the entity_links unification). One tool covers every link
6
+ * combo across tasks, epics, projects, and documents instead of having
7
+ * to remember which entity-specific tool to use.
8
+ *
9
+ * Use these in preference to manage_task / manage_epic's per-link fields
10
+ * (addDependency, addRelatedItem, ...) when the link's source and target
11
+ * are different entity types — e.g. linking a task to a related document.
12
+ */
13
+
14
+ import { LINKABLE_TYPES } from './linkable-types.js';
15
+
16
+ export const LINK_TOOLS = [
17
+ {
18
+ name: 'manage_link',
19
+ description:
20
+ 'Add, remove, or verify a link between two entities. Covers blocking ' +
21
+ 'dependencies (blocked_by) and soft cross-entity links (relates_to) ' +
22
+ 'uniformly. blocked_by is only valid for task→task and epic→epic; ' +
23
+ 'relates_to works between any of: task, epic, project, document, ' +
24
+ 'component, feature, decision, design, test_suite, feature_flag ' +
25
+ '(e.g. link a feature to the feature_flag that gates it). ' +
26
+ 'Idempotent — re-adding an existing link is a no-op, removing an ' +
27
+ 'absent link is a no-op. Use action "verify" to stamp a link\'s ' +
28
+ 'freshness (E-166/E-154): record that you confirmed it still correct, ' +
29
+ 'optionally against a commitSha — call it after touching code a linked ' +
30
+ 'document describes so the freshness signal stays accurate.',
31
+ inputSchema: {
32
+ type: 'object',
33
+ properties: {
34
+ action: {
35
+ type: 'string',
36
+ enum: ['add', 'remove', 'verify'],
37
+ description: 'Action to perform',
38
+ },
39
+ sourceType: {
40
+ type: 'string',
41
+ enum: LINKABLE_TYPES,
42
+ description:
43
+ 'The entity from which the link originates. For blocked_by ' +
44
+ 'this is the entity being blocked.',
45
+ },
46
+ sourceId: {
47
+ type: 'string',
48
+ description: 'ID of the source entity (required)',
49
+ },
50
+ targetType: {
51
+ type: 'string',
52
+ enum: LINKABLE_TYPES,
53
+ description:
54
+ 'The entity the link points to. For blocked_by this is the ' +
55
+ 'entity that must complete first.',
56
+ },
57
+ targetId: {
58
+ type: 'string',
59
+ description: 'ID of the target entity (required)',
60
+ },
61
+ linkType: {
62
+ type: 'string',
63
+ enum: ['blocked_by', 'relates_to'],
64
+ description:
65
+ 'blocked_by: source is blocked by target (only task→task or ' +
66
+ 'epic→epic). relates_to: soft cross-entity link (any combo).',
67
+ },
68
+ commitSha: {
69
+ type: 'string',
70
+ description:
71
+ 'Optional, for action "verify": the commit sha you confirmed the ' +
72
+ 'link correct against. Recorded as the verification point.',
73
+ },
74
+ },
75
+ required: ['action', 'sourceType', 'sourceId', 'targetType', 'targetId', 'linkType'],
76
+ },
77
+ },
78
+ {
79
+ name: 'list_links',
80
+ description:
81
+ 'List an entity\'s links. Returns one row per ' +
82
+ 'link (source, target, linkType, createdAt, and freshness fields ' +
83
+ 'lastVerifiedAt / lastVerifiedBy / lastVerifiedCommitSha). By default ' +
84
+ 'only OUTGOING links (the entity as source) are returned — set ' +
85
+ 'direction:"incoming" for links pointing AT it (e.g. the features that ' +
86
+ 'reference this task), or "both" for the full neighbourhood, since ' +
87
+ 'relates_to is not always stored bidirectionally. Optionally ' +
88
+ 'filter by linkType (e.g. only blockers) or targetType (e.g. only ' +
89
+ 'related documents). Set hydrate:true to also get each target\'s ' +
90
+ 'title/status, so you can read the graph without a get_* per link.',
91
+ inputSchema: {
92
+ type: 'object',
93
+ properties: {
94
+ sourceType: {
95
+ type: 'string',
96
+ enum: LINKABLE_TYPES,
97
+ description: 'Type of the source entity (required)',
98
+ },
99
+ sourceId: {
100
+ type: 'string',
101
+ description: 'ID of the source entity (required)',
102
+ },
103
+ linkType: {
104
+ type: 'string',
105
+ enum: ['blocked_by', 'relates_to'],
106
+ description: 'Optional filter: only return this kind of link',
107
+ },
108
+ targetType: {
109
+ type: 'string',
110
+ enum: LINKABLE_TYPES,
111
+ description: 'Optional filter: only return links to this entity type',
112
+ },
113
+ direction: {
114
+ type: 'string',
115
+ enum: ['outgoing', 'incoming', 'both'],
116
+ default: 'outgoing',
117
+ description:
118
+ 'Which edges to return relative to the given entity. "outgoing" ' +
119
+ '(default) = links where it is the source. "incoming" = links ' +
120
+ 'pointing at it. "both" = either. Use "both" when you want the ' +
121
+ 'entity\'s whole neighbourhood — relates_to is not always stored ' +
122
+ 'in both directions.',
123
+ },
124
+ includeSuggested: {
125
+ type: 'boolean',
126
+ description:
127
+ 'When true, also return machine-suggested (not yet confirmed) ' +
128
+ 'links alongside confirmed ones, so you can review and confirm ' +
129
+ 'them. Off by default — suggestions are not facts.',
130
+ },
131
+ hydrate: {
132
+ type: 'boolean',
133
+ description:
134
+ 'When true, each row gains a `related` object with the target\'s ' +
135
+ 'title/status/kind. Costs one lookup per link, so leave it off ' +
136
+ 'when you only need the edges.',
137
+ },
138
+ },
139
+ required: ['sourceType', 'sourceId'],
140
+ },
141
+ },
142
+ {
143
+ name: 'resolve_links',
144
+ description:
145
+ 'Given files you have touched (or are about to), return the entities you should link to: ' +
146
+ 'the components — screens, pages, codebase areas — that own those paths, plus how confident ' +
147
+ 'the match is. Read-only; nothing is written.\n\n' +
148
+ 'Call it BEFORE creating work to fill in the `links` param, or after a change set to check ' +
149
+ 'you have not missed anything. If it returns a component you did not expect, that is a signal ' +
150
+ 'your change is broader than you thought — worth reading before you continue.\n\n' +
151
+ 'Paths are repo-relative (e.g. "mobile/lib/features/auth/screens/login_screen.dart").',
152
+ inputSchema: {
153
+ type: 'object',
154
+ properties: {
155
+ projectId: { type: 'string', description: 'Project ID (required)' },
156
+ paths: {
157
+ type: 'array',
158
+ items: { type: 'string' },
159
+ description: 'Repo-relative file paths to resolve (required)',
160
+ },
161
+ },
162
+ required: ['projectId', 'paths'],
163
+ },
164
+ },
165
+ {
166
+ name: 'preview_links',
167
+ description:
168
+ 'Show what ezmodo WOULD link an entity to, without writing anything. Each proposal reports ' +
169
+ '`autoApplies`: true means ezmodo records it on its own, false means it wants a human or you ' +
170
+ 'to confirm.\n\n' +
171
+ 'Use it when you want to see and choose rather than let linking happen — confirm the ones you ' +
172
+ 'agree with via manage_link, which records them as deliberately asserted rather than derived.',
173
+ inputSchema: {
174
+ type: 'object',
175
+ properties: {
176
+ projectId: { type: 'string', description: 'Project ID' },
177
+ subjectType: {
178
+ type: 'string',
179
+ enum: LINKABLE_TYPES,
180
+ description: 'Type of the entity the links would hang off (required)',
181
+ },
182
+ subjectId: { type: 'string', description: 'ID of that entity (required)' },
183
+ paths: {
184
+ type: 'array',
185
+ items: { type: 'string' },
186
+ description: 'Repo-relative file paths the work touches',
187
+ },
188
+ componentId: { type: 'string', description: 'A component the work explicitly names' },
189
+ epicId: { type: 'string', description: 'The epic the work belongs to' },
190
+ trigger: {
191
+ type: 'string',
192
+ enum: ['files_linked', 'commit_linked', 'work_created', 'work_reparented', 'plan_approved', 'surface_imported'],
193
+ description: 'What kind of event to simulate. Defaults sensibly from whether paths were supplied.',
194
+ },
195
+ },
196
+ required: ['subjectType', 'subjectId'],
197
+ },
198
+ },
199
+ ];
@@ -0,0 +1,176 @@
1
+ /**
2
+ * Milestone Tools
3
+ * MCP tools for managing project milestones (version releases and initiatives)
4
+ *
5
+ * Milestones are project-scoped containers for tracking:
6
+ * - Version releases (e.g., v1.2.0, v2.0.0-beta)
7
+ * - Initiatives (e.g., Q1 2025, Sprint 5)
8
+ *
9
+ * Epics can be linked to milestones to track which features/work
10
+ * will be included in a release or initiative.
11
+ */
12
+
13
+ import { LINKS_ARRAY_SCHEMA } from './link-params.js';
14
+
15
+ export const MILESTONE_TOOLS = [
16
+ {
17
+ name: 'manage_milestone',
18
+ description: 'Create, update, delete milestones, or manage epic/suite linking and changelog generation. ' +
19
+ 'Milestones can be version releases or initiatives.',
20
+ inputSchema: {
21
+ type: 'object',
22
+ properties: {
23
+ action: {
24
+ type: 'string',
25
+ enum: [
26
+ 'create', 'update', 'delete',
27
+ 'link_epic', 'unlink_epic',
28
+ 'generate_changelog', 'reorder_epics',
29
+ 'link_suite', 'unlink_suite',
30
+ ],
31
+ description: 'Action to perform',
32
+ },
33
+ // --- Identifiers (used by most actions) ---
34
+ projectId: {
35
+ type: 'string',
36
+ description: 'Project ID (required for all actions)',
37
+ },
38
+ milestoneId: {
39
+ type: 'string',
40
+ description: 'Milestone ID (required for update, delete, link_epic, unlink_epic, generate_changelog, reorder_epics, link_suite, unlink_suite)',
41
+ },
42
+ milestoneSlug: {
43
+ type: 'string',
44
+ description: 'URL-friendly slug of the milestone (alternative to milestoneId)',
45
+ },
46
+ // --- Create fields ---
47
+ name: {
48
+ type: 'string',
49
+ description: 'Milestone name (required for create, e.g., "v1.2.0", "Q1 2025 Launch")',
50
+ },
51
+ type: {
52
+ type: 'string',
53
+ enum: ['version', 'initiative'],
54
+ description: 'Milestone type (required for create): "version" for releases, "initiative" for initiatives',
55
+ },
56
+ description: {
57
+ type: 'string',
58
+ description: 'Milestone description, supports markdown (create, update). ' +
59
+ 'Writing this stays plain text; it does not create a backing document.',
60
+ },
61
+ version: {
62
+ type: 'string',
63
+ description: 'Semantic version string for version type milestones (e.g., "1.2.0", "2.0.0-beta") (create, update)',
64
+ },
65
+ targetDate: {
66
+ type: 'string',
67
+ description: 'Target completion/release date (RFC3339, e.g., 2026-03-31T00:00:00Z) (create, update)',
68
+ },
69
+ parentMilestoneId: {
70
+ type: 'string',
71
+ description: 'Nest this milestone under an overarching parent milestone (tiered milestones, E-217). ' +
72
+ 'On create: empty/omitted = top-level. On update: omit = leave unchanged, empty string "" = clear (promote to top-level), ' +
73
+ 'a milestone ID = re-parent. Validated for existence, cycles, and max nesting depth (3); parent must be in the same project. (create, update)',
74
+ },
75
+ status: {
76
+ type: 'string',
77
+ enum: ['planned', 'in_progress', 'released', 'archived'],
78
+ description: 'Milestone status (default: "planned") (create, update)',
79
+ },
80
+ links: LINKS_ARRAY_SCHEMA,
81
+ items: {
82
+ type: 'array',
83
+ description: 'Initial linked items, e.g., epics (create, update)',
84
+ items: {
85
+ type: 'object',
86
+ properties: {
87
+ entityId: { type: 'string', description: 'ID of the entity to link' },
88
+ entityType: { type: 'string', description: 'Type of entity', enum: ['epic'], default: 'epic' },
89
+ priority: { type: 'string', description: 'Priority level', enum: ['critical', 'high', 'medium', 'low'] },
90
+ order: { type: 'number', description: 'Display order' },
91
+ },
92
+ required: ['entityId'],
93
+ },
94
+ },
95
+ changelog: {
96
+ type: 'string',
97
+ description: 'Markdown changelog for version type milestones (create, update)',
98
+ },
99
+ // --- Update-only fields ---
100
+ releasedDate: {
101
+ type: 'string',
102
+ description: 'Actual release date (RFC3339, typically set when status changes to "released") (update only)',
103
+ },
104
+ // --- link_epic / unlink_epic fields ---
105
+ epicId: {
106
+ type: 'string',
107
+ description: 'Epic ID to link/unlink (required for link_epic, unlink_epic)',
108
+ },
109
+ entityType: {
110
+ type: 'string',
111
+ enum: ['epic'],
112
+ description: 'Type of entity being linked (default: "epic") (link_epic only)',
113
+ default: 'epic',
114
+ },
115
+ priority: {
116
+ type: 'string',
117
+ enum: ['critical', 'high', 'medium', 'low'],
118
+ description: 'Priority of epic/suite within milestone (default: "medium") (link_epic, link_suite)',
119
+ },
120
+ // --- reorder_epics fields ---
121
+ itemIds: {
122
+ type: 'array',
123
+ items: { type: 'string' },
124
+ description: 'Ordered array of item IDs — must contain same set as currently linked (reorder_epics only)',
125
+ },
126
+ // --- link_suite / unlink_suite fields ---
127
+ suiteId: {
128
+ type: 'string',
129
+ description: 'Test suite ID to link/unlink (required for link_suite, unlink_suite)',
130
+ },
131
+ },
132
+ required: ['action', 'projectId'],
133
+ },
134
+ },
135
+ {
136
+ name: 'get_milestone',
137
+ description: 'Retrieve a single milestone by ID, or list all milestones for a project. ' +
138
+ 'Provide milestoneId (or milestoneSlug) for single lookup, or just projectId to list all. ' +
139
+ 'Optionally include progress data. ' +
140
+ 'Responses include `descriptionDocumentId` — the id of the backing rich-description Document ' +
141
+ 'when the description has been promoted to one (E-189), otherwise omitted.',
142
+ inputSchema: {
143
+ type: 'object',
144
+ properties: {
145
+ projectId: {
146
+ type: 'string',
147
+ description: 'The project ID (required)',
148
+ },
149
+ milestoneId: {
150
+ type: 'string',
151
+ description: 'Milestone ID for single lookup',
152
+ },
153
+ milestoneSlug: {
154
+ type: 'string',
155
+ description: 'Milestone slug for single lookup (alternative to milestoneId)',
156
+ },
157
+ includeProgress: {
158
+ type: 'boolean',
159
+ description: 'If true, include progress data (overall percentage, epic breakdown)',
160
+ },
161
+ // --- List filters (used when no milestoneId/milestoneSlug) ---
162
+ type: {
163
+ type: 'string',
164
+ enum: ['version', 'initiative'],
165
+ description: 'Filter by milestone type (list mode only)',
166
+ },
167
+ status: {
168
+ type: 'string',
169
+ enum: ['planned', 'in_progress', 'released', 'archived'],
170
+ description: 'Filter by milestone status (list mode only)',
171
+ },
172
+ },
173
+ required: ['projectId'],
174
+ },
175
+ },
176
+ ];
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Organization Tools
3
+ * MCP tools for managing organizations (workspaces)
4
+ */
5
+
6
+ export const ORGANIZATION_TOOLS = [
7
+ {
8
+ name: 'get_organization',
9
+ description: 'Get organization info. Returns the default/primary organization, ' +
10
+ 'or lists all organizations the user has access to. Use mode "list" to see all ' +
11
+ 'workspaces, or omit/use "default" to get the primary one.',
12
+ inputSchema: {
13
+ type: 'object',
14
+ properties: {
15
+ mode: {
16
+ type: 'string',
17
+ enum: ['default', 'list'],
18
+ description: 'Mode: "default" returns primary organization, "list" returns all. Default: "default"',
19
+ },
20
+ },
21
+ },
22
+ },
23
+ ];
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Project Tools
3
+ * MCP tools for managing projects (primary container for work)
4
+ *
5
+ * Projects are long-lived containers that hold tasks and epics.
6
+ * Types: software, marketing, sales, operations, design, infrastructure,
7
+ * creative, research, implementation, custom.
8
+ *
9
+ * A project's type decides what its work is CALLED (E-107). A marketing project
10
+ * calls an epic a "Campaign" and a component a "Channel"; a sales project calls
11
+ * a task an "Activity". `get_project` returns the resolved vocabulary as
12
+ * `terminology`, and `get_current_project_context` caches it. Use those words
13
+ * when writing anything a person reads — titles, descriptions, chat replies —
14
+ * while keeping the API's own field names (epicId, taskId) unchanged.
15
+ */
16
+
17
+ export const PROJECT_TOOLS = [
18
+ {
19
+ name: 'manage_project',
20
+ description: 'Create a new project within an organization, update an existing one, ' +
21
+ 'or (re)generate a project\'s grounded "how it works" living description.',
22
+ inputSchema: {
23
+ type: 'object',
24
+ properties: {
25
+ action: {
26
+ type: 'string',
27
+ enum: ['create', 'update', 'generate_how_it_works', 'apply_how_it_works'],
28
+ description: 'Action to perform. "update" changes an existing project ' +
29
+ '(requires projectId); only the fields you pass are touched — accepts name, ' +
30
+ 'description, slug, ownerId, gitUrl and gitProvider. Setting gitUrl after ' +
31
+ 'creation is what enables commit-to-task linking. "generate_how_it_works" (re)generates the ' +
32
+ 'project\'s grounded, source-attributed "how it works" living description from ' +
33
+ 'its linked work (requires projectId; AI-quota gated). "apply_how_it_works" (BYO-AI) ' +
34
+ 'persists a summary YOU authored (markdown + sources); the server validates your cited ' +
35
+ 'sources against the real grounded context before saving — no server model call. Read ' +
36
+ 'the result back via get_project.',
37
+ },
38
+ projectId: {
39
+ type: 'string',
40
+ description: 'Project ID (required for update, generate_how_it_works, apply_how_it_works)',
41
+ },
42
+ // --- apply_how_it_works (BYO-AI) ---
43
+ markdown: {
44
+ type: 'string',
45
+ description: 'Rendered markdown body for apply_how_it_works (the how-it-works YOU authored).',
46
+ },
47
+ sources: {
48
+ type: 'object',
49
+ description: 'Structured backing for apply_how_it_works: { claims: [{ text, sources: [ref...], ' +
50
+ 'confidence: "grounded"|"unverified" }], divergences: [{ intent, reality, severity }] }. Cite ' +
51
+ 'real source refs from the project\'s grounded context; the server drops fabricated ones.',
52
+ },
53
+ organizationId: {
54
+ type: 'string',
55
+ description: 'The ID of the organization to create the project in (required for create)',
56
+ },
57
+ name: {
58
+ type: 'string',
59
+ description: 'The project name (required for create, optional for update)',
60
+ },
61
+ slug: {
62
+ type: 'string',
63
+ description: 'URL slug for the project (update only). Changing this changes the ' +
64
+ 'project\'s URLs.',
65
+ },
66
+ description: {
67
+ type: 'string',
68
+ description: 'Project description (supports markdown). ' +
69
+ 'Writing this stays plain text; it does not create a backing document.',
70
+ },
71
+ type: {
72
+ type: 'string',
73
+ enum: ['software', 'marketing', 'sales', 'operations', 'design', 'infrastructure',
74
+ 'creative', 'research', 'implementation', 'custom'],
75
+ description: 'Project type (default: software)',
76
+ default: 'software',
77
+ },
78
+ customType: {
79
+ type: 'string',
80
+ description: 'Custom type name (only used when type is "custom")',
81
+ },
82
+ gitUrl: {
83
+ type: 'string',
84
+ description: 'Git repository URL (for software projects). Settable on create and ' +
85
+ 'update — this is what links the project to its repo so commits can be matched ' +
86
+ 'back to tasks.',
87
+ },
88
+ gitProvider: {
89
+ type: 'string',
90
+ enum: ['github', 'gitlab', 'bitbucket'],
91
+ description: 'Git provider (create and update)',
92
+ },
93
+ ownerId: {
94
+ type: 'string',
95
+ description: 'User ID of the project owner (defaults to API key user on create; ' +
96
+ 'reassigns the owner on update)',
97
+ },
98
+ },
99
+ required: ['action'],
100
+ },
101
+ },
102
+ {
103
+ name: 'get_project',
104
+ description: 'Get project info by ID (context/memory), search projects by text, or list all projects. ' +
105
+ 'Provide projectId for context retrieval, query for search, or neither to list all. ' +
106
+ 'Responses include `descriptionDocumentId` — the id of the backing rich-description Document ' +
107
+ 'when the description has been promoted to one (E-189), otherwise omitted. ' +
108
+ 'By-ID responses also carry `type` and `terminology` — the words this project uses for ' +
109
+ 'epics, tasks and components, and for their statuses (E-107). Write prose in those words; ' +
110
+ 'a marketing project calls an epic a "Campaign". `terminology` is absent for a type with no ' +
111
+ 'template, which means plain English, not an error.',
112
+ inputSchema: {
113
+ type: 'object',
114
+ properties: {
115
+ projectId: {
116
+ type: 'string',
117
+ description: 'Project ID — returns project context including memory, knowledge, ' +
118
+ 'configuration, the project `type`, and the `terminology` that type speaks',
119
+ },
120
+ query: {
121
+ type: 'string',
122
+ description: 'Search text for project name or description (case-insensitive)',
123
+ },
124
+ type: {
125
+ type: 'string',
126
+ enum: ['software', 'marketing', 'sales', 'operations', 'design', 'infrastructure',
127
+ 'creative', 'research', 'implementation', 'custom'],
128
+ description: 'Filter by project type (search/list mode)',
129
+ },
130
+ organizationId: {
131
+ type: 'string',
132
+ description: 'Filter by organization ID (search/list mode)',
133
+ },
134
+ limit: {
135
+ type: 'number',
136
+ description: 'Maximum number of results (default: 50)',
137
+ default: 50,
138
+ },
139
+ },
140
+ },
141
+ },
142
+ {
143
+ name: 'get_project_story',
144
+ description: 'Project Story (E-208): a grounded, source-attributed narrative of what has happened to a project ' +
145
+ 'since it began in ezmodo — the time-axis complement to "how it works". Use it to catch up on a project. ' +
146
+ 'Returns rendered markdown plus validated claims (each citing real event:/decision: sources), the resolved ' +
147
+ 'window, and generatedAt. Returns a fresh cached story when available, otherwise generates one (AI-quota gated).',
148
+ inputSchema: {
149
+ type: 'object',
150
+ properties: {
151
+ projectId: {
152
+ type: 'string',
153
+ description: 'Project ID to narrate',
154
+ },
155
+ window: {
156
+ type: 'string',
157
+ enum: ['since_inception', 'last_release', 'last_sprint', 'custom'],
158
+ description: 'Time window for the story (default: since_inception). last_release/last_sprint/custom require from+to.',
159
+ },
160
+ from: {
161
+ type: 'string',
162
+ description: 'Window start, RFC3339 (e.g. 2026-01-02T15:04:05Z). Required for custom/last_release/last_sprint.',
163
+ },
164
+ to: {
165
+ type: 'string',
166
+ description: 'Window end, RFC3339. Required for custom/last_release/last_sprint.',
167
+ },
168
+ },
169
+ required: ['projectId'],
170
+ },
171
+ },
172
+ ];