Install
Quick install
npx skills add https://github.com/anthropics/claude-cookbooks/tree/main/managed_agents/cma-mcpnpx skills add anthropics/claude-cookbooks --skill skill --agent claude-codenpx skills add anthropics/claude-cookbooks --skill skill --agent cursornpx skills add anthropics/claude-cookbooks --skill skill --agent codexnpx skills add anthropics/claude-cookbooks --skill skill --agent opencodenpx skills add anthropics/claude-cookbooks --skill skill --agent github-copilotnpx skills add anthropics/claude-cookbooks --skill skill --agent windsurfMore install options
Shorthand — useful for multi-skill repos:
npx skills add anthropics/claude-cookbooks --skill skillManual — clone the repo and drop the folder into your agent's skills directory:
git clone https://github.com/anthropics/claude-cookbooks.gitcp -r claude-cookbooks/managed_agents/cma-mcp ~/.claude/skills/Setup tips & tricks — CMA as an MCP server
Things that aren't obvious from the docs and tend to cost debugging time.
---
Mental model
Claude Desktop is the frontend, CMA is the backend
The Claude you're typing to in Desktop is a relay. It calls send_message with your words, calls wait_for_idle to block until the CMA agent finishes, then shows you the reply. The actual work — tool use, code execution, repo edits — happens in the CMA session, not in Desktop.
Eight 1:1 tools, one shim
list_agents, get_agent, create_session, send_message, interrupt, get_session, list_events, archive_session are straight endpoint wrappers. wait_for_idle is the only editorial: MCP is request/response and CMA completion is SSE, so something has to stream-to-idle inside a tool call.
session_id is the only state, and Claude holds it
create_session returns it; Claude passes it to every subsequent call. The MCP server itself is stateless.
Two transports, one tool set
src/tools.ts registers the nine tools. src/server.ts wraps it in stdio (Claude Desktop spawns it as a subprocess); src/server-http.ts wraps it in Streamable HTTP (claude.ai web reaches it over the network). Pick one.
---
Setup — Claude Desktop (stdio, local)
- Environment (one-time — sessions need an
environment_id):
ant beta:environments create --name cma-mcp \
--config '{type: cloud, networking: {type: unrestricted}}' --transform id -r
- Agents: this server doesn't create agents — it drives the ones already in your workspace. Create/update them via the
antCLI or the Console. .env.local:
ANTHROPIC_API_KEY=sk-ant-...
CLAUDE_ENVIRONMENT_ID=env_...
- Register with Claude Desktop — add to
~/Library/Application Support/Claude/claude_desktop_config.json(macOS) and restart Desktop:
{
"mcpServers": {
"cma": {
"command": "bun",
"args": ["run", "/absolute/path/to/managed_agents/cma-mcp/src/server.ts"],
"env": {
"ANTHROPIC_API_KEY": "sk-ant-...",
"CLAUDE_ENVIRONMENT_ID": "env_..."
}
}
}
}
- Test: in a new Desktop chat, ask "list my managed agents, start a session with the first one, and relay this message to it: hello."
---
Setup — claude.ai web (Streamable HTTP, remote)
Same tools, but the server runs at a public URL and claude.ai connects to it as a custom Connector.
- Token — generate and keep it; anyone with this token can drive your agents:
export CMA_MCP_TOKEN=$(openssl rand -hex 32)
- Run locally first (ngrok/cloudflared for a public URL while testing):
bun run http # → :3000/mcp
- Deploy — the
Dockerfiletargets Fly / Railway / Render; setANTHROPIC_API_KEY,CLAUDE_ENVIRONMENT_ID,CMA_MCP_TOKENas secrets. (Cloudflare Workers also works —WebStandardStreamableHTTPServerTransportis fetch-native; swapprocess.env→envandBun.serve→export default { fetch }.) - claude.ai → Settings → Connectors → Add custom connector:
| Field | Value |
|---|---|
| Name | CMA |
| URL | https://<your-deploy>/mcp |
| Authentication | Bearer token → your CMA_MCP_TOKEN |
> Team / Enterprise orgs: adding a connector by URL is typically org-admin only — individual users see a curated directory, not a URL field. An admin adds the URL + token once in org settings; it then appears in every member's Connectors list to enable. (This is the shape you want for rolling out to non-technical users anyway.)
- Test: new chat → enable the
CMAconnector → same prompt as above.
---
Relay mode (recommended Project instructions)
Without guidance, Desktop Claude will try to answer the user itself instead of relaying. Put this in a Project's custom instructions:
You are a frontend for a backend Managed Agent reached via thecmaMCP tools. On the first user turn:list_agents(if needed) →create_session→send_message(user text verbatim)→wait_for_idle→ return thereplyverbatim. On subsequent turns:send_message→wait_for_idle. Do not answer from your own knowledge; do not paraphrase the backend's reply. Ifwait_for_idlereturnsstatus: "timeout", tell the user it's still running and offer to keep waiting.
---
Gotchas
stdio = Desktop only; HTTP = the internet
Browser claude.ai can't spawn a local process, so stdio only works with Claude Desktop / Claude Code. The HTTP path gets you claude.ai web, but now the URL is public — the bearer token is the only gate in front of your ANTHROPIC_API_KEY's CMA quota. Don't deploy without it, don't log it, rotate it like any API key.
Long turns vs tool timeout
wait_for_idle blocks. If the CMA agent runs for minutes (big repo clone, many tool calls), the MCP client may time out the tool call. Mitigations: pass a smaller timeout_sec and loop (wait_for_idle returns status: "timeout" + last_event_id; call again), or have Claude poll get_session + list_events(after_id=...) instead.
Billing
All CMA usage bills to the ANTHROPIC_API_KEY baked into the server config — not to the Desktop user's account. That's the point (non-technical users, no keys), but size the key's workspace limits accordingly.
Deliberately not exposed
| Endpoint | Why not |
|---|---|
| agents.archive | Permanent, no undo — one bad tool call bricks a prod agent |
| agents.create / update | Authoring belongs in the ant CLI / Console, not a chat turn |
| sessions.delete, environments.* | Destructive infra ops |
| vaults., credentials. | Secrets |
| sessions.resources.add | Needs tokens/file IDs the chat user won't have |
Adding any of these is a few lines in src/cma.ts + src/server.ts if your use case needs them.
---
Debugging
| Symptom | Check |
|---|---|
| Desktop shows no cma tools | Config path wrong, or bun not on PATH for the Desktop process (use an absolute path to bun). Check Desktop's MCP logs. |
| CLAUDE_ENVIRONMENT_ID is required | Env block missing from the Desktop config — stdio servers don't read your shell's env. |
| wait_for_idle returns empty reply | Agent went idle without emitting text (e.g. only tool calls). list_events shows the full log. |
| Every turn starts a new session | Claude isn't threading session_id. Tighten the Project instructions. |
| claude.ai Connector shows "couldn't connect" | URL wrong, server not reachable, or token mismatch (check server logs for 401). |
| HTTP server returns 401 locally | CMA_MCP_TOKEN not exported in the shell you ran bun run http from. |
SKILL.md source
---
name: skill
description: Skill skill from anthropics/claude-cookbooks.
---
# Setup tips & tricks — CMA as an MCP server
Things that aren't obvious from the docs and tend to cost debugging time.
---
## Mental model
### Claude Desktop is the frontend, CMA is the backend
The Claude you're typing to in Desktop is a **relay**. It calls `send_message` with your words, calls `wait_for_idle` to block until the CMA agent finishes, then shows you the reply. The actual work — tool use, code execution, repo edits — happens in the CMA session, not in Desktop.
### Eight 1:1 tools, one shim
`list_agents`, `get_agent`, `create_session`, `send_message`, `interrupt`, `get_session`, `list_events`, `archive_session` are straight endpoint wrappers. `wait_for_idle` is the only editorial: MCP is request/response and CMA completion is SSE, so something has to stream-to-idle inside a tool call.
### `session_id` is the only state, and Claude holds it
`create_session` returns it; Claude passes it to every subsequent call. The MCP server itself is stateless.
### Two transports, one tool set
`src/tools.ts` registers the nine tools. `src/server.ts` wraps it in stdio (Claude Desktop spawns it as a subprocess); `src/server-http.ts` wraps it in Streamable HTTP (claude.ai web reaches it over the network). Pick one.
---
## Setup — Claude Desktop (stdio, local)
1. **Environment** (one-time — sessions need an `environment_id`):
```bash
ant beta:environments create --name cma-mcp \
--config '{type: cloud, networking: {type: unrestricted}}' --transform id -r
```
2. **Agents**: this server doesn't create agents — it drives the ones already in your workspace. Create/update them via the `ant` CLI or the Console.
3. **`.env.local`**:
```
ANTHROPIC_API_KEY=sk-ant-...
CLAUDE_ENVIRONMENT_ID=env_...
```
4. **Register with Claude Desktop** — add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) and restart Desktop:
```json
{
"mcpServers": {
"cma": {
"command": "bun",
"args": ["run", "/absolute/path/to/managed_agents/cma-mcp/src/server.ts"],
"env": {
"ANTHROPIC_API_KEY": "sk-ant-...",
"CLAUDE_ENVIRONMENT_ID": "env_..."
}
}
}
}
```
5. **Test**: in a new Desktop chat, ask *"list my managed agents, start a session with the first one, and relay this message to it: hello."*
---
## Setup — claude.ai web (Streamable HTTP, remote)
Same tools, but the server runs at a public URL and claude.ai connects to it as a custom Connector.
1. **Token** — generate and keep it; anyone with this token can drive your agents:
```bash
export CMA_MCP_TOKEN=$(openssl rand -hex 32)
```
2. **Run locally first** (ngrok/cloudflared for a public URL while testing):
```bash
bun run http # → :3000/mcp
```
3. **Deploy** — the `Dockerfile` targets Fly / Railway / Render; set `ANTHROPIC_API_KEY`, `CLAUDE_ENVIRONMENT_ID`, `CMA_MCP_TOKEN` as secrets. (Cloudflare Workers also works — `WebStandardStreamableHTTPServerTransport` is fetch-native; swap `process.env` → `env` and `Bun.serve` → `export default { fetch }`.)
4. **claude.ai → Settings → Connectors → Add custom connector**:
| Field | Value |
|---|---|
| Name | `CMA` |
| URL | `https://<your-deploy>/mcp` |
| Authentication | Bearer token → your `CMA_MCP_TOKEN` |
> **Team / Enterprise orgs:** adding a connector by URL is typically **org-admin only** — individual users see a curated directory, not a URL field. An admin adds the URL + token once in org settings; it then appears in every member's Connectors list to enable. (This is the shape you want for rolling out to non-technical users anyway.)
5. **Test**: new chat → enable the `CMA` connector → same prompt as above.
---
## Relay mode (recommended Project instructions)
Without guidance, Desktop Claude will try to answer the user itself instead of relaying. Put this in a Project's custom instructions:
> You are a frontend for a backend Managed Agent reached via the `cma` MCP tools. On the first user turn: `list_agents` (if needed) → `create_session` → `send_message(user text verbatim)` → `wait_for_idle` → return the `reply` verbatim. On subsequent turns: `send_message` → `wait_for_idle`. Do not answer from your own knowledge; do not paraphrase the backend's reply. If `wait_for_idle` returns `status: "timeout"`, tell the user it's still running and offer to keep waiting.
---
## Gotchas
### stdio = Desktop only; HTTP = the internet
Browser claude.ai can't spawn a local process, so stdio only works with Claude Desktop / Claude Code. The HTTP path gets you claude.ai web, but now the URL is public — the **bearer token is the only gate** in front of your `ANTHROPIC_API_KEY`'s CMA quota. Don't deploy without it, don't log it, rotate it like any API key.
### Long turns vs tool timeout
`wait_for_idle` blocks. If the CMA agent runs for minutes (big repo clone, many tool calls), the MCP client may time out the tool call. Mitigations: pass a smaller `timeout_sec` and loop (`wait_for_idle` returns `status: "timeout"` + `last_event_id`; call again), or have Claude poll `get_session` + `list_events(after_id=...)` instead.
### Billing
All CMA usage bills to the `ANTHROPIC_API_KEY` baked into the server config — not to the Desktop user's account. That's the point (non-technical users, no keys), but size the key's workspace limits accordingly.
### Deliberately not exposed
| Endpoint | Why not |
|---|---|
| `agents.archive` | Permanent, no undo — one bad tool call bricks a prod agent |
| `agents.create` / `update` | Authoring belongs in the `ant` CLI / Console, not a chat turn |
| `sessions.delete`, `environments.*` | Destructive infra ops |
| `vaults.*`, `credentials.*` | Secrets |
| `sessions.resources.add` | Needs tokens/file IDs the chat user won't have |
Adding any of these is a few lines in `src/cma.ts` + `src/server.ts` if your use case needs them.
---
## Debugging
| Symptom | Check |
|---|---|
| Desktop shows no `cma` tools | Config path wrong, or `bun` not on PATH for the Desktop process (use an absolute path to `bun`). Check Desktop's MCP logs. |
| `CLAUDE_ENVIRONMENT_ID is required` | Env block missing from the Desktop config — stdio servers don't read your shell's env. |
| `wait_for_idle` returns empty `reply` | Agent went idle without emitting text (e.g. only tool calls). `list_events` shows the full log. |
| Every turn starts a new session | Claude isn't threading `session_id`. Tighten the Project instructions. |
| claude.ai Connector shows "couldn't connect" | URL wrong, server not reachable, or token mismatch (check server logs for 401). |
| HTTP server returns 401 locally | `CMA_MCP_TOKEN` not exported in the shell you ran `bun run http` from. |
Related skills 6
running-claude-code-via-litellm-copilot
Use when routing Claude Code through a local LiteLLM proxy to GitHub Copilot, reducing direct Anthropic spend, configuring ANTHROPIC_BASE_URL or ANTHROPIC_MODEL overrides, or troubleshooting Copilot proxy setup failures such as model-not-found, no localhost traffic, or GitHub 401/403 auth errors.
skills-cli
Use when users ask to discover, install, list, check, update, remove, back up, restore, sync, or initialize Agent Skills, mention `bunx skills`, `npx skills`, `skills.sh`, or `skills-lock.json`, ask "find a skill for X", or want help extending agent capabilities with installable skills.
repo-intake-and-plan
Narrow RigorPilot helper for README-first deep learning repo reproduction. Use when the task is specifically to scan a repository, read the README and common project files, extract documented commands, classify inference, evaluation, and training candidates, and return the smallest trustworthy reproduction plan to the main orchestrator. Do not use for environment setup, asset download, command execution, final reporting, paper lookup, or end-to-end orchestration.
image-to-video
Animate any still image on RunComfy — this skill is a smart router that matches the user's intent to the right i2v model in the RunComfy catalog. Picks HappyHorse 1.0 I2V (Arena #1, native audio, identity preservation) for general animations, Wan 2.7 with `audio_url` for custom-voiceover lip-sync, or Seedance 2.0 Pro for multi-modal animation from image + reference video + reference audio. Bundles each model's documented prompting patterns so the caller gets sharper output without burning ite...
video-edit
Edit existing video on RunComfy — this skill is a smart router that matches the user's intent to the right edit model in the RunComfy catalog. Picks Wan 2.7 Edit-Video (general restyle / background swap / packaging swap, identity + motion preservation), Kling 2.6 Pro Motion Control (transfer precise motion from a reference video to a target character), or Lucy Edit Restyle (lightweight identity-stable restyle / outfit swap). Bundles each model's documented prompting patterns so the skill gets...
nano-banana-2
Generate images with Google Nano Banana 2 (Gemini-family flash-tier text-to-image) on RunComfy — bundled with the model's documented prompting patterns so the skill gets sharper output than naive prompting against the same model. Documents Nano Banana 2's strengths (rapid iteration, in-image typography rendering, predictable framing, optional web-grounded context), the resolution-tier pricing, the safety-tolerance dial, and when to route to Nano Banana Pro / GPT Image 2 / Flux 2 / Seedream in...