# MCP server

> Connect Claude, ChatGPT, Cursor, VS Code and other AI agents to your templates over the Model Context Protocol, from sign-in and tools to test mode and limits.



The MCP server lets AI agents use Dynamic Document API as a tool. An agent can list your templates, read the data a template expects, and render PDFs, images and e-invoices, then hand you the download links. It speaks the [Model Context Protocol](https://modelcontextprotocol.io), so it works with any client that supports remote MCP servers.

```
https://mcp.dynamicdocumentapi.com/mcp
```

You sign in with your Dynamic Document API account the first time a client connects, and choose the workspace it may use. There is nothing to install.

## Connect a client [#connect-a-client]

| Client                                | How                                                                                                                               |
| ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| Claude (claude.ai and Claude Desktop) | Add a custom connector with the URL above                                                                                         |
| ChatGPT                               | Add a connector with the URL above, where your plan offers custom connectors                                                      |
| Claude Code                           | `claude mcp add --transport http dynamic-document-api https://mcp.dynamicdocumentapi.com/mcp`                                     |
| Cursor                                | In `.cursor/mcp.json`: `{"mcpServers": {"dynamic-document-api": {"url": "https://mcp.dynamicdocumentapi.com/mcp"}}}`              |
| VS Code                               | In `.vscode/mcp.json`: `{"servers": {"dynamic-document-api": {"type": "http", "url": "https://mcp.dynamicdocumentapi.com/mcp"}}}` |

The client opens the Dynamic Document API sign-in in your browser. After you sign in, the consent screen shows:

* **The permissions:** list templates and read their schemas (`templates:read`), create renders (`render:write`) and read renders and their files (`renders:read`). Nothing else: the server can't change templates, keys, webhooks or settings.
* **The workspace** the client may use, and its region. You need a role that may manage integrations in it.
* **"Unverified app"** and the address you return to. AI clients register themselves when they first connect, so the name they show is their own claim. Continue only if the address belongs to the client you're connecting, such as `claude.ai` for Claude or `http://localhost` for a client on your computer.

The connection lasts until you remove it under **Settings** → **Integrations** in the dashboard. The client renews its access in the background; if you haven't used it for 90 days, it asks you to sign in again.

### With an API key [#with-an-api-key]

Clients that let you set request headers can send an API key instead of signing in:

```
Authorization: Bearer dda_test_…
```

With a key, the key decides whether renders are test renders. A test key (`dda_test_…`) makes every render a test render. A live key (`dda_live_…`) makes every render final and billed, because the API accepts [test renders](/docs/authentication#test-mode) only from test keys. Start with a test key and switch to a live key when you want the final documents.

Prefer signing in where the client supports it: a key in a client's configuration file is easy to leak.

## Tools [#tools]

| Tool                         | What it does                                                                                                                    |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `list_templates`             | Lists the workspace's published templates, filtered by type or name                                                             |
| `get_template_schema`        | Returns the data a template expects: the JSON Schema, a list of fields, an example payload and the template's default e-invoice |
| `render_pdf_from_template`   | Renders a PDF from a template and JSON data, optionally as a hybrid [e-invoice](/docs/e-invoicing)                              |
| `render_image_from_template` | Renders a PNG, JPEG or WebP from an image template                                                                              |
| `render_pdf_from_html`       | Renders a PDF from HTML and CSS                                                                                                 |
| `render_pdf_from_markdown`   | Renders a PDF from Markdown with a built-in theme                                                                               |
| `create_einvoice`            | Creates a validated e-invoice XML: XRechnung, EN 16931, Factur-X and ZUGFeRD profiles                                           |
| `get_render`                 | Returns a render's status and fresh download links                                                                              |
| `merge_pdfs`                 | Merges 2 to 200 PDFs into one, see [PDF tools](/docs/pdf-tools#merge)                                                           |

Each tool result has a short text the agent can quote and the full result as structured data. Download links are signed and expire; `get_render` returns new ones.

The server also offers each template as a resource (`template://tpl_…`, its schema and an example payload), each render as `render://rnd_…`, and two prompts that clients show as starting points: **create\_invoice\_pdf** and **make\_social\_image**.

## Test mode first [#test-mode-first]

After you sign in, renders from the MCP server are [test renders](/docs/authentication#test-mode) unless the agent passes `test: false`: they carry a TEST watermark and aren't billed. The result tells the agent how to get the final document, so ask for it when you're happy with the test version. Every final render counts like an API render.

A connection [with an API key](#with-an-api-key) has no such switch: a test key always makes test renders and a live key always final ones. The server tells the agent which of the two it is.

## Limits [#limits]

* **60 tool calls a minute** per connection. Your plan's API rate limits apply as well.
* **25 seconds per render call.** A render that takes longer keeps running and the tool returns its id with the status `queued` or `processing`; the agent collects the files with `get_render`.
* **IP allowlists:** requests reach the API from Dynamic Document API's own servers, so a workspace with an API IP allowlist (**Settings** → **Security**) refuses them.
* Renders show the source `mcp` in the dashboard's render list and usage breakdown.

## Local server [#local-server]

The server also runs on your computer, with an API key instead of a sign-in. It's published as a Docker image, [`franko727/mcp` on Docker Hub](https://hub.docker.com/r/franko727/mcp), for Intel and Apple silicon. You need Docker installed; the client starts the container itself.

1. Create an API key under **API keys** in the dashboard at [https://app.dynamicdocumentapi.com](https://app.dynamicdocumentapi.com). Start with a test key (`dda_test_…`).
2. Add the server to your client.

Claude Desktop (`claude_desktop_config.json`), Cursor (`.cursor/mcp.json`) and most other clients:

```json
{
  "mcpServers": {
    "dynamic-document-api": {
      "command": "docker",
      "args": ["run", "-i", "--rm", "-e", "DYNAMIC_DOCUMENT_API_KEY", "franko727/mcp"],
      "env": { "DYNAMIC_DOCUMENT_API_KEY": "dda_test_…" }
    }
  }
}
```

Claude Code:

```bash
claude mcp add dynamic-document-api --env DYNAMIC_DOCUMENT_API_KEY=dda_test_… -- docker run -i --rm -e DYNAMIC_DOCUMENT_API_KEY franko727/mcp
```

The local server has the same tools, resources and prompts. As [with an API key](#with-an-api-key) above, the key decides whether renders are test renders. Its requests reach the API from your computer, so a workspace's IP allowlist applies to your address. Starting it with `npx`, without Docker, is coming soon.
