MCP System
Status: Design landing Reference epics: INK-829, INK-830, INK-833, INK-838, INK-849 ADRs: ADR-016, ADR-017, ADR-022
MCP is the tool bridge. Every capability the agent can reach — reading workspace content, writing workspace content, running sandboxed Python, consulting memory, checking permissions — is an MCP tool. The same server exposes those tools to external MCP clients. There is one surface.
What the MCP server is
Section titled “What the MCP server is”The MCP server is a component inside the Python sidecar (see Process Model). It implements the Model Context Protocol and registers a catalog of tools. Those tools fall into three families:
- Workspace tools. Reads and writes against the user’s workspace — pages, blocks, properties, tags, attachments, permissions, memory tiers. Backed by Rust handlers in the Tauri host.
- Sandbox execution tool. One MCP tool that executes a Python snippet inside a fresh Wasmtime CPython instance. See Sandbox Execution for the domain semantics.
- Control tools. Small read-only capabilities for introspection — “what tools exist,” “what scopes are granted to this caller,” “what is the current workspace.”
The server serves both the in-sidecar agent and external MCP clients. There is no second server, no parallel tool registry, no “internal” tool catalog that differs from the external one.
Two client surfaces, one server
Section titled “Two client surfaces, one server”There are two classes of client. They see the same tools but reach the server differently.
In-process sidecar ↔ Rust bridge
Section titled “In-process sidecar ↔ Rust bridge”The primary client is the agent itself. When a tool node in the agent graph fires, it calls through a local MCP client that talks to the MCP server running in the same Python process. For workspace tools, the server forwards the call over the Tauri IPC surface to Rust, which runs the handler against inklings.db and returns the result. For the sandbox tool, the server starts a Wasmtime instance, runs the snippet, and returns the result.
From the agent’s perspective, every capability is an MCP tool call. There is no direct SQLite connection from the sidecar, no ambient “Rust API client” with special privileges, no separate internal tool protocol. Uniformity is the point: the agent’s world is the tool catalog.
External MCP clients
Section titled “External MCP clients”Claude Desktop, Cursor, and other external MCP hosts connect to the same server over the standard MCP transport. The workspace acts like a mountable knowledge base: an external client with the right scope can read pages, read memory, and (where scopes permit) propose writes.
External clients traverse the same submit boundary, the same permission system, and the same memory tiers as the in-sidecar agent. What differs is the caller identity — an external client is not the workspace’s World Agent, and the domain treats its writes accordingly (see Submit Boundary: Who goes through the boundary).
The external-client surface is the part of the system most users will never see, but it is the same system. That is what makes it cheap to keep.
Tool registration metadata
Section titled “Tool registration metadata”Every tool the server registers carries registration metadata beyond its signature. This metadata is what lets the submit boundary treat tool calls uniformly.
A tool’s registration declares:
- Whether the tool crosses the submit boundary. Read-only tools do not. Tools that produce a workspace write do.
- Which argument carries content. For writing tools, the body/payload field the boundary must treat as origin-tagged.
- How derivation sources are supplied. If the tool produces derived content, how the set of source entity IDs is provided — as an argument, as an implicit read trail, or both.
- What write kind the tool produces. Page, block, property, tag, attachment, permission grant — this determines which handler runs past the boundary.
- Capability scopes required. The set of permission scopes the caller must hold (see Permission System).
- Whether the tool is sandbox-eligible. Whether it may be called from inside a sandbox execution with inherited capabilities (see Sandbox Execution).
This metadata is what the submit-boundary adapter reads to turn a tool call into a WorldWrite. Without it, the adapter would need hard-coded knowledge of every writing tool; with it, adding a new writing tool is a registration change, not an adapter change.
The submit-boundary adapter
Section titled “The submit-boundary adapter”Tool calls that cross the submit boundary do not reach the workspace directly. They go through an adapter.
The adapter is a thin layer between the MCP tool dispatcher and the Rust-side domain handlers. Its job is mechanical: given a tool call and its registration metadata, build a WorldWrite carrying origin, lifecycle, and (where applicable) derivation. See Submit Boundary: Adapter mechanism for the domain-invariant side of this.
Concretely, for each writing tool call the adapter:
- Reads the caller’s identity — which agent, which thread, which run.
- Reads the registration metadata — content argument name, derivation-source rule, write kind.
- Pulls the content out of the named argument. Pulls derivation sources from the named argument or from the run’s read trail, per the rule.
- Determines origin from the caller: World Agent on behalf of an author =
agent-produced; author direct =authored; external MCP client = its declared origin; imports =imported. - Determines lifecycle from the call context: agent writes default to
candidate; authored writes default tocanonical-allowed; imports todraft. - Builds a
WorldWriteand hands it to the domain.
The domain then enforces its invariants (see Submit Boundary: Domain invariant). The adapter cannot bypass them, and no tool can reach the domain except through the adapter. This is what makes the boundary a real invariant instead of a convention.
The sandboxed CPython executor
Section titled “The sandboxed CPython executor”The sandbox execution tool is worth calling out separately because it is both a single MCP tool and the mechanism by which the agent runs arbitrary code.
The tool takes a Python snippet and a capability set, spawns a fresh Wasmtime CPython instance, runs the snippet, and returns the result. See Sandbox Execution for the domain semantics and Process Model for the process shape.
Key properties, from the MCP side:
- It is one tool. There is no family of “sandbox-read,” “sandbox-compute,” “sandbox-write” tools. The snippet describes the computation; the capability set describes what the snippet may reach.
- Inside the sandbox, the same MCP server is reachable. The Wasmtime instance is given a back-channel to the MCP server, so code running in the sandbox can call workspace tools — subject to the caller’s capability set. Writes from within the sandbox cross the same submit-boundary adapter as any other tool call.
- Capabilities are inherited, not escalated. The sandbox call runs under the capability set of the agent’s current run. The snippet cannot ask for more scope than the agent already holds.
- The instance is ephemeral. Each call gets a fresh CPython world. Snippets do not persist state between calls.
This is why the sandbox executor is called a “capability, not a runtime” in Process Model: the agent does not live in it, but the agent can reach into it with a specific question.
What MCP is not
Section titled “What MCP is not”- Not a provider abstraction. MCP is about tools. LLM provider access is described in LLM System; the LLM programming layer is DSPy, and sidecar code reaches models through DSPy rather than provider SDKs directly. Neither path goes through MCP.
- Not a private internal ABI. Every tool is discoverable. An external MCP client with appropriate scope sees the same catalog the agent sees. Tools that should not be externally reachable are scoped out at registration, not by being secret.
- Not the permission system. Scopes are enforced by the Permission System. The MCP server names the scopes a tool requires; the domain checks them. The server does not implement its own authorization.
- Not an agent bus. The sidecar does not talk to itself over MCP. Subagent dispatch is in-process LangGraph
Send; see Agent Core System.
Relationship to other systems
Section titled “Relationship to other systems”- Agent Core System — the tool node in the agent graph is an MCP client; every tool call the agent makes flows through this system.
- Process Model — the MCP server lives in the sidecar; its calls reach Rust over the IPC surface.
- Submit Boundary — the registration metadata on MCP tools is how tool calls are adapted into domain-level writes.
- Sandbox Execution — the CPython executor is one MCP tool with capability-mediated semantics.
- Permission System — scopes declared in registration are resolved here.
- Agent Memory System — the four-tier memory is exposed as MCP tools, same surface as any other capability.
Worked example
Section titled “Worked example”In Worked Example: Moment 2, the agent reads source pages, produces a derived summary, and writes it back with derivation links. Every read in that moment is an MCP tool call; the final write is an MCP tool call whose registration says “crosses the submit boundary, derivation sources supplied from read trail.” The adapter builds a WorldWrite with origin agent-produced, lifecycle candidate, and derivation links to the sources — which is what makes the moment legal.
What this page does not do
Section titled “What this page does not do”- It does not describe the agent graph or subagent dispatch. See Agent Core System.
- It does not describe persistent-sandbox investigations and how they expose tool calls back through this surface. See Investigation Pattern.
- It does not describe the Tauri host or the sidecar process boundary. See Process Model.
- It does not describe the domain invariants enforced past the adapter. See Submit Boundary.
- It does not describe sandbox domain semantics. See Sandbox Execution.
- It does not describe LLM provider access. See LLM System.
Was this page helpful?
Thanks for your feedback!