> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rkat.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# JSON-RPC stdio server

> Stateful JSON-RPC 2.0 interface for stdio, TCP, and optional live audio WebSocket hosting.

Meerkat exposes a JSON-RPC 2.0 surface for IDE integration, desktop apps, and automation tools. Stdio is the default, and the binary also supports TCP plus an optional live audio WebSocket listener (`--live-ws`). Unlike REST and MCP, the RPC server keeps agents alive between turns for fast multi-turn conversations.

The RPC surface is fully runtime-backed:

* `keep_alive` is runtime/session behavior
* `session/external_event` queues runtime-backed external work
* committed success is not rewritten to cancellation

## Getting started

<Steps>
  <Step title="Start the server">
    ```bash theme={null}
    rkat-rpc
    ```

    The server reads newline-delimited JSON (JSONL) from stdin and writes JSONL to stdout. Each line is a complete JSON-RPC 2.0 message.

    Optional listener modes:

    ```bash theme={null}
    rkat-rpc --tcp 127.0.0.1:9001
    rkat-rpc --tcp 127.0.0.1:9001 --live-ws 127.0.0.1:9002
    ```

    TCP and live WebSocket listeners are local-only by default. Binding
    a non-loopback address such as `0.0.0.0:9001` requires `--allow-remote`;
    that flag only opts in to network exposure and does not add
    authentication or encryption. Use it behind a production-safe transport
    wrapper such as SSH tunneling, mTLS, or another authenticated encrypted
    channel. Plain TCP host capabilities continue to report
    `secure_remote_rpc: false`.
  </Step>

  <Step title="Send the handshake">
    ```json theme={null}
    {"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}
    ```
  </Step>

  <Step title="Create a session">
    ```json theme={null}
    {"jsonrpc":"2.0","id":2,"method":"session/create","params":{"prompt":"Hello!"}}
    ```
  </Step>
</Steps>

<Note>
  `rkat-rpc` defaults to a new isolated realm each time. Use `--realm <id>` to share config/sessions with other surfaces or processes.
</Note>

## Runtime scope

`rkat-rpc` accepts global scope flags:

```bash theme={null}
rkat-rpc [--realm <id>] [--isolated] [--instance <id>] [--realm-backend <sqlite|jsonl>] [--state-root <path>] [--context-root <path>] [--user-config-root <path>]
```

* `--realm`: explicit sharing/isolation key
* `--instance`: optional instance metadata
* `--realm-backend`: creation hint only; actual backend is pinned per realm manifest

## Method overview

This table mirrors the generated catalog in `artifacts/schemas/rpc-methods.json`
(`meerkat_contracts::rpc_method_catalog`). `initialize` returns the methods
enabled in the running binary, so reduced or feature-limited builds expose a
subset.

| Method                           | Category     | Description                                                                                |
| -------------------------------- | ------------ | ------------------------------------------------------------------------------------------ |
| `initialize`                     | Handshake    | Returns server capabilities                                                                |
| `help/ask`                       | Help         | Ask Meerkat usage help with the embedded platform skill                                    |
| `runtime/host_info`              | Runtime      | Read host identity, endpoints, capabilities, health, and realm projection                  |
| `runtime/capabilities`           | Runtime      | Read runtime host capability flags                                                         |
| `runtime/health`                 | Runtime      | Read runtime host health                                                                   |
| `session/create`                 | Session      | Create session and run first turn                                                          |
| `session/list`                   | Session      | List active sessions                                                                       |
| `session/read`                   | Session      | Get session state                                                                          |
| `session/history`                | Session      | Get committed session transcript messages                                                  |
| `session/fork_at`                | Session      | Fork an idle session at a transcript message index                                         |
| `session/fork_replace`           | Session      | Fork an idle session and apply a typed transcript replacement                              |
| `blob/get`                       | Session      | Fetch a blob payload by id                                                                 |
| `artifact/list`                  | Artifact     | List stable artifact records                                                               |
| `artifact/get`                   | Artifact     | Get one stable artifact record                                                             |
| `artifact/download`              | Artifact     | Download blob-backed artifact payload bytes                                                |
| `approval/request`               | Approval     | Create a durable approval request                                                          |
| `approval/list`                  | Approval     | List approval records                                                                      |
| `approval/get`                   | Approval     | Get one approval record                                                                    |
| `approval/decide`                | Approval     | Record an approval decision                                                                |
| `events/latest_cursor`           | Events       | Read the latest replay cursor for a typed event scope                                      |
| `events/list_since`              | Events       | List replayable events after a typed cursor                                                |
| `events/snapshot`                | Events       | Read a point-in-time snapshot with its replay cursor                                       |
| `session/archive`                | Session      | Remove session from runtime                                                                |
| `session/external_event`         | Session      | Queue a runtime-backed external event                                                      |
| `session/peer_response_terminal` | Session      | Admit a correlated terminal peer response through the typed runtime ingress                |
| `session/inject_context`         | Session      | Append runtime-backed system context to the next turn                                      |
| `turn/start`                     | Turn         | Start a new turn on existing session                                                       |
| `turn/interrupt`                 | Turn         | Cancel in-flight turn                                                                      |
| `live/open`                      | Live         | Open a live audio/text channel for a session                                               |
| `live/webrtc/answer`             | Live         | Answer a browser WebRTC offer for an already-open live channel                             |
| `live/status`                    | Live         | Get the status of a live channel                                                           |
| `live/close`                     | Live         | Close a live channel                                                                       |
| `live/send_input`                | Live         | Send an input chunk (audio/text) to a live channel                                         |
| `live/commit_input`              | Live         | Commit pending input on a live channel (turn boundary)                                     |
| `live/interrupt`                 | Live         | Interrupt the assistant turn on a live channel (barge-in)                                  |
| `live/truncate`                  | Live         | Truncate assistant output at a client-tracked playback cursor                              |
| `live/refresh`                   | Live         | Apply mutable session config (instructions/tools/audio) to an open live channel            |
| `session/stream_open`            | Session      | Open a standalone session event stream                                                     |
| `session/stream_close`           | Session      | Close a standalone session event stream                                                    |
| `mob/create`                     | Mob          | Create a mob from a definition (mob feature)                                               |
| `mob/list`                       | Mob          | List mobs (mob feature)                                                                    |
| `mob/status`                     | Mob          | Get mob status (mob feature)                                                               |
| `mob/members`                    | Mob          | List mob members (mob feature)                                                             |
| `mob/spawn`                      | Mob          | Spawn a member into a mob (mob feature)                                                    |
| `mob/spawn_many`                 | Mob          | Spawn multiple members into a mob (mob feature)                                            |
| `mob/retire`                     | Mob          | Retire a mob member (mob feature)                                                          |
| `mob/respawn`                    | Mob          | Respawn a mob member (mob feature)                                                         |
| `mob/wire`                       | Mob          | Wire two mob members for comms (mob feature)                                               |
| `mob/unwire`                     | Mob          | Remove mob comms wiring (mob feature)                                                      |
| `mob/lifecycle`                  | Mob          | Stop, resume, complete, destroy, or reset a mob (mob feature)                              |
| `mob/events`                     | Mob          | Read mob events by cursor (mob feature)                                                    |
| `mob/ingress_interaction`        | Mob          | Ensure an ingress member and deliver user input with replay anchors (mob feature)          |
| `mob/stream_open`                | Mob          | Open mob or member event stream (mob feature)                                              |
| `mob/stream_close`               | Mob          | Close mob or member event stream (mob feature)                                             |
| `mob/member_send`                | Mob          | Deliver ordinary content to a specific mob member via the host control plane (mob feature) |
| `mob/append_system_context`      | Mob          | Stage system context for a member session (mob feature)                                    |
| `mob/flows`                      | Mob          | List mob flows (mob feature)                                                               |
| `mob/flow_run`                   | Mob          | Start a mob flow run (mob feature)                                                         |
| `mob/flow_status`                | Mob          | Get flow run status (mob feature)                                                          |
| `mob/flow_cancel`                | Mob          | Cancel a flow run (mob feature)                                                            |
| `mob/ensure_member`              | Mob          | Idempotent spawn — returns existing entry if member already exists (mob feature)           |
| `mob/reconcile`                  | Mob          | Declarative roster reconcile against a desired spec list (mob feature)                     |
| `mob/list_members_matching`      | Mob          | Label-filtered member listing with server-side filter (mob feature)                        |
| `mob/spawn_helper`               | Mob          | Spawn a helper member and wait for its result (mob feature)                                |
| `mob/fork_helper`                | Mob          | Fork a helper from an existing member and wait for its result (mob feature)                |
| `mob/force_cancel`               | Mob          | Force-cancel a member's active work (mob feature)                                          |
| `mob/turn_start`                 | Mob          | Start a turn on a mob member by identity (mob feature)                                     |
| `mob/member_status`              | Mob          | Read canonical status for a specific member (mob feature)                                  |
| `mob/snapshot`                   | Mob          | Point-in-time aggregate of mob status plus member list (mob feature)                       |
| `mob/destroy`                    | Mob          | Destroy a mob and surface the structured MobDestroyReport (mob feature)                    |
| `mob/rotate_supervisor`          | Mob          | Rotate the supervisor bridge for all members of a mob (mob feature)                        |
| `mob/submit_work`                | Mob          | Submit a unit of work to a mob member through the work lane (mob feature)                  |
| `mob/cancel_work`                | Mob          | Cancel a previously submitted unit of work (mob feature)                                   |
| `mob/cancel_all_work`            | Mob          | Cancel all in-flight work for a mob member (mob feature)                                   |
| `mob/wait_kickoff`               | Mob          | Wait for kickoff completion for a member (mob feature)                                     |
| `mob/wait_ready`                 | Mob          | Wait for mob startup readiness (members bound but kickoff not required, mob feature)       |
| `mob/profile/create`             | Mob          | Create a realm-scoped mob profile (mob feature)                                            |
| `mob/profile/get`                | Mob          | Read a realm-scoped mob profile (mob feature)                                              |
| `mob/profile/list`               | Mob          | List realm-scoped mob profiles (mob feature)                                               |
| `mob/profile/update`             | Mob          | Update a realm-scoped mob profile (mob feature)                                            |
| `mob/profile/delete`             | Mob          | Delete a realm-scoped mob profile (mob feature)                                            |
| `schedule/create`                | Schedule     | Create a new schedule (schedule feature)                                                   |
| `schedule/get`                   | Schedule     | Get a schedule by ID (schedule feature)                                                    |
| `schedule/list`                  | Schedule     | List schedules (schedule feature)                                                          |
| `schedule/update`                | Schedule     | Update a schedule (schedule feature)                                                       |
| `schedule/pause`                 | Schedule     | Pause a schedule (schedule feature)                                                        |
| `schedule/resume`                | Schedule     | Resume a paused schedule (schedule feature)                                                |
| `schedule/delete`                | Schedule     | Delete a schedule (schedule feature)                                                       |
| `schedule/occurrences`           | Schedule     | List occurrences for a schedule (schedule feature)                                         |
| `schedule/tools`                 | Schedule     | List schedule tools (schedule feature)                                                     |
| `schedule/call`                  | Schedule     | Call a schedule tool directly (schedule feature)                                           |
| `workgraph/get`                  | WorkGraph    | Read a WorkGraph item (workgraph feature)                                                  |
| `workgraph/list`                 | WorkGraph    | List WorkGraph items (workgraph feature)                                                   |
| `workgraph/ready`                | WorkGraph    | List ready WorkGraph items (workgraph feature)                                             |
| `workgraph/snapshot`             | WorkGraph    | Read a WorkGraph observability snapshot (workgraph feature)                                |
| `workgraph/events`               | WorkGraph    | Read WorkGraph event history (workgraph feature)                                           |
| `comms/send`                     | Comms        | Dispatch a comms command to a session                                                      |
| `comms/peers`                    | Comms        | List discoverable peers (comms feature)                                                    |
| `skills/list`                    | Skills       | List skills with provenance                                                                |
| `mcp/add`                        | MCP          | Stage live MCP server addition (mcp feature)                                               |
| `mcp/remove`                     | MCP          | Stage live MCP server removal (mcp feature)                                                |
| `mcp/reload`                     | MCP          | Reload one or all MCP servers (mcp feature)                                                |
| `models/catalog`                 | Models       | List available models with capabilities and provider profiles                              |
| `capabilities/get`               | Capabilities | Runtime capability report                                                                  |
| `config/get`                     | Config       | Read current config                                                                        |
| `config/set`                     | Config       | Replace config                                                                             |
| `config/patch`                   | Config       | Merge-patch config (RFC 7396)                                                              |
| `auth/profile/list`              | Auth         | List auth profiles in a realm                                                              |
| `auth/profile/get`               | Auth         | Get one auth profile                                                                       |
| `auth/profile/create`            | Auth         | Create an auth profile (CLI-driven for now)                                                |
| `auth/profile/delete`            | Auth         | Delete an auth profile (CLI-driven for now)                                                |
| `auth/login/start`               | Auth         | Begin interactive authorization-code OAuth (returns authorize\_url + pkce\_verifier)       |
| `auth/login/complete`            | Auth         | Exchange authorization code for tokens; persists to TokenStore                             |
| `auth/login/device_start`        | Auth         | Begin device-code OAuth; returns user\_code + verification URL                             |
| `auth/login/device_complete`     | Auth         | Single-poll completion for a device-code OAuth login                                       |
| `auth/login/provision_api_key`   | Auth         | Console-OAuth → API key provisioning (Anthropic oauth\_to\_api\_key)                       |
| `auth/status/get`                | Auth         | Get auth status for a profile                                                              |
| `auth/logout`                    | Auth         | Revoke + remove persisted credentials                                                      |
| `realm/list`                     | Realm        | List realm summaries                                                                       |
| `realm/get`                      | Realm        | Get full realm connection set                                                              |

<Note>
  Generated assistant images use the same surface-neutral history and blob APIs as other blob-backed artifacts. Read `session/history`, find assistant blocks with `block_type: "image"`, then call `blob/get` with `data.blob_ref.blob_id` to retrieve the base64 payload.
</Note>

<Note>
  Live channels provide low-latency audio/text streaming. Create a session
  with a realtime-capable model (e.g. `gpt-realtime-2`), then call
  `live/open` to start the channel. The `--live-ws <addr>` flag on
  `rkat-rpc` enables the WebSocket listener required for audio transport.
  Use `live/status` to observe channel state.
</Note>

<Note>
  RPC is the canonical typed substrate for the SDKs: use explicit `mob/*` lifecycle, host-ingress, and observation methods from apps. Inside running sessions, mob capability is exposed by composing `meerkat-mob-mcp` (`MobMcpState` + `AgentMobToolSurfaceFactory`) into `SessionBuildOptions.mob_tools`, which provides authorized `mob_*` tools to the agent. `external_tools` remains for callback and MCP-backed tool dispatchers.
</Note>

<Note>
  WorkGraph RPC methods are read-only observability. Programmatic hosts should
  inspect WorkGraph through `workgraph/get`, `list`, `ready`, `snapshot`, and
  `events`; agents mutate WorkGraph through the `workgraph_*` tools.
</Note>

## Protocol

Standard JSON-RPC 2.0 with `"jsonrpc": "2.0"` on every message. Three message types:

* **Request** (client -> server): has `id`, `method`, `params`
* **Response** (server -> client): has `id`, `result` or `error`
* **Notification** (server -> client): has `method`, `params`, no `id`

<Accordion title="Full lifecycle diagram">
  ```text theme={null}
  Client                          Server
    |                                |
    |-- initialize ----------------->|
    |<-- result { capabilities } ----|
    |-- initialized (notification) ->|
    |                                |
    |-- session/create { prompt } -->|
    |<-- session/event (notif) ------|  // AgentEvent stream
    |<-- session/event (notif) ------|
    |<-- result { session_id, ... } -|
    |                                |
    |-- turn/start { session_id } -->|
    |<-- session/event (notif) ------|
    |<-- result { text, usage } -----|
    |                                |
    |-- turn/interrupt { sid } ----->|
    |<-- result {} ------------------|
    |                                |
    |-- session/archive { sid } ---->|
    |<-- result {} ------------------|
  ```
</Accordion>

## Session methods

### initialize

Handshake. Returns server capabilities.

<CodeGroup>
  ```json Request theme={null}
  {"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}
  ```

  ```json Response theme={null}
  {
    "jsonrpc": "2.0",
    "id": 1,
    "result": {
      "server_info": {
        "name": "meerkat-rpc",
        "version": "0.6.6"
      },
      "contract_version": "0.6.6",
      "methods": [
        "initialize", "initialized",
        "session/create", "session/list", "session/read",
        "turn/start", "turn/interrupt",
        "runtime/host_info", "config/get",
        "live/open", "mob/create"
      ]
    }
  }
  ```
</CodeGroup>

The actual array contains every method compiled into the server. For the full
documented list and parameter/result type names, use
`artifacts/schemas/rpc-methods.json`.

<ResponseField name="server_info.name" type="string">
  Server name.
</ResponseField>

<ResponseField name="server_info.version" type="string">
  Server version.
</ResponseField>

<ResponseField name="contract_version" type="string">
  Protocol contract version.
</ResponseField>

<ResponseField name="methods" type="array">
  List of supported method names.
</ResponseField>

### session/create

Create a new session and run the first turn. Set `initial_turn` to `"deferred"` to return a pending `session_id` without running the first turn; that returned `session_id` is valid for the first `turn/start`, including sessions created with inline `external_tools`.

<CodeGroup>
  ```json Request (minimal) theme={null}
  {
    "jsonrpc": "2.0",
    "id": 2,
    "method": "session/create",
    "params": {
      "prompt": "What is Rust?"
    }
  }
  ```

  ```json Request (full) theme={null}
  {
    "jsonrpc": "2.0",
    "id": 2,
    "method": "session/create",
    "params": {
      "prompt": "What is Rust?",
      "model": "claude-sonnet-4-6",
      "provider": "anthropic",
      "max_tokens": 4096,
      "system_prompt": "You are a helpful assistant.",
      "output_schema": {
        "schema": {"type": "object", "properties": {"answer": {"type": "string"}}},
        "name": "answer"
      },
      "structured_output_retries": 2,
      "hooks_override": null,
      "enable_builtins": false,
      "enable_shell": false,
      "enable_memory": false,
      "enable_schedule": null,
      "enable_workgraph": null,
      "keep_alive": null,
      "comms_name": null,
      "provider_params": null,
      "preload_skills": [
        {"source_uuid": "dc256086-0d2f-4f61-a307-320d4148107f", "skill_name": "extraction-email"}
      ],
      "skill_refs": [
        {
          "kind": "structured",
          "source_uuid": "dc256086-0d2f-4f61-a307-320d4148107f",
          "skill_name": "formatting-markdown"
        }
      ]
    }
  }
  ```

  ```json Response theme={null}
  {
    "jsonrpc": "2.0",
    "id": 2,
    "result": {
      "session_id": "01936f8a-7b2c-7000-8000-000000000001",
      "text": "Rust is a systems programming language...",
      "turns": 1,
      "tool_calls": 0,
      "usage": {
        "input_tokens": 50,
        "output_tokens": 200,
        "total_tokens": 250
      },
      "structured_output": null,
      "schema_warnings": null
    }
  }
  ```
</CodeGroup>

Only `prompt` is required. All other fields are optional and fall back to config defaults. During execution, `session/event` notifications are emitted (see [Notifications](#notifications)).

#### Parameter reference

<ParamField body="prompt" type="string" required>
  The user prompt to send to the agent.
</ParamField>

<ParamField body="model" type="string | null" default="claude-sonnet-4-6">
  Model name (e.g. `"claude-opus-4-6"`, `"gpt-5.5"`).
</ParamField>

<ParamField body="provider" type="string | null" default="inferred from model">
  Provider name: `"anthropic"`, `"openai"`, `"gemini"`, `"self_hosted"`, `"other"`.
</ParamField>

<ParamField body="max_tokens" type="u32 | null" default="config default">
  Max tokens per turn.
</ParamField>

<ParamField body="system_prompt" type="string | null" default="null">
  Override system prompt.
</ParamField>

<ParamField body="output_schema" type="object | null" default="null">
  JSON schema for structured output extraction (wrapper or raw schema).
</ParamField>

<ParamField body="structured_output_retries" type="u32" default="2">
  Max retries for structured output validation.
</ParamField>

<ParamField body="hooks_override" type="HookRunOverrides | null" default="null">
  Run-scoped hook overrides (entries to add, hook IDs to disable).
</ParamField>

<ParamField body="enable_builtins" type="bool" default="false">
  Enable built-in tools (task management, etc.).
</ParamField>

<ParamField body="enable_shell" type="bool" default="false">
  Enable shell tool (requires `enable_builtins`).
</ParamField>

<ParamField body="enable_memory" type="bool" default="false">
  Enable semantic memory (`memory_search` tool + compaction indexing).
</ParamField>

<ParamField body="enable_schedule" type="bool | null" default="null">
  Override schedule tools for this session. `null` uses the surface default.
</ParamField>

<ParamField body="enable_workgraph" type="bool | null" default="null">
  Override WorkGraph tools for this session. `null` uses the surface default.
</ParamField>

<ParamField body="keep_alive" type="bool | null" default="null">
  Keep session alive after turn for comms. `null` = inherit persisted session intent, `true` = enable, `false` = disable. Requires `comms_name` when enabled.
</ParamField>

<ParamField body="comms_name" type="string | null" default="null">
  Agent name for inter-agent communication.
</ParamField>

<ParamField body="provider_params" type="object | null" default="null">
  Provider-specific parameters (e.g., thinking config, reasoning effort).
</ParamField>

#### Response fields

<ResponseField name="session_id" type="string">
  UUID of the created session.
</ResponseField>

<ResponseField name="text" type="string">
  The agent's response text.
</ResponseField>

<ResponseField name="turns" type="u32">
  Number of LLM calls made.
</ResponseField>

<ResponseField name="tool_calls" type="u32">
  Number of tool calls executed.
</ResponseField>

<ResponseField name="usage" type="WireUsage">
  Token usage breakdown.
</ResponseField>

<ResponseField name="structured_output" type="object | null">
  Parsed structured output.
</ResponseField>

<ResponseField name="schema_warnings" type="array | null">
  Schema compatibility warnings.
</ResponseField>

### session/history

Read committed transcript history for an existing session.

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 7,
  "method": "session/history",
  "params": {
    "session_id": "01936f8a-7b2c-7000-8000-000000000001",
    "offset": 0,
    "limit": 50
  }
}
```

Returns oldest-to-newest committed messages plus pagination metadata. This method follows the same owner-resolution rules as `session/read`, including mob-owned session IDs when mob support is enabled.

### session/fork\_at

Create a new idle session whose transcript is the source session prefix ending before `message_index`. The source session is not mutated.

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 8,
  "method": "session/fork_at",
  "params": {
    "session_id": "01936f8a-7b2c-7000-8000-000000000001",
    "message_index": 2,
    "running_behavior": "reject"
  }
}
```

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 8,
  "result": {
    "source_session_id": "01936f8a-7b2c-7000-8000-000000000001",
    "session_id": "01936f8a-7b2c-7000-8000-000000000009",
    "message_count": 2
  }
}
```

`running_behavior` currently supports `"reject"`, which returns `SESSION_BUSY` if the source session has active work.

### session/fork\_replace

Create a new idle session from the source prefix through `message_index`, replacing the addressed message or block with a typed replacement. Later source messages are intentionally omitted so callers continue from the edited branch instead of replaying stale descendants.

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 9,
  "method": "session/fork_replace",
  "params": {
    "session_id": "01936f8a-7b2c-7000-8000-000000000001",
    "message_index": 2,
    "replacement": {
      "type": "message",
      "message": {
        "role": "user",
        "content": "Edited follow-up"
      }
    },
    "running_behavior": "reject"
  }
}
```

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 9,
  "result": {
    "source_session_id": "01936f8a-7b2c-7000-8000-000000000001",
    "session_id": "01936f8a-7b2c-7000-8000-000000000010",
    "message_count": 3
  }
}
```

Supported replacement variants are `message`, `user_content_block`, `assistant_block`, and `tool_result_content_block`. Edits always create a new session identity.

### session/list

List active sessions.

<CodeGroup>
  ```json Request theme={null}
  {"jsonrpc":"2.0","id":3,"method":"session/list"}
  ```

  ```json Response theme={null}
  {
    "jsonrpc": "2.0",
    "id": 3,
    "result": {
      "sessions": [
        {"session_id": "01936f8a-...", "state": "idle"},
        {"session_id": "01936f8b-...", "state": "running"}
      ]
    }
  }
  ```
</CodeGroup>

### session/read

Get session state.

<CodeGroup>
  ```json Request theme={null}
  {"jsonrpc":"2.0","id":4,"method":"session/read","params":{"session_id":"01936f8a-..."}}
  ```

  ```json Response theme={null}
  {
    "jsonrpc": "2.0",
    "id": 4,
    "result": {
      "session_id": "01936f8a-...",
      "state": "idle",
      "message_count": 4,
      "total_tokens": 500
    }
  }
  ```
</CodeGroup>

<ParamField body="session_id" type="string" required>
  Session ID to read.
</ParamField>

### session/archive

Remove a session from the runtime.

<CodeGroup>
  ```json Request theme={null}
  {"jsonrpc":"2.0","id":5,"method":"session/archive","params":{"session_id":"01936f8a-..."}}
  ```

  ```json Response theme={null}
  {"jsonrpc":"2.0","id":5,"result":{"archived":true}}
  ```
</CodeGroup>

<ParamField body="session_id" type="string" required>
  Session ID to archive.
</ParamField>

## Turn methods

### turn/start

Start a new turn on an existing session.

<CodeGroup>
  ```json Request theme={null}
  {
    "jsonrpc": "2.0",
    "id": 6,
    "method": "turn/start",
    "params": {
      "session_id": "01936f8a-...",
      "prompt": "Can you explain ownership?",
      "skill_refs": [
        {
          "kind": "structured",
          "source_uuid": "dc256086-0d2f-4f61-a307-320d4148107f",
          "skill_name": "rust-patterns-ownership"
        }
      ]
    }
  }
  ```

  ```json Response theme={null}
  {
    "jsonrpc": "2.0",
    "id": 6,
    "result": {
      "session_id": "01936f8a-...",
      "text": "Ownership is Rust's approach to memory management...",
      "turns": 1,
      "tool_calls": 0,
      "usage": {
        "input_tokens": 300,
        "output_tokens": 400,
        "total_tokens": 700
      },
      "structured_output": null,
      "schema_warnings": null
    }
  }
  ```
</CodeGroup>

Returns the same result shape as `session/create`. Fails with error code `-32001` (SESSION\_BUSY) if a turn is already in progress.

<ParamField body="session_id" type="string" required>
  Session ID to continue.
</ParamField>

<ParamField body="prompt" type="string" required>
  The follow-up prompt.
</ParamField>

<ParamField body="model" type="string | null" default="from session">
  Model override for this turn. On pending (deferred) sessions this sets the model before materialization. On materialized sessions this hot-swaps the LLM client for the remainder of the session.
</ParamField>

<ParamField body="provider" type="string | null" default="from session">
  Provider override (e.g. `"anthropic"`, `"openai"`, `"gemini"`). Typically inferred from `model`.
</ParamField>

<ParamField body="provider_params" type="object | null" default="from session">
  Provider-specific parameters (e.g. `{"thinking_budget": 10000}`). Applied alongside model/provider override.
</ParamField>

<ParamField body="max_tokens" type="u32 | null" default="from session">
  Max tokens override.
</ParamField>

<ParamField body="skill_refs" type="object[] | null">
  Tagged structured skill references to resolve and inject for this turn. Each entry uses `{"kind":"structured","source_uuid":"...","skill_name":"..."}`.
</ParamField>

### turn/interrupt

Cancel an in-flight turn. No-op if the session is idle.

<CodeGroup>
  ```json Request theme={null}
  {"jsonrpc":"2.0","id":7,"method":"turn/interrupt","params":{"session_id":"01936f8a-..."}}
  ```

  ```json Response theme={null}
  {"jsonrpc":"2.0","id":7,"result":{"interrupted":true}}
  ```
</CodeGroup>

<ParamField body="session_id" type="string" required>
  Session ID to interrupt.
</ParamField>

## Event methods

### session/external\_event

Queue a runtime-backed external event for an existing session.

<CodeGroup>
  ```json Request theme={null}
  {
    "jsonrpc": "2.0",
    "id": 8,
    "method": "session/external_event",
    "params": {
      "session_id": "01936f8a-...",
      "kind": "generic_json",
      "event_type": "ci-pipeline",
      "payload": {"alert": "build failed", "repo": "main"}
    }
  }
  ```

  ```json Response theme={null}
  {
    "jsonrpc": "2.0",
    "id": 8,
    "result": {
      "outcome_type": "accepted"
    }
  }
  ```
</CodeGroup>

### session/peer\_response\_terminal

Admit a correlated terminal peer response through the typed runtime ingress.

<CodeGroup>
  ```json Request theme={null}
  {
    "jsonrpc": "2.0",
    "id": 9,
    "method": "session/peer_response_terminal",
    "params": {
      "session_id": "01936f8a-...",
      "peer_id": "00000000-0000-4000-8000-000000000161",
      "display_name": "analyst",
      "request_id": "00000000-0000-4000-8000-000000000162",
      "status": "completed",
      "result": {"token": "silver harbor"}
    }
  }
  ```

  ```json Response theme={null}
  {
    "jsonrpc": "2.0",
    "id": 9,
    "result": {
      "outcome_type": "accepted",
      "input_id": "01936f8a-..."
    }
  }
  ```
</CodeGroup>

<ParamField body="session_id" type="string" required>
  Session ID to admit the peer response to.
</ParamField>

<ParamField body="peer_id" type="string" required>
  Canonical peer routing ID.
</ParamField>

<ParamField body="display_name" type="string">
  Optional presentation label. It is not used as routing identity.
</ParamField>

<ParamField body="request_id" type="string" required>
  Peer correlation ID for the request this terminal response completes.
</ParamField>

<ParamField body="status" type="string" required>
  Terminal response status: `"completed"`, `"failed"`, or `"cancelled"`.
</ParamField>

<ParamField body="result" type="any JSON" required>
  Peer-returned terminal payload.
</ParamField>

Error `-32603` if runtime admission fails, `-32602` if the session locator is invalid.

This is a queue-only runtime admission path; it does not create a second direct execution loop.

### comms/peers

List discoverable peers from configured `TrustedPeers` and active in-process registrations. Requires the `comms` feature.

<CodeGroup>
  ```json Request theme={null}
  {
    "jsonrpc": "2.0",
    "id": 9,
    "method": "comms/peers",
    "params": {
      "session_id": "01936f8a-..."
    }
  }
  ```

  ```json Response theme={null}
  {
    "jsonrpc": "2.0",
    "id": 9,
    "result": {
      "peers": [
        {"name": "reviewer", "address": "127.0.0.1:4201"},
        {"name": "coordinator", "address": "inproc"}
      ]
    }
  }
  ```
</CodeGroup>

<ParamField body="session_id" type="string" required>
  Session ID to query peers for.
</ParamField>

### skills/list

List all skills with provenance information, including active and shadowed entries.

<CodeGroup>
  ```json Request theme={null}
  {"jsonrpc":"2.0","id":12,"method":"skills/list"}
  ```

  ```json Response theme={null}
  {
    "jsonrpc": "2.0",
    "id": 12,
    "result": {
      "skills": [
        {
          "key": {
            "source_uuid": "00000000-0000-4b11-8111-000000000001",
            "skill_name": "task-workflow"
          },
          "name": "Task Workflow",
          "description": "How to use task tools",
          "scope": "builtin",
          "source": {
            "source_uuid": "00000000-0000-4b11-8111-000000000001",
            "display_name": "embedded",
            "transport_kind": "embedded",
            "fingerprint": "embedded:inventory",
            "status": "active"
          },
          "is_active": true
        },
        {
          "key": {
            "source_uuid": "00000000-0000-4b11-8111-000000000002",
            "skill_name": "my-deployment"
          },
          "name": "Deployment Guide",
          "description": "Project-specific deployment",
          "scope": "project",
          "source": {
            "source_uuid": "00000000-0000-4b11-8111-000000000002",
            "display_name": "project",
            "transport_kind": "filesystem",
            "fingerprint": "filesystem:.rkat/skills",
            "status": "active"
          },
          "is_active": true
        },
        {
          "key": {
            "source_uuid": "dc256086-0d2f-4f61-a307-320d4148107f",
            "skill_name": "task-workflow"
          },
          "name": "Custom Task Workflow",
          "description": "Override tasks",
          "scope": "project",
          "source": {
            "source_uuid": "dc256086-0d2f-4f61-a307-320d4148107f",
            "display_name": "company",
            "transport_kind": "git",
            "fingerprint": "repo-7cc66f36fd9db1a1",
            "status": "active"
          },
          "is_active": true
        }
      ]
    }
  }
  ```
</CodeGroup>

<ResponseField name="skills" type="array">
  List of `SkillEntry` objects with `key`, `name`, `description`, `scope`, canonical `source` provenance, `is_active`, and optional canonical `shadowed_by` provenance.
</ResponseField>

Returns error `-32603` if skills are not enabled.

`skills/inspect` is not part of the advertised RPC catalog. Use `skills/list` for provenance, or the CLI/MCP skill inspection surfaces when full skill bodies are needed.

## Live channel methods

Live channels provide low-latency audio and text streaming on sessions that use a realtime-capable model. Create a session with a model like `gpt-realtime-2`, then call `live/open` to start the channel explicitly. The `--live-ws <addr>` flag on `rkat-rpc` enables the WebSocket listener required for audio transport. See the [Live Channels guide](/guides/realtime) for the full flow.

### Opening a live channel

Create a session on a realtime-capable model and call `live/open`:

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 19,
  "method": "session/create",
  "params": {
    "prompt": "Let's talk.",
    "model": "gpt-realtime-2",
    "provider": "openai"
  }
}
```

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 20,
  "method": "live/open",
  "params": {
    "session_id": "01936f8a-7b2c-7000-8000-000000000001"
  }
}
```

`live/open` returns a `LiveOpenResult` with transport bootstrap (e.g. WebSocket URL), `WireLiveChannelCapabilities`, and `WireLiveContinuityMode`.

### live/status

Read the current state of a live channel.

<CodeGroup>
  ```json Request theme={null}
  {
    "jsonrpc": "2.0",
    "id": 22,
    "method": "live/status",
    "params": {
      "channel_id": "ch_01936f8a-..."
    }
  }
  ```
</CodeGroup>

### Live channel lifecycle methods (`live/*`)

| Method               | Purpose                                                                                                                                                                  |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `live/open`          | Open a live audio/text channel for a session                                                                                                                             |
| `live/webrtc/answer` | Answer a browser WebRTC offer for an already-open live channel                                                                                                           |
| `live/status`        | Get the status of a live channel                                                                                                                                         |
| `live/close`         | Close a live channel                                                                                                                                                     |
| `live/send_input`    | Send an input chunk (audio/text) to a live channel                                                                                                                       |
| `live/commit_input`  | Commit pending input on a live channel (turn boundary)                                                                                                                   |
| `live/interrupt`     | Interrupt the assistant turn on a live channel (barge-in)                                                                                                                |
| `live/truncate`      | Truncate assistant output at a client-tracked playback cursor                                                                                                            |
| `live/refresh`       | Apply mutable session config (instructions/tools/audio) to an open channel; config-only, does not replay history; identity swaps (model/provider) require close + reopen |

These require `rkat-rpc` to be started with `--live-ws <addr>` for WebSocket transport.

## MCP methods

### mcp/add

Stage a live MCP server addition for a running session. The server is connected at the next turn boundary.

<CodeGroup>
  ```json Request theme={null}
  {
    "jsonrpc": "2.0",
    "id": 14,
    "method": "mcp/add",
    "params": {
      "session_id": "01936f8a-...",
      "server_config": {
        "name": "my-tools",
        "command": "npx",
        "args": ["-y", "@my/mcp-server"]
      },
      "persisted": false
    }
  }
  ```

  ```json Response theme={null}
  {
    "jsonrpc": "2.0",
    "id": 14,
    "result": {
      "session_id": "01936f8a-...",
      "operation": "add",
      "server_name": "my-tools",
      "status": "staged",
      "persisted": false,
      "applied_at_turn": null
    }
  }
  ```
</CodeGroup>

<ParamField body="session_id" type="string" required>
  Session ID to add the server to.
</ParamField>

<ParamField body="server_config" type="object" required>
  Typed MCP server configuration. Include `name` with either stdio fields (`command`, optional `args`/`env`) or HTTP fields (`url`, optional `headers`/`transport`).
</ParamField>

<ParamField body="persisted" type="bool" default="false">
  Whether to also write the server to disk config.
</ParamField>

### mcp/remove

Stage removal of an MCP server from a running session. Active tool calls drain before the server disconnects at the next turn boundary.

<CodeGroup>
  ```json Request theme={null}
  {
    "jsonrpc": "2.0",
    "id": 15,
    "method": "mcp/remove",
    "params": {
      "session_id": "01936f8a-...",
      "server_name": "my-tools",
      "persisted": false
    }
  }
  ```

  ```json Response theme={null}
  {
    "jsonrpc": "2.0",
    "id": 15,
    "result": {
      "session_id": "01936f8a-...",
      "operation": "remove",
      "server_name": "my-tools",
      "status": "staged",
      "persisted": false,
      "applied_at_turn": null
    }
  }
  ```
</CodeGroup>

<ParamField body="session_id" type="string" required>
  Session ID to remove the server from.
</ParamField>

<ParamField body="server_name" type="string" required>
  Name of the MCP server to remove.
</ParamField>

<ParamField body="persisted" type="bool" default="false">
  Whether to also remove from disk config.
</ParamField>

### mcp/reload

Reload one or all MCP servers for a running session. Useful after config changes.

<CodeGroup>
  ```json Request theme={null}
  {
    "jsonrpc": "2.0",
    "id": 16,
    "method": "mcp/reload",
    "params": {
      "session_id": "01936f8a-...",
      "server_name": "my-tools"
    }
  }
  ```

  ```json Response theme={null}
  {
    "jsonrpc": "2.0",
    "id": 16,
    "result": {
      "session_id": "01936f8a-...",
      "operation": "reload",
      "server_name": "my-tools",
      "status": "staged",
      "persisted": false,
      "applied_at_turn": null
    }
  }
  ```
</CodeGroup>

<ParamField body="session_id" type="string" required>
  Session ID to reload servers for.
</ParamField>

<ParamField body="server_name" type="string | null" default="null">
  Specific server to reload. If null, reloads all servers.
</ParamField>

## Models

### models/catalog

Return the curated model catalog with provider profiles, capability metadata, and parameter schemas.

<CodeGroup>
  ```json Request theme={null}
  {"jsonrpc":"2.0","id":17,"method":"models/catalog"}
  ```

  ```json Response theme={null}
  {
    "jsonrpc": "2.0",
    "id": 17,
    "result": {
      "contract_version": "0.6.6",
      "providers": [
        {
          "provider": "anthropic",
          "default_model_id": "claude-opus-4-7",
          "models": [
            {
              "id": "claude-opus-4-7",
              "display_name": "Claude Opus 4.7",
              "tier": "recommended",
              "context_window": 1000000,
              "max_output_tokens": 128000,
              "profile": {
                "model_family": "claude-opus-4",
                "supports_temperature": false,
                "supports_thinking": true,
                "supports_reasoning": false,
                "params_schema": {}
              }
            }
          ]
        }
      ]
    }
  }
  ```
</CodeGroup>

No parameters are required. The catalog is resolved from built-in model metadata plus config-backed provider/server entries; invalid provider config can yield `INVALID_PARAMS`.

<ResponseField name="contract_version" type="string">
  Contract version for the catalog response.
</ResponseField>

<ResponseField name="providers" type="array">
  List of provider entries, each containing `provider`, `default_model_id`, and `models`.
</ResponseField>

<ResponseField name="providers[].models[].profile" type="object">
  Model profile with capability flags (`supports_temperature`, `supports_thinking`, `supports_reasoning`) and `params_schema`.
</ResponseField>

## Capabilities

### capabilities/get

Return runtime capabilities with status resolved against config. This lists every capability known to Meerkat with its current status (available, disabled by policy, not compiled, etc.).

<CodeGroup>
  ```json Request theme={null}
  {"jsonrpc":"2.0","id":8,"method":"capabilities/get"}
  ```

  ```json Response theme={null}
  {
    "jsonrpc": "2.0",
    "id": 8,
    "result": {
      "contract_version": {"major": 0, "minor": 6, "patch": 6},
      "capabilities": [
        {
          "id": "sessions",
          "description": "Session lifecycle management",
          "status": "Available"
        },
        {
          "id": "structured_output",
          "description": "Structured output extraction with JSON schema",
          "status": "Available"
        },
        {
          "id": "hooks",
          "description": "Lifecycle hooks for tool and turn events",
          "status": "Available"
        },
        {
          "id": "builtins",
          "description": "Built-in tools (task management)",
          "status": {"DisabledByPolicy": {"description": "Disabled by config"}}
        },
        {
          "id": "shell",
          "description": "Shell command execution",
          "status": {"DisabledByPolicy": {"description": "Disabled by config"}}
        },
        {
          "id": "comms",
          "description": "Inter-agent communication",
          "status": {"NotCompiled": {"feature": "comms"}}
        },
        {
          "id": "memory_store",
          "description": "Semantic memory indexing",
          "status": "Available"
        },
        {
          "id": "skills",
          "description": "Skill loading and injection",
          "status": "Available"
        }
      ]
    }
  }
  ```
</CodeGroup>

Possible `status` values:

| Status                   | Shape                                           | Meaning                                           |
| ------------------------ | ----------------------------------------------- | ------------------------------------------------- |
| `Available`              | `"Available"`                                   | Compiled in, config-enabled, protocol supports it |
| `DisabledByPolicy`       | `{"DisabledByPolicy": {"description": "..."}}`  | Compiled in but disabled by policy                |
| `NotCompiled`            | `{"NotCompiled": {"feature": "..."}}`           | Feature flag absent at compile time               |
| `NotSupportedByProtocol` | `{"NotSupportedByProtocol": {"reason": "..."}}` | This protocol surface does not support it         |

## Config methods

### config/get

Read current realm config envelope.

<CodeGroup>
  ```json Request theme={null}
  {"jsonrpc":"2.0","id":9,"method":"config/get"}
  ```

  ```json Response theme={null}
  {
    "jsonrpc": "2.0",
    "id": 9,
    "result": {
      "config": {
        "agent": {
          "model": "claude-sonnet-4-6",
          "max_tokens_per_turn": 8192
        }
      },
      "generation": 3,
      "realm_id": "team-alpha",
      "instance_id": "rpc-worker-1",
      "backend": "sqlite",
      "resolved_paths": {
        "root": "/.../meerkat/realms/team-alpha",
        "manifest_path": "/.../realm_manifest.json",
        "config_path": "/.../config.toml",
        "sessions_sqlite_path": "/.../sessions.sqlite3",
        "sessions_jsonl_dir": "/.../sessions_jsonl"
      }
    }
  }
  ```
</CodeGroup>

### config/set

Replace config, optionally using generation CAS.

<CodeGroup>
  ```json Request theme={null}
  {
    "jsonrpc":"2.0",
    "id":10,
    "method":"config/set",
    "params":{
      "config": {"agent":{"model":"gpt-5.5"}},
      "expected_generation": 3
    }
  }
  ```

  ```json Response theme={null}
  {
    "jsonrpc":"2.0",
    "id":10,
    "result": {
      "config": {"agent":{"model":"gpt-5.5"}},
      "generation": 4,
      "realm_id": "team-alpha",
      "instance_id": null,
      "backend": "sqlite",
      "resolved_paths": { "...": "..." }
    }
  }
  ```
</CodeGroup>

### config/patch

Merge-patch the config (RFC 7396).

<CodeGroup>
  ```json Request theme={null}
  {
    "jsonrpc":"2.0",
    "id":11,
    "method":"config/patch",
    "params":{
      "patch":{"agent":{"max_tokens_per_turn":8192}},
      "expected_generation": 4
    }
  }
  ```

  ```json Response theme={null}
  {
    "jsonrpc":"2.0",
    "id":11,
    "result": {
      "config": {"agent":{"max_tokens_per_turn":8192}},
      "generation": 5,
      "realm_id": "team-alpha",
      "instance_id": null,
      "backend": "sqlite",
      "resolved_paths": { "...": "..." }
    }
  }
  ```
</CodeGroup>

<Note>
  `config/set` also accepts a direct config object as params for compatibility. In that mode, no CAS check is applied.
</Note>

## Notifications

During turn execution, the server emits `session/event` notifications containing serialized `AgentEvent` payloads:

```json theme={null}
{
  "jsonrpc": "2.0",
  "method": "session/event",
  "params": {
    "session_id": "01936f8a-...",
    "event": {
      "type": "text_delta",
      "delta": "Rust is"
    }
  }
}
```

Event types match the `AgentEvent` enum in `meerkat-core`:

| Event                          | Description                                                                                                                         |
| ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- |
| `run_started`                  | Agent execution began                                                                                                               |
| `run_completed`                | Agent run finished                                                                                                                  |
| `run_failed`                   | Agent run failed                                                                                                                    |
| `turn_started`                 | New LLM call within the turn                                                                                                        |
| `text_delta`                   | Streaming text chunk from LLM                                                                                                       |
| `text_complete`                | Full text for this turn                                                                                                             |
| `tool_call_requested`          | LLM wants to call a tool                                                                                                            |
| `tool_result_received`         | Tool result processed                                                                                                               |
| `turn_completed`               | LLM call finished                                                                                                                   |
| `tool_execution_started`       | Tool dispatch began                                                                                                                 |
| `tool_execution_completed`     | Tool returned a result                                                                                                              |
| `tool_execution_timed_out`     | Tool exceeded timeout                                                                                                               |
| `compaction_started`           | Context compaction began                                                                                                            |
| `compaction_completed`         | Compaction finished                                                                                                                 |
| `compaction_failed`            | Compaction failed                                                                                                                   |
| `budget_warning`               | Approaching resource limits                                                                                                         |
| `retrying`                     | Retrying after transient error                                                                                                      |
| `hook_started`                 | Hook execution began                                                                                                                |
| `hook_completed`               | Hook finished                                                                                                                       |
| `hook_failed`                  | Hook execution failed                                                                                                               |
| `hook_denied`                  | Hook blocked operation                                                                                                              |
| `skills_resolved`              | Skills loaded for turn                                                                                                              |
| `skill_resolution_failed`      | Skill resolution failed                                                                                                             |
| `tool_config_changed`          | Tool visibility or MCP config changed                                                                                               |
| `background_job_completed`     | Background shell job reached a typed terminal status; `terminal_status` is semantic and `status` is only an optional display mirror |
| `interaction_complete`         | Interaction-scoped stream completed                                                                                                 |
| `interaction_callback_pending` | Interaction reached a callback boundary and is waiting for tool results                                                             |
| `interaction_failed`           | Interaction-scoped stream failed                                                                                                    |
| `stream_truncated`             | Backpressure dropped intermediate events                                                                                            |

### session/stream\_event notifications

When a session event stream is open (via `session/stream_open`), the server emits `session/stream_event` notifications. The notification payload mirrors `session/event`, but is scoped to the explicit stream handle.

```json theme={null}
{
  "jsonrpc": "2.0",
  "method": "session/stream_event",
  "params": {
    "stream_id": "550e8400-e29b-41d4-a716-446655440000",
    "sequence": 1,
    "session_id": "sess_123",
    "event": { "...": "..." }
  }
}
```

### mob/stream\_event notifications

When a mob event stream is open (via `mob/stream_open`), the server emits `mob/stream_event` notifications. Requires the `mob` feature.

```json theme={null}
{
  "jsonrpc": "2.0",
  "method": "mob/stream_event",
  "params": {
    "stream_id": "550e8400-e29b-41d4-a716-446655440000",
    "sequence": 1,
    "event": { "...": "..." }
  }
}
```

For mob-wide streams the event is an `AttributedEvent` (`source` member identity + profile + envelope). For per-member streams the event is the raw `EventEnvelope<AgentEvent>`. Runtime incarnation ids and fence tokens are bridge-internal and are not part of public stream payloads.

## Error codes

Standard JSON-RPC codes plus Meerkat-specific application codes:

| Code   | Name                    | Description                            |
| ------ | ----------------------- | -------------------------------------- |
| -32700 | Parse error             | Invalid JSON                           |
| -32600 | Invalid request         | Not a valid JSON-RPC request           |
| -32601 | Method not found        | Unknown method                         |
| -32602 | Invalid params          | Missing or invalid parameters          |
| -32603 | Internal error          | Server error                           |
| -32001 | Session not found       | Session ID does not exist              |
| -32002 | Session busy            | Turn already in progress               |
| -32003 | Session not running     | Session not in running state           |
| -32010 | Provider error          | LLM provider issue (missing key, auth) |
| -32011 | Budget exhausted        | Resource limits reached                |
| -32012 | Hook denied             | Hook blocked the operation             |
| -32013 | Agent error             | Internal agent error                   |
| -32020 | Capability unavailable  | Required capability not available      |
| -32021 | Skill not found         | Requested skill does not exist         |
| -32022 | Skill resolution failed | Skill resolution error                 |

## Architecture

The RPC server is **stateful**: agents stay alive between turns. This is the key difference from REST (stateless per-request) and MCP (callback pattern).

```text theme={null}
Client (IDE, app) <--stdio JSONL--> RpcServer
                                       |
                                  MethodRouter
                                       |
                                  SessionRuntime
                                   /    |    \
                            Session  Session  Session
                             Task    Task     Task
                              |       |        |
                            Agent   Agent    Agent
                         (exclusive ownership)
```

Each session gets a dedicated tokio task that exclusively owns the `Agent`. This solves the `cancel(&mut self)` requirement without mutex. Commands (`StartTurn`, `Interrupt`, `Shutdown`) are sent via channels.

<Note>
  **Backpressure:** The notification channel is bounded. When the client reads slowly, the agent naturally slows down.
</Note>

## Comparison with other surfaces

|               | CLI    | REST             | MCP      | RPC                 |
| ------------- | ------ | ---------------- | -------- | ------------------- |
| Stateful      | No     | No               | No       | **Yes**             |
| Streaming     | stderr | SSE              | Optional | JSONL notifications |
| Multi-session | No     | No               | No       | **Yes**             |
| Cancellation  | Ctrl+C | N/A              | N/A      | `turn/interrupt`    |
| Bidirectional | No     | No (SSE one-way) | Partial  | **Yes**             |

## See also

* [CLI reference](/cli/commands)
* [REST API](/api/rest)
* [MCP](/api/mcp)
* [Architecture](/reference/architecture)
