Skip to main content
Glama
pdfluent-mcp

pdfluent-mcp

Official

An MCP server for PDFluent. It lets Claude Desktop, Claude Code, Cursor, VS Code and any other MCP client read a local PDF as markdown, plain text, tables or positioned blocks, look up its metadata, search it, and fill its form fields into a new file.

Runs on your machine, no upload. It runs over stdio, one process per client, on the pdfluent Python package. Your documents stay on your machine. The server reads and writes only inside the directories you allow. Your documents are never uploaded.

Quick start

uvx pdfluent-mcp setup --root ~/Documents

This registers a stdio server named pdfluent in the user configs of Claude Desktop, Claude Code, Cursor and Codex, then tells you to restart the clients. pipx run pdfluent-mcp setup works the same way. Without --root, file access relies on client MCP roots or PDFLUENT_MCP_ROOTS (SETUP-001, SETUP-002).

Needs Python 3.10 or later and uv. uvx downloads the server and pdfluent on first use and caches them. The balanced quality tier needs model files; pdfluent models install downloads them once.

Related MCP server: file-analyzer-mcp

Tools

Tool

What it does

Example prompt

extract_markdown

The document as markdown in reading order: headings, list items and tables. Running heads and page numbers are left out.

"Summarise ~/Documents/report.pdf."

extract_text

The plain text layer, page by page, each page behind <!-- page N -->.

"Give me the raw text of pages 0 to 2 of contract.pdf."

extract_tables

One entry per table with page, rows and header_rows.

"Pull the tables out of invoice.pdf."

extract_blocks

Positioned blocks with kind, text, page, bbox and confidence. Also lists the pages whose text layer is missing.

"List every heading in manual.pdf with its page."

get_metadata

Page count, title, author, dates, PDF version, encryption, form fields, signatures, bookmarks, and pages with no text layer.

"How many pages does scan.pdf have, and is it encrypted?"

search

The pages that mention a query, with up to three snippets per page.

"Which pages of policy.pdf mention retention?"

list_form_fields

Every form field with name, type, value and page.

"Which fields does application.pdf have?"

fill_form

Fills the named fields and writes a new file. The input is never changed.

"Fill application.pdf with my name and address, save as application-filled.pdf."

vision_advice

One entry per page with needs_vision and its reasons.

"Which pages of scan.pdf need a vision model?"

Page numbers are 0-based in every argument and result.

Tool

Returns

extract_markdown(path, pages?, quality?)

The document as markdown in reading order: headings, list items and tables. Running heads and page numbers are left out. quality is fast (default) or balanced.

extract_text(path, pages?)

The plain text layer, page by page, each page behind <!-- page N -->.

extract_tables(path, pages?)

tables: one entry per table with page, rows (lists of cell strings) and header_rows.

extract_blocks(path, pages?, include_furniture?)

blocks: each with kind, text, page, bbox and confidence, plus level, ordinal or table-cell position where they apply. pages_needing_vision: the pages whose text layer is missing.

get_metadata(path)

Page count, title, author, dates, PDF version, encryption, whether the document has form fields, how many signatures and bookmarks it has, and which pages have no text layer.

search(path, query)

The pages that mention query, with up to three snippets per page.

list_form_fields(path)

Every form field with name, type, value and page.

fill_form(path, values, out_path, overwrite?)

Fills the named fields and writes a new file at out_path. The input is never changed; an existing out_path is refused unless overwrite is true, and a symbolic link there is always refused.

vision_advice(path)

advice: one entry per page with needs_vision and its reasons.

confidence is the probability of the learned model that decided a block's kind; it is null where no model decided it, and null means unknown, not low.

Install per client

Replace /path/to/documents with the directory your PDFs live in.

Claude Desktop

Add to claude_desktop_config.json and restart Claude Desktop:

{
  "mcpServers": {
    "pdfluent": {
      "command": "uvx",
      "args": ["pdfluent-mcp", "--root", "/path/to/documents"]
    }
  }
}

macOS: ~/Library/Application Support/Claude/claude_desktop_config.json, Windows: %APPDATA%\Claude\claude_desktop_config.json.

If Claude Desktop does not find uvx, write its full path (which uvx) in command.

Claude Code

claude mcp add pdfluent -- uvx pdfluent-mcp --root /path/to/documents

Claude Code also offers its working directory as an MCP root, so inside a project claude mcp add pdfluent -- uvx pdfluent-mcp is enough. For a team, put this in the project's .mcp.json:

{
  "mcpServers": {
    "pdfluent": {
      "type": "stdio",
      "command": "uvx",
      "args": ["pdfluent-mcp"]
    }
  }
}

To tell Claude when to reach for these tools, copy skills/pdfluent from this repository to ~/.claude/skills/pdfluent (every project) or to .claude/skills/pdfluent in one project.

Cursor

Add to Cursor (then change /path/to/documents), or add to ~/.cursor/mcp.json or the project's .cursor/mcp.json:

{
  "mcpServers": {
    "pdfluent": {
      "type": "stdio",
      "command": "uvx",
      "args": ["pdfluent-mcp", "--root", "/path/to/documents"]
    }
  }
}

VS Code

Install in VS Code, or from a terminal:

code --add-mcp '{"name":"pdfluent","type":"stdio","command":"uvx","args":["pdfluent-mcp","--root","/path/to/documents"]}'

Or add to .vscode/mcp.json in the workspace (note the key is servers):

{
  "servers": {
    "pdfluent": {
      "type": "stdio",
      "command": "uvx",
      "args": ["pdfluent-mcp", "--root", "/path/to/documents"]
    }
  }
}

Codex

Add to ~/.codex/config.toml:

[mcp_servers.pdfluent]
command = "uvx"
args = ["pdfluent-mcp", "--root", "/path/to/documents"]

One-click install for Cursor and VS Code registers the server with a placeholder root that you then change.

Reference

  • --root DIR (repeatable) is added to the saved command for each directory you name, for example uvx pdfluent-mcp setup --root ~/Documents.

  • --dry-run shows what would change and writes nothing.

  • Before it replaces a config file, setup saves the previous file next to it as <name>.pdfluent-backup-<timestamp> and prints the path.

  • --client claude-desktop|claude-code|cursor|codex limits it to some clients (repeatable).

  • uvx pdfluent-mcp uninstall (or setup --uninstall) removes only the pdfluent entry, and only when it was written by pdfluent-mcp (or with --force). It does not remove the Python package.

Close the clients before you run setup or uninstall. Files are replaced atomically, but no portable call lets setup detect a client that saves the same file in the last instant; a save in that gap can be lost, and where hard links are unavailable the backup is a plain copy and the gap is wider.

Setup keeps unrelated settings and servers, preserves TOML comments, and refuses malformed configs and symbolic links. A different existing pdfluent entry is left alone unless you pass --force (SETUP-003). The saved launcher uses the absolute uvx path when found, otherwise the current Python interpreter, which must stay installed (SETUP-004).

Config files used: Claude Desktop's platform config (see above), Claude Code's ~/.claude.json, Cursor's ~/.cursor/mcp.json, and Codex's ~/.codex/config.toml (CODEX_HOME is respected).

pip install pdfluent-mcp
pdfluent-mcp --root ~/Documents

The server touches nothing outside its roots. Give them with --root (repeat for more than one), with PDFLUENT_MCP_ROOTS (:-separated, ; on Windows), or let the client offer them: Claude Code sends its working directory and --add-dir directories as MCP roots. A server with no root from any of these answers every call with a message saying so. Symlinks are resolved before the check, so a link inside a root cannot point outside it.

A root is as wide as the directory it names. A client that offers /, ~ or a drive as an MCP root widens the server to everything the user can read; if that is not what you want, start the server with --root on the folder your PDFs live in and keep the client's roots narrow.

Three more limits: --max-pages (default 200) is how many pages one call returns, so a long document is read in windows through pages (the engine still parses the whole document for every window; the cap bounds the output, not the work); --max-bytes (default 100 MiB) is the largest document the server opens; --max-output-chars (default 200000) is the longest result one call returns, after which it is cut and says so. All three also read from PDFLUENT_MCP_MAX_PAGES, PDFLUENT_MCP_MAX_BYTES and PDFLUENT_MCP_MAX_OUTPUT_CHARS.

The repository is also a Claude plugin (.claude-plugin/plugin.json). It bundles the pdfluent skill from skills/ and starts the local MCP server with uvx pdfluent-mcp==0.3.0 over stdio, so the plugin runs one pinned PyPI package and nothing else. A local MCP server loads in Claude Code and in Cowork sessions on your computer; chat on claude.ai ignores it.

What the plugin does with data: the server reads PDFs only inside the directories you allow, and writes only the new file that fill_form creates there. It stores nothing, keeps no data after a call returns, and sends nothing to any other service. The one download is the pdfluent package and, if you choose the balanced tier, the model files from pdfluent models install. The plugin is not aimed at people under 18.

pip install -e ".[test]"
pytest

The tests start the server over stdio with a real MCP client, call every tool on the PDFs in tests/fixtures, and try to step outside the allowed roots.

Licence

The same as the pdfluent package it runs on: AGPL-3.0-only, or the PDFluent Commercial Licence. See LICENSE and LICENSE-COMMERCIAL. It depends on mcp (MIT).

Privacy

Privacy policy

Available Tools

9 tools
extract_blocksA
Read-onlyIdempotent

A local PDF as blocks, in reading order, with where each one came from.

Each block has kind (paragraph, heading, list_item, table_cell, header, footer), text, page (0-based), bbox ([x0, y0, x1, y1] in PDF points, origin bottom-left) and confidence: the probability of the learned model that decided the block's kind, or null where no model did -- null means unknown, not low. Headings carry level, list items level and ordinal, table cells row, column, row_span, column_span and header.

pages_needing_vision lists the pages in the window whose text layer is missing: nothing this returns for them is the page's content.

Args: path: The PDF's absolute path on this machine. pages: The 0-based pages to return; omit for the whole document, up to the server's page limit. include_furniture: Keep running heads and page numbers (kinds header and footer); they are left out by default because they repeat on every page and crowd out the content.

Inputs: path (required), pages, include_furniture. Returns: result

extract_markdownA
Read-onlyIdempotent

A local PDF as markdown, in reading order.

Headings become # to ###, list items - and 1., a table a pipe table (or HTML where markdown cannot hold its shape), and a change of page an HTML comment. Running heads and page numbers are left out.

Args: path: The PDF's absolute path on this machine. pages: The 0-based pages to return; omit for the whole document, up to the server's page limit. The whole document is still read, because what a block is gets decided against the rest of it. quality: "fast" (default) or "balanced", the layout-model tier, which needs the models installed on this machine.

Inputs: path (required), pages, quality. Returns: result

extract_tablesA
Read-onlyIdempotent

The tables of a local PDF as rows of cell text.

tables holds one entry per table in reading order: page (0-based), rows (a list of rows, each a list of cell strings, every row the table's width) and header_rows (how many leading rows are header rows). A spanning cell sits at its first row and column; the cells it covers are empty strings.

Args: path: The PDF's absolute path on this machine. pages: The 0-based pages to look at; omit for the whole document, up to the server's page limit.

Inputs: path (required), pages. Returns: result

extract_textA
Read-onlyIdempotent

The plain text layer of a local PDF, page by page.

Each page starts with <!-- page N --> (N 0-based). No reading-order repair and no structure: use extract_markdown for that. A page with no text layer (a scan) yields nothing here; get_metadata names those pages.

Args: path: The PDF's absolute path on this machine. pages: The 0-based pages to return; omit for the whole document, up to the server's page limit.

Inputs: path (required), pages. Returns: result

fill_formA
DestructiveIdempotent

Fill the form fields of a local PDF and write the result to a new file.

values maps a field name from list_form_fields to its new value: a string for a text field, a check box or a combo box, a list of strings for a multi-select list box. The input file is never changed; out_path must be a different path inside an allowed root, and one that does not exist yet unless overwrite is true. A symbolic link at out_path is refused either way. The result names the fields that were set and the names that were unknown; nothing is written when no field was set.

Args: path: The absolute path of the PDF to fill. values: Field name to new value. out_path: The absolute path to write the filled PDF to. overwrite: Replace a file that already exists at out_path.

Inputs: path (required), values (required), out_path (required), overwrite. Returns: result

get_metadataA
Read-onlyIdempotent

What a local PDF is, before reading it.

pages, title, author, subject, created, modified, format (the PDF version), encrypted with the encryption filter name, has_forms with the form_fields count, the number of signatures and bookmarks, and pages_needing_vision: the 0-based pages that draw ink but carry no text layer. A document that needs a password to open answers encrypted: true and needs_password: true and nothing else.

Args: path: The PDF's absolute path on this machine.

Inputs: path (required). Returns: result

list_form_fieldsA
Read-onlyIdempotent

The interactive form fields of a local PDF.

fields holds one entry per field: name (the fully qualified name fill_form takes), type (text, button, choice, signature or unknown), value (the current value, or null) and page (0-based, or null when the field has no widget on a page). An empty list means the document has no AcroForm fields.

Args: path: The PDF's absolute path on this machine.

Inputs: path (required). Returns: result

vision_adviceA
Read-onlyIdempotent

Which pages of a local PDF are worth sending to a vision model, and why.

advice holds one entry per page. needs_vision is true when any reason holds. Today only no_text_layer is measured -- the page draws ink but yields no text, as a scan does; low_confidence_table and figure are always false until the engine can measure them.

Args: path: The PDF's absolute path on this machine.

Inputs: path (required). Returns: result

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A4.1/5.0

    Scored across 9 tools

    Disambiguation5/5

    Each tool targets a clearly distinct output or operation: markdown, plain text, tables, blocks, metadata, search, vision advice, and form listing/filling. Although several tools extract from the same PDF, their descriptions make the boundaries explicit and an agent can reliably choose between them.

    Naming Consistency4/5

    All names use snake_case consistently, which is a clear convention. However, verbs are not fully uniform: extract_* dominates, but vision_advice is noun-oriented and search is a bare verb, so the set is mostly rather than perfectly consistent.

    Tool Count5/5

    Nine tools is well-scoped for a PDF extraction and form-filling server. Each tool has a clear role and none appears redundant or merely decorative.

    Completeness4/5

    The surface covers the main PDF reading lifecycle: metadata, search, multiple extraction formats, vision triage, and form inspection/filling. Minor gaps remain, such as no direct page rendering or image extraction tool, but core workflows are supported.

    Maintenance

    ActivityMaintained
    ResponsivenessOutdated data

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      B
      maintenance
      MCP server that reads PDFs and exposes them as structured Markdown, metadata, outlines, images, and tables to LLM consumers via tools like pdf_read_markdown and pdf_info.
      Apache 2.0
    • F
      license
      Not graded
      quality
      C
      maintenance
      Local MCP server that extracts text and structure from PDF, DOCX, PPTX, SVG, and PNG files, along with directory and metadata listing, for read-only document analysis by LLM hosts.
      -
    • A
      license
      Not graded
      quality
      C
      maintenance
      It enables AI agents and MCP clients to read, fill, create, export, and reconstruct fillable PDF forms, plus extract, search, and edit text and layout blocks, entirely against local files over stdio.
      Apache 2.0
    • A
      license
      A
      quality
      B
      maintenance
      Local MCP server for PDFs: read PDFs into Markdown with tables, charts and OCR, export to Word, PowerPoint, EPUB and Excel, and merge, split, fill, compare and edit PDFs. Works only inside the folders you allow; nothing is uploaded.
      30
      88 npm
      Apache 2.0