Skip to main content
Meerkat ships a REST server for running and managing agent sessions over HTTP. This is the best fit if you want a simple, language-agnostic API for the Meerkat core.

Getting started

1

Build the server

cargo run --package meerkat-rest -- --realm team-alpha
2

Set API keys

Export the key for your chosen provider:
export ANTHROPIC_API_KEY="sk-..."
# or OPENAI_API_KEY, GOOGLE_API_KEY
3

Send a request

curl -X POST http://127.0.0.1:8080/sessions \
  -H "Content-Type: application/json" \
  -d '{"prompt": "Hello, Meerkat!"}'

Runtime scope

rkat-rest accepts:
  • --realm <id>
  • --instance <id>
  • --realm-backend <sqlite|redb|jsonl>
If --realm is omitted, the server creates a new isolated opaque realm (realm-...).

Endpoint overview

MethodPathDescription
POST/sessionsCreate and run a new session
GET/sessionsList sessions
GET/sessions/{id}Fetch session metadata and usage
GET/sessions/{id}/historyFetch committed session transcript messages
POST/sessions/{id}/messagesContinue an existing session
POST/sessions/{id}/external-eventsQueue a runtime-backed external event
POST/sessions/{id}/interruptInterrupt an in-flight turn
DELETE/sessions/{id}Archive (remove) a session
GET/sessions/{id}/eventsSSE stream for real-time updates
POST/sessions/{id}/mcp/addStage live MCP server addition (mcp feature)
POST/sessions/{id}/mcp/removeStage live MCP server removal (mcp feature)
POST/sessions/{id}/mcp/reloadReload MCP server(s) (mcp feature)
POST/comms/sendPush comms message into a session (comms feature)
GET/comms/peersList discoverable peers (comms feature)
GET/mob/prefabsList mob prefab templates (mob feature)
GET/mob/toolsList mob tools (mob feature)
POST/mob/callCall a mob tool (mob feature)
GET/mob/{id}/eventsMob event SSE stream (mob feature)
GET/skillsList skills with provenance
GET/skills/{id}Inspect a skill’s full content
GET/healthLiveness check
GET/models/catalogCurated model catalog with provider profiles
GET/capabilitiesRuntime capabilities
GET/configRead config
PUT/configReplace config
PATCH/configMerge-patch config (RFC 7396)
POST/requests/{request_id}/cancelCancel an uncommitted in-flight request
Mob capability on REST is exposed through the same session endpoints by composing meerkat-mob-mcp (MobMcpState + MobMcpDispatcher) into SessionBuildOptions.external_tools in the host runtime.

Request cancellation

REST request cancellation is opt-in and request-ID based.
  • send X-Meerkat-Request-Id: <id> on POST /sessions or POST /sessions/{id}/messages
  • call POST /requests/{request_id}/cancel to cancel uncommitted in-flight work
  • duplicate in-flight request IDs are rejected
Cancellation only affects uncommitted work:
  • pre-start / pre-commit work may return cancelled
  • committed success is not rewritten to cancellation
  • post-commit create failures still return session identity and remain resumable

Configuration

REST configuration is realm-scoped:
  • macOS: ~/Library/Application Support/meerkat/rest/realms/<realm>/config.toml
  • Linux: ~/.local/share/meerkat/rest/realms/<realm>/config.toml
  • Windows: %APPDATA%\\meerkat\\rest\\realms\\<realm>\\config.toml
Each realm also has realm_manifest.json (backend pinning) and config_state.json (generation CAS state).Key sections:
[rest]
host = "127.0.0.1"
port = 8080

[agent]
model = "claude-opus-4-6"
max_tokens_per_turn = 8192

[tools]
builtins_enabled = false
shell_enabled = false
API keys are provided via environment variables:
  • ANTHROPIC_API_KEY
  • OPENAI_API_KEY
  • GOOGLE_API_KEY

Endpoints

POST /sessions

Create and run a new session.
{
  "prompt": "Your prompt here"
}

Request fields

prompt
string
required
The user prompt to send to the agent.
system_prompt
string | null
default:"null"
Override the system prompt for this session.
model
string | null
default:"config default"
Model name (e.g. "claude-opus-4-6", "gpt-5.2").
provider
string | null
default:"inferred from model"
Provider: "anthropic", "openai", "gemini", "other".
max_tokens
u32 | null
default:"config default"
Max tokens per turn.
output_schema
OutputSchema | null
default:"null"
JSON schema for structured output extraction (wrapper or raw schema).
structured_output_retries
u32 | null
default:"null"
Max retries for structured output validation. null/omitted uses the server default on create and inherits the persisted session value on continue.
verbose
bool
default:"false"
Enable verbose event logging (server-side).
keep_alive
bool | null
default:"null"
Keep session alive after turn for comms. On create, null/omitted uses the create default (false), true enables, and false explicitly disables. Requires comms_name when enabled.
comms_name
string | null
default:"null"
Agent name for inter-agent communication.
peer_meta
object | null
default:"null"
Friendly metadata for peer discovery (name, description, labels).

GET /sessions//history

Read committed transcript history for a session without changing the lightweight metadata shape of GET /sessions/{id}. Query parameters:
  • offset — skip this many messages from the start of the transcript
  • limit — cap the number of returned messages
{
  "session_id": "01936f8a-7b2c-7000-8000-000000000001",
  "message_count": 8,
  "offset": 0,
  "limit": 50,
  "has_more": false,
  "messages": [
    { "role": "system", "content": "You are a helpful assistant." },
    { "role": "user", "content": "Hello" },
    { "role": "assistant", "content": "Hi there." }
  ]
}
History is returned oldest-to-newest and reflects the last committed session snapshot only.
hooks_override
HookRunOverrides | null
default:"null"
Run-scoped hook overrides (see Hooks).
enable_builtins
bool | null
default:"null (factory default)"
Enable built-in tools (task management, etc.). Omit to use factory defaults.
enable_shell
bool | null
default:"null (factory default)"
Enable shell tool (requires enable_builtins). Omit to use factory defaults.
enable_memory
bool | null
default:"null (factory default)"
Enable semantic memory. Omit to use factory defaults.

Response fields

session_id
string
UUID of the created session.
text
string
The agent’s response text.
turns
u32
Number of LLM calls made.
tool_calls
u32
Number of tool calls executed.
usage
WireUsage
Token usage breakdown.
usage.input_tokens
u64
Input tokens consumed.
usage.output_tokens
u64
Output tokens generated.
usage.total_tokens
u64
Total tokens (input + output).
usage.cache_creation_tokens
u64 | null
Cache creation tokens (provider-specific).
usage.cache_read_tokens
u64 | null
Cache read tokens (provider-specific).
structured_output
object | null
Parsed structured output (when output_schema was provided).
schema_warnings
array | null
Schema compatibility warnings per provider.
{
  "prompt": "Extract the capital of France",
  "model": "claude-opus-4-6",
  "output_schema": {
    "schema": {
      "type": "object",
      "properties": {
        "country": {"type": "string"},
        "capital": {"type": "string"}
      },
      "required": ["country", "capital"]
    },
    "name": "capital_extraction",
    "strict": false,
    "compat": "lossy",
    "format": "meerkat_v1"
  },
  "structured_output_retries": 2
}
When output_schema is provided, the text field contains the schema-only JSON string produced by the extraction turn (no extra prose). The structured_output field contains the parsed JSON value for convenience.output_schema may be provided as a wrapper object (shown above) or as a raw JSON Schema object. The wrapper enables explicit compat/format settings.

GET /sessions

List sessions. Supports optional label filters via query parameters.
Response
{
  "sessions": [
    {
      "session_id": "01936f8a-7b2c-7000-8000-000000000001",
      "state": "idle",
      "created_at": "2025-01-15T10:30:00Z"
    }
  ]
}

POST /sessions/{id}/messages

Continue an existing session.
{
  "session_id": "01936f8a-7b2c-7000-8000-000000000001",
  "prompt": "Follow-up message"
}

Request fields

session_id
string
required
Session ID (must match the path {id}).
prompt
string
required
Follow-up prompt.
system_prompt
string | null
default:"null"
Override the system prompt.
model
string | null
default:"from session"
Model override for this turn. On materialized sessions this hot-swaps the LLM client for the remainder of the session.
provider
string | null
default:"from session"
Provider override for this turn. Typically inferred from model. Used with model for mid-session provider switching.
max_tokens
u32 | null
default:"from session"
Max tokens override for this turn.
output_schema
OutputSchema | null
default:"null"
Structured output schema for this turn.
structured_output_retries
u32 | null
default:"null"
Max retries for structured output validation. Omit / null to inherit the persisted session value on continue.
verbose
bool
default:"false"
Enable verbose event logging.
keep_alive
bool | null
default:"null"
Keep-alive override for this turn. null = inherit persisted session intent, true = enable, false = disable.
comms_name
string | null
default:"from session"
Agent name for comms.
hooks_override
HookRunOverrides | null
default:"null"
Run-scoped hook overrides.

Response fields

Same shape as POST /sessions.

POST /sessions/{id}/interrupt

Interrupt an in-flight turn. No-op if the session is idle.
Response
{"interrupted": true}

POST /requests/{request_id}/cancel

Cancel an uncommitted in-flight request that previously supplied X-Meerkat-Request-Id.
Response
{"cancelled": true}
Returns 404 if the session is not found, 409 if the session is not running.

DELETE /sessions/{id}

Archive (remove) a session.
Response
{"archived": true}
Returns 404 if the session is not found.

POST /sessions/{id}/external-events

Queue an external event through the runtime-backed admission path.
curl -X POST http://localhost:8080/sessions/sid_abc/external-events \
  -H "Content-Type: application/json" \
  -d '{"alert": "CPU spike", "host": "web-03"}'
This route keeps the optional RKAT_WEBHOOK_SECRET header auth used for webhook delivery, but the admitted event now flows through runtime input acceptance instead of a surface-local injector path.
(body)
any JSON
required
Any JSON payload. Pretty-printed and injected as an event into the agent’s inbox.
X-Webhook-Secret
string
Webhook secret for authentication. Required when RKAT_WEBHOOK_SECRET env var is set on the server. Compared using constant-time equality (subtle::ConstantTimeEq).
HTTP StatusWhen
202Event queued successfully
404Session not found
401Missing or invalid webhook secret
503Event inbox is full

GET /sessions/{id}

Fetch session metadata and usage.
Response
{
  "session_id": "01936f8a-7b2c-7000-8000-000000000001",
  "created_at": "2025-01-15T10:30:00Z",
  "updated_at": "2025-01-15T10:31:00Z",
  "message_count": 4,
  "total_tokens": 500
}

GET /sessions/{id}/events

Server-Sent Events (SSE) stream for real-time updates. Event types:
EventDescription
session_loadedEmitted on connect with session metadata
run_startedAgent execution began
run_completedAgent run finished
run_failedAgent run failed
turn_startedNew LLM call within the turn
text_deltaStreaming text chunk from LLM
text_completeFull text for this turn
tool_call_requestedLLM wants to call a tool
tool_result_receivedTool result processed
turn_completedLLM call finished
tool_execution_startedTool dispatch began
tool_execution_completedTool returned a result
tool_execution_timed_outTool exceeded timeout
compaction_startedContext compaction began
compaction_completedCompaction finished
compaction_failedCompaction failed
budget_warningApproaching resource limits
retryingRetrying after transient error
hook_startedHook execution began
hook_completedHook finished
hook_failedHook execution failed
hook_deniedHook blocked operation
skills_resolvedSkills loaded for turn
skill_resolution_failedSkill resolution failed
doneEmitted when the broadcast channel closes

GET /skills

List all skills with provenance information. Returns active and shadowed entries.
Response
{
  "skills": [
    {
      "id": "task-workflow",
      "name": "Task Workflow",
      "description": "How to use task tools",
      "scope": "builtin",
      "source": "embedded",
      "is_active": true
    },
    {
      "id": "task-workflow",
      "name": "Custom Task Workflow",
      "description": "Override tasks",
      "scope": "project",
      "source": "company",
      "is_active": false,
      "shadowed_by": "embedded"
    }
  ]
}
Returns 404 if skills are not enabled.

GET /skills/{id}

Inspect a skill’s full content by its ID.
Response
{
  "id": "task-workflow",
  "name": "Task Workflow",
  "description": "How to use task tools",
  "scope": "builtin",
  "source": "embedded",
  "body": "# Task Workflow\n\nUse task_create to..."
}
Returns 404 if the skill is not found or skills are not enabled.

GET /health

Returns "ok" (HTTP 200). Use for liveness checks.

GET /models/catalog

Return the curated model catalog with provider profiles, capability metadata, and parameter schemas.
Response (abbreviated)
{
  "contract_version": "0.5.0",
  "providers": [
    {
      "provider": "anthropic",
      "default_model_id": "claude-sonnet-4-5",
      "models": [
        {
          "id": "claude-sonnet-4-5",
          "display_name": "Claude Sonnet 4.5",
          "tier": "standard",
          "context_window": 200000,
          "max_output_tokens": 64000,
          "profile": {
            "model_family": "claude",
            "supports_temperature": true,
            "supports_thinking": true,
            "supports_reasoning": false,
            "params_schema": {}
          }
        }
      ]
    }
  ]
}
The catalog is compiled from meerkat-models at build time. Each provider entry includes the default model and a list of models with their capabilities and parameter schemas.

GET /capabilities

Returns runtime capabilities with status resolved against config.
Response (abbreviated)
{
  "contract_version": {"major": 0, "minor": 4, "patch": 1},
  "capabilities": [
    {
      "id": "sessions",
      "description": "Session lifecycle management",
      "status": "Available"
    },
    {
      "id": "shell",
      "description": "Shell command execution",
      "status": {"DisabledByPolicy": {"description": "Disabled by config"}}
    }
  ]
}
The full response includes all capabilities: sessions, streaming, structured_output, hooks, builtins, shell, comms, memory_store, session_store, session_compaction, skills, mcp_live. See the RPC capabilities endpoint for the complete example.

GET /config

Returns a realm config envelope:
  • config
  • generation
  • realm_id
  • instance_id
  • backend
  • resolved_paths

PUT /config

Replaces config. Accepted request forms:
  • Direct config object (compat)
  • Wrapped form with CAS:
    • { "config": <Config>, "expected_generation": <u64|null> }

PATCH /config

Applies RFC 7396 merge patch. Accepted request forms:
  • Direct patch object (compat)
  • Wrapped form with CAS:
    • { "patch": <JSON>, "expected_generation": <u64|null> }
If expected_generation is stale, the server returns 400 with a generation-conflict message.

POST /comms/send

Push a canonical comms command into a running session. Requires the comms feature.
{
  "session_id": "01936f8a-7b2c-7000-8000-000000000001",
  "kind": "peer_message",
  "to": "reviewer",
  "body": "Please review the latest diff",
  "source": "ci-pipeline"
}
session_id
string
required
Session ID to dispatch the comms command to.
kind
string
required
Command kind: "peer_message", "peer_request", or "peer_response".
to
string | null
Peer name to send to (required for peer_message and peer_request).
body
string | null
Message body (required for peer_message).
intent
string | null
Request intent (required for peer_request, e.g. "review", "delegate").
params
object | null
Request parameters (optional for peer_request).
in_reply_to
string | null
ID of the request being responded to (required for peer_response).
status
string | null
Response status: "accepted", "completed", or "failed" (for peer_response).
result
object | null
Response result data (optional for peer_response).
source
string | null
Optional source label.
stream
string | null
Optional stream identifier.
allow_self_session
bool | null
Allow the session to send a message to itself (default: false).
Unlike POST /sessions/{id}/external-events (which uses RKAT_WEBHOOK_SECRET header auth), /comms/send has no authentication. It is intended for trusted internal callers that already know the session ID.

GET /comms/peers

List discoverable peers for a session. Requires the comms feature.
session_id
string
required
Session ID to query peers for.
Response
{
  "peers": [
    {"name": "reviewer", "address": "127.0.0.1:4201"},
    {"name": "coordinator", "address": "inproc"}
  ]
}

Error responses

All errors are returned as JSON with an HTTP status code:
{
  "error": "Human-readable error message",
  "code": "ERROR_CODE"
}
HTTP StatusCodeWhen
400BAD_REQUESTInvalid parameters, session ID mismatch, keep_alive without comms
403HOOK_DENIEDHook blocked the operation
404SESSION_NOT_FOUNDSession does not exist
404SKILL_NOT_FOUNDRequested skill does not exist
409SESSION_BUSYTurn already in progress
409SESSION_NOT_RUNNINGSession not in running state
422SKILL_RESOLUTION_FAILEDSkill resolution error
429BUDGET_EXHAUSTEDResource limits reached
500AGENT_ERRORLLM provider error, tool dispatch failure, agent loop error
500INTERNAL_ERRORStore initialization failure, unexpected server error
501CAPABILITY_UNAVAILABLERequired capability not compiled in or disabled
502PROVIDER_ERRORLLM provider issue (missing key, auth failure)
{
  "error": "Session not found: 01936f8a-7b2c-7000-8000-000000000099",
  "code": "SESSION_NOT_FOUND"
}

Notes

Keep-alive mode requires keep_alive: true and a comms_name. If keep_alive is requested but the binary was not compiled with comms support, the server returns a BAD_REQUEST error.
POST /sessions/{id}/external-events queues a runtime-backed external event. It is a queue-only admission path, not a second direct execution loop.
hooks_override allows per-request hook overrides including adding extra hook entries and disabling specific hooks by ID. See Hooks for the HookRunOverrides schema.