Architecture
Status: Living document — reflects the codebase as of v1.16.x.
1. Purpose
This document defines the architecture of cks-mcp: its role in the CKS
ecosystem, the principles guiding its design, and the components that make
it up. It's written for anyone who wants to understand, extend, or embed
the server — not for API consumers, who want
Tools Reference instead.
Two topics that used to live here now have dedicated documents, since both grew past what a single "Architecture" section could hold: Security Model and Extension Model. The request lifecycle for a representative tool call is in Request Lifecycle.
2. Architectural Role
cks-mcp is the Exposure Layer of the CKS ecosystem: it translates
external, LLM-friendly requests (MCP tool calls) into internal, canonical
operations managed by cks-runtime and validated by cks-core.
LLMs (Claude Desktop, etc.)
│
▼
cks-mcp ← Exposure Layer
│
▼
cks-runtime ← Operational Layer (sessions, transactions, storage, versioning)
│
▼
cks-core ← Semantic Layer (canonical structure, validation, evolution)
cks-mcp contains no semantic logic and no operational state
management of its own. It is a thin, stateless translator between two
protocols — Model Context Protocol and CKS Canonical Operations — plus the
integrity guarantees (provenance, SSRF protection) that only make sense at
the boundary between an untrusted LLM and the rest of the ecosystem.
3. Architectural Principles
Thin translator. The server's job is mapping MCP tool calls to
cks-runtime operations and returning the results — never implementing
validation, evolution, or persistence logic itself. See
ADR-001.
Provenance over trust. An LLM can generate convincing but fabricated
data. The server never trusts the content of a request for operations like
source verification — it enforces provenance instead: a VerificationRecord
is only genuine if verify_source built it. See
Security Model.
Unconditional safety. Critical integrity checks don't depend on the
LLM asking for them correctly. A forgetful or malicious model might omit
an extensions parameter, so checks like VerificationRecord signature
verification run regardless. See Extension Model.
Defense in depth. Security protections are implemented at multiple independent levels rather than relying on any single check — see Security Model for how this plays out in SSRF protection specifically.
4. Components
server.py MCP transport (JSON-RPC over stdio)
└─ tool_registry.py Tool schemas + dispatch, wrapped in middleware
└─ tools/ 24 operation handlers, one concern each
middleware.py Composable validation stacks (require_fields, ...)
errors.py Structured, LLM-friendly error responses
provenance.py HMAC signing/verification for VerificationRecord
resources.py MCP Resources — sessions/versions as browsable URIs
prompts.py MCP Prompts — templated multi-tool workflows
observability.py Structured stderr logs + EventBus subscriptions
telemetry.py In-memory per-tool call metrics (get_metrics)
diffing.py Shared field-level diff (merge conflicts, explain_diff)
paths.py Resolves the ~/.cks-mcp data directory
server.py — MCP Transport
Owns the JSON-RPC-over-stdio transport, the MCP lifecycle methods
(initialize, ping, tools/list, resources/list, resources/read,
prompts/list, prompts/get), and routes tools/call to the registry.
Reads a .env file at ~/.cks-mcp/.env on startup if present (see
Getting Started).
tool_registry.py — Tool Registry & Schemas
The TOOLS dict: one entry per tool with its name, description,
inputSchema, and handler, wired through middleware.py's composition
helpers (_wrap / _wrap_session / _wrap_open_session) rather than
each handler managing its own validation stack.
tools/ — Operation Handlers
Each of the 24 tools is a standalone module implementing a single canonical operation — see Tools Reference for the full, grouped list with parameters and examples.
middleware.py — Composable Middleware
A small decorator-factory layer (require_fields, require_session,
require_open_session, catch_unhandled_errors, composed via
with_middleware) sitting between the transport and each handler. See
ADR-003 for why this replaced
per-handler validation.
errors.py — Structured Errors
Maps internal failures to structured {"error": ..., "message": ...}
dicts that give the calling LLM actionable information, instead of a raw
exception or protocol-level fault.
provenance.py — Cryptographic Trust
Signs and verifies VerificationRecord objects. See
Security Model for the full
mechanism, and ADR-002 for why
HMAC over a process-local secret rather than an alternative like
asymmetric signing.
resources.py / prompts.py — MCP-Native Extras
Expose sessions/versions as browsable Resources and common multi-tool workflows as templated Prompts. See Resources and Prompts — these are documented nowhere else, so read those directly rather than expecting more detail here.
observability.py / telemetry.py — Observability
observability.py writes structured JSON logs to stderr (never stdout,
which the MCP transport owns) and subscribes to cks-runtime's EventBus
for lifecycle events. telemetry.py is a separate in-memory aggregator —
deliberately split out so the "log to stderr" concern and the "aggregate
for get_metrics" concern can change independently.
diffing.py — Shared Diff Helper
One field_level_diff implementation shared by merge.py's conflict
reporting and explain_diff.py, so "what changed about this identity"
is computed the same way in both places instead of two implementations
slowly drifting apart.
paths.py — Data Directory
Resolves ~/.cks-mcp (or CKS_MCP_DATA_DIR) once, cwd-independent — the
one place the SQLite database, the persisted provenance secret, and the
optional .env file all live.
5. Where to go next
- Request Lifecycle — how a
validate_knowledgecall actually flows through these components. - Security Model — the full SSRF and provenance story.
- Extension Model — the six opt-in validation extensions.
- ADRs — why specific components look the way they do.