Summary
Add ana mcp — a read-only MCP server over scan.json + the proof chain, as a long-running stdio subcommand of the existing anatomia-cli binary (not a separate package). It exposes a small set of task-shaped, provenance-stamped tools so any MCP-capable agent (Cursor, Cline, Zed, Claude Desktop, Claude Code) can query Anatomia's ground truth — and so the Claude plugin's .mcp.json and the MCP Registry listing have a target.
This is the "one binary, three channels" model RepoWise uses: their server.json runs repowise mcp <path> --transport stdio — the MCP server is the CLI in a different mode. We mirror it with ana mcp.
Listing on the MCP Registry is a separate issue (procedural/GTM, DNS auth, blocked-by this build). This issue is the code.
Why it matters
- Reaches every non-Claude MCP agent — the literal "extend to other CLI agents cheaply" constraint (
project-context.md).
- Carries the "Verified Context" wedge (
COMPETITIVE_RESEARCH_repowise.md §5): our answers carry a provenance stamp competitors structurally cannot — "verified 2026-06-01 by proof entry X" vs "inferred."
- Unblocks the plugin's
.mcp.json block.
Recommended tool surface (all read-only; reuse existing readers)
| Tool |
Args |
Reuses |
Returns |
get_scan_context |
files? |
scan.json reader (check.ts:672), projecting stack/conventions.*/patterns.* |
house style + stack for those files (our conventions wedge) |
query_findings |
files |
getProofContext(files, root) verbatim (proofSummary.ts:1198) |
active proof findings touching those files, stamped |
get_proof |
slug |
slug lookup (proof.ts:822) over proof_chain.json |
one proof entry: contract, assertions, result, findings |
should_i_touch |
files |
composite of the above (+ blast-radius once #293/#297 land) |
"before you edit" digest; degrades gracefully today |
Ship get_scan_context, query_findings, get_proof in v1; should_i_touch as a thin composite (enriches later).
Provenance-stamp design (the wedge — and the honesty rule)
Every answer carries:
provenance: {
status: "verified" | "detected" | "inferred",
source: "proof_chain" | "scan",
entry?: string, // present iff verified — the proof slug
verified_at?: string, // entry.completed_at
confidence?: number // for scan conventions
}
- proof-chain answers (
query_findings, get_proof) → status:"verified", source:"proof_chain", entry, verified_at (honest because the entry passed an independent verify stage: result:'PASS' + assertions[].status, proof.ts:127,133).
- scan conventions →
status:"detected" with the existing confidence. Never stamp scan data as verified — that hollows out the word the product is built on (COMPETITIVE_RESEARCH_repowise.md §3.3/§9.2).
Integration points (verified)
- New
src/commands/mcp.ts — structured like _capture.ts (process-lifecycle, stdout discipline) but a normal long-running command. Uses @modelcontextprotocol/sdk McpServer + StdioServerTransport; registerTool(name, {description, inputSchema: zodSchema}, handler). Resolve root via findProjectRoot() (validators.ts:169) — no repo_path arg needed (cleaner than RepoWise, matches every other ana command).
src/index.ts — register in the INTELLIGENCE group (near proof/learn, index.ts:75-78); NOT hidden (unlike _capture at :82).
- Dependency: add
@modelcontextprotocol/sdk (^1.29.x) to packages/cli/package.json:57. zod is already ^4.3.6 (SDK peer ^3.25 || ^4.0 — compatible, no bump). The SDK transitively pulls express/hono/jose/cors (HTTP-transport deps); verify tsup tree-shakes the unused HTTP path so the global install doesn't balloon — mark them external if not.
mcpName in package.json: add "mcpName": "dev.anatomia/anatomia" (registry link; must equal server.json name — handled in the listing issue).
Phasing
Gotchas
- Read-only is absolute. Never mutate
.ana/*, never call scanProject() (no re-scan), never spawn the pipeline. Surface scannedAt/verified_at; if stale, that's the user's ana scan problem.
- stdout is JSON-RPC — no stray logging. Any
console.log corrupts the stream (inverse of normal ana commands). All diagnostics → stderr. Audit read paths that log (e.g. check.ts:689).
- Stateful-server test strategy: follow the
_capture spawn-based harness (tests/commands/_capture.test.ts) — spawn dist/index.js mcp, write one JSON-RPC initialize + tools/call to stdin, read one framed response, assert, proc.kill(). Use a tmp project with fixture .ana/scan.json + .ana/proof_chain.json. Unit-test the pure handlers directly + one spawn smoke test. (Note: the real CI matrix is ubuntu × Node 22/24, test.yml:23 — the "3 OS" line in project-context.md is stale; lower flakiness risk. Coverage thresholds 80/75/80/80, vitest.config.ts:26; consider excluding the connect/transport glue.)
- Provenance honesty — never stamp scan-detected conventions as
verified.
- Bundle weight — confirm the unused HTTP transport is tree-shaken.
Acceptance criteria
node dist/index.js mcp --transport stdio responds to JSON-RPC initialize; tools/list returns exactly the v1 tools.
tools/call query_findings {files:[…]} returns the same data as ana proof context <files> --json (reuses getProofContext), each finding stamped status:"verified", source:"proof_chain", entry, verified_at.
get_scan_context conventions carry status:"detected" (never "verified") with the scan's confidence; an assertion enforces no scan-sourced field is stamped verified.
- The server performs zero writes under
.ana/ and never invokes scanProject (spawn test against a read-only fixture confirms no files change).
- Nothing on stdout except framed JSON-RPC (clean round-trip parse, no leading/trailing noise).
pnpm --filter anatomia-cli build && pnpm vitest run passes, test count not decreased, dist/index.js builds cleanly with the SDK bundled, existing ana scan/ana proof unchanged.
Effort
~2–3 engineer-days. The tools are thin wrappers over getProofContext, the slug lookup, and a scan.json reader — real reuse. Cost is SDK + stdio wiring, the provenance-stamp layer, the stdout-discipline audit, bundle verification, and the spawn-based test harness.
Sequencing
Plugin ships first (its .mcp.json is added when this lands). Design tools to degrade gracefully now and enrich when #293/#297/#301 populate the dormant scan fields. Per the research, the MCP server should come after #295 proves ambient-context value — this unlocks non-Claude agents but isn't the most urgent distribution move.
Summary
Add
ana mcp— a read-only MCP server overscan.json+ the proof chain, as a long-running stdio subcommand of the existinganatomia-clibinary (not a separate package). It exposes a small set of task-shaped, provenance-stamped tools so any MCP-capable agent (Cursor, Cline, Zed, Claude Desktop, Claude Code) can query Anatomia's ground truth — and so the Claude plugin's.mcp.jsonand the MCP Registry listing have a target.This is the "one binary, three channels" model RepoWise uses: their
server.jsonrunsrepowise mcp <path> --transport stdio— the MCP server is the CLI in a different mode. We mirror it withana mcp.Why it matters
project-context.md).COMPETITIVE_RESEARCH_repowise.md§5): our answers carry a provenance stamp competitors structurally cannot — "verified 2026-06-01 by proof entry X" vs "inferred.".mcp.jsonblock.Recommended tool surface (all read-only; reuse existing readers)
get_scan_contextfiles?check.ts:672), projectingstack/conventions.*/patterns.*query_findingsfilesgetProofContext(files, root)verbatim (proofSummary.ts:1198)get_proofslugproof.ts:822) overproof_chain.jsonshould_i_touchfilesShip
get_scan_context,query_findings,get_proofin v1;should_i_touchas a thin composite (enriches later).Provenance-stamp design (the wedge — and the honesty rule)
Every answer carries:
query_findings,get_proof) →status:"verified",source:"proof_chain",entry,verified_at(honest because the entry passed an independent verify stage:result:'PASS'+assertions[].status,proof.ts:127,133).status:"detected"with the existingconfidence. Never stamp scan data asverified— that hollows out the word the product is built on (COMPETITIVE_RESEARCH_repowise.md§3.3/§9.2).Integration points (verified)
src/commands/mcp.ts— structured like_capture.ts(process-lifecycle, stdout discipline) but a normal long-running command. Uses@modelcontextprotocol/sdkMcpServer+StdioServerTransport;registerTool(name, {description, inputSchema: zodSchema}, handler). Resolve root viafindProjectRoot()(validators.ts:169) — norepo_patharg needed (cleaner than RepoWise, matches every otheranacommand).src/index.ts— register in the INTELLIGENCE group (nearproof/learn,index.ts:75-78); NOT hidden (unlike_captureat:82).@modelcontextprotocol/sdk(^1.29.x) topackages/cli/package.json:57. zod is already^4.3.6(SDK peer^3.25 || ^4.0— compatible, no bump). The SDK transitively pulls express/hono/jose/cors (HTTP-transport deps); verify tsup tree-shakes the unused HTTP path so the global install doesn't balloon — mark themexternalif not.mcpNameinpackage.json: add"mcpName": "dev.anatomia/anatomia"(registry link; must equalserver.jsonname — handled in the listing issue).Phasing
should_i_touch, stdio only, provenance-stamped. Read-only, no pipeline, no mutation.should_i_touch/blast-radius by reading the now-populated dormant fields (gitIntelligence.coChangeCoupling/circularDeps,engineResult.ts:264-294, defaultnulltoday) — pure projection, no signature change, forward-compatible.Gotchas
.ana/*, never callscanProject()(no re-scan), never spawn the pipeline. SurfacescannedAt/verified_at; if stale, that's the user'sana scanproblem.console.logcorrupts the stream (inverse of normalanacommands). All diagnostics → stderr. Audit read paths that log (e.g.check.ts:689)._capturespawn-based harness (tests/commands/_capture.test.ts) — spawndist/index.js mcp, write one JSON-RPCinitialize+tools/callto stdin, read one framed response, assert,proc.kill(). Use a tmp project with fixture.ana/scan.json+.ana/proof_chain.json. Unit-test the pure handlers directly + one spawn smoke test. (Note: the real CI matrix is ubuntu × Node 22/24,test.yml:23— the "3 OS" line in project-context.md is stale; lower flakiness risk. Coverage thresholds 80/75/80/80,vitest.config.ts:26; consider excluding the connect/transport glue.)verified.Acceptance criteria
node dist/index.js mcp --transport stdioresponds to JSON-RPCinitialize;tools/listreturns exactly the v1 tools.tools/call query_findings {files:[…]}returns the same data asana proof context <files> --json(reusesgetProofContext), each finding stampedstatus:"verified",source:"proof_chain",entry,verified_at.get_scan_contextconventions carrystatus:"detected"(never"verified") with the scan's confidence; an assertion enforces no scan-sourced field is stamped verified..ana/and never invokesscanProject(spawn test against a read-only fixture confirms no files change).pnpm --filter anatomia-cli build && pnpm vitest runpasses, test count not decreased,dist/index.jsbuilds cleanly with the SDK bundled, existingana scan/ana proofunchanged.Effort
~2–3 engineer-days. The tools are thin wrappers over
getProofContext, the slug lookup, and a scan.json reader — real reuse. Cost is SDK + stdio wiring, the provenance-stamp layer, the stdout-discipline audit, bundle verification, and the spawn-based test harness.Sequencing
Plugin ships first (its
.mcp.jsonis added when this lands). Design tools to degrade gracefully now and enrich when #293/#297/#301 populate the dormant scan fields. Per the research, the MCP server should come after #295 proves ambient-context value — this unlocks non-Claude agents but isn't the most urgent distribution move.