Skip to content
Raster
Esc
navigateopen⌘Jpreview
On this page

MCP

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

@raster/mcp is raster as a Model Context Protocol server. An MCP client launches it over stdio with an API key in its environment, 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

{
  "mcpServers": {
    "raster": {
      "command": "npx",
      "args": ["@raster/mcp"],
      "env": {
        "RASTER_API_KEY": "sk_...",
        "RASTER_BASE_URL": "https://api.example.com/v1"
      }
    }
  }
}

The server refuses to start without RASTER_API_KEY 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, optionally after moving the pointer
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: your plan allows 3 running machines (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

import { createMcpServer, clientFromEnv } from "@raster/mcp";

const server = createMcpServer({ client: clientFromEnv() });
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.

Was this page helpful?