# AI assistants (MCP)

Our hosted MCP server lets an AI assistant make PDFs and screenshots on your PDFDispatch plan. Give the assistant one URL, then sign in from it, or give it a key.

## What it is {#about}

MCP (Model Context Protocol) is how AI assistants such as Claude Code, Cursor and VS Code call outside tools. Our
MCP server sits in front of the API and turns each tool call into one API request:

```text
https://mcp.pdfdispatch.com/mcp
```

It speaks Streamable HTTP. The server has no accounts of its own: every request carries your PDFDispatch key, or a
sign-in token that stands for a key of yours, and the API's rules apply as they do to any other client (your plan,
its size limit, its requests per minute). You do not need anything else installed.

The assistant gets the finished file as a link. It can download it into your project, show it to you, or pass it to
another tool, for example to merge it with other PDFs.

## Setup {#setup}

There are two ways in:

- **Sign in** from the assistant: nothing to copy, the assistant opens pdfdispatch.com in your browser. Works in
  claude.ai, Claude Desktop, Claude Code and other clients that support MCP sign-in (OAuth).
- **A key** in a header: for clients without sign-in, and for setups you share with a team or run unattended.

### Sign in {#sign-in}

Add the server by its URL alone, `https://mcp.pdfdispatch.com/mcp`, with no header. The assistant sees that the server
wants a sign-in and opens pdfdispatch.com: log in, check which app is asking, and choose **Connect**. You are sent back
to the assistant, which can now use the tools.

- **claude.ai and Claude Desktop**: Settings, Connectors, **Add custom connector**, and paste the URL.
- **Claude Code**: `claude mcp add --transport http pdfdispatch https://mcp.pdfdispatch.com/mcp`, then run `/mcp`,
  pick `pdfdispatch` and choose **Authenticate**.
- **Other clients**: add a remote (Streamable HTTP) server with the URL and no headers; a client that supports MCP
  sign-in starts it by itself.

Each connected assistant gets its own key on [your Keys page](https://pdfdispatch.com/app/keys), marked **Connection**. Revoke
that key to disconnect the assistant; it stops working within a minute. Connecting the same assistant again later
makes a new key. You never see or copy a connection's key: it stays between PDFDispatch and the MCP server.

### With a key {#key}

You need a key from [pdfdispatch.com/app/keys](https://pdfdispatch.com/app/keys) (see [Authentication](https://docs.pdfdispatch.com/authentication/)).
Make one just for the assistant, so you can revoke it alone. The server expects it in a header:

```text
Authorization: Bearer pdd_...
```

Keys from a contract account work too.

Keep the key in an environment variable or in the client's secret store. Do not write it into a config file that
goes into git: every PDF made with it counts on your plan. The examples below read it from `PDD_KEY`:

```bash
export PDD_KEY=pdd_your_key_here
```

### Claude Code {#claude-code}

The easiest way is our plugin. It adds the server, asks for your key once and keeps it in your system's secure
storage, and comes with a skill that teaches Claude to write print CSS, check layouts with a screenshot and save the
results into your project. In Claude Code:

```text
/plugin marketplace add soriresitve/pdfdispatch-ai
/plugin install pdfdispatch@pdfdispatch
```

To change the key later, open `/plugin` and configure PDFDispatch. The plugin's source is public:
[github.com/soriresitve/pdfdispatch-ai](https://github.com/soriresitve/pdfdispatch-ai).

To add only the server, without the plugin, one command:

```bash
claude mcp add --transport http pdfdispatch https://mcp.pdfdispatch.com/mcp \
  --header "Authorization: Bearer $PDD_KEY"
```

Your shell fills in the key when you run the command, and Claude Code saves it in `~/.claude.json`, outside your
project. By default the server is there only for you in the current project (`--scope local`). Add `--scope user` to
have it in all your projects.

To share the setup with your team, do not use `--scope project` with the command above: it would write your key into
`.mcp.json`, which is meant to be committed. Write `.mcp.json` yourself instead, with a variable that Claude Code
fills in when it starts, so each person uses their own key:

```json
{
  "mcpServers": {
    "pdfdispatch": {
      "type": "http",
      "url": "https://mcp.pdfdispatch.com/mcp",
      "headers": {
        "Authorization": "Bearer ${PDD_KEY}"
      }
    }
  }
}
```

Run `/mcp` in Claude Code to check that the server is connected.

### Cursor {#cursor}

Add the server to `~/.cursor/mcp.json` (all projects) or `.cursor/mcp.json` (one project). Cursor fills in
`${env:PDD_KEY}` from your environment, so the file holds no key:

```json
{
  "mcpServers": {
    "pdfdispatch": {
      "url": "https://mcp.pdfdispatch.com/mcp",
      "headers": {
        "Authorization": "Bearer ${env:PDD_KEY}"
      }
    }
  }
}
```

Set `PDD_KEY` in your shell profile or system environment, then restart Cursor.

The "Add to Cursor" button on [pdfdispatch.com/ai](https://pdfdispatch.com/ai) writes the same entry for you. For the same
habits as the Claude Code plugin, copy our rule file
[`pdfdispatch.mdc`](https://github.com/soriresitve/pdfdispatch-ai/blob/master/cursor/pdfdispatch.mdc) into your
project as `.cursor/rules/pdfdispatch.mdc`.

### VS Code {#vscode}

Add `.vscode/mcp.json` to the project (or run **MCP: Open User Configuration** for all projects). With an input,
VS Code asks for the key the first time the server starts and keeps it in its secret storage:

```json
{
  "inputs": [
    {
      "type": "promptString",
      "id": "pdfdispatch-key",
      "description": "PDFDispatch API key",
      "password": true
    }
  ],
  "servers": {
    "pdfdispatch": {
      "type": "http",
      "url": "https://mcp.pdfdispatch.com/mcp",
      "headers": {
        "Authorization": "Bearer ${input:pdfdispatch-key}"
      }
    }
  }
}
```

The file holds no key, so it can be committed. If you prefer an environment variable,
`"Bearer ${env:PDD_KEY}"` works too.

The "Install in VS Code" button on [pdfdispatch.com/ai](https://pdfdispatch.com/ai) adds the server to your user
configuration in one click; VS Code then asks for the key once.

### Claude Desktop {#claude-desktop}

The simple way is to [sign in](#sign-in) with a custom connector. To use a key instead: Claude Desktop cannot send a
header to a remote server from its config file, but it can reach ours through the `mcp-remote` bridge, which runs
locally with Node.js and adds the header. In `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "pdfdispatch": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "https://mcp.pdfdispatch.com/mcp",
        "--header",
        "Authorization:${AUTH_HEADER}"
      ],
      "env": {
        "AUTH_HEADER": "Bearer pdd_your_key_here"
      }
    }
  }
}
```

Leave no space around the colon in `Authorization:${AUTH_HEADER}`: on some systems a space inside `args` breaks
the command. This file stays on your computer, but it does hold the key, so keep it out of backups you share.

### Other clients {#other-clients}

Any client that can add a remote MCP server over Streamable HTTP with a custom header works. Give it the URL
`https://mcp.pdfdispatch.com/mcp` and the header `Authorization: Bearer pdd_...`, taken from a variable or a secret
field if the client has one.

To give the assistant the same PDF habits as our Claude Code plugin, paste
[`pdf-documents.md`](https://github.com/soriresitve/pdfdispatch-ai/blob/master/rules/pdf-documents.md) into its
instructions (`AGENTS.md`, a system prompt or similar).

A client that cannot send a header can [sign in](#sign-in) instead.

## Tools {#tools}

| Tool | What it does | Main inputs |
|---|---|---|
| `html_to_pdf` | A complete HTML page to PDF, with Chromium. Best for invoices, reports, tickets and certificates. | `html`, optional `header_html` and `footer_html` (printed on every page), `filename` |
| `markdown_to_pdf` | Markdown to a PDF with a clean built-in style. | `markdown`, optional `title`, `css` (replaces the built-in style), `filename` |
| `url_to_pdf` | Prints a public web page. | `url`, optional `screen` (the page's screen styles instead of its print styles), `filename` |
| `office_to_pdf` | Word, Excel, PowerPoint, OpenDocument, RTF, CSV and many more formats to PDF, with LibreOffice. | `files` (links), optional `merge`, `landscape`, `pages` (for example `1-3`) |
| `merge_pdfs` | Two or more PDFs into one, in the order given. | `files` (links) |
| `screenshot` | HTML or a public web page as a PNG, JPEG or WebP image. | `html` or `url`, `width` (1200), `height` (800), `full_page` (true), `selector`, `format` (png) |
| `upload_file` | A link to upload a file from your computer, for `office_to_pdf` and `merge_pdfs`. | `filename` with its extension |

You do not call these yourself. You ask the assistant for a PDF and it picks the tool and fills in the inputs. It
helps to say which tool you want when you care, for example "use markdown_to_pdf" for a plain text report.

### Page options {#page-options}

`html_to_pdf`, `markdown_to_pdf` and `url_to_pdf` share these:

| Input | Default | What |
|---|---|---|
| `paper` | `A4` | `A3`, `A4`, `A5`, `Letter`, `Legal` or `Tabloid`. |
| `landscape` | false | Landscape instead of portrait. |
| `margin` | about 1 cm | All four sides, for example `15mm` or `0.5in`. |
| `print_background` | true | Print background colours and images. |
| `prefer_css_page_size` | false | Take the size from the page's CSS `@page` rule instead of `paper`. |
| `scale` | 1 | Zoom of the content, 0.1 to 2. |
| `outline` | false | PDF bookmarks from the headings. |
| `accessible` | false | A tagged PDF for screen readers. |
| `wait_delay`, `wait_for_expression` | none | Wait before printing, for pages that draw with scripts ([waiting](https://docs.pdfdispatch.com/convert/options/#waiting)). |

Two defaults differ from the API's own: the MCP server uses A4 paper (the API uses Letter) and prints backgrounds
(the API does not), because that is what most people asking an assistant for a document expect.

### Other API fields {#extra-fields}

Every tool except `upload_file` takes `extra_fields`: any other form field of its API route, by its API name. For
example `{"pdfa": "PDF/A-2b"}` for a [PDF/A](https://docs.pdfdispatch.com/pdf/pdfa-pdfua/) file, or `{"userPassword": "..."}` to
[encrypt](https://docs.pdfdispatch.com/pdf/encrypt/) it. A field named there replaces the server's own value. This is also how you reach the
PDF tools that have no tool of their own yet: the conversion routes accept their options in the same request (see
[Combining with PDF options](https://docs.pdfdispatch.com/convert/options/#pdf-options)).

## Results and links {#results}

Every tool that makes a file stores it and answers with a link:

```text
https://mcp.pdfdispatch.com/files/<id>/invoice-1042.pdf
```

The link works for one hour. After that it answers `410 Gone`. To keep a file, have the assistant download it, for
example with `curl -fsSL -o invoice-1042.pdf "<link>"`. The answer also gives the file's name, type, size, the time
the link expires and the API's request id (quote it when you write to support).

Screenshots up to 1 MiB also come back as the image itself, so the assistant can look at the layout it made and fix
it before making the PDF. Larger screenshots come back as a link only.

A link from one tool can go straight into another: make three PDFs, then give their links to `merge_pdfs`.

## Files on your computer {#local-files}

`office_to_pdf` and `merge_pdfs` take their files as links, because the assistant talks to the server over HTTP and
cannot attach a file to a tool call. A link can be:

- a public `https` URL, which the server downloads (public addresses only, nothing on your network),
- a link the server gave as a result,
- an upload made with `upload_file`.

For a file on your computer, the assistant calls `upload_file` with the file's name. It gets an `upload_url` and a
`file_url`, uploads the file with an HTTP `PUT`:

```bash
curl -fsS -T contract.docx "<upload_url>"
```

and then passes `file_url` to the next tool. The upload link works for 15 minutes; the uploaded file is kept for one
hour. Uploads are free and can never be downloaded again, by you or anyone else: only results are served.

The format is read from the file's extension, so the name must have one (`contract.docx`, not `contract`).

## What it costs {#cost}

Each tool call that makes a file counts as one PDF on your plan, exactly like one API request
([what counts](https://docs.pdfdispatch.com/concepts/#one-request-one-pdf)). A screenshot and a merge count as one each. With several Office
files and no `merge`, you get one ZIP and it still counts as one.

These cost nothing:

- `upload_file`, and the upload itself.
- Calls the server refuses before it reaches the API: an empty `html`, an unknown paper size, a `file://` URL, a
  link that cannot be downloaded, a file over 50 MiB.
- Calls the API refuses. They follow the API's rules: a request that fails counts nothing.

In the rare case that the API made the file but the server could not store it, the call still counts, and the error
says so and gives the request id.

## Limits {#limits}

| Limit | Value |
|---|---|
| One MCP request (the HTML or Markdown in a tool call, for example) | 10 MiB |
| One input file, downloaded or uploaded | 50 MiB |
| Files in one `office_to_pdf` or `merge_pdfs` call | 50 |
| Result links | 1 hour |
| Upload links | 15 minutes to upload, then 1 hour to use |
| Inline screenshots | up to 1 MiB |

Your plan's limits apply on top: the API still refuses a request larger than your plan allows (5 MB on the Free plan,
see [Limits per plan](https://docs.pdfdispatch.com/limits/#plans)), and its requests per minute and time limits are the same.

## Errors {#errors}

A failed call comes back as a tool result marked as an error, with a sentence the assistant can act on. When the API
refused the request, the result keeps its status, its code and the link to that code's row in these docs:

```text
PDFDispatch refused the request (HTTP 429, code 42910): ... More: https://docs.pdfdispatch.com/limits/#42910
```

The codes are the API's own, listed in [Limits and errors](https://docs.pdfdispatch.com/limits/#errors). Errors from the PDF engine (a field it
does not accept, for example) come back with their plain text.

A request without a key, or with something that does not look like one, gets `401` before any tool runs. Check that
the header reaches the server: a variable that was not set often sends `Bearer ` with nothing after it. With sign-in,
a `401` with `error="invalid_token"` means the sign-in ran out or the assistant was disconnected on the Keys page:
the assistant signs in again.

## Privacy {#privacy}

- Your key is passed to the API with each request and is never stored or logged by the MCP server.
- With sign-in, the MCP server gets a token from the assistant, asks PDFDispatch whether it is good, and calls the API
  with the key of that connection. The token never goes to the API. PDFDispatch keeps connection keys encrypted, and
  the MCP server holds one only in memory, for at most a minute.
- Results and uploads are stored in the EU, under a random name that cannot be guessed. Results can be downloaded for
  one hour; everything is deleted after one day at the latest.
- The link is the only protection. Anyone who has a result link can download that file during its hour, so treat
  links like the files themselves.
- The server logs one line per tool call: the tool, how long it took, the size and the API's request id. Never the
  key, your input, a file or a link.

## Not yet {#not-yet}

- Tools of their own for split, compress, encrypt and the other [PDF tools](https://docs.pdfdispatch.com/pdf/). Until then, use
  them through [`extra_fields`](#extra-fields) on a conversion.

If a client does not work with the server, or you have a question, write to [support@pdfdispatch.com](mailto:support@pdfdispatch.com).
