Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

MCP overview

Outcome: wire an editor or agent so it can deposit, read the field, and observe the mux — without inventing tool names.

Castellan exposes stigmergy, memory, and harness tools via castellan mcp (stdio JSON-RPC) and live episodes (castellan run --mcp). Both paths share one registry, so tool names, schemas, and governance behavior never drift between editor use and headless runs.

Same tools, two surfaces

castellan mcp
# or during a live episode:
castellan run --goal "…" --plugin shell --mcp

stdio JSON-RPC on stdin/stdout. Methods: initialize, tools/list, tools/call.

Start the server

castellan mcp

Wire your MCP client to stdin/stdout JSON-RPC. Same registry as live episodes — see crates/castellan-mcp/src/registry.rs.

Anatomy: discovery → describe → invoke

flowchart LR
  A[Agent starts] --> B{castellan_discover_tools<br/>or castellan_search_tools}
  B --> C[castellan_describe_tool]
  C --> D[Invoke operational tool]
  D --> E[Governance + trace]

  classDef runtime stroke:#0969da,stroke-width:2px
  classDef substrate stroke:#5b6b8c,stroke-width:2px
  classDef gate stroke:#1a7f37,stroke-width:2px

  class A,B runtime
  class C,D substrate
  class E gate
  1. Discovercastellan_discover_tools returns domain-indexed cards (no full schemas). Optional domain filter.
  2. Searchcastellan_search_tools with query narrows by intent; BM25-ranked, substring fallback for partial words.
  3. Describecastellan_describe_tool with name returns the full schema, preconditions, side effects, and examples.
  4. Invoketools/call (stdio) or the mcp_tools observation payload (live episodes).

Meta-tools always ship full schemas in tools/list; operational tools ship compact cards to keep context small — call castellan_describe_tool before invoking one you haven’t used yet.

Full tool catalog

Verified against LIVE_TOOL_NAMES in crates/castellan-mcp/src/registry.rsdo not invent tool names; if a tool you need isn’t here, it doesn’t exist yet.

Meta tools

ToolPurpose
castellan_discover_toolsBrowse domains and tool names
castellan_search_toolsBM25-ranked keyword search over the catalog
castellan_describe_toolFull schema + preconditions + side effects for one tool

Operational tools

ToolDomainSummaryAnnotations
castellan_deposit_signalsubstrateDeposit pheromone into a zonedestructive, governed pre/post
castellan_read_signalsubstrateRead signal strength from a zoneread-only
castellan_read_blackboardsubstrateRead a blackboard slot by keyread-only
castellan_topology_snapshotsubstrateHarness topology JSON (agents, edges, zones)read-only
castellan_metricssubstrateSession counters (tool calls, deposits, mutations)read-only
castellan_multiplexer_statusmultiplexerObservabilitySnapshot + freshness (stale, age_secs, schema_version)read-only
castellan_propose_mutationevolvePreview a topology mutation (diff + drift); does not applypreview-only
castellan_resolve_mutationevolveAccept or reject a persisted proposaldestructive, high-impact gate
castellan_read_resourcegovernanceResolve a typed URI (episode://, genome://, pane://, blackboard://)read-only, allowlisted schemes
castellan_recallmemoryQuery durable memory (episodes, checkpoint, observations, topology, traces)read-only
castellan_remembermemoryPersist an agent observation to durable memorydestructive
castellan_query_field_historymemoryQuery checkpoint-time pheromone field snapshotsread-only

All 12 operational tools are in LIVE_TOOL_NAMES, so they work identically on castellan run --mcp.

Full schemas, examples, and design rationale: MCP tools reference.

Discovery sequence (copy-paste)

{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"castellan_search_tools","arguments":{"query":"deposit","domain":"substrate"}}}
{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"castellan_describe_tool","arguments":{"name":"castellan_deposit_signal"}}}
{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"castellan_deposit_signal","arguments":{"zone":"grid","signal":"coordination","amount":2.0}}}
Offline / no CDN — static transcript
$ printf '{"jsonrpc":"2.0","id":1,"method":"initialize"}\n' | castellan mcp
{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2024-11-05",...}}
$ echo '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"castellan_search_tools","arguments":{"query":"deposit"}}}' | castellan mcp
{"jsonrpc":"2.0","id":2,"result":{"hits":[{"name":"castellan_deposit_signal",...}]}}

Download castellan-mcp-demo.cast

Live field instrument (castellan mcp --attach)

While castellan run is active it publishes a live session under .castellan/live/ (active.json pointer, per-goal snapshot JSON, and an append-only deposit queue). The transport is a file bridge (not a Unix domain socket): the engine drains the queue at tick boundaries—the same semantics a socket would provide, without an async listener inside the sync run loop. External MCP clients hit the same field the scheduler ticks:

castellan mcp --attach auto         # bind to the active live session
castellan mcp --attach <goal_id>    # bind to a specific run
  • castellan_deposit_signal enqueues into the live run; the engine drains the queue each tick, emits CastellanEvent::Deposit, and records the deposit on the episode’s instrument_summary with source: "mcp_attach".
  • castellan_topology_snapshot returns the last published live snapshot (tick, topology, field zones, wake stats).
  • Mutation tools (castellan_propose_mutation, castellan_resolve_mutation) are denied in attach sessions (attach_denied error).
  • If no live session exists, --attach fails at startup (attach_unavailable) instead of silently degrading to a standalone engine. If the run ends while attached, calls fail with attach_lost.

Without --attach, plain castellan mcp still auto-routes deposits to an active live session when one exists (source: "external_mcp"), falling back to its own in-memory engine otherwise. castellan doctor reports the live_session stage.

Episode JSON records all MCP instrument activity:

"instrument_summary": {
  "mcp_tool_calls": 3,
  "mcp_deposits": [
    { "tick": 2, "zone": "grid", "signal": "coordination", "amount": 1.5, "source": "mcp_attach" }
  ],
  "topology_snapshots": [{ "tick": 2, "nodes": 2, "edges": 1 }]
}

Live episode example (castellan run --mcp)

{
  "mcp_tools": [
    {
      "tool": "castellan_deposit_signal",
      "args": { "zone": "grid", "signal": "coordination", "amount": 2.0 }
    }
  ]
}

Put entries under mcp_tools in a plugin’s observation payload; CastellanEngine::call_mcp_tool dispatches them against the same registry and governance pipeline as stdio.

Failure paths

SymptomCauseFix
JSON-RPC -32601Unknown method or tool nameCheck spelling against the table above; run tools/list to confirm
JSON-RPC -32602Invalid params for a toolCall castellan_describe_tool first — required fields are enumerated in inputSchema
castellan_search_tools query rejectedQuery over 256 charactersShorten the query string
Tool call silently no-ops on --mcpTool not in LIVE_TOOL_NAMESAll 12 operational tools currently are — check mcp_tools payload shape matches the JSON above
castellan_propose_mutation never appliesTwo-phase by designCall castellan_resolve_mutation with accept: true and the returned proposal_id
Deposits rejected mid-episodeGovernance pre_execute hook deniedCheck castellan.toml governance section and --audit <PATH> JSONL

Trace JSONL emits mcp_discovery for meta-tools and mcp_tool for operational calls — use --audit on castellan run to capture both.

Editor integration