Gateway Module — `mcp/`
1. Purpose
The mcp module is the stdio MCP server — a thin pass-through wrapper that exposes the gateway's REST API as Model Context Protocol tools to Claude Code / Genkan sessions. It is not mounted in the Express app; it is a separate process launched over StdioServerTransport, forwarding every tool call to the gateway over HTTP (GATEWAY_URL, Bearer-authenticated). Its central architectural discipline is the Raw Twin pattern (Dispatch 26/31): any mutation tool whose backing Zod schema evolves is exposed as a schemaless {payload: string} tool so the Claude Code MCP client's startup schema-cache can never silently strip evolving fields. The client-visible schema stays forever-stable; all validation happens server-side at the REST/Zod boundary. This single 1,601-line file registers 30 tools (12 with _raw twins, 1 raw-only) and one handler switch.
2. File Inventory
| File | Lines | Responsibility |
|---|---|---|
index.ts | 1,601 | Complete MCP server: token bootstrap, gatewayFetch, ListTools registration (30 tools), CallTool handler switch, stdio transport bind |
| Total | 1,601 |
Single-file module. No types.ts/repository.ts — the MCP layer is permissive-flat by design (JSON Schema Draft 7 cannot render Zod discriminated unions cleanly across MCP clients), so it carries no schemas of its own; the gateway Zod is the sole source of truth.
3. Public API Surface
REST Endpoints
None. The module registers no Express routes and is not app.use-mounted. It is a client of the gateway REST surface, not a provider.
MCP Tools (30 registered)
Legend: R = has _raw twin · L = legacy/permissive-flat retained · RO = read-only (no twin by design) · RAW-ONLY = only a raw variant exists.
| Tool | Twin status | Forwards to | Notes |
|---|---|---|---|
assemble_context | RO | POST /api/context/assemble | Surfaces assembly_id + source_ids header for RAG-loop closure. Read-only fanout — drift = graceful degradation, no twin |
context_feedback / context_feedback_raw | L / R | POST /api/context/feedback | used_source_ids array → coercion class; raw canonical |
commit_memory / commit_memory_raw | L / R | POST /api/memory | Content is a Zod discriminated union on type |
update_memory | L | PUT /api/memory/:id | No twin — REST uses manual destructure not Zod (tech-debt flag: harden Zod first) |
commit_agent_memory / commit_agent_memory_raw | L / R | POST /api/agent-memory/commit | Nested task_context + memory_type enum evolution risk |
kg_add_triple / kg_add_triple_raw | L / R | POST /api/knowledge-graph/* | AK-372 envelope wrapper; D4 admission symmetric |
kg_query_entity / kg_query_entity_raw | L / R | knowledge-graph | — |
kg_timeline / kg_timeline_raw | L / R | knowledge-graph | active + invalidated, chronological |
kg_invalidate / kg_invalidate_raw | L / R | knowledge-graph | id OR SPO lookup |
report_dispatch / report_dispatch_raw | L / R | POST /api/dispatch/ | schema evolved (retry_of D77, plan_slug D16); milestones array-coercion |
report_progress / report_progress_raw | L / R | PUT /api/dispatch/:id/progress | dispatch_id extracted from payload for URL path |
report_complete / report_complete_raw | L / R | PUT /api/dispatch/:id/complete | CONFIRMED D26 victim (domain_affinity boolean coercion); twin mandatory |
write_psychic_cache / write_psychic_cache_raw | L (DEPRECATED) / R | POST /api/psychic-cache/write | The founding raw twin (D26); discriminated union on context_type |
clear_psychic_cache | admin, no twin | DELETE /api/psychic-cache/* | URL path + query only, no JSON body — raw pattern doesn't fit |
session_search | RO | GET /api/search (sessions) | Read-only |
list_dispatches | RO | GET /api/dispatch/* | Read-only |
log_activity | L, no twin | POST /api/activity/log | REST uses manual destructure + VALID_LOG_TYPES set, not Zod — tech-debt flag; low-stakes telemetry |
list_sessions | RO | GET /api/sessions/active | Read-only |
post_session_directive_raw | RAW-ONLY | POST /api/sessions/directive | Cross-channel directive; write-path so _raw only per Raw Twin Discipline — no legacy sibling ever existed |
Twin coverage: 12 _raw twins (context_feedback_raw, commit_memory_raw, commit_agent_memory_raw, kg_add_triple_raw, kg_query_entity_raw, kg_timeline_raw, kg_invalidate_raw, report_dispatch_raw, report_progress_raw, report_complete_raw, write_psychic_cache_raw, post_session_directive_raw).
4. Internal API
No exports consumed by other modules (separate process). Key internal functions in index.ts:
resolveMcpAuthToken()— credential-store-first (env fallback) withprocess.env.GATEWAY_AUTH_TOKENfallback (D1689 AK-415 parity with gateway boot).gatewayFetch(path, opts)— re-reads the token per call, setsAuthorization: Bearer, forwards to${GATEWAY_URL}${path}; returnsnullon connection failure (surfaced as a "start the server" hint).toolResult/toolError/gatewayError/formatGatewayError— response envelope helpers.CallToolRequestSchemahandler — oneswitch (name)over all 30 tools; eachcaseforwardsJSON.stringify(args)(raw tools forward the opaquepayloadstring).
5. Background Services
None. Request/response only over stdio; no timers, no daemons.
6. Data Contracts
The MCP layer holds no Zod schemas. Per the module header governance block: the client-visible inputSchema objects are JSON Schema Draft 7, permissive-flat, and are the lowest-common-denominator union of all server-side branches. Per-branch validation, required-field enforcement, and .strict() unknown-field rejection are performed exclusively by the gateway Zod schemas in psychic-cache/types.ts, dispatch/types.ts, context/types.ts, agent-memory/types.ts, knowledge-graph/types.ts, and sessions/types.ts. Pass-through rule: every handler MUST forward the full args via JSON.stringify(args); destructuring is permitted only to extract URL path/query params. Per-field stripping is the silent-strip bug class and is forbidden.
The observable contract surface (for freshness diffing) lives in the sibling tools module (SCHEMA_VERSION + buildToolsList), not here.
7. Dependencies
- Gateway modules consumed: none at import-time (HTTP boundary). Logically fronts context, memory, agent-memory, dispatch, psychic-cache, knowledge-graph, sessions, activity, search REST surfaces.
- External libraries:
@modelcontextprotocol/sdk(Server,StdioServerTransport,CallToolRequestSchema,ListToolsRequestSchema),dotenv, Nodefs/path. - Environment variables:
GATEWAY_URL(default: the gateway endpoint, tailnet-internal),GATEWAY_AUTH_TOKEN(env fallback),CREDENTIALS_DIRECTORY(credstore-first token path).
8. Test Coverage
| Layer | File | Notes |
|---|---|---|
| Server | test/mcp-index.test.ts | Tool registration, handler dispatch, forwarding shape |
| KG envelope | test/mcp-kg-envelope.test.ts | D4 admission on KG raw twins |
| Contract parity | test/schema-introspection.test.ts | Enforces "every mutation tool has a raw_tool sibling" invariant (via tools module) |
9. Known Limitations
update_memoryandlog_activitylack raw twins by design-debt, not design. Their REST endpoints use manual destructure rather than Zod, so moving to raw would shift the client surface without strengthening server validation. Flagged tech-debt: harden REST Zod first, then convert. Until then they remain exposed to the client-cache staleness bug class for any future non-string field.- No automated MCP↔Zod parity test. The
toolsmodule'sSCHEMA_VERSIONadvertisement is hand-curated against the Zod source of truth; the pair is enforced only by the Dependency Propagation Protocol (governance), not a mechanical check. A new field added to a Zod branch but not toschema-introspection.tsdrifts silently until the freshness probe flags a version mismatch. - Single 1,601-line file — no internal module decomposition. All 30 tool registrations + the 30-case handler switch live in one
index.ts.
10. Change History
| Date | Dispatch | Summary |
|---|---|---|
| 2026-07-04 | 2296 | Module spec authored (Phase 4 §5.2), smoke-clone-2, HEAD 5c9a304 |
| — | 26 | write_psychic_cache_raw — founding raw twin; schemaless payload pattern |
| — | 31 | Raw-payload sweep of report_* / context_feedback / commit_agent_memory family |
| — | 399 | post_session_directive_raw (raw-only cross-channel directive) |
| — | 1546 | AK-372 KG tool family + raw twins (envelope D4 admission) |
| — | 1689 | AK-415 credstore-first Bearer token bootstrap (parity with gateway) |