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

# MCP

> Use Meerkat as an MCP client to call external tools, or expose Meerkat as MCP tools for other clients.

Model Context Protocol (MCP) is the tool protocol Meerkat uses in two ways: as a **client** calling external MCP servers for tools, and as a **server** exposing itself as MCP tools for other clients.

## Getting started as a client

<Steps>
  <Step title="Add an MCP server">
    ```bash theme={null}
    rkat mcp add filesystem -- npx -y @modelcontextprotocol/server-filesystem /tmp
    ```
  </Step>

  <Step title="Verify it's registered">
    ```bash theme={null}
    rkat mcp list
    ```
  </Step>

  <Step title="Use it in a session">
    The agent will automatically discover and use tools from registered MCP servers.
  </Step>
</Steps>

## Tool overview (server mode)

The `meerkat-mcp-server` crate exposes Meerkat as MCP tools that other clients can call.

`meerkat_run` and `meerkat_resume` keep the same public MCP tool contract, but
their execution path is runtime-backed: `meerkat_run` allocates a session
through the shared session service and waits for that admitted turn to finish,
and `meerkat_resume` re-enters the same `session_id` instead of starting a
second surface-local execution loop.

| Tool                                | Description                                                                        |
| ----------------------------------- | ---------------------------------------------------------------------------------- |
| `meerkat_run`                       | Start a new agent session                                                          |
| `meerkat_resume`                    | Continue a session or provide tool results                                         |
| `meerkat_help`                      | Ask Meerkat usage help with the embedded platform skill                            |
| `meerkat_read`                      | Read a session's state                                                             |
| `meerkat_history`                   | Read a session's committed history                                                 |
| `meerkat_sessions`                  | List sessions in the active realm                                                  |
| `meerkat_blob_get`                  | Fetch a blob payload by id                                                         |
| `meerkat_interrupt`                 | Interrupt an in-flight turn                                                        |
| `meerkat_archive`                   | Archive (remove) a session                                                         |
| `meerkat_config`                    | Get or update server config                                                        |
| `meerkat_capabilities`              | Query runtime capabilities                                                         |
| `meerkat_models_catalog`            | List available models with provider profiles and capabilities                      |
| `meerkat_skills`                    | List or inspect available skills                                                   |
| `meerkat_schedule_*` tools          | Agent-facing schedule create/get/list/update/pause/resume/delete/occurrences tools |
| `meerkat_mcp_add`                   | Stage a live MCP server addition on an active session                              |
| `meerkat_mcp_remove`                | Stage a live MCP server removal on an active session                               |
| `meerkat_mcp_reload`                | Stage a live MCP server reload on an active session                                |
| `meerkat_event_stream_open`         | Open a session-level agent event stream                                            |
| `meerkat_event_stream_read`         | Read the next item from an open event stream                                       |
| `meerkat_event_stream_close`        | Close a previously opened event stream                                             |
| `meerkat_mob_create`                | Create a mob from a typed public definition (mob feature)                          |
| `meerkat_mob_list`                  | List active mobs (mob feature)                                                     |
| `meerkat_mob_status`                | Get lifecycle status for one mob (mob feature)                                     |
| `meerkat_mob_lifecycle`             | Apply a lifecycle action to a mob (mob feature)                                    |
| `meerkat_mob_spawn`                 | Spawn a member into a mob (mob feature)                                            |
| `meerkat_mob_spawn_many`            | Spawn multiple members into a mob (mob feature)                                    |
| `meerkat_mob_retire`                | Retire a mob member (mob feature)                                                  |
| `meerkat_mob_respawn`               | Respawn a mob member with topology restore (mob feature)                           |
| `meerkat_mob_wire`                  | Wire two mob members or an external peer (mob feature)                             |
| `meerkat_mob_unwire`                | Remove mob comms wiring (mob feature)                                              |
| `meerkat_mob_member_send`           | Deliver host-owned work to a specific mob member (mob feature)                     |
| `meerkat_mob_append_system_context` | Stage system context for a member session (mob feature)                            |
| `meerkat_mob_events`                | Read mob event history (mob feature)                                               |
| `meerkat_mob_flows`                 | List flows defined for a mob (mob feature)                                         |
| `meerkat_mob_flow_run`              | Start a mob flow run (mob feature)                                                 |
| `meerkat_mob_flow_status`           | Get flow run status (mob feature)                                                  |
| `meerkat_mob_flow_cancel`           | Cancel a flow run (mob feature)                                                    |
| `meerkat_mob_force_cancel`          | Force-cancel a member's active work (mob feature)                                  |
| `meerkat_mob_wait_kickoff`          | Wait for autonomous kickoff completion (mob feature)                               |
| `meerkat_mob_wait_ready`            | Wait for mob startup readiness (mob feature)                                       |
| `meerkat_mob_profile_*` tools       | Realm-scoped mob profile create/get/list/update/delete (mob feature)               |
| `meerkat_mob_event_stream_open`     | Open a mob-level event stream (mob feature)                                        |
| `meerkat_mob_event_stream_read`     | Read the next event from an open mob stream (mob feature)                          |
| `meerkat_mob_event_stream_close`    | Close a previously opened mob event stream (mob feature)                           |
| `meerkat_comms_send`                | Send a canonical comms command to a session (comms feature)                        |
| `meerkat_comms_peers`               | List peers visible to a session (comms feature)                                    |

<Note>
  Default `rkat-mcp` builds are driven by the crate feature set. The base/default
  tool surface includes core session/config/history/blob/skills tools and
  schedule tools. Mob tools appear when the `mob` feature is compiled in.
</Note>

<Note>
  When the `mob` feature is enabled, the public MCP host surface uses typed
  `meerkat_mob_*` control-plane tools. Inside running sessions, mob capability is
  still agent-side and comes from composing a mob tools factory into
  `SessionBuildOptions.mob_tools`, which provides `mob_*` tools to the
  model. Public host MCP surfaces should not re-export that raw dispatcher.
</Note>

## Use MCP servers as tools (client)

### CLI configuration (recommended)

Use the `rkat mcp` commands to manage MCP servers:

```bash theme={null}
# Add a stdio server
rkat mcp add filesystem -- npx -y @modelcontextprotocol/server-filesystem /tmp

# Add a streamable HTTP server
rkat mcp add api --transport http --url https://mcp.example.com/api

# List servers
rkat mcp list

# Remove a server
rkat mcp remove filesystem
```

### Config file format

MCP servers are stored in TOML in two scopes:

* **Project**: `.rkat/mcp.toml`
* **User**: `~/.rkat/mcp.toml`

Project config wins on name collisions.

<Accordion title="Config file examples">
  ```toml theme={null}
  # Stdio server (local process)
  [[servers]]
  name = "filesystem"
  command = "npx"
  args = ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/allowed/dir"]

  # Stdio server with env
  [[servers]]
  name = "database"
  command = "python"
  args = ["-m", "db_mcp_server"]
  env = { DATABASE_URL = "${DATABASE_URL}" }

  # HTTP server (streamable HTTP is default for URLs)
  [[servers]]
  name = "remote-api"
  url = "https://mcp.example.com/api"
  headers = { Authorization = "Bearer ${MCP_API_TOKEN}" }

  # Legacy SSE server
  [[servers]]
  name = "legacy-sse"
  url = "https://old.example.com/sse"
  transport = "sse"
  ```
</Accordion>

<Note>
  Supported transports: **stdio**, **streamable HTTP** (default for URLs), and **SSE**.
  Environment variables in config use `${VAR_NAME}` syntax.
</Note>

## Meerkat as an MCP server

### Connecting other clients

You can connect any MCP-capable client to a Meerkat MCP server. Run a server process that exposes the `meerkat_*` tools (stdio or HTTP), then point the client at it.

<Tabs>
  <Tab title="Claude Code">
    Claude Code reads MCP servers from a `.mcp.json` file in your project root.
    Example stdio config:

    ```json theme={null}
    {
      "mcpServers": {
        "meerkat": {
          "command": "rkat-mcp",
          "args": [],
          "env": {
            "ANTHROPIC_API_KEY": "your-key"
          }
        }
      }
    }
    ```
  </Tab>

  <Tab title="Codex CLI">
    Codex CLI supports MCP servers via a command and also via config. Example:

    ```bash theme={null}
    codex mcp add meerkat --env ANTHROPIC_API_KEY=your-key -- rkat-mcp
    ```

    You can also configure MCP servers in `~/.codex/config.toml` under
    `[mcp_servers.<name>]`.
  </Tab>

  <Tab title="Gemini CLI">
    Gemini CLI can add MCP servers via CLI or config. Example:

    ```bash theme={null}
    # HTTP/streamable HTTP
    gemini mcp add --scope=project --transport=http meerkat https://your-mcp-server.example.com
    ```

    Or configure in `.gemini/settings.json` (project-scoped) with an `mcpServers`
    map that points to your server URL.
  </Tab>
</Tabs>

### Hosting the MCP server

`meerkat-mcp-server` is a library crate that provides tool schemas and handlers.
If you need a full public MCP host, embed its `tools_list()` and
`handle_tools_call*` entrypoints or run the bundled `rkat-mcp` binary. If you
need a mob-only MCP host, `meerkat-mob-mcp` exposes `public_tools_list()` and
`handle_public_tools_call()` for the typed `meerkat_mob_*` control plane.

`meerkat-mob-mcp::tools_list()` and `handle_tools_call()` remain the internal
agent-side `mob_*` dispatcher helpers; they are not the public host contract.

The bundled `rkat-mcp` binary supports realm scope flags:

```bash theme={null}
rkat-mcp --realm team-alpha --instance worker-1 --realm-backend sqlite
```

If `--realm` is omitted, the server creates a new isolated realm by default.
`--realm-backend` is a creation hint only; after the first open, the manifest-pinned backend is authoritative.

## Tool reference

### meerkat\_run

Start a new Meerkat agent session with the given prompt.

<CodeGroup>
  ```json Request (minimal) theme={null}
  {
    "prompt": "Write a haiku about Rust"
  }
  ```

  ```json Request (full) theme={null}
  {
    "prompt": "Analyze this code for bugs",
    "system_prompt": "You are a senior code reviewer.",
    "model": "claude-opus-4-6",
    "max_tokens": 4096,
    "provider": "anthropic",
    "output_schema": {
      "type": "object",
      "properties": {
        "bugs": {"type": "array", "items": {"type": "string"}},
        "severity": {"type": "string"}
      },
      "required": ["bugs", "severity"]
    },
    "structured_output_retries": 2,
    "stream": false,
    "verbose": false,
    "tools": [
      {
        "name": "read_file",
        "description": "Read a file from disk",
        "input_schema": {
          "type": "object",
          "properties": {"path": {"type": "string"}},
          "required": ["path"]
        },
        "handler": "callback"
      }
    ],
    "enable_builtins": false,
    "builtin_config": {
      "enable_shell": false,
      "shell_timeout_secs": 30
    },
    "keep_alive": null,
    "comms_name": null,
    "hooks_override": null
  }
  ```

  ```json Response (success) theme={null}
  {
    "content": [{
      "type": "text",
      "text": "{\"content\":[{\"type\":\"text\",\"text\":\"Agent response here\"}],\"session_id\":\"01936f8a-...\",\"turns\":1,\"tool_calls\":0,\"structured_output\":null,\"schema_warnings\":null}"
    }]
  }
  ```
</CodeGroup>

#### Parameter reference

<ParamField body="prompt" type="string" required>
  User prompt for the agent.
</ParamField>

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

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

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

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

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

<ParamField body="structured_output_retries" type="u32 | null" default="null">
  Max retries for structured output validation. Omit / `null` to use the product default on run or inherit the persisted session value on resume.
</ParamField>

<ParamField body="stream" type="bool" default="false">
  Stream agent events via MCP notifications.
</ParamField>

<ParamField body="verbose" type="bool" default="false">
  Enable verbose event logging (server-side).
</ParamField>

<ParamField body="tools" type="array" default="[]">
  Tool definitions for the agent (see [McpToolDef schema](#mcptooldef-schema) below).
</ParamField>

<ParamField body="enable_builtins" type="bool | null" default="null">
  Builtins override. Omit / `null` to use the default on run or inherit the persisted session value on resume.
</ParamField>

<ParamField body="builtin_config" type="object | null" default="null">
  Config for builtins (only used when `enable_builtins` is true).
</ParamField>

<ParamField body="builtin_config.enable_shell" type="bool | null" default="null">
  Shell-tools override. Omit / `null` to use the default on run or inherit the persisted session value on resume.
</ParamField>

<ParamField body="builtin_config.shell_timeout_secs" type="u64 | null" default="30">
  Default shell command timeout.
</ParamField>

<ParamField body="keep_alive" type="bool | null" default="null">
  Keep session alive after turn for comms. On `meerkat_run`, `null`/omitted uses the run default (`false`), `true` enables, and `false` explicitly disables. Requires `comms_name` when enabled.
</ParamField>

<ParamField body="comms_name" type="string | null" default="null">
  Agent name for comms.
</ParamField>

<ParamField body="hooks_override" type="HookRunOverrides | null" default="null">
  Run-scoped hook overrides.
</ParamField>

#### McpToolDef schema

<ParamField body="name" type="string" required>
  Tool name.
</ParamField>

<ParamField body="description" type="string" required>
  Tool description.
</ParamField>

<ParamField body="input_schema" type="object" required>
  JSON Schema for tool input.
</ParamField>

<ParamField body="handler" type="string | null" default="callback">
  Handler type (`"callback"` = result provided via `meerkat_resume`).
</ParamField>

When tools with `handler: "callback"` are provided and the agent requests a tool call, the response includes `pending_tool_calls`. The MCP client must execute the tool and provide results via `meerkat_resume`.

### meerkat\_resume

Resume an existing runtime-backed session or provide tool results for pending
tool calls. The call waits for the resumed turn to complete and returns the same
`session_id` that `meerkat_run` originally materialized.

<CodeGroup>
  ```json Request (minimal) theme={null}
  {
    "session_id": "01936f8a-7b2c-7000-8000-000000000001",
    "prompt": "Continue with this"
  }
  ```

  ```json Request (with tool results) theme={null}
  {
    "session_id": "01936f8a-7b2c-7000-8000-000000000001",
    "prompt": "",
    "stream": false,
    "verbose": false,
    "tools": [],
    "tool_results": [
      {
        "tool_use_id": "tc_abc123",
        "content": "File contents here...",
        "is_error": false
      }
    ],
    "enable_builtins": false,
    "builtin_config": null,
    "keep_alive": null,
    "comms_name": null,
    "model": null,
    "max_tokens": null,
    "provider": null,
    "hooks_override": null
  }
  ```

  ```json Response (pending tool call) theme={null}
  {
    "content": [{
      "type": "text",
      "text": "{\"content\":[{\"type\":\"text\",\"text\":\"Agent is waiting for tool results\"}],\"session_id\":\"01936f8a-...\",\"status\":\"pending_tool_call\",\"pending_tool_calls\":[{\"tool_name\":\"read_file\",\"args\":{\"path\":\"/tmp/data.txt\"}}]}"
    }]
  }
  ```
</CodeGroup>

#### Parameter reference

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

<ParamField body="prompt" type="string" required>
  Follow-up prompt (can be empty when providing tool results).
</ParamField>

<ParamField body="stream" type="bool" default="false">
  Stream agent events via MCP notifications.
</ParamField>

<ParamField body="verbose" type="bool" default="false">
  Enable verbose event logging.
</ParamField>

<ParamField body="tools" type="array" default="[]">
  Tool definitions (should match the original run).
</ParamField>

<ParamField body="tool_results" type="array" default="[]">
  Tool results for pending tool calls.
</ParamField>

<ParamField body="tool_results[].tool_use_id" type="string" required>
  ID of the tool call.
</ParamField>

<ParamField body="tool_results[].content" type="string" required>
  Result content (or error message).
</ParamField>

<ParamField body="tool_results[].is_error" type="bool" default="false">
  Whether this is an error result.
</ParamField>

<ParamField body="enable_builtins" type="bool | null" default="null">
  Builtins override. Omit / `null` to inherit the persisted session value.
</ParamField>

<ParamField body="builtin_config" type="object | null" default="null">
  Builtin tool config.
</ParamField>

<ParamField body="builtin_config.enable_shell" type="bool | null" default="null">
  Shell-tools override. Omit / `null` to inherit the persisted session value.
</ParamField>

<ParamField body="keep_alive" type="bool | null" default="null">
  Keep-alive override for this resume. `null` = inherit persisted session intent, `true` = enable, `false` = disable.
</ParamField>

<Note>
  `meerkat_run` and `meerkat_resume` follow the same commit/cancel rule as the other interactive surfaces: committed success is not rewritten to cancellation, and post-commit create failure returns session identity so the session can be resumed.
</Note>

<ParamField body="comms_name" type="string | null" default="from session">
  Agent name for comms.
</ParamField>

<ParamField body="model" type="string | null" default="from session">
  Model override. On materialized sessions this hot-swaps the LLM client for the remainder of the session.
</ParamField>

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

<ParamField body="provider" type="string | null" default="from session">
  Provider override. Typically inferred from `model`. Used with `model` for mid-session provider switching.
</ParamField>

<ParamField body="hooks_override" type="HookRunOverrides | null" default="null">
  Run-scoped hook overrides.
</ParamField>

### Response format

Both `meerkat_run` and `meerkat_resume` return MCP-standard tool results. The inner `text` field is a JSON-encoded payload containing:

<ResponseField name="content" type="array">
  MCP content blocks with the agent's text.
</ResponseField>

<ResponseField name="session_id" type="string">
  Session ID (save for `meerkat_resume`).
</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="structured_output" type="object | null">
  Parsed structured output.
</ResponseField>

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

### meerkat\_config

Get or update realm config for this MCP server instance.

<CodeGroup>
  ```json Get theme={null}
  { "action": "get" }
  ```

  ```json Set theme={null}
  {
    "action": "set",
    "config": { "agent": { "model": "gpt-5.5" } },
    "expected_generation": 3
  }
  ```

  ```json Patch theme={null}
  {
    "action": "patch",
    "patch": { "agent": { "max_tokens_per_turn": 8192 } },
    "expected_generation": 4
  }
  ```
</CodeGroup>

<ParamField body="action" type="string" required>
  One of `"get"`, `"set"`, `"patch"`.
</ParamField>

<ParamField body="config" type="object | null">
  Full config to replace (for `set` action).
</ParamField>

<ParamField body="patch" type="object | null">
  RFC 7396 merge-patch delta (for `patch` action).
</ParamField>

<ParamField body="expected_generation" type="u64 | null">
  Optional optimistic concurrency check for `set` and `patch`.
</ParamField>

Response includes config envelope fields:

* `config`
* `generation`
* `realm_id`
* `instance_id`
* `backend`
* `resolved_paths`

### meerkat\_capabilities

Returns the runtime capability set with status resolved against config.

<CodeGroup>
  ```json Request theme={null}
  {}
  ```

  ```json Response theme={null}
  {
    "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  |
| `DisabledByPolicy`       | `{"DisabledByPolicy": {"description": "..."}}`  | Compiled in but disabled     |
| `NotCompiled`            | `{"NotCompiled": {"feature": "..."}}`           | Feature flag absent          |
| `NotSupportedByProtocol` | `{"NotSupportedByProtocol": {"reason": "..."}}` | Protocol does not support it |

### meerkat\_models\_catalog

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

<CodeGroup>
  ```json Request theme={null}
  {}
  ```

  ```json Response theme={null}
  {
    "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 required. The catalog is resolved from built-in model metadata plus config-backed provider/server entries.

### meerkat\_skills

List available skills with provenance information, or inspect one skill's full
body by typed `SkillKey`.

<CodeGroup>
  ```json List all skills theme={null}
  { "action": "list" }
  ```

  ```json Inspect a skill theme={null}
  {
    "action": "inspect",
    "skill_key": {
      "source_uuid": "00000000-0000-4b11-8111-000000000001",
      "skill_name": "task-workflow"
    }
  }
  ```

  ```json Inspect a specific source theme={null}
  {
    "action": "inspect",
    "skill_key": {
      "source_uuid": "00000000-0000-4b11-8111-000000000001",
      "skill_name": "task-workflow"
    },
    "source": "00000000-0000-4b11-8111-000000000001"
  }
  ```

  ```json List response theme={null}
  {
    "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
      }
    ]
  }
  ```

  ```json Inspect response theme={null}
  {
    "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"
    },
    "body": "# Task Workflow\n\n..."
  }
  ```
</CodeGroup>

<ParamField body="action" type="string" required>
  `"list"` to list all skills, or `"inspect"` to load one skill's full body.
</ParamField>

<ParamField body="skill_key" type="object">
  Required for `inspect`. The typed skill identity with `source_uuid` and
  `skill_name` fields.
</ParamField>

<ParamField body="source" type="string">
  Optional source UUID selector for `inspect`; omit it to load the canonical
  source for `skill_key`.
</ParamField>

The list response includes both active and shadowed skills. Shadowed skills have
`is_active: false` and a `shadowed_by` field indicating which source has
precedence. Inspect canonicalizes `skill_key` through the identity registry
before loading the body.
