> ## Documentation Index
> Fetch the complete documentation index at: https://docs.anyformat.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# MCP server

> Connect an AI agent host to anyformat over the Model Context Protocol: endpoint, auth, client setup, and the tools available today.

anyformat runs a remote [Model Context Protocol](https://modelcontextprotocol.io) server. Any MCP host, such as Claude Code, Claude Desktop or Cursor, connects to it and calls anyformat as tools. The tools are the same v3 endpoints you call by hand, under the same API key, rate limits and error envelope.

## Endpoint

| | |
| - | - |
| URL | `https://api.anyformat.ai/mcp` |
| Transport | Streamable HTTP, stateless. Every request is independent; there are no sessions to resume. |
| Auth | `Authorization: Bearer <your af_... key>` |

`/mcp` and `/mcp/` are the same endpoint. There is no stdio package; a host that only speaks stdio connects through a bridge, `mcp-remote` or `fastmcp-remote` (below).

## Auth

The server accepts the same `af_...` API key as the REST API, as a bearer token. A key that works on `/v3` works on `/mcp`. Mint one at [app.anyformat.ai/api-key](https://app.anyformat.ai/api-key).

A missing or unknown key returns `401` with the API's [error envelope](/api-reference-v3/introduction#errors) (`MISSING_API_KEY` or `INVALID_API_KEY`). Repeated invalid keys from one address are throttled and return `429` with `Retry-After`, as on the REST API.

There is no OAuth flow today.

## Connect

<Tabs>
  <Tab title="Claude Code">
    Add the server with the `claude mcp add` command. `--scope user` makes it available in every project; `--scope project` writes it to the project's `.mcp.json`.

    ```bash theme={null}
    claude mcp add --transport http anyformat https://api.anyformat.ai/mcp \
      --header "Authorization: Bearer $ANYFORMAT_API_KEY" --scope user
    ```

    Or write `.mcp.json` in the project root. Claude Code expands `${VAR}` from the environment.

    ```json theme={null}
    {
      "mcpServers": {
        "anyformat": {
          "type": "http",
          "url": "https://api.anyformat.ai/mcp",
          "headers": { "Authorization": "Bearer ${ANYFORMAT_API_KEY}" }
        }
      }
    }
    ```

    Check with `claude mcp list`, then ask Claude to list your workflows.
  </Tab>

  <Tab title="Cursor">
    Add the server to `~/.cursor/mcp.json` (all projects) or `.cursor/mcp.json` (one project). Cursor expands `${env:VAR}`.

    ```json theme={null}
    {
      "mcpServers": {
        "anyformat": {
          "url": "https://api.anyformat.ai/mcp",
          "headers": { "Authorization": "Bearer ${env:ANYFORMAT_API_KEY}" }
        }
      }
    }
    ```
  </Tab>

  <Tab title="Claude Desktop and stdio clients">
    A host that only launches local stdio servers connects through [`mcp-remote`](https://github.com/geelen/mcp-remote), which bridges stdio to the HTTP endpoint. Put the header value in an environment variable so the space in `Bearer ...` survives argument parsing.

    ```json theme={null}
    {
      "mcpServers": {
        "anyformat": {
          "command": "npx",
          "args": [
            "mcp-remote",
            "https://api.anyformat.ai/mcp",
            "--header",
            "Authorization:${AUTH_HEADER}"
          ],
          "env": { "AUTH_HEADER": "Bearer af_..." }
        }
      }
    }
    ```

    [`fastmcp-remote`](https://pypi.org/project/fastmcp-remote/) is the same bridge from the Python ecosystem, run with `uvx`. It takes the header as one `Name: Value` argument, so no environment variable is needed. On the command line:

    ```bash theme={null}
    uvx fastmcp-remote https://api.anyformat.ai/mcp \
      --header "Authorization: Bearer $ANYFORMAT_API_KEY"
    ```

    And as a host config entry:

    ```json theme={null}
    {
      "mcpServers": {
        "anyformat": {
          "command": "uvx",
          "args": [
            "fastmcp-remote",
            "https://api.anyformat.ai/mcp",
            "--header",
            "Authorization: Bearer af_..."
          ]
        }
      }
    }
    ```

    Both bridges default to the streamable HTTP transport, and both skip their OAuth flow when an `Authorization` header is given.

    For Claude Desktop the file is `claude_desktop_config.json`, under Settings, Developer.
  </Tab>

  <Tab title="Any client">
    Send MCP JSON-RPC over HTTP `POST` to the endpoint with the bearer header. This `initialize` call shows the handshake:

    ```bash theme={null}
    curl -X POST https://api.anyformat.ai/mcp \
      -H "Authorization: Bearer $ANYFORMAT_API_KEY" \
      -H "Content-Type: application/json" \
      -H "Accept: application/json, text/event-stream" \
      -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"0"}}}'
    ```
  </Tab>
</Tabs>

## Tools

The server exposes these 21 tools today. Each API tool calls the matching v3 endpoint under your key, so the endpoint page is the reference for its inputs, outputs and errors. The `Scope` column names the key scope the tool needs. For a worked end-to-end session with real payloads, see [Agents over MCP](/guides/mcp).

| Tool | What it does | Endpoint | Scope |
| - | - | - | - |
| `list_workflows` | Lists your organization's workflows, newest first. Takes `query: { limit, cursor }`; `limit` is 1 to 100, default 20. Keyset-paginated: pass the response's `next_cursor` back as `cursor` until it is null. | [List workflows](/api-reference-v3/workflows/list) | `read` |
| `get_workflow` | Returns a workflow with its complete typed graph inline: the latest version by default, or the one named by an optional `version` (a `version_id` from `list_workflow_versions`). Takes `workflow_id`. The `{ name, description, nodes, edges }` sub-shape is what `update_workflow` accepts. | [Get workflow](/api-reference-v3/workflows/get) | `read` |
| `list_workflow_versions` | Lists a workflow's versions, newest first; item 0 of the first page is the version runs use. Takes `workflow_id` and `query: { limit, cursor }`. Keyset-paginated like `list_workflows`. Versions are read-only, for comparison and audit: read one with `get_workflow` and its `version`. | [List workflow versions](/api-reference-v3/workflows/versions) | `read` |
| `create_workflow` | Creates a workflow from an explicit typed graph. Takes `body: { name, description, nodes, edges }`; omit `persistent_id` on every field. An invalid topology is rejected with `TOPOLOGY_INVALID` and a `detail.violations[]` list naming the rules broken. | [Create workflow](/api-reference-v3/workflows/create) | `write` |
| `update_workflow` | Replaces a workflow's typed graph atomically. Takes `workflow_id` and `body`. Echo each existing field's `persistent_id` unchanged, including across renames; omit it only for new fields. | [Update workflow](/api-reference-v3/workflows/update) | `write` |
| `delete_workflow` | Deletes a workflow and all its document packets and runs. Takes `workflow_id`. Irreversible. Returns `{ "deleted": "<workflow_id>" }`. | [Delete workflow](/api-reference-v3/workflows/delete) | `write` |
| `stage_files` | Turns files on the caller's machine into references the other tools accept. Takes `body: { files: [{ filename, declared_size, content_type }] }` (1 to 10) and returns, per file, a presigned upload form for the bytes plus a short `object_id` to refer to them by. Staged bytes belong to no workflow and expire within a day. | [Agents over MCP](/guides/mcp#1-stage-a-local-file) | `write` |
| `upload_documents` | Imports files into one document packet, without running it. Takes `workflow_id` and `body: { urls, staged_files }`: HTTPS URLs fetched server-side, `{ object_id, filename }` references from `stage_files`, or a mix, 1 to 100 in total. A URL-fetched file is named by its response's `Content-Disposition`, else by the URL's path; a staged file by its `filename`. The packet is registered only after every fetch succeeded. | [Upload from URL](/api-reference-v3/workflows/upload-from-url) | `write` |
| `run_document_packet` | Runs, or re-runs, a packet on the latest version of its workflow. Takes `document_packet_id` and an optional `idempotency_key`; every call without a key starts a new billed run. A keyed retry replays the original run with its current status, so a run that already finished answers `processed`. | [Run a document packet](/api-reference-v3/document-packets/run) | `write` |
| `parse_document` | Parses one document with the fast lite parse. It is a quick one-off, not a production pipeline. Takes `body: { url }` with an HTTPS URL, or `body: { object_id, filename }` from `stage_files`. Returns a run reference. Once the run is `processed`, the markdown is at `results.parse.markdown`. Save it yourself: quick-parse output is not retained. | [Parse from URL](/api-reference-v3/parse/parse-from-url) | `write` |
| `get_run` | Returns one run with its results inline; `results` is null until `status` is `processed`. Takes `run_id`, an optional `wait_seconds` from 0 to 60, and an optional `parse_output`. With `wait_seconds`, the tool polls every 2 seconds until the run is `processed`, `error` or `cancelled`, or until the budget runs out. `parse_output` selects how much of the parsed document comes back: `markdown` by default, or `none`, `text`, `blocks` or `all`. `none` is by far the smallest response when only the extracted fields matter. | [Get run](/api-reference-v3/runs/get) | `read` |
| `list_workflow_runs` | Lists a workflow's runs, newest first. Takes `workflow_id` and `query: { limit, cursor, document_packet_created_after, document_packet_created_before }`. Keyset-paginated like `list_workflows`. | [List runs](/api-reference-v3/runs/list) | `read` |
| `list_workflow_document_packets` | Lists a workflow's document packets, newest first. Takes `workflow_id` and `query: { limit, cursor }`. Keyset-paginated like `list_workflows`. | [List document packets](/api-reference-v3/document-packets/list) | `read` |
| `get_document_packet` | Returns one packet: status, timestamps, its files and `latest_run_id`, which is null until the packet has been run. Takes `document_packet_id`. | [Get document packet](/api-reference-v3/document-packets/get) | `read` |
| `delete_document_packet` | Deletes a packet and all its files. Takes `document_packet_id`. Irreversible. Returns `{ "deleted": "<document_packet_id>" }`. | [Delete document packet](/api-reference-v3/document-packets/delete) | `write` |
| `ask_knowledge` | Asks a question about the content of a workflow's documents and returns an answer with citations, each resolved to a page and a region of the source PDF. Takes `workflow_id` and `body: { question, thread_id }`; a `thread_id` you mint (prefix `kb-`) keeps follow-ups in context. The workflow needs a [Knowledge](/guides/nodes/knowledge) node (`KNOWLEDGE_NOT_ENABLED` otherwise). `KNOWLEDGE_NOT_READY` means no usable snapshot is available. Wait when indexing is in progress; otherwise a member indexes or updates in the app's Knowledge tab. MCP exposes no indexing action. `KNOWLEDGE_THREAD_MISMATCH` means `thread_id` belongs to a different workflow — use a new one or ask the workflow that owns it. `KNOWLEDGE_UNSUPPORTED_SNAPSHOT_FORMAT` means the published snapshot is in a format this deployment no longer reads, permanent until an operator republishes it. Every question is billed and can take a few minutes. | [Ask the knowledge base](/api-reference-v3/knowledge/ask) | `write` |
| `list_knowledge` | Lists a workflow's knowledge corpus tree: every source document plus the generated navigation pages (`kind: "index"`), sorted by path. Takes `workflow_id`, an optional `folder` narrowing to entries directly in it or beneath it, and an optional `cursor`. Keyset-paginated: pass the response's `next_cursor` back as `cursor` until it is null. Same `KNOWLEDGE_NOT_ENABLED`/`KNOWLEDGE_NOT_READY`/`KNOWLEDGE_UNSUPPORTED_SNAPSHOT_FORMAT` conditions as `ask_knowledge`, but never billed. | [List knowledge entries](/api-reference-v3/knowledge/list) | `read` |
| `search_knowledge` | Ranks a workflow's knowledge corpus by relevance (BM25) to `query` — for a topic-style question, not an exact string. Takes `workflow_id`, `query` (1 to 512 characters), an optional `folder` narrowing to one top-level source folder, and an optional `limit` (1 to 50, default 10). An unrecognized `folder` is `NOT_FOUND`. Each hit carries a relevance `rank` and a `snippet` around the best-matching term. Same `KNOWLEDGE_NOT_ENABLED`/`KNOWLEDGE_NOT_READY`/`KNOWLEDGE_UNSUPPORTED_SNAPSHOT_FORMAT` conditions as `ask_knowledge`, but never billed. | [Search knowledge](/api-reference-v3/knowledge/search) | `read` |
| `read_knowledge` | Reads one document's or navigation page's text from a workflow's knowledge corpus. Takes `workflow_id` and `path` (from `list_knowledge` or `search_knowledge`). `content` is capped; `truncated` is `true` when it was cut off. An unknown `path` is `NOT_FOUND`. Same `KNOWLEDGE_NOT_ENABLED`/`KNOWLEDGE_NOT_READY`/`KNOWLEDGE_UNSUPPORTED_SNAPSHOT_FORMAT` conditions as `ask_knowledge`, but never billed. | [Read knowledge](/api-reference-v3/knowledge/read) | `read` |
| `download_knowledge` | Returns a presigned URL for a workflow's whole knowledge corpus, as a tar. Possession of the URL is read permission for the corpus until it expires (15 minutes) — prefer `list_knowledge`/`read_knowledge` for anything that stays inside this API. Takes only `workflow_id`. Same `KNOWLEDGE_NOT_ENABLED`/`KNOWLEDGE_NOT_READY` conditions as `ask_knowledge`, but never billed. | [Download knowledge](/api-reference-v3/knowledge/download) | `read` |
| `request_approval` | Shows you an approval card with a `summary`, optional `details` and Approve/Reject buttons, then makes the agent wait for your click. Calls no endpoint and changes nothing on anyformat; the delete tools ask the agent to call it first. | none | none |

The graph a tool takes or returns is the one on the [Node schemas](/api-reference-v3/node-schemas) page. A host reads the full input schema from the server, so an agent can compose a valid graph without you pasting the schema into the conversation.

The document flow an agent follows is `upload_documents`, then `run_document_packet` with an `idempotency_key`, then `get_run` with `wait_seconds` set, so one call waits for the result instead of the agent polling. A file on the agent's machine enters the flow through `stage_files`: upload the bytes to the returned form, then pass `{ object_id, filename }` to `parse_document` or `upload_documents`. Pass the short id, never the kilobytes-long presigned `read_url`. `parse_document` is the one-call shortcut when the agent needs only the markdown of a single document. Save that markdown: its output is not retained.

Every extracted `value` is a JSON string on the wire, whatever the field's declared type. A `float` field answers `"4594.62"`, so parse numbers before comparing them. A field's `confidence` is a number from 0 to 100, such as `87.5`, or `null` when none was produced. Each field carries the `evidence` text and the page it was read from.

<Note>
  `delete_workflow` and `delete_document_packet` take effect at once: the API stops returning the workflow or packet immediately. A deleted packet is deleted permanently with its files and runs, as are the packets and runs of a deleted workflow. The workflow's own files stay in storage for a grace window of 14 days, or 24 hours for an organization with zero data retention, before they become eligible for a permanent purge. No MCP tool undoes a delete. Their descriptions ask the agent to call `request_approval` with a one-line summary and to wait for your approval before deleting. That approval is a card the host shows you; a host that does not render it still sees the `destructiveHint` and can ask you its own way.
</Note>

### Tool annotations

Every API tool declares the standard MCP annotations, so a host can decide when to ask you before calling it. `request_approval` declares none: it touches nothing on anyformat.

| Tool | `readOnlyHint` | `destructiveHint` | `idempotentHint` |
| - | - | - | - |
| `list_workflows` | true | | true |
| `get_workflow` | true | | true |
| `list_workflow_versions` | true | | true |
| `create_workflow` | false | | false |
| `update_workflow` | false | | false |
| `delete_workflow` | false | true | true |
| `stage_files` | false | | false |
| `upload_documents` | false | | false |
| `run_document_packet` | false | | false |
| `parse_document` | false | | false |
| `get_run` | true | | true |
| `list_workflow_runs` | true | | true |
| `list_workflow_document_packets` | true | | true |
| `get_document_packet` | true | | true |
| `delete_document_packet` | false | true | true |
| `ask_knowledge` | false | | false |
| `list_knowledge` | true | | true |
| `search_knowledge` | true | | true |
| `read_knowledge` | true | | true |
| `download_knowledge` | true | | true |
| `request_approval` | | | |

All API tools set `openWorldHint: false`: they only reach anyformat.

## Skill resources

The server also serves the anyformat skill, the design know-how an agent reads before it builds a workflow, as MCP resources. It is the same content that ships as the [`@anyformat/skill`](https://www.npmjs.com/package/@anyformat/skill) npm package; the resource path needs no install step.

| Resource | Content | MIME type |
| - | - | - |
| `skill://anyformat/SKILL.md` | The skill itself: auth, the tool flow, how to design a graph and read results. | `text/markdown` |
| `skill://anyformat/_manifest` | `{ "skill": "anyformat", "files": [ { "path", "size", "hash" } ] }`: every file of the skill, so a host can check what it cached against what the server serves. | `application/json` |
| `skill://anyformat/reference/curl.md` | Raw HTTP calls, the result and error envelopes, and the multipart local-file upload no MCP tool covers. | `text/markdown` |
| `skill://anyformat/reference/sdks.md` | TypeScript and Python SDK quickstarts. | `text/markdown` |

A host discovers them with `resources/list` and reads one with `resources/read` and its URI; any valid key can read them, whatever its scopes. In Claude Code, mention a resource as `@anyformat:skill://anyformat/SKILL.md` to pull it into the conversation. Over plain HTTP:

```bash theme={null}
curl -X POST https://api.anyformat.ai/mcp \
  -H "Authorization: Bearer $ANYFORMAT_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":2,"method":"resources/read","params":{"uri":"skill://anyformat/SKILL.md"}}'
```

## Authorization and limits

Authorization follows the API key. Whatever the key can do on `/v3`, it can do over `/mcp`, on the key's organization only. Each API tool needs one of the key's scopes: the list and get tools need `read`, the tools that create, run, update or delete need `write`; `request_approval` needs none. A tool the key cannot call is left out of the server's tool list, and a direct call to it is answered as an unknown tool. Every tool call passes through the gateway's normal rate limiting, so the [rate limit tiers](/api-reference-v3/introduction#rate-limits) apply per tool call as they do per request.

## Errors

A failed tool call returns an MCP tool error whose message is the API's error envelope as JSON: `{ error, detail, error_code, retryable, request_id, status }`. Read `error_code` to branch, `status` for the equivalent HTTP status code, and quote `request_id` in a support request. A `TOPOLOGY_INVALID` error carries `detail.violations[]`, one entry per broken rule, with the node ids involved.

## Related

* [Agents over MCP](/guides/mcp): a worked end-to-end session with real payloads, covering stage, build, run and read.
* [Coding assistant](/guides/coding-assistant): the `@anyformat/skill` package, the same content as the [skill resources](#skill-resources) above.
* [Python SDK](/api-reference-v3/sdks/python) and [TypeScript SDK](/api-reference-v3/sdks/typescript): the same calls from code.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.