---
title: MCP server
description: Let Claude Code, Cursor, Codex and other MCP clients create and drive pols sandboxes through the pols mcp server.
---

The `pols` CLI includes an MCP (Model Context Protocol) server. Your MCP client starts it on your machine with `pols mcp` and talks to it over standard input and output; the server calls the pols API with your API key. Through it, an agent can create, stop, resume, fork and delete sandboxes, run commands, read and write files, and use the sandbox's desktop and Chrome.

## Set it up

1. **Install the CLI and log in**

    ```sh
    curl -fsSL https://pols.so/install.sh | sh
    pols login
    ```

    `pols mcp` uses the key `pols login` stored, so your client configuration holds no secret. If Claude Code is installed, the install script and `pols login` offer to register the server for you.

2. **Register the server with your client**

    Every client runs the same command: `pols` with the argument `mcp`. Desktop apps often do not see your shell's `PATH`; if a client cannot find `pols`, use the full path that `command -v pols` prints.

### Claude Code

```sh
claude mcp add --scope user pols -- pols mcp
```

To use `POLS_API_KEY` from your environment instead of `pols login`, add the server to a project's `.mcp.json` and pass the variable through by reference:

```json .mcp.json
{
  "mcpServers": {
    "pols": {
      "command": "pols",
      "args": ["mcp"],
      "env": { "POLS_API_KEY": "${POLS_API_KEY}" }
    }
  }
}
```

### Cursor

In `~/.cursor/mcp.json` for all projects, or `.cursor/mcp.json` for one:

```json mcp.json
{
  "mcpServers": {
    "pols": { "command": "pols", "args": ["mcp"] }
  }
}
```

To use `POLS_API_KEY`, add `"env": { "POLS_API_KEY": "${env:POLS_API_KEY}" }` to the entry.

### Codex

```sh
codex mcp add pols -- pols mcp
```

Or in `~/.codex/config.toml`, where `env_vars` passes `POLS_API_KEY` through when you use it instead of `pols login`:

```toml config.toml
[mcp_servers.pols]
command = "pols"
args = ["mcp"]
env_vars = ["POLS_API_KEY"]
```

### Other clients

Any client that runs stdio servers takes the same command, `pols` with the arguments `["mcp"]`. Never put the key itself into a configuration file; use `pols login` or a reference to an environment variable.

## Tools

Sandboxes are named by ID or name. Lifecycle tools wait until the sandbox has arrived (up to `wait_timeout_seconds`, default 300) unless you set `no_wait`.

| Tool | Does | Hint to the client |
| --- | --- | --- |
| `sandbox_create` | create a sandbox (`name`, `size`, `template`, `env`, `secrets`) and wait until it runs | not destructive |
| `sandbox_list` | list the org's sandboxes, newest first; `include_deleted` adds deleted ones | read-only |
| `sandbox_get` | one sandbox with its size, status, desired state and last error | read-only |
| `sandbox_stats` | a sandbox's CPU, memory and disk use, with the samples of the last hour | read-only |
| `org_stats` | CPU, memory and disk use summed over the running sandboxes | read-only |
| `vault_list` | names and kinds of the vault entries, never values | read-only |
| `sandbox_stop` | stop a sandbox; its disk is kept | destructive |
| `sandbox_resume` | resume a stopped sandbox | not destructive |
| `sandbox_fork` | fork a sandbox into a new running one; `secrets` adds vault entries | not destructive |
| `sandbox_delete` | delete a sandbox and its disk | destructive |
| `sandbox_exec` | run a command (an argument array, such as `["bash", "-lc", "npm test"]`) with `cwd`, `env`, `root`, `stdin` and `timeout_seconds` (default 600); returns exit code, stdout and stderr | open world |
| `file_read` | read a file as UTF-8 text, or base64 for binary data; at most 1 MiB (`max_bytes`) | read-only |
| `file_write` | create or overwrite a file (`encoding` `utf-8` or `base64`, `mode`, `root`); the parent directory must exist | may overwrite |
| `computer_screenshot` | a PNG of the whole 1920x1080 desktop, as image content | read-only |
| `computer_click`, `computer_double_click` | click a mouse button at `x`, `y` | open world |
| `computer_drag` | drag with the left button from `x`, `y` to `to_x`, `to_y` | open world |
| `computer_type` | type up to 10,000 characters into the focused window | open world |
| `computer_key` | press keys, such as `Return`, `ctrl+l` or `Escape Tab Tab` | open world |
| `computer_scroll` | scroll `up`, `down`, `left` or `right` by 1 to 50 clicks at `x`, `y` | open world |
| `browser_cdp` | a WebSocket URL for Playwright or Puppeteer to drive the sandbox's Chrome, valid for 5 minutes | not destructive |

"Open world" tools run arbitrary commands or input in the sandbox, which may reach the internet. Your client may use the hints to decide which calls to confirm with you. Creating, forking and resuming start billed running time; ask your agent to stop sandboxes it no longer needs.

A non-zero exit code from `sandbox_exec` is a result, not a tool error.

## Limits

- `sandbox_exec` returns at most 512 KiB per output stream and sets `truncated` when it cut the output.
- A result larger than the MCP response size limit, such as a very long sandbox list or a large screenshot, comes back as an error instead of a partial result. Use the CLI or the API for those.
- Egress policies, templates, published ports, desktop links and SSH are not available over MCP. Use `pols new --egress`, `pols template`, `pols host`, `pols desktop` and `pols ssh`.

## Example

Ask your agent:

```text
Create a small pols sandbox called demo, clone https://github.com/octocat/Hello-World
into it and show me the README. Stop the sandbox when you are done.
```

It calls the tools roughly in this order:

```text
sandbox_create { "name": "demo", "size": "small" }
sandbox_exec   { "sandbox": "demo", "command": ["bash", "-lc", "git clone https://github.com/octocat/Hello-World"] }
file_read      { "sandbox": "demo", "path": "/home/user/Hello-World/README" }
sandbox_stop   { "sandbox": "demo" }
```
