# MCP

https://instabox.sh/docs/mcp.md

Sandbox tools for Claude Code, Cursor, Codex, OpenCode and Antigravity over the Model Context Protocol.

The `instabox` CLI and the instabox agent skills are the main way for an agent to use sandboxes. MCP is one more way in, for a client that should call tools without a CLI. The tools are the same either way: `create_machine`, `list_machines`, `get_machine`, `exec`, `read_file`, `write_file`, `suspend_machine`, `resume_machine`, `save_snapshot`, `list_snapshots`, `delete_machine` and `get_usage`. Each answers JSON; a refused call is a tool error with the API's `code` and `message` (for example `missing_scope` and the scope it needs).

### Set it up
```
instabox mcp init claude            # or cursor, codex, opencode, antigravity: the client runs `instabox mcp start`
instabox mcp init claude --hosted   # the hosted server, with the key from INSTABOX_API_KEY
instabox mcp init antigravity --hosted --inline-key   # Antigravity: the key goes into the file
```
Restart the client afterwards. `init` adds one server named `instabox` and keeps the rest of the file:

| Client | Your config | `--project` |
|---|---|---|
| Claude Code | `~/.claude.json` | `./.mcp.json` |
| Cursor | `~/.cursor/mcp.json` | `./.cursor/mcp.json` |
| Codex | `~/.codex/config.toml` | `./.codex/config.toml` |
| OpenCode | `~/.config/opencode/opencode.json` | `./opencode.json` |
| Antigravity | `~/.gemini/config/mcp_config.json` | `./.agents/mcp_config.json` |

With `--hosted` the file holds a reference to `INSTABOX_API_KEY` (`${INSTABOX_API_KEY}` for Claude Code, `${env:INSTABOX_API_KEY}` for Cursor, `env_http_headers` for Codex, `{env:INSTABOX_API_KEY}` for OpenCode), not the key, so the variable must be set where the client starts. `--inline-key` writes the key itself and makes the file readable by you only; keep such a file out of git. Antigravity reads no variables from its config, so `--hosted` needs `--inline-key` there.

### The hosted server
URL: `https://api.instabox.sh/mcp` (Streamable HTTP). Send an API key as `X-API-Key: ibk_...` or `Authorization: Bearer ibk_...`; a signed-in CLI's access token works as a bearer too. Create a key with `instabox keys create --name mcp` or on the **API keys** page. For Claude Code by hand:
```
claude mcp add --transport http instabox https://api.instabox.sh/mcp --header "X-API-Key: $INSTABOX_API_KEY"
```
The server keeps no session: every request is one JSON-RPC message with its own credentials, there is no `Mcp-Session-Id` and no event stream (`GET /mcp` is `405`). A tool call runs the same route as the REST API, so the key's scopes, your plan, quotas and rate limits apply as they do there.

### Local: `instabox mcp start`
A stdio server for clients that start a command. It sends each message to the hosted server with the CLI's credentials: `INSTABOX_API_KEY` when set, otherwise your `instabox login` sign-in, refreshed as needed. It never opens a browser; sign in once first, or set the key.

### Tools and scopes
| Tool | Does | Scope |
|---|---|---|
| `list_machines`, `get_machine` | your sandboxes; one by ID or name | `machines:read` |
| `create_machine` | from a template (default `agent-browser`) or snapshot, waits until it runs | `machines:write` |
| `exec` | `argv` (no shell) or `cmd` (`bash -lc`), `timeout_seconds` up to 600; a non-zero exit is a result | `machines:write` |
| `read_file`, `write_file` | up to 1 MiB; text as UTF-8, other bytes as base64 | `machines:write` |
| `suspend_machine`, `resume_machine` | pause and resume, memory kept; they wait for the operation | `machines:write` |
| `save_snapshot`, `list_snapshots` | save a sandbox's disks; list snapshots | `images:write`, `images:read` |
| `delete_machine` | deletes it for good; `confirm` must be its ID or name | `machines:delete` |
| `get_usage` | counts, running time and limits (or one sandbox's) | `machines:read` or `images:read` |

A tool given a name instead of an ID looks the sandbox up first, which needs `machines:read`. Lifecycle tools wait up to 120 s by default (`wait_seconds`, at most 600: a larger value waits 600, 0 or less the default; `wait: false` returns the operation at once). `read_file` reads up to `max_bytes` (default 256 KiB, at most 1 MiB: a larger value is taken as 1 MiB, 0 or less as the default). For files over 1 MiB use `instabox cp`.
