Files
msd-core/docs/how-to/connect-msd-mcp-server.md
Jakub Zych 6cfa0c55d2 refactor: drop 12 runtimes, keep Claude, Codex, OpenCode, Cursor, ZCode, Antigravity
Removes kilo, kimi, kimi-code, copilot, windsurf, augment, trae, qwen, hermes,
cline, codebuddy and pi end to end: capability descriptors, installer branches
and converters (bin/install.js 14.9k -> 11.2k lines), TypeScript converters,
hook surfaces and runtime homes, review lanes qwen/kimi-code, the two pi
migrations, Kimi payload normalization in the hook guards, dead hostBehaviors
vocabulary, launcher home probes, fixtures, runtime-specific tests and the
prose that presented them as supported.

Installer output for the six kept runtimes is byte-identical to before the
prune. The Kimi tool-vocabulary tests in workflow-guard, read-guard and
read-injection-scanner are left in place pending a decision.
2026-10-06 20:02:40 +02:00

7.1 KiB

How to connect a host to the MSD companion MCP server

This guide shows you how to make a MCP-capable host (Claude Code, Codex, OpenCode, VS Code, Antigravity CLI, Cursor) drive MSD — run MSD commands and read/write .planning/ state — through the companion MCP server, with no bespoke plugin.

Once connected, three tools appear in the host alongside its others: msd_invoke_command, msd_read_state, msd_write_state. The server also serves a read-only catalog of MSD's own content — workflows and references as MCP resources, and the /msd-* commands as MCP prompts — so a host can browse and pull that content directly instead of shelling out to the CLI. (For the tool contracts, see the reference section below; for why this server exists and its trust model, see ADR-1239 and the capability trust model.)

1. Add the server to your host's MCP config

The entry shape is the same everywhere; only the config file and key differ by host.

{
  "msd": {
    "command": "npx",
    "args": ["-y", "@golem15/msd-core", "msd-mcp-server"],
    "cwd": "/abs/path/to/your/project"
  }
}
  • Claude Code / Codex / Cursor — under the host's mcpServers object (project or user config).
  • VS Code — in the workspace MCP servers list.
  • Antigravity — under the mcpServers block of its standalone mcp_config.json profile (not embedded in settings.json) — global at ~/.gemini/antigravity/mcp_config.json (or the sibling antigravity-ide/antigravity-cli dir MSD resolved into), project-local at .agents/mcp_config.json. MSD's installer configures this entry automatically (--antigravity installs).
  • OpenCode — under the mcp key (not mcpServers), in ~/.config/opencode/opencode.jsonc (global) or ./opencode.json (project). The entry shape also differs — see below.

Set cwd to the project whose .planning/ you want MSD to manage — the server resolves state paths against it.

OpenCode entry shape

OpenCode uses a type/command/timeout entry under the mcp key instead of the generic command/args/cwd form above:

{
  "mcp": {
    "msd": {
      "type": "local",
      "command": ["npx", "-y", "@golem15/msd-core", "msd-mcp-server"],
      "timeout": 10000
    }
  }
}

2. Restart the host

On startup the host performs the MCP initialize handshake. The response advertises tools, resources, and prompts capabilities, so the three MSD tools become callable and the host can also list the served catalog (resources and prompts) described below. The server never advertises resources.subscribe or listChanged — the catalog is fixed for the life of the server process, so there is nothing to subscribe to.

3. Verify

Ask the host to read an existing planning file:

{ "name": "msd_read_state", "arguments": { "path": "/abs/path/to/your/project/.planning/STATE.md" } }

It returns the file's contents. msd_invoke_command takes {family, subcommand, args} and returns the command-routing hub's structured result (the same shape msd-tools produces).

4. Browse the catalog (resources and prompts)

The server also exposes MSD's own content tree as MCP resources and the commands/msd/*.md command set as MCP prompts. This is additive: the file-copy install (the default for every runtime) is unchanged, and the catalog only adds a way for an MCP-capable host to read the same content directly over the protocol.

List and read a resource

List available resources (paginated — ask the host to follow nextCursor until it is absent):

{ "name": "resources/list", "arguments": { "cursor": null } }

Each entry has a msd://<segment>/<relpath> URI, where <segment> is workflows, references, or commands, and <relpath> is the file's path within that segment (for example, msd://workflows/plan-phase.md, msd://references/untrusted-input-boundary.md, or msd://commands/plan-phase.md). Commands appear in both surfaces: read one as a resource to get its raw markdown, or get it as a prompt to have the host treat it as an invocable message. Read one by URI:

{ "name": "resources/read", "arguments": { "uri": "msd://workflows/plan-phase.md" } }

An unknown or unrecognized URI (including any path-traversal or absolute-path attempt) returns a JSON-RPC error rather than an empty or partial result.

List and get a prompt

{ "name": "prompts/list", "arguments": {} }

Each entry is keyed by its bare command name — plan-phase, not a path. Get one:

{ "name": "prompts/get", "arguments": { "name": "plan-phase" } }

An unknown prompt name returns a JSON-RPC error. prompts/get accepts an arguments object but ignores it — no shipped command template takes injected arguments today.

Composed vs. verbatim content

Workflow resources (msd://workflows/…) are served composed — with <!-- msd:section --> markers stripped — exactly as the installed file tree gets them, while reference and command content is served verbatim, because some reference and command docs use that marker syntax as a documented example rather than a real marker.

If something does not work

  • command not found: msd-mcp-server — invoke via npx as shown above, or install the package globally first (npm i -g @golem15/msd-core).
  • msd_read_state fails with ENOENT — the path is resolved literally; pass an absolute path under the project's .planning/.
  • The host lists no MSD tools — confirm the server starts in isolation: npx @golem15/msd-core msd-mcp-server then send an initialize request on stdin; it writes a protocolVersion response and exits on EOF.
  • You manage multiple projects — register one msd entry per project with a distinct name and cwd; the server is stateless across projects.

Reference — the three tools

Tool Arguments Returns
msd_invoke_command {family: string, subcommand: string, args?: unknown[]} the command-routing hub result ({ok, …}) as JSON text
msd_read_state {path: string} the file contents as text
msd_write_state {path: string, content: string} {ok: true, path} as JSON text

Errors from a tool are returned as MCP tool errors (isError: true), not as JSON-RPC protocol errors — the host surfaces them in its normal tool-failure UX.

Reference — the served catalog

Method Arguments Returns
resources/list {cursor?: string} {resources: [{uri, name, title, description, mimeType}], nextCursor?: string}
resources/read {uri: string} {contents: [{uri, mimeType, text}]}
prompts/list {} {prompts: [{name, title, description}]}
prompts/get {name: string, arguments?: object} {description, messages: [{role: "user", content: {type: "text", text}}]}

Errors from the catalog (unknown URI, unknown prompt name, malformed cursor, a refused path-traversal attempt) are returned as JSON-RPC protocol errors, not MCP tool errors — unlike the three tools above.