You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(mcp): transport parity — ingest_coverage + query baseline (#167)
* feat(mcp): add ingest_coverage and query baseline parity
Close the last two agent-relevant transport gaps: MCP/HTTP can load coverage artifacts and diff queries against saved baselines in one call, matching CLI ingest-coverage and query --baseline.
* fix(mcp): address PR #167 review cycles 1–2
Lift CLI baseline diff onto compareQueryBaseline, sync consumer docs to 20 tools,
transport-neutral baseline error hints, and expand ingest/baseline test coverage.
* fix(mcp): address PR #167 review cycle 3
Expand ingest-coverage and HTTP transport tests, document baseline/ingest_coverage on MCP/HTTP surfaces, and fix V8 runtime test fixtures.
* fix(mcp): address PR #167 review cycle 4
Delete shipped plan docs, fix consumer-surface leak, document ingest_coverage in CSRF guard, and add MCP/HTTP transport tests for baseline compare and ingest_coverage.
* docs(mcp): close PR #167 review cycle 5 glossary gaps
Cross-ref MCP/HTTP baseline on query baseline entry, document ingest-coverage-run orchestration layer, and note baseline format incompatibilities in cmd-mcp help.
`codemap ingest-coverage <path> [--runtime] [--json]` reads a coverage artifact and writes statement-level rows into the `coverage` table. Three formats:
156
+
`codemap ingest-coverage <path> [--runtime] [--json]` reads a coverage artifact and writes statement-level rows into the `coverage` table. MCP/HTTP twin: **`ingest_coverage`** (`{path, runtime?}`). Three formats:
Engine: `application/coverage-engine.ts` — pure `upsertCoverageRows({db, projectRoot, rows, format, sourcePath})` core consumed by `ingestIstanbul`, `ingestLcov`, and `ingestV8`.
168
+
Engine: `application/coverage-engine.ts` — pure `upsertCoverageRows({db, projectRoot, rows, format, sourcePath})` core consumed by `ingestIstanbul`, `ingestLcov`, and `ingestV8`. Transport orchestration (path resolution, artifact I/O, V8 directory merge) lives in `application/ingest-coverage-run.ts` (`runIngestCoverageOnDb`), shared by CLI, MCP `ingest_coverage`, and HTTP.
169
169
170
170
### `content_hash`
171
171
@@ -361,7 +361,7 @@ Rust-based CSS parser (NAPI bindings). Codemap's `src/css-parser.ts` uses its vi
361
361
362
362
### `codemap mcp` / MCP server
363
363
364
-
Stdio MCP (Model Context Protocol) server exposing codemap's structural-query surface to agent hosts (Claude Code, Cursor, Codex, generic MCP clients) as JSON-RPC tools — eliminates the bash round-trip on every agent invocation. **19 tools:** `query`, `query_batch`, `query_recipe`, `audit`, `save_baseline`, `list_baselines`, `drop_baseline`, `context`, `validate`, `show`, `snippet`, `impact`, `affected`, `trace`, `explore`, `node`, `apply`, `apply_rows`, `apply_diff_input`. Each has a CLI twin with the same JSON payload except transport-only MCP resources (`codemap://mcp-instructions`, initialize `instructions`). Subset via **`CODEMAP_MCP_TOOLS`** ([agents.md § MCP tool allowlist](./agents.md#mcp-tool-allowlist)). **Resources:** `codemap://schema`, `codemap://skill`, `codemap://rule`, `codemap://mcp-instructions`, `codemap://recipes`, `codemap://recipes/{id}`, `codemap://files/{path}`, `codemap://symbols/{name}`. Resource freshness is split by contract: schema / skill / rule / mcp-instructions are lazy-cached per server process; recipes, files, and symbols are live read-per-call so inline recency fields and index mutations under `--watch` don't freeze at first read. HTTP's `GET /resources/{encoded-uri}` uses the same resource handler. **Baseline tools** (`save_baseline`, `list_baselines`, `drop_baseline`) mirror `query --save-baseline` / `--baselines` / `--drop-baseline`. **CLI twins:** `query batch`, `trace`, `explore`, `node`, `file`, `schema`, `symbols`, `context --include-snippets`. Tool input/output keys are snake_case on MCP/HTTP — Codemap's convention; CLI stays kebab. Output shape matches each tool's CLI JSON payload; MCP wraps payloads in `{content: [{type: "text", text: …}]}`. Bootstrap once at server boot; tool handlers (in `application/tool-handlers.ts`) and resource handlers (in `application/resource-handlers.ts`) are pure transport-agnostic — the same handlers serve `codemap serve` (HTTP) via `POST /tool/{name}` and `GET /resources/{encoded-uri}`. **Session lifecycle:** exits on client disconnect (stdin EOF, stdout broken pipe, parent process exit, SIGINT/SIGTERM) via `session-lifecycle.ts`; **no idle timeout** — the process stays up while the pipe is open even without tool calls (see [§ Session lifecycle](./architecture.md#cli-usage)). With `--watch`, the watcher starts before connect and drains on exit. Implementation: `src/cli/cmd-mcp.ts` (CLI shell) + `src/application/mcp-server.ts` (engine). See [`architecture.md` § MCP wiring](./architecture.md#cli-usage).
364
+
Stdio MCP (Model Context Protocol) server exposing codemap's structural-query surface to agent hosts (Claude Code, Cursor, Codex, generic MCP clients) as JSON-RPC tools — eliminates the bash round-trip on every agent invocation. **20 tools:** `query`, `query_batch`, `query_recipe`, `audit`, `save_baseline`, `list_baselines`, `drop_baseline`, `context`, `validate`, `show`, `snippet`, `impact`, `affected`, `trace`, `explore`, `node`, `apply`, `apply_rows`, `apply_diff_input`, `ingest_coverage`. Each has a CLI twin with the same JSON payload except transport-only MCP resources (`codemap://mcp-instructions`, initialize `instructions`). Subset via **`CODEMAP_MCP_TOOLS`** ([agents.md § MCP tool allowlist](./agents.md#mcp-tool-allowlist)). **Resources:** `codemap://schema`, `codemap://skill`, `codemap://rule`, `codemap://mcp-instructions`, `codemap://recipes`, `codemap://recipes/{id}`, `codemap://files/{path}`, `codemap://symbols/{name}`. Resource freshness is split by contract: schema / skill / rule / mcp-instructions are lazy-cached per server process; recipes, files, and symbols are live read-per-call so inline recency fields and index mutations under `--watch` don't freeze at first read. HTTP's `GET /resources/{encoded-uri}` uses the same resource handler. **Baseline tools** (`save_baseline`, `list_baselines`, `drop_baseline`) mirror `query --save-baseline` / `--baselines` / `--drop-baseline`; **`query` / `query_recipe`** also accept optional `baseline` for one-shot row diff vs saved snapshots (same envelope as CLI `query --baseline`). **CLI twins:** `query batch`, `trace`, `explore`, `node`, `file`, `schema`, `symbols`, `context --include-snippets`, `ingest-coverage`. Tool input/output keys are snake_case on MCP/HTTP — Codemap's convention; CLI stays kebab. Output shape matches each tool's CLI JSON payload; MCP wraps payloads in `{content: [{type: "text", text: …}]}`. Bootstrap once at server boot; tool handlers (in `application/tool-handlers.ts`) and resource handlers (in `application/resource-handlers.ts`) are pure transport-agnostic — the same handlers serve `codemap serve` (HTTP) via `POST /tool/{name}` and `GET /resources/{encoded-uri}`. **Session lifecycle:** exits on client disconnect (stdin EOF, stdout broken pipe, parent process exit, SIGINT/SIGTERM) via `session-lifecycle.ts`; **no idle timeout** — the process stays up while the pipe is open even without tool calls (see [§ Session lifecycle](./architecture.md#cli-usage)). With `--watch`, the watcher starts before connect and drains on exit. Implementation: `src/cli/cmd-mcp.ts` (CLI shell) + `src/application/mcp-server.ts` (engine). See [`architecture.md` § MCP wiring](./architecture.md#cli-usage).
365
365
366
366
### `query_batch`
367
367
@@ -453,7 +453,7 @@ Any SQL run against `.codemap/index.db` — either a **recipe** (saved SQL by id
453
453
454
454
### query baseline
455
455
456
-
A snapshot of a query result set saved by `codemap query --save-baseline[=<name>]` and replayed by `codemap query --baseline[=<name>]` for added/removed diffs. Stored in the `query_baselines` table inside `<state-dir>/index.db` (default `.codemap/index.db`; no parallel JSON files; survives `--full` and `SCHEMA_VERSION` rebuilds because the table is intentionally absent from `dropAll()`). Default name = `--recipe` id; ad-hoc SQL must pass an explicit name. Diff identity is per-row `JSON.stringify` equality — exact match, no fuzzy "changed" category in v1.
456
+
A snapshot of a query result set saved by `codemap query --save-baseline[=<name>]` and replayed by `codemap query --baseline[=<name>]` for added/removed diffs. MCP/HTTP twin: optional `baseline` on `query` / `query_recipe` (explicit name string; incompatible with non-`json``format` and `group_by`). Stored in the `query_baselines` table inside `<state-dir>/index.db` (default `.codemap/index.db`; no parallel JSON files; survives `--full` and `SCHEMA_VERSION` rebuilds because the table is intentionally absent from `dropAll()`). Default name = `--recipe` id; ad-hoc SQL must pass an explicit name. Diff identity is per-row `JSON.stringify` equality — exact match, no fuzzy "changed" category in v1.
457
457
458
458
### query recipe
459
459
@@ -544,7 +544,7 @@ Long-running process that subscribes to filesystem changes via [chokidar v5](htt
544
544
545
545
### `codemap serve` / HTTP server
546
546
547
-
Long-running HTTP server exposing the same tool taxonomy as `codemap mcp` over `POST /tool/{name}` for non-MCP consumers (CI scripts, simple `curl`, IDE plugins that don't speak MCP). Default bind **`127.0.0.1:7878`** (loopback only — refuse `0.0.0.0` unless explicitly opted in via `--host 0.0.0.0`); optional `--token <secret>` requires `Authorization: Bearer <secret>` on every request. HTTP returns each tool's native JSON payload directly (NOT MCP's `{content: [...]}` wrapper); `query` / `query_recipe` match `codemap query --json` row arrays unless `summary` / `group_by` reshape the envelope (baseline save/compare is separate tools — not on MCP/HTTP `query` / `query_recipe`); parity twins (`query batch`, `trace`, `explore`, `node`, `file`, `schema`, `symbols`, `context`) always emit JSON on CLI without `--json`; other tools match their CLI `--json` payloads when that flag is set; `format: "sarif"` payloads ship as `application/sarif+json`, `format: "annotations"` / `"mermaid"` / `"diff"` as `text/plain; charset=utf-8`, `format: "diff-json"` as `application/json; charset=utf-8`. Routes: `POST /tool/{name}` (every MCP tool), `GET /resources/{encoded-uri}` (resource handler for `codemap://recipes`, `codemap://recipes/{id}`, `codemap://schema`, `codemap://skill`, `codemap://rule`, `codemap://mcp-instructions`, `codemap://files/{path}`, and `codemap://symbols/{name}`), `GET /health` (auth-exempt liveness probe — does not start the watcher), `GET /tools` / `GET /resources` (catalogs). With `--watch`, chokidar is refcount-gated per request and stops 5s after the last client (`HTTP_WATCH_RELEASE_GRACE_MS`) — distinct from MCP idle shutdown; the HTTP process keeps listening. Pure transport — same `tool-handlers.ts` / `resource-handlers.ts` MCP uses; no engine duplication. Errors → `{"error": "..."}` with HTTP status 400 / 401 / 403 / 404 / 500. SIGINT / SIGTERM → graceful drain. Every response carries `X-Codemap-Version: <semver>`. **CSRF + DNS-rebinding guard:** every request (including auth-exempt `/health`) is evaluated against `Sec-Fetch-Site` / `Origin` / `Host` when present — modern browsers send `Sec-Fetch-Site` and `Origin` on cross-origin fetches (header presence varies by request type, browser, and privacy settings), so the guard rejects browser-driven cross-origin requests like a malicious local webpage `fetch`-ing `http://127.0.0.1:7878/tool/save_baseline` to mutate `.codemap/index.db`. `Host` mismatch on a loopback bind blocks DNS rebinding (an attacker resolving `evil.com` to `127.0.0.1` post-load). Non-browser clients (curl, fetch from Node, MCP hosts, CI scripts) typically omit these headers and pass through. Implementation: `src/cli/cmd-serve.ts` (CLI shell) + `src/application/http-server.ts` (transport). See [`architecture.md` § HTTP wiring](./architecture.md#cli-usage).
547
+
Long-running HTTP server exposing the same tool taxonomy as `codemap mcp` over `POST /tool/{name}` for non-MCP consumers (CI scripts, simple `curl`, IDE plugins that don't speak MCP). Default bind **`127.0.0.1:7878`** (loopback only — refuse `0.0.0.0` unless explicitly opted in via `--host 0.0.0.0`); optional `--token <secret>` requires `Authorization: Bearer <secret>` on every request. HTTP returns each tool's native JSON payload directly (NOT MCP's `{content: [...]}` wrapper); `query` / `query_recipe` match `codemap query --json` row arrays unless `summary` / `group_by` reshape the envelope, or `baseline` returns a diff envelope (incompatible with non-`json` `format` / `group_by`; save/list/drop remain separate tools); parity twins (`query batch`, `trace`, `explore`, `node`, `file`, `schema`, `symbols`, `context`, `ingest-coverage`) always emit JSON on CLI without `--json`; other tools match their CLI `--json` payloads when that flag is set; `format: "sarif"` payloads ship as `application/sarif+json`, `format: "annotations"` / `"mermaid"` / `"diff"` as `text/plain; charset=utf-8`, `format: "diff-json"` as `application/json; charset=utf-8`. Routes: `POST /tool/{name}` (every MCP tool), `GET /resources/{encoded-uri}` (resource handler for `codemap://recipes`, `codemap://recipes/{id}`, `codemap://schema`, `codemap://skill`, `codemap://rule`, `codemap://mcp-instructions`, `codemap://files/{path}`, and `codemap://symbols/{name}`), `GET /health` (auth-exempt liveness probe — does not start the watcher), `GET /tools` / `GET /resources` (catalogs). With `--watch`, chokidar is refcount-gated per request and stops 5s after the last client (`HTTP_WATCH_RELEASE_GRACE_MS`) — distinct from MCP idle shutdown; the HTTP process keeps listening. Pure transport — same `tool-handlers.ts` / `resource-handlers.ts` MCP uses; no engine duplication. Errors → `{"error": "..."}` with HTTP status 400 / 401 / 403 / 404 / 500. SIGINT / SIGTERM → graceful drain. Every response carries `X-Codemap-Version: <semver>`. **CSRF + DNS-rebinding guard:** every request (including auth-exempt `/health`) is evaluated against `Sec-Fetch-Site` / `Origin` / `Host` when present — modern browsers send `Sec-Fetch-Site` and `Origin` on cross-origin fetches (header presence varies by request type, browser, and privacy settings), so the guard rejects browser-driven cross-origin requests like a malicious local webpage `fetch`-ing `http://127.0.0.1:7878/tool/save_baseline` to mutate `.codemap/index.db`. `Host` mismatch on a loopback bind blocks DNS rebinding (an attacker resolving `evil.com` to `127.0.0.1` post-load). Non-browser clients (curl, fetch from Node, MCP hosts, CI scripts) typically omit these headers and pass through. Implementation: `src/cli/cmd-serve.ts` (CLI shell) + `src/application/http-server.ts` (transport). See [`architecture.md` § HTTP wiring](./architecture.md#cli-usage).
0 commit comments