跳转到内容

Python MCP 服务器

NextPDF Python SDK 内置一个 Model Context Protocol(MCP)服务器,可将 PDF 提取操作以原生 agent 工具的形式对外开放。支持 MCP 的 agent(例如 Claude Code)只需注册一次该服务器,之后调用 NextPDF 工具的方式就与调用其他任何工具相同。

这个服务器只是一层轻量适配层。每个工具都会从本地磁盘读取一份 PDF,调用你的 NextPDF Connect endpoint 的异步客户端,再将结果以 JSON 字符串返回。服务器本身不包含任何业务逻辑,也不会在调用之间保存任何数据。

使用 MCP extra 安装 SDK:

Terminal window
pip install nextpdf[mcp]

这个 mcp extra 会安装上游 mcp 包(版本限制为 mcp>=1.0,<2.0)。服务器需要 Python 3.10 或更新版本。

从你的 MCP 客户端配置启动这个服务器模块。下面的示例会从宿主机环境读取两个连接值,而不是把秘密直接写进配置文件(参见 安全性一节):

{
"mcpServers": {
"nextpdf": {
"command": "python",
"args": ["-m", "nextpdf.mcp"],
"env": {
"NEXTPDF_BASE_URL": "https://connect.example.com",
"NEXTPDF_API_KEY": "${NEXTPDF_API_KEY}"
}
}
}
}

通过 python -m nextpdf.mcp 这个入口点会执行 main(),并通过 asyncio.run(serve()) 在标准输入/输出(stdio)上启动服务器。不要将它与 python -m nextpdf 混淆,后者运行的是命令行接口(CLI),不是 MCP 服务器。

NEXTPDF_BASE_URLNEXTPDF_API_KEY 都是必填项。服务器会在第一次工具调用时才延迟创建客户端。如果任一变量为空,它会抛出 RuntimeError,并以工具错误的形式返回给 agent,而不会导致进程崩溃。

服务器会注册八个工具。每个工具名称都带有 nextpdf_ 前缀。每个工具都对应到异步客户端 ast namespace(命名空间,AsyncNextPDF.ast)上的某个方法,只有下文提到的两个复合工具例外;它们是在服务器内部由较低层的调用组装而成。

MCP 工具SDK 调用备注
nextpdf_extract_textast.extract_cited_text(pdf_data, page_index=..., headings_only=...)返回一份 CitedTextBlock 列表。
nextpdf_extract_tablesast.extract_cited_tables(pdf_data, page_range=...)返回 ExtractCitedTablesResponse
nextpdf_get_astast.get_document_ast(pdf_data, page_range_start=0, page_range_end=..., token_budget=...)返回 AstDocument
nextpdf_infoast.get_document_ast(pdf_data)服务器生成一份元数据摘要;没有专用端点。
nextpdf_health只检查环境变量;不会发出任何网络调用。
nextpdf_searchast.search_ast_nodes(pdf_data, node_type=..., page_index=..., text_query=..., max_results=...)返回 SearchAstNodesResponse
nextpdf_get_outlineast.search_ast_nodes(pdf_data, node_type="heading", max_results=500)服务器将标题节点重新整理为大纲。
nextpdf_diffast.get_ast_diff(original_pdf_data, modified_pdf_data)返回 GetAstDiffResponse

在接入 agent 之前,请注意以下工具输入要求:

  • 所有路径输入(pdf_pathoriginal_pdf_pathmodified_pdf_path)都是运行该服务器的机器上文件的绝对路径。agent 传入一个路径;服务器在本地读取字节。没有上传工具。
  • nextpdf_extract_text 在其输入结构描述中声明了一个 max_pages 字段,但文本处理程序不会把它传给 SDK。文本的分页范围通过 page_index(单个以 0 为起始的页面)控制。当你需要限定整份文档的遍历范围时,请改用 nextpdf_get_ast 搭配 max_pages
  • nextpdf_get_ast 会把 max_pages 换算成 [0, max_pages - 1] 这个包含端点的页面范围(max_pages 默认为 50)。传入 token_budget 可限制返回树状结构的大小。
  • nextpdf_info 会返回 schema_versionsource_hashpage_countestimated_tokensroot_node_type,以及 root_children_count。这些值来自 AstDocument 模型,其中 estimated_tokens 是计算属性(大约每四个字符算一个 token)。
  • nextpdf_get_outline 会为每个标题返回一条项目,包含 idpage_indextext,以及 depth(从该节点的 attributes["level"] 读取,默认为 1),再加上 heading_counttotal_matches,以及 truncated

附带引用的提取工具会在每条结果上附加一个 CitationAnchor。每个 anchor 都带有 node_idpage_index、一个归一化后的 bbox(坐标范围为 0.0 到 1.0),以及一个 confidence 置信度分数(0.0 到 1.0)。需要 provenance(来源信息)的 agent 应呈现这些字段,而不是只显示原始文本。

服务器绝不会让异常泄漏到 agent 传输层。call_tool 分发器会拦截每个错误,并以 JSON TextContent 形式返回,因此一次失败的工具调用会生成一份结构化载荷供 agent 读取,而不是中断连接。载荷的形状如下:

条件返回的 JSON
未知工具名称{"error": "Unknown tool: <name>"}
找不到输入文件{"error": "PDF file not found: <path>"}
任何 NextPDFError 子类{"error": "<message>", "error_type": "<class>", "status_code": <int?>}
任何其他异常{"error": "Unexpected error: <message>"}

status_code 只有在底层错误本身带有它时才会一起包含。SDK 会把 HTTP 响应映射到一套带类型的异常层级,全部以 NextPDFError 为根:

例外HTTP 状态error_code时机
NextPDFLicenseError402license/tier-required该端点对此操作要求更高的服务器端授权层级。
AstNoStructTreeError422ast/no-struct-tree这份 PDF 未加标记,并且服务器未启用启发式回退。
QuotaExceededError429quota/exceeded触发了速率限制或配额。会带有 retry_after(单位为秒);前提是服务器发送了 Retry-After 标头。
AstBuildTimeoutError504ast/build-timeoutAST 构建超过了服务器的时间预算。请缩小页面范围。
NextPDFAPIError其他 4xx/5xx 错误由服务器提供任何其他 API 层级失败。

面向 agent 集成的实践建议:

  • 超时。 HTTP 客户端使用固定的默认超时:总计 60 秒,连接超时为 10 秒。一份处理较慢或体积较大的文档,会以下列两种形式之一表现出来:AstBuildTimeoutError(服务器放弃构建 AST),或者,如果是客户端本身超时,则会返回来自传输层的 Unexpected error 载荷。当你看到 ast/build-timeout 时,请指示 agent 缩小范围:调低 max_pages(位于 nextpdf_get_ast 上),或在提取工具上设置 page_index / page_startpage_end
  • 配额与退避。 遇到 429 时,工具返回的 error_type 会是 QuotaExceededError,并带有 status_code 429。retry_after 这个值存在于异常对象上。由于服务器只序列化 errorerror_typestatus_code,agent 应把 429 视为暂停并稍后重试的信号,而不是从工具输出中解析某个重试标头。请在 Connect endpoint 上强制执行配额,而不是放在 agent 内。
  • 未加标记的 PDF。 422 ast/no-struct-tree 表示来源 PDF 没有结构树。请在服务器上为这类文件启用启发式模式,或在提取前先把它们导向加标记步骤。

安全性:API 密钥范围限定与最小权限

标题为“安全性:API 密钥范围限定与最小权限”的章节

请把 API 密钥当作秘密处理,像对待数据库密码一样谨慎。

  • 绝不要把密钥嵌入 MCP 配置文件。 上方的 JSON 示例引用 ${NEXTPDF_API_KEY},因此该值会在启动时从宿主机环境或秘密管理器 resolve(解析)而来。配置文件可能会提交进源代码管理;秘密则绝不可提交。
  • 把密钥范围限定为只读提取。 MCP 服务器只会调用 AST 提取面(extract_cited_textextract_cited_tablesget_document_astsearch_ast_nodesget_ast_diff)。它不会执行任何绘制、签名、涂黑或文档变更。请发给 agent 一把服务器端范围仅限于这些只读路径的密钥,这样即使 agent 遭入侵,也无法触及写入或更高层级的操作。
  • 每个 agent 使用专属密钥。 采用每个 agent 一把密钥,可以让你撤销或轮换某个集成而不影响其他集成,也能让端点日志归因到特定 agent。
  • 约束文件系统。 由于每个工具都会从本地磁盘读取一个绝对路径,服务器可以读取主机进程有权限读取的任何文件。请以非特权用户运行它,将其工作目录限制在某个文件夹内,并且绝不要用特权账号运行它。
  • 优先采用传输层安全性(TLS)。 在任何非本地部署中,请把 NEXTPDF_BASE_URL 指向 https:// 端点。SDK 会把密钥以 Bearer token 形式放在 Authorization 标头中发送,因此明文传输会在传输途中暴露它。

有关支持这些客户端实践的端点侧控制措施,请参见 Connect 安全性与运维一节。

在你连接 agent 之前,先单独验证服务器。最快的检查不需要 PDF,也不需要网络:

Terminal window
python -c "from nextpdf.mcp import _tool_definitions; print(len(_tool_definitions()))"

正确安装会打印 8。如果你看到一个 ImportError,且它提及 mcp extra,表示缺少该可选依赖;请用 pip install nextpdf[mcp] 重新安装。

接着,通过 CLI 演练这些工具所使用的相同 SDK 路径。CLI 会使用相同的两个环境变量与你的端点通信。先设置一次:

Terminal window
export NEXTPDF_BASE_URL="https://connect.example.com"
export NEXTPDF_API_KEY="$(cat /run/secrets/nextpdf_api_key)"

然后确认版本、连接能力,以及一次真正的提取:

Terminal window
nextpdf version
nextpdf info /path/to/sample.pdf
nextpdf extract text /path/to/sample.pdf --headings-only

nextpdf version 不需要凭证即可运行,并会确认包可以正常导入。nextpdf info 会演练 get_document_ast,也就是 nextpdf_get_astnextpdf_info 背后相同的调用。如果两者都成功,说明凭证与端点配置正确,对应的 MCP 工具也可以运行。

若要直接驱动 MCP 协议,请使用上游 MCP Inspector(随 mcp 包一并提供)。把它指向你的 agent 将使用的相同命令与环境,然后手动列出并调用工具。确认 nextpdf_health 报告 status: "ok"。只要 NEXTPDF_BASE_URLNEXTPDF_API_KEY 未设置,它就会返回 misconfigured;这是在 agent 真正调用工具之前,最快发现遗漏环境值的方式。

MCP 服务器通过 stdio 通信,因此它的标准输出承载着协议流,必须保持干净。服务器不会配置自己的应用程序日志,这意味着你主要的可观测性来源是结构化工具错误载荷、CLI,以及端点自身的日志。

  • 工具错误载荷就是信号。 每一次失败的调用都会返回一个 JSON 对象,内含 error;对于 SDK 错误,还会包含 error_typestatus_code(参见 错误处理一节)。请让 agent 主机记录这些载荷;无需在服务器内额外安装任何检测逻辑,就能指出失败的工具与确切原因。
  • 通过 CLI 搭配调试日志重现。 MCP 服务器本身不输出任何日志,但 CLI 会演练相同的 SDK 调用并且会记录日志。请用对应的 CLI 命令搭配 --log-level debug 重现失败的工具调用。CLI 会把带时间戳的记录写到 stderr,并为非预期错误记录完整回溯;这是不挂接调试器也能看清某个处理程序行为的最直接方式。
  • 以健康检查作为探针。 调用 nextpdf_health 以确认服务器看得到一个基础 URL 与一把 API 密钥。结果会返回 sdk_versionserver_urlapi_key_configured(一个布尔值,绝不会是密钥本身),以及 status
  • 端点端的可观测性。 由于每个工具都对应一个 Connect 请求,请根据 API 密钥与时间戳,把工具活动与端点访问日志相互关联。请让端点在与你用于其他服务客户端相同的验证、配额与可观测性控制下运行。
症状可能原因解决方式
服务器无法启动,并出现一个与 mcp extra 有关的 ImportError尚未安装 mcp 可选依赖使用 pip install nextpdf[mcp] 安装。
第一次工具调用返回 {"error": "NEXTPDF_BASE_URL environment variable is required..."}MCP 的 env 块没有传入基础 URL,或 shell 没有展开 ${NEXTPDF_BASE_URL}请在 agent 主机环境中设置该变量,并确认启动器会展开它。
nextpdf_health 报告 "status": "misconfigured"两个必填变量之一为空请同时提供 NEXTPDF_BASE_URLNEXTPDF_API_KEY
每个基于路径的工具都返回 {"error": "PDF file not found: <path>"}agent 传入了服务器进程看不到的相对路径或主机端路径请传入服务器用户可读的绝对路径;以 nextpdf info <path> 确认。
工具返回 error_typeNextPDFLicenseError(状态 402)此操作需要更高的服务器端授权层级请使用一个有权执行此操作的端点与密钥。
工具返回 error_typeAstNoStructTreeError(状态 422)这份 PDF 未加标记,而启发式回退为关闭请在端点上启用启发式模式,或先为这份 PDF 加标记。
工具返回 error_typeQuotaExceededError(状态 429)触发了速率限制或配额请暂停并重试;若上限过低,请提高端点配额。
工具返回 error_typeAstBuildTimeoutError(状态 504),或一个传输超时这份文档超出了时间预算可承受的大小请以 max_pagespage_index,或 page_start/page_end 缩小范围。
agent 没有注册任何 NextPDF 工具agent 调用了 python -m nextpdf(CLI),而不是 python -m nextpdf.mcp请把 python -m nextpdf.mcp 用作 command/args

关于端点层级的失败与部署检查,请参见 Connect 故障排查一节。关于这些工具封装的底层 SDK 操作,请参见 CLI 参考SDK 概览