@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.
- package/README.md +305 -0
- package/config/development.js +20 -0
- package/config/endpoint-map.js +351 -0
- package/config/index.js +34 -0
- package/config/production.js +18 -0
- package/config/staging.js +18 -0
- package/handlers/access.js +141 -0
- package/handlers/activity.js +112 -0
- package/handlers/agents.js +95 -0
- package/handlers/ai-intelligence.js +55 -0
- package/handlers/attachments.js +30 -0
- package/handlers/catalogs.js +169 -0
- package/handlers/components.js +282 -0
- package/handlers/context-manifest.js +1150 -0
- package/handlers/decisions.js +114 -0
- package/handlers/designs.js +118 -0
- package/handlers/documents.js +227 -0
- package/handlers/entities.js +95 -0
- package/handlers/epics.js +190 -0
- package/handlers/facts.js +62 -0
- package/handlers/feature-flags.js +142 -0
- package/handlers/features.js +137 -0
- package/handlers/folders.js +127 -0
- package/handlers/git-context.js +917 -0
- package/handlers/github.js +72 -0
- package/handlers/graph.js +23 -0
- package/handlers/index.js +205 -0
- package/handlers/links.js +156 -0
- package/handlers/milestones.js +131 -0
- package/handlers/organizations.js +14 -0
- package/handlers/projects.js +122 -0
- package/handlers/recurring-tasks.js +33 -0
- package/handlers/tags.js +124 -0
- package/handlers/tasks.js +561 -0
- package/handlers/testing.js +116 -0
- package/handlers/todos.js +43 -0
- package/handlers/watchers.js +54 -0
- package/handlers/work-templates.js +32 -0
- package/index.js +175 -0
- package/lib/active-session.js +86 -0
- package/lib/auto-assign.js +93 -0
- package/lib/autolink.js +176 -0
- package/lib/changed-files.js +22 -0
- package/lib/env.js +45 -0
- package/lib/git-helpers.js +553 -0
- package/lib/git-utils.js +73 -0
- package/lib/http-client.js +164 -0
- package/lib/links-at-create.js +94 -0
- package/lib/local-cache.js +140 -0
- package/lib/logger.js +109 -0
- package/lib/manifest-loader.js +182 -0
- package/lib/manifest-query.js +686 -0
- package/lib/repo-config-dir.js +118 -0
- package/lib/version.js +10 -0
- package/lib/web-url.js +69 -0
- package/lib/worktree-tools.js +950 -0
- package/package.json +62 -0
- package/prompts/ai-workflow-automation.js +96 -0
- package/prompts/index.js +39 -0
- package/prompts/zephly-usage-guide-content.txt +631 -0
- package/prompts/zephly-usage-guide.js +119 -0
- package/tools/access-entity-types.js +28 -0
- package/tools/access.js +152 -0
- package/tools/activity.js +38 -0
- package/tools/agents.js +208 -0
- package/tools/ai-intelligence.js +111 -0
- package/tools/attachments.js +92 -0
- package/tools/catalogs.js +341 -0
- package/tools/components.js +249 -0
- package/tools/context-manifest.js +236 -0
- package/tools/decisions.js +168 -0
- package/tools/designs.js +222 -0
- package/tools/documents.js +287 -0
- package/tools/entities.js +223 -0
- package/tools/epics.js +267 -0
- package/tools/facts.js +70 -0
- package/tools/feature-flags.js +300 -0
- package/tools/features.js +246 -0
- package/tools/folders.js +122 -0
- package/tools/git-context.js +109 -0
- package/tools/github.js +172 -0
- package/tools/graph.js +70 -0
- package/tools/index.js +77 -0
- package/tools/link-params.js +93 -0
- package/tools/linkable-types.js +36 -0
- package/tools/links.js +199 -0
- package/tools/milestones.js +176 -0
- package/tools/organizations.js +23 -0
- package/tools/projects.js +172 -0
- package/tools/recurring-tasks.js +115 -0
- package/tools/tags.js +219 -0
- package/tools/task-item-schema.js +57 -0
- package/tools/task-type.js +33 -0
- package/tools/tasks.js +680 -0
- package/tools/testing.js +344 -0
- package/tools/todos.js +69 -0
- package/tools/watchers.js +81 -0
- package/tools/work-templates.js +96 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Handlers
|
|
3
|
+
* Handler functions for GitHub-related MCP tools
|
|
4
|
+
*
|
|
5
|
+
* Consolidated: managePullRequest dispatches create/update/merge/close/request_review.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { callZephlyAPI } from '../lib/http-client.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* List the git repositories linked to a project, each with the `repoId` every
|
|
12
|
+
* manage_pull_request action requires.
|
|
13
|
+
*
|
|
14
|
+
* This exists because that id was otherwise unknowable. It is
|
|
15
|
+
* `installationId:owner/repo`, and no other tool exposes an installation id:
|
|
16
|
+
* get_project returns `settings.integrations: null`, get_current_project_context
|
|
17
|
+
* has no repo fields, and GitHub will not tell an agent either — the endpoints
|
|
18
|
+
* that would (`/repos/{owner}/{repo}/installation`, `/user/installations`) need
|
|
19
|
+
* an app JWT or an app-authorized token. ezmodo holds the fact, so ezmodo has
|
|
20
|
+
* to hand it over.
|
|
21
|
+
*/
|
|
22
|
+
export async function listRepositories({ projectId }) {
|
|
23
|
+
return callZephlyAPI('mcpListRepositories', { projectId });
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Dispatch manage_pull_request actions to the appropriate handler
|
|
28
|
+
*/
|
|
29
|
+
export async function managePullRequest(args) {
|
|
30
|
+
const { action, ...params } = args;
|
|
31
|
+
switch (action) {
|
|
32
|
+
case 'create': return createPullRequest(params);
|
|
33
|
+
case 'update': return updatePullRequest(params);
|
|
34
|
+
case 'merge': return mergePullRequest(params);
|
|
35
|
+
case 'close': return closePullRequest(params);
|
|
36
|
+
case 'request_review': return requestPRReview(params);
|
|
37
|
+
case 'suggest_reviewers': return suggestReviewers(params);
|
|
38
|
+
default: throw new Error(`Unknown action: ${action}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// --- Private helpers ---
|
|
43
|
+
|
|
44
|
+
async function createPullRequest(args) {
|
|
45
|
+
return callZephlyAPI('mcpCreatePullRequest', args);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function updatePullRequest(args) {
|
|
49
|
+
return callZephlyAPI('mcpUpdatePullRequest', args);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function mergePullRequest(args) {
|
|
53
|
+
return callZephlyAPI('mcpMergePullRequest', args);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function closePullRequest(args) {
|
|
57
|
+
return callZephlyAPI('mcpClosePullRequest', args);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function requestPRReview(args) {
|
|
61
|
+
return callZephlyAPI('mcpRequestPRReview', args);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Ranks reviewer candidates from CODEOWNERS and file history. Suggests only —
|
|
66
|
+
* requesting the review is the separate 'request_review' action, because
|
|
67
|
+
* pinging the wrong three people is a social cost a suggestion tool has no
|
|
68
|
+
* business incurring on its own.
|
|
69
|
+
*/
|
|
70
|
+
async function suggestReviewers(args) {
|
|
71
|
+
return callZephlyAPI('mcpSuggestReviewers', args);
|
|
72
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Graph Handlers
|
|
3
|
+
*
|
|
4
|
+
* Thin wrapper over /api/mcp/v1/graph/traverse — the navigation-graph
|
|
5
|
+
* projection (E-209). The BFS walk + node hydration happen server-side in
|
|
6
|
+
* core/graph.ProjectionService; the MCP layer just forwards the request and
|
|
7
|
+
* only sends the optional bounding params when provided.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { callZephlyAPI } from '../lib/http-client.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Walk the compendium navigation graph outward from a root node. projectId,
|
|
14
|
+
* rootType, and rootId are required; scope/depth/maxNodes are optional bounds
|
|
15
|
+
* (omitted params fall back to server defaults).
|
|
16
|
+
*/
|
|
17
|
+
export async function getGraph({ projectId, rootType, rootId, scope, depth, maxNodes }) {
|
|
18
|
+
const params = { projectId, rootType, rootId };
|
|
19
|
+
if (scope) params.scope = scope;
|
|
20
|
+
if (depth !== undefined) params.depth = depth;
|
|
21
|
+
if (maxNodes !== undefined) params.maxNodes = maxNodes;
|
|
22
|
+
return callZephlyAPI('mcpTraverseGraph', params);
|
|
23
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handler Functions Index
|
|
3
|
+
* Aggregates all MCP handler functions
|
|
4
|
+
*
|
|
5
|
+
* Consolidated handler set matching the ~40-tool definitions.
|
|
6
|
+
* Each manage_* handler dispatches to internal helpers based on action param.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import * as organizationHandlers from './organizations.js';
|
|
10
|
+
import * as projectHandlers from './projects.js';
|
|
11
|
+
import * as epicHandlers from './epics.js';
|
|
12
|
+
import * as componentHandlers from './components.js';
|
|
13
|
+
import * as milestoneHandlers from './milestones.js';
|
|
14
|
+
import * as featureHandlers from './features.js';
|
|
15
|
+
import * as decisionHandlers from './decisions.js';
|
|
16
|
+
import * as designHandlers from './designs.js';
|
|
17
|
+
import * as catalogHandlers from './catalogs.js';
|
|
18
|
+
import * as featureFlagHandlers from './feature-flags.js';
|
|
19
|
+
import * as taskHandlers from './tasks.js';
|
|
20
|
+
import * as documentHandlers from './documents.js';
|
|
21
|
+
import * as folderHandlers from './folders.js';
|
|
22
|
+
import * as attachmentHandlers from './attachments.js';
|
|
23
|
+
import * as entityHandlers from './entities.js';
|
|
24
|
+
import * as linkHandlers from './links.js';
|
|
25
|
+
import * as accessHandlers from './access.js';
|
|
26
|
+
import * as graphHandlers from './graph.js';
|
|
27
|
+
import * as tagHandlers from './tags.js';
|
|
28
|
+
import * as watcherHandlers from './watchers.js';
|
|
29
|
+
import * as aiIntelligenceHandlers from './ai-intelligence.js';
|
|
30
|
+
import * as gitContextHandlers from './git-context.js';
|
|
31
|
+
import * as githubHandlers from './github.js';
|
|
32
|
+
import * as todoHandlers from './todos.js';
|
|
33
|
+
import * as testingHandlers from './testing.js';
|
|
34
|
+
import * as contextManifestHandlers from './context-manifest.js';
|
|
35
|
+
import * as activityHandlers from './activity.js';
|
|
36
|
+
import * as factHandlers from './facts.js';
|
|
37
|
+
import * as agentHandlers from './agents.js';
|
|
38
|
+
import * as recurringTaskHandlers from './recurring-tasks.js';
|
|
39
|
+
import * as workTemplateHandlers from './work-templates.js';
|
|
40
|
+
import { manageWorktree, listProjectWorktrees } from '../lib/worktree-tools.js';
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Map of tool names to handler functions
|
|
44
|
+
* Used by the MCP server to route tool calls
|
|
45
|
+
*/
|
|
46
|
+
export const HANDLERS = {
|
|
47
|
+
// Organizations
|
|
48
|
+
get_organization: organizationHandlers.getOrganization,
|
|
49
|
+
|
|
50
|
+
// Projects
|
|
51
|
+
manage_project: projectHandlers.manageProject,
|
|
52
|
+
get_project: projectHandlers.getProject,
|
|
53
|
+
get_project_story: projectHandlers.getProjectStory,
|
|
54
|
+
|
|
55
|
+
// Epics
|
|
56
|
+
manage_epic: epicHandlers.manageEpic,
|
|
57
|
+
search_epics: epicHandlers.searchEpics,
|
|
58
|
+
list_epics: epicHandlers.listEpics,
|
|
59
|
+
get_epic: epicHandlers.getEpic,
|
|
60
|
+
|
|
61
|
+
// Components
|
|
62
|
+
manage_component: componentHandlers.manageComponent,
|
|
63
|
+
list_components: componentHandlers.listComponents,
|
|
64
|
+
|
|
65
|
+
// Milestones
|
|
66
|
+
manage_milestone: milestoneHandlers.manageMilestone,
|
|
67
|
+
get_milestone: milestoneHandlers.getMilestone,
|
|
68
|
+
|
|
69
|
+
// Features (Feature Compendium, E-162)
|
|
70
|
+
manage_feature: featureHandlers.manageFeature,
|
|
71
|
+
get_feature: featureHandlers.getFeature,
|
|
72
|
+
search_features: featureHandlers.searchFeatures,
|
|
73
|
+
|
|
74
|
+
// Decisions / ADRs (E-170)
|
|
75
|
+
manage_decision: decisionHandlers.manageDecision,
|
|
76
|
+
get_decision: decisionHandlers.getDecision,
|
|
77
|
+
|
|
78
|
+
// Recurring task schedules + work templates (E-211)
|
|
79
|
+
manage_recurring_task: recurringTaskHandlers.manageRecurringTask,
|
|
80
|
+
manage_work_template: workTemplateHandlers.manageWorkTemplate,
|
|
81
|
+
|
|
82
|
+
// Designs (living design system)
|
|
83
|
+
manage_design: designHandlers.manageDesign,
|
|
84
|
+
get_design: designHandlers.getDesign,
|
|
85
|
+
list_designs: designHandlers.listDesigns,
|
|
86
|
+
get_design_system: designHandlers.getDesignSystem,
|
|
87
|
+
|
|
88
|
+
// Catalogs (generalized code-derived catalogs, E-215)
|
|
89
|
+
manage_catalog: catalogHandlers.manageCatalog,
|
|
90
|
+
get_catalog: catalogHandlers.getCatalog,
|
|
91
|
+
list_catalogs: catalogHandlers.listCatalogs,
|
|
92
|
+
list_catalog_items: catalogHandlers.listCatalogItems,
|
|
93
|
+
get_catalog_diff: catalogHandlers.getCatalogDiff,
|
|
94
|
+
|
|
95
|
+
// Feature Flags (E-149)
|
|
96
|
+
manage_feature_flag: featureFlagHandlers.manageFeatureFlag,
|
|
97
|
+
get_feature_flag: featureFlagHandlers.getFeatureFlag,
|
|
98
|
+
list_feature_flags: featureFlagHandlers.listFeatureFlags,
|
|
99
|
+
evaluate_feature_flag: featureFlagHandlers.evaluateFeatureFlag,
|
|
100
|
+
manage_environment: featureFlagHandlers.manageEnvironment,
|
|
101
|
+
|
|
102
|
+
// Tasks
|
|
103
|
+
manage_task: taskHandlers.manageTask,
|
|
104
|
+
create_tasks: taskHandlers.bulkCreateTasks,
|
|
105
|
+
search_tasks: taskHandlers.searchTasks,
|
|
106
|
+
get_task: taskHandlers.getTask,
|
|
107
|
+
report_untracked_work: taskHandlers.reportUntrackedWork,
|
|
108
|
+
|
|
109
|
+
// Documents
|
|
110
|
+
manage_document: documentHandlers.manageDocument,
|
|
111
|
+
manage_document_template: documentHandlers.manageDocumentTemplate,
|
|
112
|
+
get_document: documentHandlers.getDocument,
|
|
113
|
+
get_document_template: documentHandlers.getDocumentTemplate,
|
|
114
|
+
list_org_documents: documentHandlers.listOrgDocuments,
|
|
115
|
+
|
|
116
|
+
// Folders
|
|
117
|
+
manage_folder: folderHandlers.manageFolder,
|
|
118
|
+
list_folders: folderHandlers.listFolders,
|
|
119
|
+
get_org_areas: folderHandlers.getOrgAreas,
|
|
120
|
+
|
|
121
|
+
// Attachments (read/manage only — E-21 #158)
|
|
122
|
+
list_attachments: attachmentHandlers.listAttachments,
|
|
123
|
+
get_attachment_url: attachmentHandlers.getAttachmentUrl,
|
|
124
|
+
delete_attachment: attachmentHandlers.deleteAttachment,
|
|
125
|
+
|
|
126
|
+
// Entities (goals, teams)
|
|
127
|
+
manage_goal: entityHandlers.manageGoal,
|
|
128
|
+
manage_team: entityHandlers.manageTeam,
|
|
129
|
+
get_goal: entityHandlers.getGoal,
|
|
130
|
+
|
|
131
|
+
// Tags
|
|
132
|
+
manage_tag: tagHandlers.manageTag,
|
|
133
|
+
list_tags: tagHandlers.listTags,
|
|
134
|
+
|
|
135
|
+
// Watching / subscriptions (E-41)
|
|
136
|
+
manage_watch: watcherHandlers.manageWatch,
|
|
137
|
+
list_watched: watcherHandlers.listWatched,
|
|
138
|
+
list_notifications: watcherHandlers.listNotifications,
|
|
139
|
+
|
|
140
|
+
// Links (polymorphic entity_links — task/epic/project/document)
|
|
141
|
+
manage_link: linkHandlers.manageLink,
|
|
142
|
+
list_links: linkHandlers.listLinks,
|
|
143
|
+
resolve_links: linkHandlers.resolveLinks,
|
|
144
|
+
preview_links: linkHandlers.previewLinks,
|
|
145
|
+
|
|
146
|
+
// Entity access control (#2170) — polymorphic over the types the access
|
|
147
|
+
// service supports. Mutations need an API key with the write:access scope.
|
|
148
|
+
get_access: accessHandlers.getAccess,
|
|
149
|
+
manage_access: accessHandlers.manageAccess,
|
|
150
|
+
|
|
151
|
+
// Navigation graph (E-209) — walk the compendium from a root node
|
|
152
|
+
get_graph: graphHandlers.getGraph,
|
|
153
|
+
|
|
154
|
+
// Background agents (E-150)
|
|
155
|
+
list_agent_suggestions: agentHandlers.listAgentSuggestions,
|
|
156
|
+
resolve_link_suggestions: agentHandlers.resolveLinkSuggestions,
|
|
157
|
+
accept_agent_suggestion: agentHandlers.acceptAgentSuggestion,
|
|
158
|
+
reject_agent_suggestion: agentHandlers.rejectAgentSuggestion,
|
|
159
|
+
run_agent_now: agentHandlers.runAgentNow,
|
|
160
|
+
configure_agent: agentHandlers.configureAgent,
|
|
161
|
+
|
|
162
|
+
// AI Intelligence
|
|
163
|
+
get_ai_insights: aiIntelligenceHandlers.getAiInsights,
|
|
164
|
+
estimate_task: aiIntelligenceHandlers.estimateTask,
|
|
165
|
+
infer_dependencies: aiIntelligenceHandlers.inferDependencies,
|
|
166
|
+
|
|
167
|
+
// Git Context
|
|
168
|
+
detect_git_repository: gitContextHandlers.detectGitRepository,
|
|
169
|
+
get_current_project_context: gitContextHandlers.getCurrentProjectContext,
|
|
170
|
+
initialize_project_context: gitContextHandlers.initializeProjectContext,
|
|
171
|
+
|
|
172
|
+
// Worktrees
|
|
173
|
+
manage_worktree: manageWorktree,
|
|
174
|
+
list_project_worktrees: listProjectWorktrees,
|
|
175
|
+
|
|
176
|
+
// GitHub
|
|
177
|
+
manage_pull_request: githubHandlers.managePullRequest,
|
|
178
|
+
list_repositories: githubHandlers.listRepositories,
|
|
179
|
+
|
|
180
|
+
// Todos
|
|
181
|
+
manage_todo: todoHandlers.manageTodo,
|
|
182
|
+
list_todos: todoHandlers.listTodos,
|
|
183
|
+
|
|
184
|
+
// Testing
|
|
185
|
+
manage_test_case: testingHandlers.manageTestCase,
|
|
186
|
+
manage_test_suite: testingHandlers.manageTestSuite,
|
|
187
|
+
list_test_cases: testingHandlers.listTestCases,
|
|
188
|
+
list_test_suites: testingHandlers.listTestSuites,
|
|
189
|
+
get_testing_summary: testingHandlers.getTestingSummary,
|
|
190
|
+
|
|
191
|
+
// Activity Timeline
|
|
192
|
+
get_project_changes: activityHandlers.getProjectChanges,
|
|
193
|
+
|
|
194
|
+
// Facts
|
|
195
|
+
manage_fact: factHandlers.manageFact,
|
|
196
|
+
list_facts: factHandlers.listFacts,
|
|
197
|
+
|
|
198
|
+
// Context Manifest
|
|
199
|
+
get_context: contextManifestHandlers.getContext,
|
|
200
|
+
rebuild_manifest: contextManifestHandlers.rebuildManifest,
|
|
201
|
+
update_manifest_entries: contextManifestHandlers.updateManifestEntriesHandler,
|
|
202
|
+
get_manifest_schema: contextManifestHandlers.getManifestSchema,
|
|
203
|
+
validate_manifest: contextManifestHandlers.validateManifest,
|
|
204
|
+
resolve_concepts: contextManifestHandlers.resolveConcepts,
|
|
205
|
+
};
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Links Handlers
|
|
3
|
+
*
|
|
4
|
+
* Thin wrappers over /api/mcp/v1/links — the polymorphic entity_links
|
|
5
|
+
* surface added in Phase 3. Dispatch happens server-side in
|
|
6
|
+
* core/links.Service; the MCP layer just forwards the request.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
resolvePathsToComponents,
|
|
11
|
+
previewEntityLinks,
|
|
12
|
+
partitionProposals,
|
|
13
|
+
attachSuggestionIds,
|
|
14
|
+
} from '../lib/autolink.js';
|
|
15
|
+
import { callZephlyAPI } from '../lib/http-client.js';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Dispatch manage_link to add or remove a link.
|
|
19
|
+
*/
|
|
20
|
+
export async function manageLink(args) {
|
|
21
|
+
const { action, ...params } = args;
|
|
22
|
+
switch (action) {
|
|
23
|
+
case 'add':
|
|
24
|
+
return addLink(params);
|
|
25
|
+
case 'remove':
|
|
26
|
+
return removeLink(params);
|
|
27
|
+
case 'verify':
|
|
28
|
+
return verifyLink(params);
|
|
29
|
+
default:
|
|
30
|
+
throw new Error(`Unknown action: ${action}. Expected "add", "remove", or "verify".`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function addLink({ sourceType, sourceId, targetType, targetId, linkType }) {
|
|
35
|
+
return callZephlyAPI('mcpAddLink', {
|
|
36
|
+
sourceType,
|
|
37
|
+
sourceId,
|
|
38
|
+
targetType,
|
|
39
|
+
targetId,
|
|
40
|
+
linkType,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function removeLink({ sourceType, sourceId, targetType, targetId, linkType }) {
|
|
45
|
+
return callZephlyAPI('mcpRemoveLink', {
|
|
46
|
+
sourceType,
|
|
47
|
+
sourceId,
|
|
48
|
+
targetType,
|
|
49
|
+
targetId,
|
|
50
|
+
linkType,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Stamp a link's freshness (E-166/E-154): record that you confirmed the link
|
|
56
|
+
* is still correct, optionally against the commit you verified it at. Call this
|
|
57
|
+
* after touching code that a linked document describes so the freshness signal
|
|
58
|
+
* stays accurate. commitSha is optional.
|
|
59
|
+
*/
|
|
60
|
+
async function verifyLink({ sourceType, sourceId, targetType, targetId, linkType, commitSha }) {
|
|
61
|
+
const params = { sourceType, sourceId, targetType, targetId, linkType };
|
|
62
|
+
if (commitSha) params.commitSha = commitSha;
|
|
63
|
+
return callZephlyAPI('mcpVerifyLink', params);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* The tool speaks the agent's vocabulary (outgoing/incoming/both) because
|
|
68
|
+
* "outgoing" is unambiguous about whose perspective it is; the API's
|
|
69
|
+
* ?direction= speaks in anchor terms (source/target/both). Translate here —
|
|
70
|
+
* one mapping in one place — rather than leaking the API's wording into every
|
|
71
|
+
* agent's mental model.
|
|
72
|
+
*/
|
|
73
|
+
const DIRECTION_TO_API = {
|
|
74
|
+
outgoing: 'source',
|
|
75
|
+
incoming: 'target',
|
|
76
|
+
both: 'both',
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* List a given entity's links. Filters on linkType and targetType are
|
|
81
|
+
* optional — omit them for "all outgoing links".
|
|
82
|
+
*
|
|
83
|
+
* `direction` selects outgoing (default), incoming, or both — relates_to is
|
|
84
|
+
* not always stored bidirectionally, so "both" is what you want for an
|
|
85
|
+
* entity's full neighbourhood.
|
|
86
|
+
*
|
|
87
|
+
* `hydrate` resolves each target to its title/status so the caller can read the
|
|
88
|
+
* graph without a follow-up get_* per link. Opt-in because it costs a lookup
|
|
89
|
+
* per row.
|
|
90
|
+
*/
|
|
91
|
+
export async function listLinks({
|
|
92
|
+
sourceType,
|
|
93
|
+
sourceId,
|
|
94
|
+
linkType,
|
|
95
|
+
targetType,
|
|
96
|
+
hydrate,
|
|
97
|
+
direction,
|
|
98
|
+
includeSuggested,
|
|
99
|
+
}) {
|
|
100
|
+
const params = { sourceType, sourceId };
|
|
101
|
+
if (linkType) params.linkType = linkType;
|
|
102
|
+
if (targetType) params.targetType = targetType;
|
|
103
|
+
if (hydrate) params.hydrate = 'true';
|
|
104
|
+
if (direction) params.direction = DIRECTION_TO_API[direction] || direction;
|
|
105
|
+
if (includeSuggested) params.includeSuggested = 'true';
|
|
106
|
+
return callZephlyAPI('mcpListLinks', params);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* resolve_links — which components own these files?
|
|
111
|
+
*
|
|
112
|
+
* The read that makes linking cheap: an agent asks once, before creating work,
|
|
113
|
+
* and gets back the entities it should attach rather than having to know the
|
|
114
|
+
* component inventory. Returns `unresolved` too, because a path nothing covers
|
|
115
|
+
* is itself information — it usually means the inventory has a gap.
|
|
116
|
+
*/
|
|
117
|
+
export async function resolveLinks({ projectId, paths }) {
|
|
118
|
+
const { matches, unresolved } = await resolvePathsToComponents({ projectId, paths });
|
|
119
|
+
return {
|
|
120
|
+
deterministic: matches.filter((m) => m.score >= 1),
|
|
121
|
+
probable: matches.filter((m) => m.score < 1),
|
|
122
|
+
unmatchedPaths: unresolved,
|
|
123
|
+
count: matches.length,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* preview_links — what would be linked, without writing.
|
|
129
|
+
*
|
|
130
|
+
* Split into the two groups a caller acts on differently: what EzModo records
|
|
131
|
+
* on its own, and what it wants confirmed.
|
|
132
|
+
*/
|
|
133
|
+
export async function previewLinks({
|
|
134
|
+
projectId,
|
|
135
|
+
subjectType,
|
|
136
|
+
subjectId,
|
|
137
|
+
paths,
|
|
138
|
+
componentId,
|
|
139
|
+
epicId,
|
|
140
|
+
trigger,
|
|
141
|
+
}) {
|
|
142
|
+
const { proposals } = await previewEntityLinks({
|
|
143
|
+
projectId, subjectType, subjectId, paths, componentId, epicId, trigger,
|
|
144
|
+
});
|
|
145
|
+
const { autoLinked, linkSuggestions } = partitionProposals(proposals);
|
|
146
|
+
|
|
147
|
+
// Preview writes nothing, but the engine may already have queued rows for this
|
|
148
|
+
// entity from an earlier trigger. Attaching their ids lets a caller act on what
|
|
149
|
+
// it sees instead of taking a second lookup to find out how (#2297).
|
|
150
|
+
const resolved = await attachSuggestionIds({ subjectType, subjectId, linkSuggestions });
|
|
151
|
+
return {
|
|
152
|
+
autoLinked,
|
|
153
|
+
linkSuggestions: resolved.linkSuggestions,
|
|
154
|
+
count: autoLinked.length + resolved.linkSuggestions.length,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Milestone Handlers
|
|
3
|
+
* Handler functions for milestone-related MCP tools
|
|
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
|
|
10
|
+
* will be included in a release or initiative.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { callZephlyAPI } from '../lib/http-client.js';
|
|
14
|
+
import { buildMilestoneUrl } from '../lib/web-url.js';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Dispatch manage_milestone actions to the appropriate handler
|
|
18
|
+
*/
|
|
19
|
+
export async function manageMilestone(args) {
|
|
20
|
+
const { action, ...params } = args;
|
|
21
|
+
switch (action) {
|
|
22
|
+
case 'create': return createMilestone(params);
|
|
23
|
+
case 'update': return updateMilestone(params);
|
|
24
|
+
case 'delete': return deleteMilestone(params);
|
|
25
|
+
case 'link_epic': return linkEpicToMilestone(params);
|
|
26
|
+
case 'unlink_epic': return unlinkEpicFromMilestone(params);
|
|
27
|
+
case 'generate_changelog': return generateMilestoneChangelog(params);
|
|
28
|
+
case 'reorder_epics': return reorderMilestoneEpics(params);
|
|
29
|
+
case 'link_suite': return linkSuiteToMilestone(params);
|
|
30
|
+
case 'unlink_suite': return unlinkSuiteFromMilestone(params);
|
|
31
|
+
default: throw new Error(`Unknown action: ${action}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Unified get/list handler for milestones.
|
|
37
|
+
* - If milestoneId or milestoneSlug is provided, returns a single milestone.
|
|
38
|
+
* - Otherwise, lists all milestones for the project with optional filters.
|
|
39
|
+
* - If includeProgress is true, also fetches progress data.
|
|
40
|
+
*/
|
|
41
|
+
export async function getMilestone(args) {
|
|
42
|
+
const { includeProgress, type, status, ...rest } = args;
|
|
43
|
+
const isSingleLookup = rest.milestoneId || rest.milestoneSlug;
|
|
44
|
+
|
|
45
|
+
if (isSingleLookup) {
|
|
46
|
+
const result = await fetchSingleMilestone(rest);
|
|
47
|
+
|
|
48
|
+
if (includeProgress) {
|
|
49
|
+
const progress = await fetchMilestoneProgress(rest);
|
|
50
|
+
result.progress = progress;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// List mode
|
|
57
|
+
const listArgs = { projectId: rest.projectId };
|
|
58
|
+
if (type) listArgs.type = type;
|
|
59
|
+
if (status) listArgs.status = status;
|
|
60
|
+
|
|
61
|
+
return listMilestones(listArgs);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// --- Private helpers ---
|
|
65
|
+
|
|
66
|
+
async function createMilestone(args) {
|
|
67
|
+
const result = await callZephlyAPI('mcpCreateMilestone', args);
|
|
68
|
+
const webUrl = await buildMilestoneUrl(result?.slug);
|
|
69
|
+
if (webUrl) result.webUrl = webUrl;
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function fetchSingleMilestone(args) {
|
|
74
|
+
const result = await callZephlyAPI('mcpGetMilestone', args);
|
|
75
|
+
const webUrl = await buildMilestoneUrl(result?.milestone?.slug);
|
|
76
|
+
if (webUrl && result?.milestone) result.milestone.webUrl = webUrl;
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function updateMilestone(args) {
|
|
81
|
+
return callZephlyAPI('mcpUpdateMilestone', args);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function listMilestones(args) {
|
|
85
|
+
return callZephlyAPI('mcpListMilestones', args);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function deleteMilestone(args) {
|
|
89
|
+
return callZephlyAPI('mcpDeleteMilestone', args);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function linkEpicToMilestone(args) {
|
|
93
|
+
const { epicId, entityType, ...rest } = args;
|
|
94
|
+
return callZephlyAPI('mcpLinkEpicToMilestone', {
|
|
95
|
+
...rest,
|
|
96
|
+
entityId: epicId,
|
|
97
|
+
entityType: entityType || 'epic',
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async function unlinkEpicFromMilestone(args) {
|
|
102
|
+
return callZephlyAPI('mcpUnlinkEpicFromMilestone', args);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function generateMilestoneChangelog(args) {
|
|
106
|
+
return callZephlyAPI('mcpGenerateMilestoneChangelog', args);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async function fetchMilestoneProgress(args) {
|
|
110
|
+
return callZephlyAPI('mcpGetMilestoneProgress', args);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async function reorderMilestoneEpics(args) {
|
|
114
|
+
const { itemIds, ...rest } = args;
|
|
115
|
+
return callZephlyAPI('mcpReorderMilestoneEpics', {
|
|
116
|
+
...rest,
|
|
117
|
+
itemIds,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async function linkSuiteToMilestone(args) {
|
|
122
|
+
const { suiteId, ...rest } = args;
|
|
123
|
+
return callZephlyAPI('mcpLinkSuiteToMilestone', {
|
|
124
|
+
...rest,
|
|
125
|
+
suiteId,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async function unlinkSuiteFromMilestone(args) {
|
|
130
|
+
return callZephlyAPI('mcpUnlinkSuiteFromMilestone', args);
|
|
131
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Organization Handlers
|
|
3
|
+
* Handler functions for organization-related MCP tools
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { callZephlyAPI } from '../lib/http-client.js';
|
|
7
|
+
|
|
8
|
+
export async function getOrganization(args) {
|
|
9
|
+
const { mode = 'default' } = args;
|
|
10
|
+
if (mode === 'list') {
|
|
11
|
+
return callZephlyAPI('mcpListOrganizations', {});
|
|
12
|
+
}
|
|
13
|
+
return callZephlyAPI('mcpGetDefaultOrganization', {});
|
|
14
|
+
}
|