# Agents

An agent reaches raw.computer three ways, and they answer alike: the [`raw` CLI](/docs/cli) with `--json`, the
[HTTP API](/docs/api), and the **raw MCP server** (`raw mcp`), whose tools are the CLI's commands. The
**raw-computer skill** tells the agent how to use them: signing in, the budget, creating sandboxes, Idempotency-Key and
`next_step`. One command puts both into the harnesses on your machine:

```sh
curl -fsSL https://raw.computer/install.sh | sh
raw login
raw agent setup
```

```text
Claude Code  skill  ~/.claude/skills/raw-computer/SKILL.md  written
             mcp    ~/.claude.json mcpServers.raw           written
Codex        skill  ~/.agents/skills/raw-computer/SKILL.md  written
             mcp    ~/.codex/config.toml [mcp_servers.raw]  written
Cursor       not found  ~/.cursor

The MCP server runs: /home/ada/.local/bin/raw mcp
Restart a harness that is running to load them. Take them out with: raw agent setup --remove
```

During early access every sandbox create answers [`capacity_unavailable`](/docs/errors#capacity_unavailable) ([Limits](/docs/limits#early-access)),
and the skill says so, so an agent tells you rather than retrying. Running commands in a sandbox, files and SSH are not
there yet; the skill and the tools offer only what the API answers today.

## The skill

`raw-computer` is an [Agent Skill](https://agentskills.io): one `SKILL.md` with a name and a description, which a
harness loads when a task needs it. It covers:

- signing in: `raw login` for a person at a browser, an API key in `RAW_API_KEY` where there is none;
- the three ways in (MCP tools, the CLI with `--json`, the API) and that they are the same commands;
- reading the workspace's budget first, and starting nothing when `can_start` is `false`;
- creating a sandbox, the queue, and what `capacity_unavailable` means during early access;
- sending an `Idempotency-Key` and reusing it on a retry;
- branching on `error.code` and doing what `next_step` says.

Install it with `raw agent setup` (below), or into any harness the [skills CLI](https://skills.sh) knows:

```sh
npx skills add https://raw.computer
```

The site publishes it for discovery at two paths:

| Path                                              | What it holds                                                                  |
| ------------------------------------------------- | ------------------------------------------------------------------------------ |
| `/.well-known/agent-skills/index.json`            | the index (discovery v0.2.0): each skill's URL and the sha256 of its file      |
| `/.well-known/skills/index.json`                  | the same skills in the index's earlier form (v0.1: a list of files)            |
| `/.well-known/agent-skills/raw-computer/SKILL.md` | the skill itself; `/.well-known/skills/raw-computer/SKILL.md` is the same file |

There is one copy of each skill. The CLI carries the same file inside its binary, so when the CLI updates itself to a
release whose skill changed, its next run writes the new one where `raw agent setup` put the old.

## `raw agent setup`

```sh
raw agent setup                    # every harness found
raw agent setup --harness codex    # only this one, found or not
raw agent setup --remove           # take out what it wrote
```

It looks in your home directory for each harness and writes, at user level so every project has them:

| Harness     | Found by                        | The skill                                | The MCP server                                      |
| ----------- | ------------------------------- | ---------------------------------------- | --------------------------------------------------- |
| Claude Code | `~/.claude` or `~/.claude.json` | `~/.claude/skills/raw-computer/SKILL.md` | `mcpServers.raw` in `~/.claude.json`                |
| Codex       | `~/.codex` (`CODEX_HOME`)       | `~/.agents/skills/raw-computer/SKILL.md` | a marked `[mcp_servers.raw]` block in `config.toml` |
| Cursor      | `~/.cursor`                     | `~/.agents/skills/raw-computer/SKILL.md` | `mcpServers.raw` in `~/.cursor/mcp.json`            |

Codex and Cursor both read `~/.agents/skills`, so they share one copy (Cursor would otherwise list the skill twice).
`CLAUDE_CONFIG_DIR` moves Claude Code's paths, as it does for Claude Code. The MCP server's command is the full path
of the `raw` binary that ran setup, so a harness started from an app, without your shell's `PATH`, still finds it.

- **Run again, it changes nothing** that is right already: each line says `unchanged`.
- **It touches only its own entry.** Every other key of a JSON file stays, in its order; `config.toml` stays byte for
  byte outside the block between `# >>> raw.computer` and `# <<< raw.computer <<<`. An `env` you add to the raw entry is
  kept.
- **What is yours stays yours:** a server named `raw` that runs something else, or a `[mcp_servers.raw]` you wrote by
  hand, is left alone (`kept`), and `--remove` leaves it too.
- **`--remove`** deletes the skill and the entry it wrote, and nothing else; a skill another harness still set up
  reads is kept.

With `--json`, stdout holds one document:

```json
{
	"object": "agent_setup",
	"action": "setup",
	"mcp_command": ["/home/ada/.local/bin/raw", "mcp"],
	"harnesses": [
		{
			"id": "claude-code",
			"name": "Claude Code",
			"found": true,
			"skills": [{ "path": "/home/ada/.claude/skills/raw-computer/SKILL.md", "status": "written" }],
			"mcp": { "path": "/home/ada/.claude.json", "status": "written" }
		},
		{ "id": "cursor", "name": "Cursor", "found": false, "skills": [], "mcp": null }
	]
}
```

A status is `written`, `unchanged`, `removed`, `absent` (nothing to remove), `kept` (not ours, left alone, with a
`note`) or `failed` (with a `note` saying why; the command then exits `1`). No harness found is not an error: it says
so, and how else to get the skill.

## The MCP server

`raw mcp` is a local MCP server on stdin and stdout, started by the harness. To add it by hand instead of with
`raw agent setup`:

```sh
claude mcp add --scope user raw -- raw mcp   # Claude Code
codex mcp add raw -- raw mcp                 # Codex
```

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

(the last for Cursor's `~/.cursor/mcp.json`, or any harness that reads that shape).

It acts with the token `raw login` stored, or with `RAW_API_KEY` (an API key) or `RAW_TOKEN` and `RAW_WORKSPACE` in its
environment, as the CLI does. `raw mcp --workspace ci` sets the workspace a tool acts in when a call names none, and
`--api` or `RAW_API_URL` another origin. It reads the stored token at every call, so `raw login` while the harness runs
is enough.

| Tool                       | Is                                         | Returns                          |
| -------------------------- | ------------------------------------------ | -------------------------------- |
| `whoami`                   | `raw whoami`                               | `GET /v1/me`                     |
| `workspaces_list`          | `raw workspaces list`                      | `GET /v1/workspaces`             |
| `members_list`             | `raw members list`                         | `GET /v1/members`                |
| `invitations_list`         | `raw invitations list`                     | `GET /v1/invitations`            |
| `workspaces_rename`        | `raw workspaces rename <name>`             | `POST /v1/workspaces/{id}`       |
| `workspaces_set-residency` | `raw workspaces set-residency <residency>` | `POST /v1/workspaces/{id}`       |
| `sandboxes_list`           | `raw sandboxes list`                       | `GET /v1/sandboxes`              |
| `sandboxes_get`            | `raw sandboxes get <sandbox>`              | `GET /v1/sandboxes/{id}`         |
| `sandboxes_create`         | `raw sandboxes create`                     | `POST /v1/sandboxes`             |
| `sandboxes_stop`           | `raw sandboxes stop <sandbox>`             | `POST /v1/sandboxes/{id}/stop`   |
| `sandboxes_start`          | `raw sandboxes start <sandbox>`            | `POST /v1/sandboxes/{id}/start`  |
| `sandboxes_fork`           | `raw sandboxes fork <sandbox>`             | `POST /v1/sandboxes/{id}/fork`   |
| `sandboxes_delete`         | `raw sandboxes delete <sandbox>`           | `DELETE /v1/sandboxes/{id}`      |
| `snapshots_list`           | `raw snapshots list`                       | `GET /v1/snapshots`              |
| `snapshots_get`            | `raw snapshots get <snapshot>`             | `GET /v1/snapshots/{id}`         |
| `snapshots_create`         | `raw snapshots create <sandbox>`           | `POST /v1/snapshots`             |
| `snapshots_delete`         | `raw snapshots delete <snapshot>`          | `DELETE /v1/snapshots/{id}`      |
| `templates_list`           | `raw templates list`                       | `GET /v1/templates`              |
| `templates_get`            | `raw templates get <template>`             | `GET /v1/templates/{id}`         |
| `usage`                    | `raw usage`                                | `GET /v1/usage`                  |
| `budget`                   | `raw budget`                               | `GET /v1/workspaces/{id}/budget` |
| `wallet`                   | `raw wallet`                               | `GET /v1/wallet`                 |

A tool's arguments are its command's arguments and flags, in snake case (`sandbox`, `class`, `size`, `name`,
`template`, `on_full`, `idempotency_key`, `limit`, `starting_after`, `from`, `to`), and `workspace` (an id or a code). A call runs the command
in the server with `--json`, so it does exactly what the command does: the same headers, a new `Idempotency-Key` per
call unless `idempotency_key` names one, the same errors. Its result is one text: the API's JSON as it came. A failure
is marked as an error and holds the API's error, `next_step` included:

```json
{
	"error": {
		"code": "capacity_unavailable",
		"message": "No cell serves std yet: raw.computer is in early access.",
		"next_step": {
			"message": "Read the early-access note; sandbox creates open when the first cell does.",
			"url": "https://raw.computer/docs/limits#early-access"
		},
		"docs": "https://raw.computer/docs/errors#capacity_unavailable"
	}
}
```

A call the schema does not allow (a missing `sandbox`, an argument it does not know, `limit` that is not a number)
is refused before anything is sent, as `invalid_request` in the same shape.

### What it does not serve

Every command is a tool except these. An agent's tools never make, show or revoke credentials: there is no tool for
`raw keys` (a key made by a tool would outlive the session and the person's sign-out), nor for `raw api`, which reaches
every endpoint and so would make keys too. Nor do they change who holds access to a workspace: there is no tool for
`raw members invite`, `raw members set`, `raw members remove` or `raw invitations withdraw` (an invitation mails a link
that lets a person in, and making someone an admin is a person's call); `invitations_list` only reads. `raw login` and `raw logout` are a person's (an approval in a browser, a
sign-out); `raw workspaces use` changes what the person's own commands act in, so a tool names its workspace instead.
`raw version`, `raw update`, `raw agent setup` and `raw mcp` itself are the CLI's own upkeep.

## Without the CLI

An agent with only HTTP reads [llms.txt](https://raw.computer/llms.txt) (every docs page as Markdown), the
[OpenAPI document](https://raw.computer/v1/openapi.json), and the skill at
`https://raw.computer/.well-known/agent-skills/raw-computer/SKILL.md`, and calls the [API](/docs/api) with an API key.
