# The MCP server

The same API, spoken over the Model Context Protocol, so an AI client can render a page itself instead of being told how to write the HTTP request. Five tools: one takes a screenshot, one prints a PDF, one reports where the account stands, and two submit and poll a batch. Underneath they are the endpoints the rest of this documentation describes, running the same validation, the same permissions, the same credits and the same cache.

> Connecting a client mints no second credential and grants no second allowance. The tokens are the ones you already have, and what a render costs is counted in the same place it is counted for an HTTP call.

## Connecting a client

The server is at `https://screenshotbuddy.io/mcp`, over streamable HTTP. A client needs two things from you: that URL and an `Authorization` header carrying an API token, the same header a REST call sends. There is no OAuth step and nothing to install.

```bash
claude mcp add --transport http screenshotbuddy https://screenshotbuddy.io/mcp --header "Authorization: Bearer <your API key>"
```

Run it in the project you want the server available in. Claude Code keeps the header with the server, so the token travels on every call and never has to be repeated.

```json
{
    "mcpServers": {
        "screenshotbuddy": {
            "url": "https://screenshotbuddy.io/mcp",
            "headers": {
                "Authorization": "Bearer <your API key>"
            }
        }
    }
}
```

Cursor reads `.cursor/mcp.json` in a project and `~/.cursor/mcp.json` everywhere. Any other client that can add a remote MCP server takes the same two values under whatever it calls them, so if you can give it a URL and a header you can connect it. The [agents page](https://screenshotbuddy.io/agents) carries the same setup for a few more clients, and the one for a client that reads Agent Skills rather than MCP servers.

Whatever the client, commit the URL and not the token. The file that names the server is a file people share; the token is a credential that spends your credits.

## The five tools

`take-screenshot` and `create-pdf` take the parameters their REST counterparts take, from the same description of the API, and are checked by the same rules: a combination refused over `GET https://api.screenshotbuddy.io/v1/snap` is refused here in the same sentence. There is no `pdf` switch to set, because the tool is the mode. That is also what lets each tool publish the exact range its own mode allows rather than the wider of the two.

| Tool | What it answers with |
| --- | --- |
| `take-screenshot` | The image itself, in the conversation, so the model can look at what it captured rather than read a description of it. Beside it comes a line naming the URL, the size and the format, and saying what the render cost. Its parameters are the [screenshot options](https://screenshotbuddy.io/documentation/taking-screenshots.md). |
| `create-pdf` | A link to the rendering rather than the file, because a PDF is far too large to travel in a tool result. The page is rendered on the call, so a failure surfaces to the agent that can act on it rather than to whoever clicks the link. Its parameters are the [PDF options](https://screenshotbuddy.io/documentation/creating-pdfs.md). |
| `check-usage` | The document `GET https://api.screenshotbuddy.io/v1/usage` serves, built by the same code: credits remaining, the plan, when the period resets and both per-minute limits. It reports zero rather than refusing when the account has nothing left, which is the case it exists for. See [the usage endpoint](https://screenshotbuddy.io/documentation/rate-limits.md). |
| `submit-render-batch` | The batch that will carry up to 20 queued renders, answered immediately with its id, every item still queued. Nothing renders on the call and nothing is billed by it: each item is rendered in the background and billed as it would have been one at a time. An item takes the same parameters as the two rendering tools, plus `pdf` to make it a PDF, under the [batch rules](https://screenshotbuddy.io/documentation/batch-renders.md). |
| `check-render-batch` | Where a submitted batch has got to: the document `GET /v1/renders/{batch}` serves, built by the same code, with a signed URL for every finished item and the machine readable reason for every failure. Polling is free the way `check-usage` is free, so poll a few seconds apart until the batch reports itself finished. |

The link `create-pdf` answers with is an ordinary [signed URL](https://screenshotbuddy.io/documentation/signed-urls.md), minted for you rather than by you. It needs no token, so it can be opened or forwarded as it is, and it works for at most 24 hours, less when the cached copy behind it lapses first. It is signed with the signing secret of the token that called the tool, which means it carries that token's permissions and stops working the moment the token is revoked or rotated. Opening it while the copy is still cached is free; a call that turned caching off leaves nothing to serve, so opening that link renders the page again and costs another credit, and the tool says so.

A refusal comes back as the error a REST caller would have received, carrying the same machine readable `code` from the same published set. A model can therefore tell `missing_ability`, which no retry fixes, from `render_timeout`, which one might, without reading the English beside it. See [every error code](https://screenshotbuddy.io/documentation/errors.md).

## The token it authenticates with

The same tokens as the REST API, created in the same place: [Settings, API tokens](https://screenshotbuddy.io/settings/api-tokens). Copy the value while it is on screen and paste it into the header above. There is deliberately no endpoint that mints a token, so an agent cannot create one for itself, and a token that expires or is [rotated](https://screenshotbuddy.io/documentation/authentication.md) stops working here at the same moment it stops working everywhere else.

The two [permissions](https://screenshotbuddy.io/documentation/authentication.md) narrow the tools exactly as they narrow the endpoints. A token granted `screenshot` and not `pdf` can call `take-screenshot` and is refused by `create-pdf`, with the same `missing_ability` the REST call answers `403` with, and the refusal happens before any credit is spent. Narrowing a token before handing it to an agent is the cheapest way to decide what it may spend your credits on.

A call with no token at all, or with one we do not recognise, never reaches a tool: the endpoint answers `401` and the client reports that it cannot connect.

## What it costs

One credit per fresh render, exactly as over HTTP. An identical call repeated is answered from your [cache](https://screenshotbuddy.io/documentation/caching.md) and costs nothing, and both rendering tools say which of the two happened in the line beside their answer, so a model can tell a paid render from a free one and has a reason to reach for the cache. `check-usage` is free whatever it reports.

The per-minute budgets are counted in the flow both surfaces share, not per surface, so a client gets no second allowance by asking over MCP rather than over HTTP. A fresh render spends the render budget; an answer served from the cache comes out of the separate and far larger cached-answer budget instead. See [how the two budgets work](https://screenshotbuddy.io/documentation/rate-limits.md).

The transport carries a ceiling of its own, set at the sum of those two budgets, and it exists for the calls that never reach a render at all: the protocol handshake, the tool listing, `check-usage`. Those cost a slot here and nothing anywhere else, which is why asking how much is left never spends a render. A client using the tools as intended meets its render budget long before the ceiling, which is only there to clip a flood.

## When the capture is not the page you asked for

A login wall, a `404` page and a rate limit notice all render perfectly, so the image alone gives a model no way to tell them from the page it wanted. Over HTTP that is the [`X-Target-Status`](https://screenshotbuddy.io/documentation/taking-screenshots.md) header. Over MCP, `take-screenshot` says it in words, at the end of the line that describes the capture, and only when the target answered something other than a `2xx`: a sentence on every successful capture would be noise a model learns to skip, and the point of this one is that it is unusual.

Nothing about billing moves. The page the server actually served was loaded and rendered, so it costs the credit any render costs. `create-pdf` reports no such status, here as over HTTP, so the document itself is the only thing that says what was captured.

## Batches go over the REST endpoint

The tools render one page at a time, which is what a conversation wants. A set of captures, or a page slow enough to outlast a tool call, is submitted as a batch over the REST endpoint instead: `POST https://api.screenshotbuddy.io/v1/renders` with the same bearer token. There is no batch tool on this server, so that submission is an HTTP request rather than a tool call. See [batch and async renders](https://screenshotbuddy.io/documentation/batch-renders.md).
