LISAOS // DOCS
GATEWAY // MCP

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

FileLinesResponsibility
index.ts1,601Complete MCP server: token bootstrap, gatewayFetch, ListTools registration (30 tools), CallTool handler switch, stdio transport bind
Total1,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.

ToolTwin statusForwards toNotes
assemble_contextROPOST /api/context/assembleSurfaces assembly_id + source_ids header for RAG-loop closure. Read-only fanout — drift = graceful degradation, no twin
context_feedback / context_feedback_rawL / RPOST /api/context/feedbackused_source_ids array → coercion class; raw canonical
commit_memory / commit_memory_rawL / RPOST /api/memoryContent is a Zod discriminated union on type
update_memoryLPUT /api/memory/:idNo twin — REST uses manual destructure not Zod (tech-debt flag: harden Zod first)
commit_agent_memory / commit_agent_memory_rawL / RPOST /api/agent-memory/commitNested task_context + memory_type enum evolution risk
kg_add_triple / kg_add_triple_rawL / RPOST /api/knowledge-graph/*AK-372 envelope wrapper; D4 admission symmetric
kg_query_entity / kg_query_entity_rawL / Rknowledge-graph
kg_timeline / kg_timeline_rawL / Rknowledge-graphactive + invalidated, chronological
kg_invalidate / kg_invalidate_rawL / Rknowledge-graphid OR SPO lookup
report_dispatch / report_dispatch_rawL / RPOST /api/dispatch/schema evolved (retry_of D77, plan_slug D16); milestones array-coercion
report_progress / report_progress_rawL / RPUT /api/dispatch/:id/progressdispatch_id extracted from payload for URL path
report_complete / report_complete_rawL / RPUT /api/dispatch/:id/completeCONFIRMED D26 victim (domain_affinity boolean coercion); twin mandatory
write_psychic_cache / write_psychic_cache_rawL (DEPRECATED) / RPOST /api/psychic-cache/writeThe founding raw twin (D26); discriminated union on context_type
clear_psychic_cacheadmin, no twinDELETE /api/psychic-cache/*URL path + query only, no JSON body — raw pattern doesn't fit
session_searchROGET /api/search (sessions)Read-only
list_dispatchesROGET /api/dispatch/*Read-only
log_activityL, no twinPOST /api/activity/logREST uses manual destructure + VALID_LOG_TYPES set, not Zod — tech-debt flag; low-stakes telemetry
list_sessionsROGET /api/sessions/activeRead-only
post_session_directive_rawRAW-ONLYPOST /api/sessions/directiveCross-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) with process.env.GATEWAY_AUTH_TOKEN fallback (D1689 AK-415 parity with gateway boot).
  • gatewayFetch(path, opts) — re-reads the token per call, sets Authorization: Bearer, forwards to ${GATEWAY_URL}${path}; returns null on connection failure (surfaced as a "start the server" hint).
  • toolResult / toolError / gatewayError / formatGatewayError — response envelope helpers.
  • CallToolRequestSchema handler — one switch (name) over all 30 tools; each case forwards JSON.stringify(args) (raw tools forward the opaque payload string).

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, Node fs/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

LayerFileNotes
Servertest/mcp-index.test.tsTool registration, handler dispatch, forwarding shape
KG envelopetest/mcp-kg-envelope.test.tsD4 admission on KG raw twins
Contract paritytest/schema-introspection.test.tsEnforces "every mutation tool has a raw_tool sibling" invariant (via tools module)

9. Known Limitations

  • update_memory and log_activity lack 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 tools module's SCHEMA_VERSION advertisement 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 to schema-introspection.ts drifts 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

DateDispatchSummary
2026-07-042296Module spec authored (Phase 4 §5.2), smoke-clone-2, HEAD 5c9a304
26write_psychic_cache_raw — founding raw twin; schemaless payload pattern
31Raw-payload sweep of report_* / context_feedback / commit_agent_memory family
399post_session_directive_raw (raw-only cross-channel directive)
1546AK-372 KG tool family + raw twins (envelope D4 admission)
1689AK-415 credstore-first Bearer token bootstrap (parity with gateway)

On this page