pdf-extract-mcp
pdf-extract-mcp
一个模型上下文协议(Model Context Protocol,MCP)服务器,用于确定性地从非结构化 PDF 文档中提取结构化数据——采用纯文本提取加正则表达式/启发式字段匹配,在提取时不调用任何 LLM API。
功能特性
真正的 MCP 服务器——基于官方 MCP Python SDK(2.x)构建,通过 stdio、SSE 或 streamable HTTP 使用该协议通信。由一项端到端测试验证,该测试使用官方客户端驱动实际服务器。
Schema 驱动的提取——将 extract_fields 指向任意 JSON Schema,即可返回与你所请求字段完全对应的结构化 JSON。
确定且可检查——正则/启发式匹配,无 LLM API 调用,无隐藏成本,无黑盒。每次提取都可重复并可审计。
人类可读的验证报告——validate_against_schema 会逐字段解释其通过、失败或缺失的原因。
预置 schema——invoice、resume 和 purchase_order 开箱即用,另附合成示例 PDF,所有功能均可开箱演示。
优雅的错误处理——损坏的 PDF、缺失的文件和错误的 schema 会返回结构化错误,绝不抛出堆栈跟踪。
Related MCP server: StructureAI MCP Server
什么是 MCP,为什么它很有用
模型上下文协议(Model Context Protocol)是一种开放标准,允许 AI 助手(Claude、Cursor 等)通过持久化的双向连接调用外部工具。助手无需把 PDF 文本粘贴到聊天中再让模型“自行解读”,而是可以直接调用 pdf-extract-mcp,接收与你提供的 schema 匹配的结构化 JSON,并据此采取行动。由于这里的提取是确定性的(正则表达式 + 启发式),而非概率性的模型调用,因此每个结果都可检查、可重复且成本低廉。这使得它非常适合自动化文档流水线(发票到财务、简历到 ATS、采购订单到采购流程)——在这些场景中,你需要知道为什么某个字段会以某种方式被提取。
安装
cd pdf-extract-mcp
python3 -m venv .venv
source .venv/bin/activate
make install # pip install -e ".[dev]" (installs the console script too)或者,使用纯 pip:
pip install -e ".[dev]"该服务器使用官方 MCP Python SDK(mcp >= 2.x,当前发布线,提供 MCPServer API)。pdfplumber 负责文本提取,jsonschema 负责验证,reportlab 生成示例 PDF。
安装后还会提供一个 pdf-extract-mcp 控制台脚本,因此你可以从任何位置运行服务器:
pdf-extract-mcp # stdio (default)
pdf-extract-mcp --transport streamable-http --host 127.0.0.1 --port 8000运行
python server.py这会通过 stdio 提供 MCP 服务(这是默认方式,也是 Claude Code / Claude Desktop 所期望的方式)。你还可以将其暴露为网络服务:
python server.py --transport streamable-http --host 127.0.0.1 --port 8000
python server.py --transport sse --host 127.0.0.1 --port 8001连接 Claude Code / Claude Desktop
Claude Code — 在项目根目录添加 .mcp.json:
{
"mcpServers": {
"pdf-extract": {
"command": "python",
"args": ["/absolute/path/to/pdf-extract-mcp/server.py"],
"env": {}
}
}
}Claude Desktop — 将同样的配置块添加到你的 Claude Desktop 配置中(claude_desktop_config.json,在 macOS 上位于 ~/Library/Application Support/Claude/ 下):
{
"mcpServers": {
"pdf-extract": {
"command": "python",
"args": ["/absolute/path/to/pdf-extract-mcp/server.py"]
}
}
}保存后重启客户端。你应该会看到三个新工具:extract_fields、validate_against_schema 和 list_supported_document_types。
工具
工具 | 用途 |
extract_fields(pdf_path, schema) | 从 PDF 中提取与 JSON Schema 匹配的结构化字段 -> {"ok": true, "data": {...}} |
validate_against_schema(data, schema) | 根据 schema 检查提取的数据 -> 通过/失败/缺失报告,附人类可读的原因 |
list_supported_document_types() | 列出随预置 schema 一起提供的文档类型 |
extract_fields 的 schema 参数接受 JSON Schema 对象、内置 schema 的名称(例如 "invoice"),或 .json schema 文件的路径。内置 schema 位于 schemas/ 目录:
invoice — vendor_name、invoice_number、total_amount、due_date(必填)+ issue_date、customer_name
resume — name、email(必填)+ phone、skills
purchase_order — po_number、vendor_name、total_amount(必填)+ issue_date、customer_name
工作示例
首先生成示例 PDF(仓库中已存在;随时可用以下命令重新生成):
python sample_pdfs/generate_samples.py现在使用内置的 invoice schema 名称对示例发票调用 extract_fields。在 Claude Code 中,你只需说“使用 invoice schema 从 sample_pdfs/invoice.pdf 中提取字段”;其底层会发出一个等价于以下内容的工具调用:
{
"name": "extract_fields",
"arguments": {
"pdf_path": "/absolute/path/to/pdf-extract-mcp/sample_pdfs/invoice.pdf",
"schema": "invoice"
}
}实际预期结果:
{
"ok": true,
"data": {
"vendor_name": "Acme Widgets Corp",
"invoice_number": "INV-2024-0087",
"total_amount": 1750.0,
"due_date": "April 1, 2024",
"issue_date": "March 1, 2024",
"customer_name": "Globex Industries"
},
"text_length": 372
}使用同一个 schema 将数据提供给 validate_against_schema:
{
"ok": true,
"valid": true,
"passed": ["customer_name", "due_date", "invoice_number", "issue_date", "total_amount", "vendor_name"],
"failed": [],
"missing": [],
"summary": "Valid: all 6 present field(s) conform to the schema.",
"error": null
}直接从 Python 运行这些代码即可实时查看效果:
import json
from tools.extract import extract_fields
from tools.validate import validate_against_schema
schema = json.load(open("schemas/invoice.json"))
result = extract_fields("sample_pdfs/invoice.pdf", schema)
print(result["data"])
print(validate_against_schema(result["data"], schema))server.py 中 MCP 工具注册的工作原理
这是项目的核心,因此值得准确理解 SDK 在幕后为你做了些什么。
1. 创建服务器对象。
from mcp.server.mcpserver import MCPServer
mcp = MCPServer(
"pdf-extract-mcp",
title="PDF Extract MCP",
description="Deterministic structured-data extraction from PDF documents",
version="0.2.0",
)MCPServer 是 mcp SDK 2.x 的服务器类。它实现了 MCP 线路协议:知道如何应答客户端在 MCP 握手期间发送的 JSON-RPC 消息(initialize、tools/list、tools/call 等)。构造函数参数是元数据——服务器的名称(协议握手所必需)以及可选的 title/description/version,客户端可能会将其展示给用户。
2. 使用装饰器注册每个工具。
@mcp.tool()
def extract_fields(pdf_path: str, schema: dict) -> dict:
"""Extract structured fields from an unstructured PDF ..."""
return _extract_fields(pdf_path, schema)该装饰器为你完成三项工作:
名称注册 — 函数名 extract_fields 成为客户端调用该工具时使用的工具名称。(你可以通过 @mcp.tool(name="...") 覆盖它。)
Schema 推断 — SDK 会检查函数的类型注解(pdf_path: str、schema: dict),并自动生成工具的 JSON 输入 schema。这正是 MCP 客户端在调用之前就知道 pdf_path 是字符串、schema 是对象的原因。这与 FastAPI 使用的模式相同——类型就是契约。
描述 — 文档字符串(docstring)成为工具的描述,Claude 会阅读它来决定何时调用该工具以及使用哪些参数。
因此,当客户端询问服务器“你能做什么?”(tools/list)时,SDK 会为每个被装饰的函数返回名称、描述和推断出的输入 schema——无需手动维护注册表来保持同步。
3. 函数体就是普通的 Python 代码。
当客户端调用该工具(带参数的 tools/call)时,SDK 会反序列化 JSON 参数,用它们调用你的函数,并将返回值通过线路协议序列化回去。返回值就是客户端看到的内容——这就是为什么这些工具总是返回普通的可 JSON 序列化字典,并且绝不抛出异常:异常会变成不透明的协议错误,而结构化的 {"ok": false, "error": "..."} 字典则可以被 Claude 读取并作出响应。实际的提取/验证逻辑位于 tools/extract.py 和 tools/validate.py 中,因此即使没有 MCP 客户端,这些逻辑也保持可单元测试。
4. 运行它。
if __name__ == "__main__":
main() # argparse -> mcp.run(transport="stdio")mcp.run(transport="stdio") 启动协议循环:它从 stdin 读取换行分隔的 JSON-RPC 请求,将其分发给已注册的工具,并将响应写入 stdout。这就是整个服务器——没有 HTTP 框架、没有路由、没有手动请求处理。(对于 streamable-http / sse,相同的 run() 调用会启动一个内部 ASGI 应用。)
还有一个值得注意的细节:extract_fields 使用一个很小的辅助函数 _load_schema,它接受 schema 字典、内置 schema 名称或文件路径——因此同一个工具既可以处理 "invoice",也可以处理完整的 schema 对象。实际的提取函数保持严格(仅接受 dict),由服务器层处理便捷转换。
提取的工作原理(确定性、可检查)
文本提取 — pdfplumber 打开 PDF 并提取每一页的纯文本。
字段匹配 — 对于 schema 中的每个属性,按顺序尝试一系列正则表达式;第一个匹配者胜出(tools/extract.py -> _FIELD_PATTERNS)。模式按“最具体优先”排序,未知字段名会回退到通用的“字段名: 值”匹配,外加同义词表(_FIELD_ALIASES)。
类型强制转换 — 匹配到的字符串会被转换为 JSON Schema 类型(例如 "type": "number" 时 "$1,750.00" -> 1750.0;数组则按逗号拆分)。强制转换失败时会回退到原始字符串,而不是丢失数据。
验证 — validate_against_schema 使用 jsonschema 包重新检查提取的数据,并逐字段报告其是通过、失败(附人类可读的原因),还是完全缺失。
由于每一步都是普通代码,你可以准确追踪某个字段为什么被提取或未被提取——没有黑盒。
错误处理
这三个工具在任何路径下都返回结构化 JSON——它们绝不会跨 MCP 边界抛出堆栈跟踪:
损坏/无法读取的 PDF -> {"ok": false, "error": "Could not read PDF ..."}
文件缺失 -> {"ok": false, "error": "PDF not found: ..."}
无可提取文本的 PDF -> {"ok": false, "error": "... contains no extractable text."}
无效的 schema(为空、没有 properties,或不是有效的 JSON Schema)-> 返回结构化错误键
缺少必填字段 -> 在 "missing" 中列出;格式错误的值 -> 在 "failed" 中列出并附原因
测试
pytest tests/ -v19 个测试,覆盖:
对全部三种文档类型(invoice、resume、purchase_order)的成功提取
缺少必填字段的 PDF(反向提取)
schema 验证能够捕获字段类型错误、缺少必填字段、enum/pattern 违规
错误路径:损坏的 PDF、不存在的文件、无文本的 PDF、无效的 schema
一项真正的端到端 MCP 测试(tests/test_mcp_end_to_end.py),它将 server.py 作为子进程启动,通过 stdio 与官方 MCP 客户端连接,并通过协议调用全部三个工具——证明这是一个真正的 MCP 服务器,而非伪装成服务器的库
示例 PDF 如果缺失,会由 tests/conftest.py 自动重新生成。
仓库结构
pdf-extract-mcp/
server.py # MCP server: MCPServer + tool registration + transports
tools/
__init__.py
extract.py # pdfplumber text extraction + regex field matching
validate.py # jsonschema validation with structured reports
schemas/
invoice.json # pre-built schema: invoice
resume.json # pre-built schema: resume
purchase_order.json # pre-built schema: purchase_order
sample_pdfs/
generate_samples.py # reportlab generator for the 4 sample PDFs
invoice.pdf
invoice_missing_fields.pdf
resume.pdf
purchase_order.pdf
tests/
conftest.py # auto-generates sample PDFs if missing
test_tools.py # unit tests for extract/validate
test_mcp_end_to_end.py # end-to-end test over the real MCP stdio transport
README.md
requirements.txt故障排查
ModuleNotFoundError: No module named 'mcp' — 你不在虚拟环境中:请运行 source .venv/bin/activate(或使用 ./.venv/bin/python server.py)。
FastMCP 导入错误 — server.py 面向 mcp 2.x API(MCPServer)。如果你的环境中有 mcp 1.x,请使用 pip install -U "mcp>=2.0" 重新安装。
Claude 中不显示工具 — 编辑配置后重启客户端,并确保 "args" 指向 server.py 的绝对路径,必要时使用虚拟环境中的 python 作为命令。
提取时漏掉某个字段 — 在 tools/extract.py 的 _FIELD_PATTERNS 中为其添加模式(或依赖通用的“字段名: 值”回退以及同义词表)。
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables AI-powered extraction and analysis of PDF documents with 40+ specialized tools for text, tables, images, layout analysis, security assessment, and document intelligence. Supports both text-based and scanned PDFs with OCR capabilities.10MIT
- FlicenseAqualityDmaintenanceExtracts structured JSON data from unstructured text using predefined schemas for receipts, invoices, resumes, and emails. It allows users to transform messy text into organized data through built-in or custom-defined fields.1
- AlicenseAqualityDmaintenanceEnables RAG over messy PDFs — extract, chunk, embed, and search scanned, multi-column, and table-heavy documents.6MIT
- AlicenseNot gradedqualityAmaintenanceExtracts text and tables from PDFs for AI agents via MCP, enabling structured data retrieval from invoices, reports, and statements.1MIT
Related MCP Connectors
Turn any PDF into structured JSON via AI + OCR: invoices, bank statements, contracts.
Fill existing fillable, flat and scanned PDF forms from structured data; save reusable templates
Extract, search and tag any document: invoices, receipts, contracts, templates. OAuth or API key.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/Pranavdmg20/pdf-extract-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server