---
title: MCP
description: A stdio MCP server any client can launch, with eleven tools over one API key.
---

`raster mcp` is the CLI as a Model Context Protocol server. An MCP client launches it over
stdio, and every tool is one call into the public SDK, which is one call into the public API.

That thinness is the whole security story: there is **no second credential in this process** and
no path to the substrate from it. An MCP client operating a machine can do exactly what a
customer holding that API key can do, and nothing else.

## Configuration

Install the CLI (`npm install -g @rastercompute/cli`) and hand your MCP client the command:

```json
{
  "mcpServers": {
    "raster": {
      "command": "raster",
      "args": ["mcp"],
      "env": {
        "RASTER_API_KEY": "sk_...",
        "RASTER_BASE_URL": "https://api.raster.sh/v1"
      }
    }
  }
}
```

On a machine where you have already run `raster login`, the `env` block is unnecessary — the
server uses the stored credential. Without one, both variables are required: `RASTER_API_KEY`
says who you are and `RASTER_BASE_URL` says where the API is.

The server **refuses to start** without a credential rather than coming up and failing every
tool call with `unauthenticated` — which, to a model, looks like the machine is broken.

Stdout is the protocol. Nothing in the process writes to it; every diagnostic goes to stderr.

## The tools

| Tool                 | What it does                                                  |
| -------------------- | ------------------------------------------------------------- |
| `machine_create`     | create a machine and **wait for its desktop**; returns the id |
| `machine_get`        | current state, size and creation time                         |
| `machine_screenshot` | the screen, plus the geometry it was captured at              |
| `machine_click`      | click at a coordinate                                         |
| `machine_type`       | type text                                                     |
| `machine_scroll`     | scroll at a coordinate                                        |
| `machine_exec`       | run a command as argv                                         |
| `machine_read_file`  | read a file                                                   |
| `machine_write_file` | write a file                                                  |
| `machine_snapshot`   | capture the machine's disk                                    |
| `machine_fork`       | a new machine from a copy of the disk                         |

`machine_create` waits for the desktop before returning, so the id it hands back is one the very
next tool call can use. That matters more here than in an SDK: a model has no good way to poll.

A screenshot comes back as two content parts — the geometry as text, then the image — so the
model reads the coordinate space it is about to click in from the same result.

## What the server tells the model

The server ships instructions with it, and they are the three things that go wrong most:

> Operate a cloud Linux machine with a desktop, a shell and a browser. Create one machine per
> task and reuse it: a machine keeps its filesystem. Take a screenshot before clicking or
> scrolling — every coordinate is in the pixel space of the most recent screenshot, and nothing
> is scaled for you.

## Failures are content, not transport errors

A refused call comes back as a tool result with `isError` and a sentence the model can act on:

```
quota_exceeded: This organization already runs 3 of 3 machines at once; stop one or move to a larger plan (running_machines: 3/3)
```

Throwing instead would give the model a protocol error with nothing in it to try differently. A
`quota_exceeded` carries the limit, the current value and the maximum inline for the same reason.

## Embedding it

```ts
import { Client } from "@rastercompute/sdk";
import { createMcpServer } from "@rastercompute/cli";

const server = createMcpServer({ client: new Client() });
await server.connect(transport);
```

`createMcpServer` takes a `client`, an optional `tools` array to override the shipped set, and a
`version`. `TOOLS` and `toToolError` are exported if you are assembling your own server around
them.
