---
icon: lucide/wrench
---

# Agent Orchestration

Use these tools and presets to coordinate other agents, save reusable Dynamic Workflows, change runtime configuration, import OpenClaw-style workspaces, and keep long-lived Claude coding sessions alive across turns.

## What This Page Covers

This page documents the built-in tools in the `agent-orchestration` group.
Use these tools when you need multi-agent coordination, reusable workflow runs, runtime config changes, config-only presets, or persistent Claude Agent SDK sessions.

## Tools On This Page

- [`subagents`] - Spawn Matrix-backed sub-agent sessions and message them later by session key or label.
- [`delegate`] - Run another configured agent as a one-shot specialist and return its answer inline.
- [`dynamic_workflow`] - Create, update, run, and inspect saved Dynamic Workflows with persisted report artifacts.
- [`report_publishing`] - Publish authorized report artifacts through revocable public links.
- [`config_manager`] - Inspect MindRoom config and create, update, validate, or template agents and teams.
- [`self_config`] - Let an agent read and update only its own configuration.
- [`openclaw_compat`] - Config-only preset that expands to native MindRoom tools.
- [`claude_agent`] - Persistent Claude Agent SDK sessions with optional gateway support and per-session labels.

## Common Setup Notes

All eight entries on this page are MindRoom-native orchestration features rather than third-party OAuth integrations.
Only [`claude_agent`] has tool-specific credential fields.
[`delegate`] and [`self_config`] can be added automatically based on agent config, so they are not limited to explicit `tools:` entries.
`agents.<name>.delegate_to` auto-enables [`delegate`] when the list is non-empty and the current delegation depth is below the hard limit of 3.
`agents.<name>.allow_self_config` or `defaults.allow_self_config` auto-enables [`self_config`].
[`config_manager`] and [`self_config`] both save changes by revalidating the full runtime config before rewriting `config.yaml`.
[`subagents`] requires a live Matrix tool runtime context with `room_id`, `requester_id`, Matrix client access, and a writable storage path.
[`dynamic_workflow`] requires a live tool runtime context, a writable storage path, and a configured agent model.
[`report_publishing`] requires a live tool runtime context, a writable storage path, and an authorized report source.
[`openclaw_compat`] is a config preset, not a runtime toolkit.
`Config.expand_tool_names()` expands presets and implied tools while deduping and preserving order.
For [`openclaw_compat`], that means `matrix_message` is added directly and `attachments` is added indirectly through `Config.IMPLIED_TOOLS`.

## [`subagents`]

`subagents` creates and tracks Matrix-backed sub-agent sessions that can continue across multiple tool calls.

### What It Does

`subagents` exposes `agents_list()`, `sessions_spawn()`, `sessions_send()`, and `list_sessions()`.
All four calls return JSON strings with a `status` field, a `tool` field, and operation-specific payload data.
`agents_list()` returns the current agent name plus `agents`, a sorted array of row objects with `name`, `can_delegate`, `can_spawn`, and `description`.
`name` is the value to pass as `agent_id` when the relevant capability flag allows that operation.
`can_spawn` means the agent is eligible in the current room, and `can_delegate` means the agent is listed in the caller's `delegate_to` allowlist.
`sessions_spawn(task, summary, tag, label=None, agent_id=None)` requires a non-empty task plus a normalized summary and tag.
`sessions_spawn()` posts a fresh room-level Matrix message that mentions the target agent, then treats the resulting event ID as the root of a new isolated session thread.
After the spawn succeeds, it writes the requested thread summary and tag through the lower-level thread summary and thread tag APIs.
If you pass a `label` and the current `(agent_name, room_id, requester_id)` scope already has a matching tracked session, `sessions_spawn()` reuses that session instead of creating a new one and still applies the requested summary and tag to the existing thread.
If the post-spawn summary or tag write fails, the spawn still succeeds and the response includes a `warnings` list describing the follow-up failure.
`sessions_send()` sends a follow-up message into an existing tracked session.
If you omit `session_key`, `sessions_send()` defaults to the current room or thread session key from `create_session_id(room_id, thread_id)`.
If you pass `label` without `session_key`, `sessions_send()` resolves the most recent in-scope session with that label.
If you pass `agent_id`, `sessions_send()` prefixes the outgoing message with that agent's current full Matrix ID before sending it.
Tracked sessions are persisted in `subagents/session_registry.json` under the current runtime storage root.
`list_sessions()` paginates those tracked sessions with a default `limit` of 50 and a maximum of 200.
Isolated spawned sessions require thread-capable agents.
If the target agent uses `thread_mode=room`, `sessions_spawn()` fails and threaded `sessions_send()` calls to that session also fail.

### Configuration

This tool has no tool-specific inline configuration fields.

### Example

```yaml
agents:
  coordinator:
    display_name: Coordinator
    role: Break work into long-running threaded sub-sessions
    model: sonnet
    tools:
      - subagents
```

```python
agents_list()
sessions_spawn(
    task="Review the failing deployment and propose a rollback plan.",
    summary="Investigate the failing deployment and propose a safe rollback plan.",
    tag="incident-rollback",
    label="incident-42",
    agent_id="ops",
)
sessions_send(
    message="Add a short list of commands we should run first.",
    label="incident-42",
)
list_sessions(limit=20)
```

### Notes

- Session tracking is scoped to the current `agent_name`, `room_id`, and `requester_id`, so labels are not global across unrelated conversations.
- `sessions_spawn()` returns normalized `summary` and `tag` values in the success payload and may include `warnings` if the follow-up summary or tag write fails after the session is created.
- Use [`subagents`] when you want a continuing Matrix thread that other agents or humans can revisit later.
- Use [`delegate`] instead when you want a one-shot specialist answer returned directly as the tool result.

## [`delegate`]

`delegate` runs another configured agent as a fresh one-shot specialist and returns that agent's response inline.

### What It Does

`delegate` exposes one tool call, `delegate_task(agent_name, task)`.
The delegated agent is created with `create_agent()` and runs independently with no shared session or chat history from the caller.
The caller waits for the delegated agent to finish, and the delegated agent's `response.content` becomes the tool result.
MindRoom gives the delegated agent any already-published last-good knowledge indexes and schedules missing or stale refresh work in the background.
Interactive questions are disabled for delegated runs.
Unlike [`subagents`], [`delegate`] does not create a Matrix thread, does not write to the room timeline, and does not keep a reusable session handle.
If `agent_name` is not in the caller's allowed `delegate_to` list, the tool returns an error string.
Empty tasks are rejected.

### Configuration

This tool has no tool-specific inline configuration fields.
Enable it by setting `delegate_to` on the agent config.
MindRoom adds the tool automatically when `delegate_to` is non-empty, so listing `delegate` in `tools:` is usually unnecessary.

### Example

```yaml
agents:
  lead:
    display_name: Lead
    role: Coordinate specialist agents
    model: sonnet
    delegate_to:
      - code
      - research

  code:
    display_name: Code
    role: Implement and debug code changes
    model: sonnet
    tools:
      - coding
      - shell

  research:
    display_name: Research
    role: Gather sources and summarize findings
    model: sonnet
    tools:
      - duckduckgo
```

```python
delegate_task(
    agent_name="research",
    task="Summarize the three main risks in this proposal and cite supporting facts.",
)
```

### Notes

- `Config.validate_delegate_to()` rejects self-delegation and unknown target agents at config-load time.
- Recursive delegation is supported, but only up to a maximum depth of 3.
- Use [`subagents`] when you need an ongoing threaded workflow.
- Use [`delegate`] when you need a synchronous specialist answer inside the current run.

## [`dynamic_workflow`]

`dynamic_workflow` lets an agent save a reusable workflow, publish immutable revisions, run the active revision, and inspect stored run records.

### What It Does

`dynamic_workflow` exposes `create_workflow()`, `validate_workflow()`, `update_workflow()`, `run_workflow()`, `get_workflow_run()`, `list_workflows()`, and `list_workflow_revisions()`.
All calls return JSON strings with a `status` field and operation-specific payload data.
Saved specs live under `MINDROOM_STORAGE_PATH/dynamic_workflows/`.
Each update creates a new immutable `revisions/<revision>.yaml` file and updates the small `workflow.yaml` pointer file.
Each run pins the active revision at start time, writes a `runs/<run_id>.json` record, and writes `report.md`, `report.html`, and `step_outputs.json` under that run's artifact directory.
If `MINDROOM_PUBLIC_URL` is set, successful and failed run payloads include a private report URL under `/reports/private/...`.
Private report routes authorize the dashboard requester against the run's `requested_by` identity.
Use [`report_publishing`] to publish a completed Dynamic Workflow run report through a revocable public URL under `/reports/public/<slug>`.
If `MINDROOM_PUBLIC_URL` is unset, the report artifacts are still persisted on disk and listed in the run payload.

### Configuration

Enable the tool by adding `dynamic_workflow` to the agent that should be allowed to create and run workflows.
The current implementation supports agent-scoped workflows from agent tools.
Room and tenant scopes are reserved for a future approval policy, so tool calls with `scope="room"` or `scope="tenant"` return an error today.

```yaml
agents:
  coordinator:
    display_name: Coordinator
    role: Build and run reusable Dynamic Workflows
    model: sonnet
    tools:
      - dynamic_workflow
```

### Spec Shape

Workflow specs are declarative JSON/YAML objects with `schema_version: 1`.
The top-level fields are `id`, `name`, `description`, `kind`, `inputs`, `participants`, `workflow`, `outputs`, and `permissions`.
`kind` must be `workflow`.
`inputs` supports an object schema with `required`, `properties`, property `type`, property `description`, and property `enum`.
Participants can be `ephemeral_agent` or `room_agent`.
An `ephemeral_agent` can declare `id`, `name`, `role`, `description`, `model`, `tools`, and `instructions`.
Ephemeral participant `tools` may grant any registered tool except agent-infrastructure tools (`memory`, `delegate`, `self_config`, `compact_context`, `dynamic_workflow`, `dynamic_tools`).
Every participant tool must also be listed in `permissions.tools`.
Participant tool calls require per-call user approval in the originating room unless the tool is pre-approved by the caller's `dynamic_workflow` `allowed_tools` config.
Setting `allowed_tools` to `["*"]` pre-approves every granted tool.
A `room_agent` can declare `id`, `agent`, and an empty `tools` list.
Room-agent participants must already be available to the requester in the current room, use their configured model, and run without tools, skills, knowledge, durable state, or preloaded context files.
Step types are `transform_step`, `agent_step`, and `report_step`.
`transform_step` renders a template without calling a model.
`agent_step` renders a prompt and sends it to the selected participant.
`report_step` renders Markdown report content from input and prior step outputs.
Outputs declare `id`, `type`, and `from_step`.
Output `type` may be `text`, `markdown`, `json`, or `html_report`.
Permissions support runtime caps, model caps, and tool grants.
`permissions.data` must keep `matrix_history`, `attachments`, and `knowledge_bases` disabled until approval-backed data grants exist.

### Example

```python
create_workflow(
    spec={
        "schema_version": 1,
        "id": "brief-report",
        "name": "Brief Report",
        "description": "Create a short HTML report from one topic.",
        "kind": "workflow",
        "inputs": {
            "type": "object",
            "required": ["topic"],
            "properties": {"topic": {"type": "string"}},
        },
        "participants": [
            {
                "id": "writer",
                "kind": "ephemeral_agent",
                "name": "Report Writer",
                "model": "claude-sonnet-4-6",
                "tools": ["duckduckgo", "website"],
            },
        ],
        "workflow": [
            {
                "id": "write",
                "type": "agent_step",
                "participant": "writer",
                "prompt": "Research the web and write a concise cited report about {input.topic}.",
            },
        ],
        "outputs": [{"id": "report_html", "type": "html_report", "from_step": "write"}],
        "permissions": {
            "max_runtime_seconds": 1800,
            "max_concurrent_agents": 4,
            "max_total_agents": 8,
            "models": ["claude-sonnet-4-6"],
            "tools": ["duckduckgo", "website"],
            "data": {"matrix_history": "none", "attachments": "none", "knowledge_bases": []},
        },
    },
    reason="initial report workflow",
)
run_workflow("brief-report", {"topic": "Agno factories"})
list_workflows()
get_workflow_run("brief-report", "run_...")
```

### Pre-approving participant tools

Configure `allowed_tools` on the calling agent's `dynamic_workflow` tool entry to skip per-call approval for trusted tools.

```yaml
agents:
  builder:
    display_name: Workflow Builder
    tools:
      - dynamic_workflow:
          allowed_tools: [duckduckgo, website]
```

Use `allowed_tools: ["*"]` to pre-approve every tool a workflow grants.
Tools outside `allowed_tools` still run, but each call posts an approval card in the originating room and waits for the requester's decision.

### Notes

- Dynamic Workflow runs execute synchronously on the current tool call path today.
- Long-running background workflow management, workflow-activation approval cards, Matrix history grants, attachment grants, and knowledge-base grants are future work.
- Ephemeral agents can only use models allowed by both the workflow permissions and the caller's current model policy.
- Granted tools run with the calling agent's tool routing (credentials, worker sandboxing, and egress proxying), and the tool-hook bridge applies plugin gating plus the per-call approval flow.
- Room-agent participants can reuse only agents that normal room routing would expose to the requester.
- Runtime caps are enforced for sync and async runs, and async runs are marked failed at the deadline even if participant cancellation is delayed.

## [`report_publishing`]

`report_publishing` lets an agent intentionally publish authorized report artifacts through revocable public links.

### What It Does

`report_publishing` exposes `publish_report()` and `revoke_public_report(slug)`.
All calls return JSON strings with a `status` field and operation-specific payload data.
The tool does not accept arbitrary filesystem paths.
It publishes only registered source references that the current Matrix requester is authorized to read.
The current source types are `dynamic_workflow_run` and `static_site`.
Use `dynamic_workflow_run` to publish a completed Dynamic Workflow HTML report.
Use `static_site` to publish a copied workspace directory that contains `index.html` and optional CSS, JavaScript, images, fonts, or JSON assets.
A `static_site` source path may also point at one workspace HTML file, which is copied and served as `index.html`.
The static site source path is workspace-relative and the published copy is stored under `MINDROOM_STORAGE_PATH/report_publishing/artifacts/<slug>/`.
A static site snapshot may contain at most 200 files and 10 MiB of total data, and publishing fails with an explanatory error beyond either limit.
Static site links serve under the trailing-slash form `/reports/public/<slug>/`, and the slash-less form redirects there so relative asset URLs resolve.
JavaScript is allowed for static sites, but the public route serves static sites with a sandbox CSP that omits `allow-same-origin` and sets `connect-src 'none'`.
That means scripts can drive local page interactivity, but they cannot act as logged-in MindRoom dashboard code or call MindRoom APIs.
Published link records live under `MINDROOM_STORAGE_PATH/report_publishing/`.
Public report links serve the registered artifact without dashboard authentication until `revoke_public_report(slug)` revokes the slug.
The `slug` is the public-report identifier returned by `publish_report()`.
If `MINDROOM_PUBLIC_URL` is set, successful publish payloads include the absolute public URL.

### Configuration

Enable the tool by adding `report_publishing` to any agent that should be allowed to publish report artifacts.

```yaml
agents:
  coordinator:
    display_name: Coordinator
    role: Build workflows and publish report links
    model: sonnet
    tools:
      - dynamic_workflow
      - report_publishing
```

### Example

```python
publish_report(
    source_type="dynamic_workflow_run",
    source={"workflow_id": "brief-report", "run_id": "run_..."},
    confirm_public=True,
)
publish_report(
    source_type="static_site",
    source={"path": "public-demo", "title": "Public Demo"},
    confirm_public=True,
)
revoke_public_report("pub_...")
```

### Notes

- `confirm_public=True` is required so accidental publish calls fail closed.
- Dynamic Workflow source references default to `scope="agent"` and may include an explicit `scope`.
- Static site publishing requires an agent workspace and publishes an immutable copy, so later workspace edits need a new `publish_report()` call.
- An agent has a workspace when it uses `memory_backend: file` or a `private:` workspace configuration, and the source path resolves against that canonical workspace root.
- Only the run requester or the user who published the link may revoke it.
- Additional registered report sources can be added without changing Dynamic Workflow storage.
- No extra proxy route is needed when `/reports/public/*` already reaches the MindRoom backend.
- If the dashboard frontend and Python backend are split across upstreams, route `/reports/public/*` to the Python backend and do not put dashboard-login middleware on that path.
- Set `MINDROOM_PUBLIC_URL` to the externally reachable dashboard origin, such as `https://mindroom.lab.mindroom.chat`, so publish payloads include clickable absolute URLs.

## [`config_manager`]

`config_manager` is the broad config-authoring toolkit for inspecting MindRoom and creating or updating agents and teams.

### What It Does

`config_manager` exposes `get_info()`, `manage_agent()`, and `manage_team()`.
`get_info(info_type, name=None)` supports `mindroom_docs`, `config_schema`, `available_models`, `agents`, `teams`, `available_tools`, `tool_details`, `agent_config`, and `agent_template`.
`tool_details` requires `name` and reads from live `TOOL_METADATA`, so it includes real config fields and statuses from the current worktree.
`agent_config` returns the authored YAML for a specific agent.
`agent_template` generates starter YAML for one of the built-in template types: `researcher`, `developer`, `social`, `communicator`, `analyst`, or `productivity`.
`manage_agent()` supports `create`, `update`, and `validate`.
Agent creates and updates validate tool names against the live registry and validate knowledge base IDs against the current config.
When a plain string tool list replaces an existing tool list, `config_manager` preserves inline overrides for retained tools instead of flattening them away.
On create, `include_default_tools` falls back to `true` when you omit it.
`manage_team()` creates a new team with `coordinate` or `collaborate` mode and rejects unknown member agents or duplicate team names.
All writes go through full runtime config validation before `config.yaml` is saved.

### Configuration

This tool has no tool-specific inline configuration fields.

### Example

```yaml
agents:
  builder:
    display_name: Builder
    role: Create and maintain MindRoom agents and teams
    model: sonnet
    tools:
      - config_manager
```

```python
get_info("available_tools")
get_info("tool_details", name="claude_agent")
manage_agent(
    operation="create",
    agent_name="triage",
    display_name="Triage",
    role="Sort incoming requests and hand them to the right specialist.",
    tools=["duckduckgo", "subagents"],
    model="default",
    rooms=["lobby"],
)
manage_team(
    team_name="incident_team",
    display_name="Incident Team",
    role="Coordinate incident response across ops and code agents.",
    agents=["ops", "code"],
    mode="coordinate",
)
```

### Notes

- [`config_manager`] is broader and more privileged than [`self_config`] because it can inspect and modify other agents and teams.
- `manage_team()` creates teams, but it does not expose a separate update operation on this branch.
- Use [Agent Configuration](../configuration/agents.md) for the full authored schema outside the tool's curated helper surface.

## [`self_config`]

`self_config` lets an agent inspect and update only its own config entry.

### What It Does

`self_config` exposes `get_own_config()` and `update_own_config()`.
`get_own_config()` returns the current agent's authored YAML block.
`update_own_config()` only changes fields that you pass explicitly.
On this branch, `update_own_config()` can modify `display_name`, `role`, `instructions`, `tools`, `model`, `rooms`, `markdown`, `learning`, `learning_mode`, `knowledge_bases`, `skills`, `include_default_tools`, `show_tool_calls`, `thread_mode`, `num_history_runs`, `num_history_messages`, `compress_tool_results`, `max_tool_calls_from_history`, and `context_files`.
The update path validates tool names against the live registry and validates knowledge base IDs against the current config.
It also preserves inline tool overrides for retained tools when a string-only tool list is provided.
Updates are validated through `AgentConfig.model_validate()` before the file is saved.
Only the current agent can be changed.
There is no path to modify other agents or teams through this tool.

### Configuration

This tool has no tool-specific inline configuration fields.
The normal way to enable it is `agents.<name>.allow_self_config: true` or `defaults.allow_self_config: true`.

### Example

```yaml
defaults:
  allow_self_config: false

agents:
  research:
    display_name: Research
    role: Research and summarize external sources
    model: sonnet
    allow_self_config: true
    tools:
      - duckduckgo
      - wikipedia
```

```python
get_own_config()
update_own_config(
    instructions=[
        "Cite sources for factual claims.",
        "Prefer concise summaries with clear takeaways.",
    ],
    tools=["duckduckgo", "wikipedia", "subagents"],
    thread_mode="room",
    context_files=["SOUL.md", "USER.md"],
)
```

### Notes

- `self_config` blocks privileged self-escalation by rejecting `config_manager` in its `tools` update list.
- `include_default_tools=True` is also rejected when `defaults.tools` contains blocked privileged tools such as `config_manager`.
- Use [`self_config`] for narrow self-tuning at runtime and [`config_manager`] for full config-authoring workflows.

## [`openclaw_compat`]

`openclaw_compat` is a config-only preset for OpenClaw-style workspace portability.

### What It Does

`openclaw_compat` is not a runtime toolkit.
The registered factory returns an empty `Toolkit`, and the real behavior comes from `Config.TOOL_PRESETS`.
`Config.expand_tool_names()` expands `openclaw_compat` into `shell`, `coding`, `duckduckgo`, `website`, `browser`, `scheduler`, `subagents`, and `matrix_message`.
`matrix_message` then implies `attachments`, so the effective enabled set also includes `attachments` even though the preset does not list it directly.
Preset expansion dedupes while preserving order, so adding `openclaw_compat` alongside one of its member tools does not create duplicates.
This preset is meant for OpenClaw-compatible workspace behavior inside MindRoom rather than for cloning the full OpenClaw gateway control plane.

### Configuration

This preset has no inline configuration fields and cannot use `defer` or `initial`.
Configure individual member tools directly when they need lazy loading.

### Example

```yaml
agents:
  openclaw:
    display_name: OpenClawAgent
    role: OpenClaw-style personal assistant with a file-first workspace
    model: opus
    include_default_tools: false
    learning: false
    memory_backend: file
    context_files:
      - SOUL.md
      - AGENTS.md
      - USER.md
      - IDENTITY.md
      - TOOLS.md
      - HEARTBEAT.md
    tools:
      - openclaw_compat
      - python
```

### Notes

- [`openclaw_compat`] is a preset name that belongs in `tools:` but does not expose callable runtime methods of its own.
- Use the dedicated [OpenClaw Workspace Import](../openclaw.md) guide for workspace layout, file memory behavior, and migration details.
- If you only need one or two of the member tools, configure those tools directly instead of using the preset.

## [`claude_agent`]

`claude_agent` keeps persistent Claude Agent SDK coding sessions alive across turns and exposes explicit session lifecycle controls.

### What It Does

`claude_agent` exposes `claude_start_session()`, `claude_send()`, `claude_session_status()`, `claude_interrupt()`, and `claude_end_session()`.
`claude_send()` automatically creates the session if it does not already exist, so `claude_start_session()` is optional.
Session keys are namespaced by agent identity and Agno run session ID, with optional `session_label` suffixes for parallel sub-sessions.
The same session key is serialized by an `asyncio.Lock`, so concurrent calls to one label run one after the other.
Different `session_label` values create distinct Claude sessions that can proceed independently.
Idle sessions expire after `session_ttl_minutes`, which defaults to 60 minutes.
The process-wide session manager keeps at most `max_sessions` active sessions per agent namespace, defaulting to 200.
`resume` and `fork_session` only apply when creating a new session.
`fork_session=True` requires a non-empty `resume` session ID.
If a session already exists for the computed key, passing `resume` or `fork_session` returns an error instead of silently changing the live session.
`claude_session_status()` reports age, idle time, and the underlying Claude session ID once Claude has returned a result.
On SDK failures, the tool includes recent Claude CLI stderr lines in its error output to help debug gateway or CLI issues.

### Configuration

| Option | Type | Required | Default | Notes |
| --- | --- | --- | --- | --- |
| `api_key` | `password` | `no` | `null` | Anthropic API key or gateway-compatible key material. Usually stored in credentials JSON or dashboard setup instead of inline YAML. |
| `anthropic_base_url` | `url` | `no` | `null` | Optional Anthropic-compatible gateway root URL. Use the host root, not a `/v1` suffix. |
| `anthropic_auth_token` | `password` | `no` | `null` | Optional bearer token for Anthropic-compatible gateways. |
| `disable_experimental_betas` | `boolean` | `no` | `false` | Sets `CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1` for gateway compatibility. |
| `cwd` | `text` | `no` | `null` | Working directory passed to the Claude Agent SDK client. |
| `model` | `text` | `no` | `null` | Claude model override. When omitted, the tool falls back to the current agent model ID when one is available. |
| `permission_mode` | `text` | `no` | `default` | One of `default`, `acceptEdits`, `plan`, or `bypassPermissions`. Invalid values fall back to `default`. |
| `continue_conversation` | `boolean` | `no` | `false` | Continue the same Claude conversation context across queries in one session. |
| `allowed_tools` | `text` | `no` | `null` | Comma-separated Claude Code tool names to allow. |
| `disallowed_tools` | `text` | `no` | `null` | Comma-separated Claude Code tool names to deny. |
| `max_turns` | `number` | `no` | `null` | Maximum Claude turns per query. Values below 1 are normalized up to 1. |
| `system_prompt` | `text` | `no` | `null` | Extra system prompt passed directly to the Claude Agent SDK. |
| `cli_path` | `text` | `no` | `null` | Optional path to the Claude CLI executable. |
| `session_ttl_minutes` | `number` | `no` | `60` | Idle-session expiration window in minutes. Values below 1 are normalized up to 1. |
| `max_sessions` | `number` | `no` | `200` | Maximum live sessions per agent namespace. Values below 1 are normalized up to 1. |

### Example

```yaml
agents:
  code:
    display_name: Code Agent
    role: Coding assistant with persistent Claude sessions
    model: default
    tools:
      - claude_agent:
          model: claude-sonnet-4-6
          cwd: /workspace/project
          permission_mode: acceptEdits
          continue_conversation: true
          session_ttl_minutes: 180
          max_sessions: 20
```

```json
{
  "api_key": "sk-ant-or-proxy-key",
  "model": "claude-sonnet-4-6",
  "permission_mode": "default",
  "continue_conversation": true,
  "session_ttl_minutes": 60,
  "max_sessions": 200
}
```

```json
{
  "api_key": "sk-dummy",
  "anthropic_base_url": "http://litellm.local",
  "anthropic_auth_token": "sk-dummy",
  "disable_experimental_betas": true
}
```

```python
claude_send(
    prompt="Refactor the failing test and explain the diff.",
    session_label="bugfix",
)
claude_session_status(session_label="bugfix")
claude_interrupt(session_label="bugfix")
claude_end_session(session_label="bugfix")
```

### Notes

- Dashboard setup and `mindroom_data/credentials/claude_agent_credentials.json` both feed the same tool credential fields, because runtime credentials are stored as `<service>_credentials.json`.
- For Anthropic-compatible gateways, set `anthropic_base_url` to the gateway root without `/v1`, because the Claude client appends its own API path.
- Some gateways reject Claude beta headers, so `disable_experimental_betas: true` is the compatibility switch for that case.
- When you use MindRoom's OpenAI-compatible API, keep the same `X-Session-Id` across requests so the same Claude session key is reused.
- See [OpenAI-Compatible API](../openai-api.md) for request-level session continuity details.

## Related Docs

- [Tools Overview](index.md)
- [Agent Configuration](../configuration/agents.md)
- [OpenClaw Workspace Import](../openclaw.md)
- [OpenAI-Compatible API](../openai-api.md)
