> ## Documentation Index
> Fetch the complete documentation index at: https://docs.chatsailer.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Agent Studio

> Inspect an agent, test the working copy in a sandbox, publish only when you mean it.

URL: `https://mcp.chatsailer.com/mcp/studio`

Inspect and test Sailer AI agents. Edits apply to the **working copy**. Live
conversations keep running the published version until someone publishes.

Connect this server only if you actually edit agents. It is a separate OAuth
audience — a CRM token is 401 here, not "missing scope". Reconnect; don't retry.

<Note>
  First connection is [read-only](/mcp/auth). `publish_agent` and the sandbox
  will not appear until you reconnect with `agents:publish` and `sandbox:run`.
</Note>

`whoami` is mounted here too. Same tool as CRM: which workspace, who
authorized, which scopes.

## Inspect

### `list_agents`

Scope: `agents:read`. Newest first, `limit` 1–100 (default 25). Returns `id`,
`name`, `is_published`. Use this to get an `agent_id`.

> Which agents are in this workspace?

### `get_agent_graph`

Scope: `agents:read`. Working-copy nodes and connections. Nodes are addressed
by a stable key (falling back to the name) — talk in those keys, not internal
ids. The result includes a `head_hash`; pass it back on any write so a
concurrent edit is detected instead of silently overwritten.

> Show me this agent's graph.

### `validate_agent`

Scope: `agents:read`. Every problem at once, addressed by node key. An empty
list means the graph would publish cleanly.

> Is this agent's graph valid?

### `diff_agent_vs_production`

Scope: `agents:read`. What would change if you published right now, plus the
`head_hash` and `change_count` that `publish_agent` requires.

> What would change if I published this agent?

Call this before you ship. Publishing without having just read the diff is
rejected.

## Publish

### `publish_agent`

Scope: `agents:publish`. Destructive. Live customer conversations start using
the working copy **immediately**. It ships every unpublished change, not a
selected subset.

Required, and verified server-side:

* `expected_head_hash` — from `get_agent_graph` or `diff_agent_vs_production`
* `acknowledged_change_count` — from `diff_agent_vs_production`

If someone else edited the agent in between, the call fails and tells you to
diff again. Optional `version_name` / `version_description` land in version
history.

`agents:publish` is split from read on purpose: you can hand out a connection
that iterates on a draft and cannot ship it.

> Diff this agent against production, then publish it.

## Sandbox

The sandbox runs the working copy. Nothing here touches real customers. A turn
takes tens of seconds, so it is two tools instead of one: a blocking call
would die on the load balancer after the work had already committed.

### `start_sandbox_turn`

Scope: `sandbox:run`. Send a message (max 4,000 characters). Returns
immediately with a `turn_id`.

| Argument           | What it does                                                           |
| ------------------ | ---------------------------------------------------------------------- |
| `message`          | What the simulated contact says                                        |
| `agent_id`         | Which agent answers. Omit to let the queue decide — that is production |
| `queue_id`         | Route into a specific queue                                            |
| `conversation_key` | Continue an earlier sandbox conversation. Omit to start fresh          |

Multi-turn behaviour depends on history. A graph that only misbehaves on the
third turn cannot be reproduced one message at a time — pass the same
`conversation_key` back.

> In the sandbox, say hi to this agent as a new contact.

### `get_sandbox_turn`

Scope: `sandbox:run`. Poll with the `turn_id`. While `settled` is false, the
agent may still reply — that is not silence. Once settled, `outcome` says what
happened and `remedy` says what to do about it. "No reply" has several causes;
they are not the same fix.

> Has that sandbox turn finished?

### `get_turn_trace`

Scopes: `sandbox:run` **and** `inference:read`. Call after the turn has
settled. Which edges were evaluated, which was taken, and for each one that
lost, which condition failed and what the compared field actually held.

This is the tool for "why didn't it move on?". Usually a gate compared a field
that was never collected — `actual` is null and `likely_cause` names the field.
It does not include prompts or model completions.

> Why didn't the agent leave that node?
