Skip to main content
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-channel 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

1

Start the server

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:
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.rkat-rpc --tcp is a JSON-RPC host transport. It is not the signed Meerkat peer/comms channel used by remote agents or external mob members; use rkat run --comms-listen-tcp ... for that.
2

Send the handshake

3

Create a session

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

Runtime scope

rkat-rpc accepts global scope flags:
  • --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.
Every Schedule result is one flattened public object. Configuration fields including planning_horizon_days, planning_horizon_occurrences, labels, created_at_utc, and updated_at_utc are top-level; there is no nested config and persisted machine authority is not exposed as machine_state.
WorkGraph goal and attention mutation methods require trusted host/session authority. JSON-RPC exposes observability reads; goal creation, reassignment, policy escalation, confirmation, and closure stay on trusted in-process host and agent-tool surfaces.
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.
Live channels provide low-latency audio/text streaming with model-gated image input. 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 and check live/open’s capabilities.image_in before sending image context.
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.
WorkGraph RPC methods are observability reads. Programmatic hosts inspect WorkGraph through workgraph/get, list, ready, snapshot, events, goal/status, and attention/list; agents mutate ordinary WorkGraph state through the workgraph_* tools. Authority-bearing attention reassignment and policy escalation are runtime-injected WorkGraph tool operations, not public JSON-RPC methods.

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

Session methods

initialize

Handshake. Returns server capabilities.
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.
string
Server name.
string
Server version.
string
Protocol contract version.
array
List of supported method names.

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.
Only prompt is required. All other fields are optional and fall back to config defaults. During execution, session/event notifications are emitted (see Notifications).

Parameter reference

string
required
The user prompt to send to the agent.
ContentInput[] | null
default:"null"
Host-attached injected context for the first turn. Each entry materializes as a separate typed injected-context user-channel message immediately before the first turn’s user message, in order; injected context is excluded from semantic-memory indexing.
string | null
default:"null"
Non-empty, exact request-only host facts for the immediate first turn; whitespace is significant. The pending runtime input retains the bytes for crash retry; the Session transcript, compaction summarizer, and extraction phase never receive them. Deferred create rejects this field; provide it on the eventual turn/start.
string | null
default:"config/catalog default"
Model name (e.g. "claude-opus-4-8", "gpt-5.5").
string | null
default:"inferred from model"
Provider name: "anthropic", "openai", "gemini", "self_hosted", "other".
u32 | null
default:"config default"
Max tokens per turn.
string | null
default:"null"
Override system prompt.
object | null
default:"null"
JSON schema for structured output extraction (wrapper or raw schema).
u32
default:"2"
Max retries for structured output validation.
HookRunOverrides | null
default:"null"
Run-scoped hook overrides (entries to add, hook IDs to disable).
bool
default:"false"
Enable built-in tools (task management, etc.).
bool
default:"false"
Enable shell tool (requires enable_builtins).
bool
default:"false"
Enable semantic memory (memory_search tool + compaction indexing).
bool | null
default:"null"
Override schedule tools for this session. null uses the surface default.
bool | null
default:"null"
Override WorkGraph tools for this session. null uses the surface default.
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.
string | null
default:"null"
Agent name for inter-agent communication.
object | null
default:"null"
Provider-specific parameters (e.g., thinking config, reasoning effort).

Response fields

string
UUID of the created session.
string
The agent’s response text.
u32
Number of LLM calls made.
u32
Number of tool calls executed.
WireUsage
Token usage breakdown.
object | null
Parsed structured output.
array | null
Schema compatibility warnings.

session/history

Read committed transcript history for an existing session.
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. When limit is omitted, the server returns at most 100 messages. The maximum accepted limit is 1,000 and the maximum offset is 1,000,000; larger values fail with INVALID_PARAMS before the session store is queried.

session/rewrite_transcript

Commit a transcript rewrite without changing the session identity. The request names a message-range selection, supplies replacement messages, and records a machine-readable reason. The session appends a rewrite commit, advances its transcript head, and retains the parent revision for audit and restore.
Returns the stable session_id, the parent revision, the new revision, and the updated message count. running_behavior currently supports "reject", which returns SESSION_BUSY if the target session has active work.

session/transcript_revision

Read one retained transcript revision body by revision id.
Use "current" to read the active transcript head, or pass a concrete revision returned by session/rewrite_transcript or session/restore_transcript_revision.

session/transcript_revisions

List retained transcript revision commits (oldest first) together with the current transcript head revision. Each entry records the revision the commit produced, the parent it was applied against, the recorded actor, the rendered rewrite reason, and the commit timestamp.
Sessions without any rewrite commit return an empty list; head_revision still names the active transcript head. Pass a returned revision to session/transcript_revision to read the retained body, or to session/restore_transcript_revision to restore it.

session/restore_transcript_revision

Restore a retained transcript revision as the active transcript head without changing the session identity. Restore is represented as another rewrite commit so the graph remains append-only even though the source transcript projection is updated in place. revision accepts the same selector as session/transcript_revision: "current" resolves to the head revision, so restoring it surfaces the typed no-op rewrite error.

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.
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.
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.
session/list is owner-paginated rather than load-all/truncate: omitted limit defaults to 100, the maximum is 1,000, and offset is capped at 1,000,000. The same 100/1,000 collection limits apply to events/list_since, workgraph/list, workgraph/ready, workgraph/snapshot, and workgraph/events. WorkGraph ready-set and snapshot projections are atomic only within explicit process bounds. A projection that would need to scan more than 1,000 items, 1,000 edges, or 1,000 attention bindings fails closed with INVALID_PARAMS; narrow the realm/namespace/filter and retry. The store does not materialize an oversized graph and truncate it after the fact.

session/read

Get session state.
string
required
Session ID to read.

session/archive

Remove a session from the runtime.
string
required
Session ID to archive.

Turn methods

turn/start

Start a new turn on an existing session.
Returns the same result shape as session/create. Fails with error code -32001 (SESSION_BUSY) if a turn is already in progress.
string
required
Session ID to continue.
string
required
The follow-up prompt.
ContentInput[] | null
default:"null"
Host-attached injected context for this turn. Each entry materializes as a separate typed injected-context user-channel message immediately before the turn’s user message, in order; injected context is excluded from semantic-memory indexing.
string | null
default:"null"
Non-empty, exact request-only host facts for this newly admitted turn; whitespace is significant. Projected before the admitted conversational user message for foreground model calls only; never appended to Session.
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.
string | null
default:"from session"
Provider override (e.g. "anthropic", "openai", "gemini"). Typically inferred from model.
object | null
default:"from session"
Provider-specific parameters (e.g. {"thinking_budget": 10000}). Applied alongside model/provider override.
u32 | null
default:"from session"
Max tokens override.
object[] | null
Tagged structured skill references to resolve and inject for this turn. Each entry uses {"kind":"structured","source_uuid":"...","skill_name":"..."}.

turn/interrupt

Cancel an in-flight turn. No-op if the session is idle.
string
required
Session ID to interrupt.

Event methods

session/external_event

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

session/peer_response_terminal

Admit a correlated terminal peer response through the typed runtime ingress.
string
required
Session ID to admit the peer response to.
string
required
Canonical peer routing ID.
string
Optional presentation label. It is not used as routing identity.
string
required
Peer correlation ID for the request this terminal response completes.
string
required
Terminal response status: "completed", "failed", or "cancelled".
any JSON
required
Peer-returned terminal payload.
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.
string
required
Session ID to query peers for.
Use peer_id as the to value for comms/send; name is display-only and may collide. Each entry uses the canonical typed PeerDirectoryEntry shape: address is {transport, endpoint}, source records discovery provenance, sendable_kinds states supported comms operations, capabilities carries the versioned capability envelope, and meta contains supplementary description and labels.

skills/list

List all skills with provenance information, including active and shadowed entries.
array
List of SkillEntry objects with key, name, description, scope, canonical source provenance, is_active, and optional canonical shadowed_by provenance.
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 plus model-gated still-image input 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 for the full flow.

Opening a live channel

Create a session on a realtime-capable model and call live/open:
live/open returns a LiveOpenResult with transport bootstrap (e.g. WebSocket URL), WireLiveChannelCapabilities, and WireLiveContinuityMode.
string
required
Session whose canonical history seeds the live channel.
provider_managed | explicit_commit | null
default:"null"
Optional input-turning mode. Omitted preserves provider-managed behavior.
websocket | webrtc | null
default:"null"
Optional live transport. Omitted uses the server default.
usize | null
default:"null"
Optional positive serialized-character budget for the core-owned seed message window. Omitted preserves the complete canonical history. When present, core selects a recent whole-turn suffix. Every ordered System message is retained and their combined serialized size must fit; an existing compaction-summary head may also be retained. Any truncation returns degraded continuity. Zero is rejected.
System messages are ordinary ordered transcript data and are retained by the message window rules above. Canonical multimodal sidecars remain outside the message window. Image identity, tombstones, and aggregate accounting stay complete even when older seed messages are omitted.

live/status

Read the current state of a live channel.

live/send_input

Send one typed input chunk. Image input is available only when LiveOpenResult.capabilities.image_in is true; an image is staged as context for the next text, audio, or explicitly committed response. Every image must carry a caller-stable, session-scoped idempotency_key. The JSON-RPC JSONL transport accepts frames up to 64 MiB, excluding the trailing newline. Direct live WebSocket frames are a different input path: they support JSON text chunks and negotiated raw PCM audio only, with a 2 MiB aggregate/per-frame ceiling. Images always use this JSON-RPC method, even when the channel’s observation/audio transport is WebSocket or WebRTC. Server-originated JSON-RPC responses are capped at 32 MiB and queued notifications at 8 MiB. Their byte/count reservations are process-wide and remain owned through the bounded transport write; oversized collection reads must use pagination instead of relying on socket buffering.
The key must be non-empty, no more than 128 UTF-8 bytes, contain no control characters, and have no leading or trailing whitespace. Replay the same key with the same canonical MIME and bytes after a lost receipt: Meerkat does not resend it to the provider and emits the existing durable identity again. Reusing a key for different content is rejected as image_input_idempotency_conflict. If text or audio is already staged, commit it before submitting an image; otherwise the image is rejected as image_input_requires_commit. { "status": "sent" } proves only that the adapter queue accepted the command. Wire validation and queue admission can instead fail immediately with typed LiveSendInputErrorData. A check that fails after acceptance is emitted as a typed command_rejected observation. Only a user_content_committed observation carrying the same idempotency_key proves the provider-acknowledged image reached durable canonical session history. The receipt is redacted: it contains identity, ordering, media type, and the key, never image bytes. Each image is limited to 20 MiB decoded, and canonical live image history is limited to 40 MiB decoded in aggregate, counting repeated references as separate occurrences. live/send_input rejects a new image that would cross the aggregate ceiling as image_input_history_budget_exceeded before provider send or persistence. That keeps every successfully committed live image reopenable. Legacy or out-of-band history already above the ceiling, missing blobs, and content-address mismatches fail live/open rather than trimming, substituting, or changing image context. See the Live Channels guide for retry and transport details.

Live channel lifecycle methods (live/*)

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.
string
required
Session ID to add the server to.
object
required
Typed MCP server configuration. Include name with either stdio fields (command, optional args/env) or HTTP fields (url, optional headers/transport).
bool
default:"false"
Whether to also write the server to disk config.

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.
string
required
Session ID to remove the server from.
string
required
Name of the MCP server to remove.
bool
default:"false"
Whether to also remove from disk config.

mcp/reload

Reload one or all MCP servers for a running session. Useful after config changes.
string
required
Session ID to reload servers for.
string | null
default:"null"
Specific server to reload. If null, reloads all servers.

Models

models/catalog

Return the curated model catalog with provider profiles, capability metadata, and parameter schemas.
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.
string
Contract version for the catalog response.
array
List of provider entries, each containing provider, default_model_id, and models.
object
Model profile with capability flags (supports_temperature, supports_thinking, supports_reasoning) and params_schema.

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.).
Possible status values:

Config methods

config/get

Read current realm config envelope.

config/set

Replace config, optionally using generation CAS.

config/patch

Merge-patch the config (RFC 7396).
config/set also accepts a direct config object as params for compatibility. In that mode, no CAS check is applied.

Notifications

During turn execution, the server emits session/event notifications containing serialized AgentEvent payloads:
Event types match the AgentEvent enum in meerkat-core/src/event.rs (agent_event_type assigns each variant its stable wire name) and are emitted to artifacts/schemas/events.json. New variants must appear in both that enum and this table.

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.

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.
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:

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).
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.
Backpressure: The notification channel is bounded. When the client reads slowly, the agent naturally slows down.

Comparison with other surfaces

See also