diff --git a/packages/mcp-server/README.md b/packages/mcp-server/README.md index fd4783ea9..dcc32aa94 100644 --- a/packages/mcp-server/README.md +++ b/packages/mcp-server/README.md @@ -4,6 +4,11 @@ MCP server exposing GSD orchestration tools for Claude Code, Cursor, and other M Start GSD auto-mode sessions, poll progress, resolve blockers, and retrieve results — all through the [Model Context Protocol](https://modelcontextprotocol.io/). +This package now exposes two tool surfaces: + +- session/read tools for starting and inspecting GSD sessions +- workflow mutation tools for planning, completion, validation, reassessment, and gate persistence + ## Installation ```bash @@ -69,6 +74,38 @@ Add to `.cursor/mcp.json`: ## Tools +### Workflow mutation tools + +The workflow MCP surface includes: + +- `gsd_plan_milestone` +- `gsd_plan_slice` +- `gsd_replan_slice` +- `gsd_slice_replan` +- `gsd_task_complete` +- `gsd_complete_task` +- `gsd_slice_complete` +- `gsd_complete_slice` +- `gsd_validate_milestone` +- `gsd_milestone_validate` +- `gsd_complete_milestone` +- `gsd_milestone_complete` +- `gsd_reassess_roadmap` +- `gsd_roadmap_reassess` +- `gsd_save_gate_result` +- `gsd_summary_save` +- `gsd_milestone_status` + +These mutation tools use the same GSD workflow handlers as the native in-process tool path. + +Current support boundary: + +- when running inside the GSD monorepo checkout, the MCP server auto-discovers the shared workflow executor module +- outside the monorepo, set `GSD_WORKFLOW_EXECUTORS_MODULE` to an importable `workflow-tool-executors` module path if you want the mutation tools enabled +- session/read tools do not depend on this bridge + +If the executor bridge cannot be loaded, workflow mutation calls will fail with a precise configuration error instead of silently degrading. + ### `gsd_execute` Start a GSD auto-mode session for a project directory. @@ -175,6 +212,7 @@ Resolve a pending blocker in a session by sending a response to the blocked UI r | Variable | Description | |----------|-------------| | `GSD_CLI_PATH` | Absolute path to the GSD CLI binary. If not set, the server resolves `gsd` via `which`. | +| `GSD_WORKFLOW_EXECUTORS_MODULE` | Optional absolute path or `file:` URL for the shared GSD workflow executor module used by workflow mutation tools. | ## Architecture diff --git a/packages/mcp-server/src/server.ts b/packages/mcp-server/src/server.ts index 0b65e13ef..f4f5fe206 100644 --- a/packages/mcp-server/src/server.ts +++ b/packages/mcp-server/src/server.ts @@ -1,9 +1,9 @@ /** - * MCP Server — registers GSD orchestration + read-only project state tools. + * MCP Server — registers GSD orchestration, project-state, and workflow tools. * * Session tools (6): gsd_execute, gsd_status, gsd_result, gsd_cancel, gsd_query, gsd_resolve_blocker * Read-only tools (6): gsd_progress, gsd_roadmap, gsd_history, gsd_doctor, gsd_captures, gsd_knowledge - * Workflow tools (3): gsd_summary_save, gsd_task_complete, gsd_milestone_status + * Workflow tools (17): planning, replanning, completion, validation, reassessment, gate result, and milestone status tools * * Uses dynamic imports for @modelcontextprotocol/sdk because TS Node16 * cannot resolve the SDK's subpath exports statically (same pattern as @@ -117,7 +117,7 @@ interface McpServerInstance { // --------------------------------------------------------------------------- /** - * Create and configure an MCP server with 12 GSD tools (6 session + 6 read-only). + * Create and configure an MCP server with session, read-only, and workflow tools. * * Returns the McpServer instance — call `connect(transport)` to start serving. * Uses dynamic imports for the MCP SDK to avoid TS subpath resolution issues. diff --git a/packages/mcp-server/src/workflow-tools.ts b/packages/mcp-server/src/workflow-tools.ts index 523005943..4784863fa 100644 --- a/packages/mcp-server/src/workflow-tools.ts +++ b/packages/mcp-server/src/workflow-tools.ts @@ -2,6 +2,8 @@ * Workflow MCP tools — exposes the core GSD mutation/read handlers over MCP. */ +import { resolve } from "node:path"; +import { pathToFileURL } from "node:url"; import { z } from "zod"; const SUMMARY_ARTIFACT_TYPES = ["SUMMARY", "RESEARCH", "CONTEXT", "ASSESSMENT", "CONTEXT-DRAFT"] as const; @@ -217,12 +219,46 @@ type WorkflowToolExecutors = { let workflowToolExecutorsPromise: Promise | null = null; +function toFileUrl(modulePath: string): string { + return pathToFileURL(resolve(modulePath)).href; +} + +function getWorkflowExecutorModuleCandidates(env: NodeJS.ProcessEnv = process.env): string[] { + const candidates: string[] = []; + const explicitModule = env.GSD_WORKFLOW_EXECUTORS_MODULE?.trim(); + if (explicitModule) { + candidates.push( + explicitModule.startsWith("file:") || explicitModule.startsWith("data:") ? explicitModule : toFileUrl(explicitModule), + ); + } + + candidates.push( + new URL("../../../src/resources/extensions/gsd/tools/workflow-tool-executors.js", import.meta.url).href, + new URL("../../../src/resources/extensions/gsd/tools/workflow-tool-executors.ts", import.meta.url).href, + ); + + return [...new Set(candidates)]; +} + async function getWorkflowToolExecutors(): Promise { if (!workflowToolExecutorsPromise) { - const jsUrl = new URL("../../../src/resources/extensions/gsd/tools/workflow-tool-executors.js", import.meta.url).href; - const tsUrl = new URL("../../../src/resources/extensions/gsd/tools/workflow-tool-executors.ts", import.meta.url).href; - workflowToolExecutorsPromise = import(jsUrl) - .catch(() => import(tsUrl)) as Promise; + workflowToolExecutorsPromise = (async () => { + const attempts: string[] = []; + for (const candidate of getWorkflowExecutorModuleCandidates()) { + try { + return await import(candidate) as WorkflowToolExecutors; + } catch (err) { + attempts.push(`${candidate} (${err instanceof Error ? err.message : String(err)})`); + } + } + + throw new Error( + "Unable to load GSD workflow executor bridge for MCP mutation tools. " + + "Set GSD_WORKFLOW_EXECUTORS_MODULE to an importable workflow-tool-executors module, " + + "or run the MCP server from a GSD checkout that includes src/resources/extensions/gsd/tools/workflow-tool-executors.(js|ts). " + + `Attempts: ${attempts.join("; ")}`, + ); + })(); } return workflowToolExecutorsPromise; }