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

# Capability matrix

> Build profiles, feature gates, error codes, and capability behavior across Meerkat configurations.

Meerkat is modular. The `meerkat` facade defaults to the three provider
features (`anthropic`, `openai`, `gemini`); storage, comms, MCP, skills,
schedule, memory, and live-channel orchestration are opt-in. This matrix shows
what works in common build profiles.

## Build profiles

| Profile              | Cargo Features                     | Use Case                                                                                   |
| -------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------ |
| **Minimal**          | `--no-default-features`            | Ephemeral agent, no persistence, no compaction                                             |
| **Provider default** | default features                   | Anthropic + OpenAI + Gemini providers, no persistence or optional subsystems               |
| **Persistent**       | `session-store`                    | Sessions survive restart with the realm-pinned backend (`sqlite` by default when compiled) |
| **Compacting**       | `session-compaction`               | Auto-compact long conversations                                                            |
| **Full**             | `session-store,session-compaction` | Persistent + compacting                                                                    |
| **Live-capable**     | `openai-realtime,live`             | Caller-initiated `live/*` channels for realtime-capable sessions                           |

## Capability behavior

| Operation                | Minimal             | Persistent                         | Compacting            | Full                    |
| ------------------------ | ------------------- | ---------------------------------- | --------------------- | ----------------------- |
| `create_session`         | Works               | Works                              | Works                 | Works                   |
| `start_turn`             | Works               | Works + snapshot saved             | Works                 | Works + snapshot saved  |
| `interrupt`              | Works               | Works                              | Works                 | Works                   |
| `read` (running session) | Works               | Works                              | Works                 | Works                   |
| `read` (archived)        | `SESSION_NOT_FOUND` | Falls back to store                | `SESSION_NOT_FOUND`   | Falls back to store     |
| `list`                   | Live sessions only  | Live + stored sessions             | Live sessions only    | Live + stored sessions  |
| `archive`                | Removes from memory | Removes from memory, snapshot kept | Removes from memory   | Removes + snapshot kept |
| Auto-compaction          | No-op               | No-op                              | Triggers at threshold | Triggers at threshold   |

## Error codes

Two layers matter here:

* **Session transport mapping**: how `SessionError` is projected by session-facing transports
* **Canonical wire `ErrorCode` mapping**: the higher-level `meerkat-contracts` envelope model used across public protocol surfaces

The table below is the current **session transport** mapping:

| `SessionError`        | Code                           | JSON-RPC | REST | MCP        | CLI    |
| --------------------- | ------------------------------ | -------- | ---- | ---------- | ------ |
| `NotFound`            | `SESSION_NOT_FOUND`            | -32001   | 404  | tool error | exit 1 |
| `Busy`                | `SESSION_BUSY`                 | -32002   | 409  | tool error | exit 1 |
| `PersistenceDisabled` | `SESSION_PERSISTENCE_DISABLED` | -32003   | 501  | tool error | exit 0 |
| `CompactionDisabled`  | `SESSION_COMPACTION_DISABLED`  | -32004   | 501  | tool error | exit 0 |
| `NotRunning`          | `SESSION_NOT_RUNNING`          | -32005   | 409  | tool error | exit 1 |
| `Store`               | `SESSION_STORE_ERROR`          | -32006   | 500  | tool error | exit 1 |
| `Unsupported`         | `SESSION_UNSUPPORTED`          | -32007   | 501  | tool error | exit 1 |
| `Agent`               | `AGENT_ERROR`                  | -32000   | 500  | tool error | exit 1 |

### Transport error mapping summary

* **JSON-RPC**: Custom numeric transport codes in the -32000..-32099 range.
* **REST**: HTTP status codes (404, 409, 500, 501). Error body includes `{ "code": "...", "message": "..." }`.
* **MCP Server**: Tool calls return `is_error: true` with the error message and code in the content.
* **CLI**: `SessionError` transport projection is intentionally coarse: most failures collapse to exit 1, while persistence/compaction-disabled remain informational (exit 0). Error details are printed to stderr.

## Pick only what you need

```toml theme={null}
# Minimal: just the agent loop
[dependencies]
meerkat = { version = "0.6.6", default-features = false, features = ["anthropic"] }

# Add persistence
meerkat = { version = "0.6.6", default-features = false, features = ["anthropic", "session-store"] }

# Add compaction
meerkat = { version = "0.6.6", default-features = false, features = ["anthropic", "session-store", "session-compaction"] }

# Add scheduling
meerkat = { version = "0.6.6", default-features = false, features = ["anthropic", "session-store", "schedule"] }

# Kitchen sink
meerkat = { version = "0.6.6", features = [
  "all-providers", "openai-realtime", "session-store", "session-compaction",
  "memory-store-session", "comms", "mcp", "skills", "schedule", "workgraph", "live"
] }
```

## Runtime Capabilities

`GET /capabilities` and `capabilities/get` report compiled and policy-enabled
runtime capabilities. Tool-family companion skills use the same capability
tokens for gating.

| Capability     | Config gate                                  | Notes                                     |
| -------------- | -------------------------------------------- | ----------------------------------------- |
| `builtins`     | `tools.builtins_enabled`                     | Builtin tasks and utility tools           |
| `shell`        | `tools.shell_enabled`                        | Shell and shell job tools                 |
| `schedule`     | `tools.schedule_enabled`                     | Durable schedules and occurrence delivery |
| `work_graph`   | `tools.workgraph_enabled`                    | WorkGraph tools and companion workflow    |
| `memory_store` | feature/runtime store                        | Semantic memory retrieval                 |
| `skills`       | `skills.enabled` and compiled skills support | Skill discovery and loading               |

WorkGraph's companion skill uses the same canonical capability token as public
capability reports: `work_graph`.

Optional capability bundles are declared by the owning feature crate.
`meerkat-capabilities` supplies the typed vocabulary and collection seam;
contracts and facades may project those declarations into `CapabilityStatus`,
but they must not invent feature policy or translate capability aliases. For
example, `meerkat-schedule` owns the Schedule policy declaration,
`meerkat-workgraph` owns the WorkGraph policy declaration and registration, and
`meerkat-skills` owns the Skills policy declaration.

## Model capabilities

`ModelProfile` describes per-model capabilities used for feature gating and tool visibility. Two fields are relevant to multimodal content support:

| Capability            | Description                                             | Anthropic | OpenAI                                                  | Gemini                 |
| --------------------- | ------------------------------------------------------- | --------- | ------------------------------------------------------- | ---------------------- |
| `vision`              | Model can process image content in user messages        | Yes       | Yes                                                     | Yes                    |
| `image_tool_results`  | Model can process image content in tool results         | Yes       | No                                                      | Yes                    |
| `inline_video`        | Model can process inline video content in user messages | No        | No                                                      | Yes                    |
| `realtime`            | Model can back a caller-initiated live channel          | No        | `gpt-realtime-2` only                                   | No current catalog row |
| `supports_web_search` | Model supports provider-native web search tools         | Yes       | Yes for chat catalog rows; not `gpt-realtime-2` / Codex | Yes                    |

These fields are set in `meerkat-core/src/model_profile/**` (`meerkat-models` is a compatibility shim) and exposed via `ModelProfile`. They control:

* **`view_image` tool visibility** -- currently hidden via `ToolScope` when `image_tool_results` is `false`.
* **`ContentBlock::Image` in tool results** -- providers that do not support `image_tool_results` will receive image content blocks converted to text descriptions.
* **`ContentBlock::Image` in user messages** -- providers that do not support `vision` will not receive image content blocks in user messages.
* **Provider-native web search** -- injected by default when `supports_web_search` is `true` and the corresponding `provider_tools.*` config is enabled.

Image generation uses provider-owned image profiles rather than `ModelProfile` chat-model flags. OpenAI and Gemini provider crates advertise image targets, supported backend plans, dimensions/aspect ratios, and provider-specific `provider_params`; the runtime exposes one model-facing `generate_image` tool and commits generated outputs as blob-backed assistant image blocks.

## Provider-native web search

Web search is enabled by default for all catalog models with `supports_web_search: true`. The factory resolves provider-specific tool config at build time and injects it as non-persisted `AgentConfig.provider_tool_defaults`, merged per-turn with explicit `provider_params` via RFC 7396 merge-patch.

| Provider    | Tool type             | Config key                                  | Default |
| ----------- | --------------------- | ------------------------------------------- | ------- |
| Anthropic   | `web_search_20250305` | `provider_tools.anthropic.web_search`       | `true`  |
| OpenAI      | `web_search`          | `provider_tools.openai.web_search`          | `true`  |
| Gemini      | `google_search`       | `provider_tools.gemini.google_search`       | `true`  |
| Self-hosted | Not wired (v1)        | `SelfHostedModelConfig.supports_web_search` | `false` |

**Opt-out:**

* Config-level: set the matching config key from the table above to `false`
* CLI: `rkat run --no-web-search "..."`.
* Per-request Anthropic/OpenAI: `provider_params: {"web_search": null}` (RFC 7396 null removal)
* Per-request Gemini: `provider_params: {"google_search": null}` (RFC 7396 null removal)
* Per-request boolean: `false` for the provider-native search key is treated as disable

**Resume behavior:** Tool defaults are re-derived on every build (including resume) from current config and model profile. Config changes take effect immediately on resumed sessions. Explicit `provider_params` overrides are persisted in `SessionMetadata`.

**Extraction turns:** Web search tools are stripped during structured output extraction to maintain the deterministic, tool-free invariant.

**Hook interaction:** Pre-LLM hooks see the merged tool defaults in `HookLlmRequest.provider_params` as an observational projection. Hooks cannot rewrite effective provider params.

## MCP server loading

MCP server connections are **non-blocking across all surfaces**. Servers connect in parallel in the background after `apply_staged()`. The agent loop polls `poll_external_updates()` at each `CallingLlm` boundary and injects a `[MCP_PENDING]` system notice while servers are still connecting. Tools become visible as each server completes its handshake.

* Per-server timeout: `connect_timeout_secs` in MCP config (default: 10s)
* CLI: `--wait-for-mcp` blocks before the first turn until all servers finish connecting
* SDK: `McpRouterAdapter::wait_until_ready(timeout)` provides the same blocking behavior programmatically

## Compaction behavior

When `session-compaction` is enabled:

* **Trigger**: `last_input_tokens >= threshold` OR `estimated_history_tokens >= threshold`
* **Guards**: Never on first turn. Minimum 3 turns between compactions.
* **Failure**: Non-fatal. `CompactionFailed` event emitted, agent continues with uncompacted history.
* **Budget**: Compaction LLM call draws from the same token budget as regular turns.

Events emitted: `CompactionStarted`, `CompactionCompleted`, `CompactionFailed`.

## Concurrency

* At most one turn runs per session at a time.
* Second `start_turn` while one is in-flight returns `SESSION_BUSY`.
* `interrupt()` cancels the in-flight turn.
* `read()` and `list()` are non-blocking.
* No queueing: callers retry on `Busy`.

## Durability

* **Ephemeral**: Process death loses all state.
* **Persistent**: Snapshot saved after turn completion according to the pinned realm backend.
* Backends are pinned per realm (`realm_manifest.json`) and shared across surfaces only when `realm_id` matches.
* SQLite-backed realms are the standard same-realm multi-process mode.

## See also

* [Session contracts](/reference/session-contracts) -- concurrency, durability, and compaction semantics
* [Architecture](/reference/architecture) -- crate structure and agent loop details
