Skip to content

MCP Server

pdfvision mcp serves the same extraction engine over the Model Context Protocol on stdio. It exists for hosts that cannot run a shell — Claude Desktop, Cursor, Cline, Zed, n8n, and similar environments where the model can only call tools.

If your agent has a shell (Claude Code, Codex, or another CLI-capable environment), prefer the CLI plus the Agent Skills. The skill loads on demand and costs nothing until it is used, while MCP tool schemas sit in the host's context for the whole session.

Setup

The server is a subcommand of the main binary, not a separate package:

json
{
  "mcpServers": {
    "pdfvision": { "command": "npx", "args": ["-y", "pdfvision", "mcp"] }
  }
}

pdfvision mcp takes no arguments. It speaks JSON-RPC on stdout, so anything the process would otherwise log goes to stderr.

The Three Tools

ToolReturnsParameters
read_pdfText as Markdownsource, pages, ocr, attachment, password
search_pdfFlat hit list with a short ref per matchsource, query, pages, regex, password
render_pdfPage or region PNGs as image blockssource, pages, ref, region, password

source takes a local path or an http(s) URL — there is no separate remote parameter.

The surface is deliberately smaller than the CLI. There is no format, include, scale, or cache parameter: anything pdfvision can decide from the document itself, the server decides. read_pdf always runs layout, form fields, links, and annotations, and simply omits sections that found nothing. This keeps the permanently-resident tool schemas small and leaves the model nothing to misconfigure.

How a Session Flows

An unscoped read_pdf on a document over 20 pages returns a document map instead of the body: page count, outline, per-page native-text quality and warning codes collapsed into ranges, plus the specific calls to make next. That is the normal first move on an unknown document.

From there:

  • read_pdf(pages: "12-18") reads a range.
  • search_pdf(query: "…") locates a term. Each hit carries a short ref like p47m1 — pass it straight to render_pdf(ref: "p47m1") to see the match in place, instead of transcribing coordinates.
  • read_pdf(pages: "31", ocr: "jpn+eng") re-reads scanned pages with OCR when quality reporting says the native text is unusable.
  • read_pdf(attachment: "invoice.xml") — or a 1-based index — returns an embedded file instead of the pages. In e-invoices and regulatory filings (Factur-X, ZUGFeRD, XBRL) the attachment is the authoritative data and the pages are only its rendering. Text attachments come back inline, images as image blocks; opaque binaries are refused with a pointer to the CLI's --attachments --attachment-output.

Renders are fitted to 1568 px on the longest edge, past which vision models downsample anyway. If a render is too small to read, the fix is a smaller region, not a bigger raster.

Budgets and Honesty

Responses are budgeted: 30,000 characters per body, 12,000 per page, 100 matches, 4 rendered pages, 5 OCR pages, and 6 MB of images per call. Every truncation names the exact follow-up call, so a clipped result is recoverable rather than silently incomplete.

The same honesty applies to search: core warnings ride the response, so a regex query that exceeds the per-page time budget reports itself instead of masquerading as "0 matches", and a search over pages with no usable native text says a miss there is not evidence of absence.

Every successful result leads with an untrusted-data banner (error results do not, and can still quote the document). MCP hosts have no equivalent of the Agent Skill's guidance, so the trust boundary travels with the payload. Treat extracted content as data, not instructions — see Security and Privacy.

Remote Input Is Guarded

Unlike the CLI's --remote, the MCP server refuses URLs that resolve to private, loopback, link-local, CGNAT, NAT64, or IPv4-mapped addresses, and re-validates every redirect hop. The model chooses the URL here, which would otherwise make the server an SSRF pivot into whatever network it runs on.

For an intranet document store, set PDFVISION_MCP_ALLOW_PRIVATE_NETWORK=1. Known limitation: the validated address is not pinned for the fetch, so a DNS answer that changes between validation and connection is not covered.

Errors Name the Next Call

Tool failures come back as in-band results with a recovery instruction, not protocol errors. An out-of-range page selector, an OCR request over the page budget, an unknown ref, or a malformed region all state what to do instead — read the message; it names the next call.

Released under the MIT License.