--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_aliveis runtime/session behaviorsession/external_eventqueues runtime-backed external work- committed success is not rewritten to cancellation
Getting started
1
Start the server
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 inartifacts/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.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,resultorerror - Notification (server -> client): has
method,params, noid
Full lifecycle diagram
Full lifecycle diagram
Session methods
initialize
Handshake. Returns server capabilities.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. Setinitial_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.
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.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.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."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.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 beforemessage_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 throughmessage_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.
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.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.
-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 configuredTrustedPeers and active in-process registrations. Requires the comms feature.
string
required
Session ID to query peers for.
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.-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 likegpt-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 calllive/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.
live/status
Read the current state of a live channel.live/send_input
Send one typed input chunk. Image input is available only whenLiveOpenResult.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.
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.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.).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 emitssession/event notifications containing serialized AgentEvent payloads:
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 (viasession/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 (viamob/stream_open), the server emits mob/stream_event notifications. Requires the mob feature.
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).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.
