Python MCP 服务器
Python MCP 服务器
标题为“Python MCP 服务器”的章节NextPDF Python SDK 内置一个 Model Context Protocol(MCP)服务器,可将 PDF 提取操作以原生 agent 工具的形式对外开放。支持 MCP 的 agent(例如 Claude Code)只需注册一次该服务器,之后调用 NextPDF 工具的方式就与调用其他任何工具相同。
这个服务器只是一层轻量适配层。每个工具都会从本地磁盘读取一份 PDF,调用你的 NextPDF Connect endpoint 的异步客户端,再将结果以 JSON 字符串返回。服务器本身不包含任何业务逻辑,也不会在调用之间保存任何数据。
使用 MCP extra 安装 SDK:
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_URL 和 NEXTPDF_API_KEY 都是必填项。服务器会在第一次工具调用时才延迟创建客户端。如果任一变量为空,它会抛出 RuntimeError,并以工具错误的形式返回给 agent,而不会导致进程崩溃。
工具清单与 SDK 对应
标题为“工具清单与 SDK 对应”的章节服务器会注册八个工具。每个工具名称都带有 nextpdf_ 前缀。每个工具都对应到异步客户端 ast namespace(命名空间,AsyncNextPDF.ast)上的某个方法,只有下文提到的两个复合工具例外;它们是在服务器内部由较低层的调用组装而成。
| MCP 工具 | SDK 调用 | 备注 |
|---|---|---|
nextpdf_extract_text | ast.extract_cited_text(pdf_data, page_index=..., headings_only=...) | 返回一份 CitedTextBlock 列表。 |
nextpdf_extract_tables | ast.extract_cited_tables(pdf_data, page_range=...) | 返回 ExtractCitedTablesResponse。 |
nextpdf_get_ast | ast.get_document_ast(pdf_data, page_range_start=0, page_range_end=..., token_budget=...) | 返回 AstDocument。 |
nextpdf_info | ast.get_document_ast(pdf_data) | 服务器生成一份元数据摘要;没有专用端点。 |
nextpdf_health | 无 | 只检查环境变量;不会发出任何网络调用。 |
nextpdf_search | ast.search_ast_nodes(pdf_data, node_type=..., page_index=..., text_query=..., max_results=...) | 返回 SearchAstNodesResponse。 |
nextpdf_get_outline | ast.search_ast_nodes(pdf_data, node_type="heading", max_results=500) | 服务器将标题节点重新整理为大纲。 |
nextpdf_diff | ast.get_ast_diff(original_pdf_data, modified_pdf_data) | 返回 GetAstDiffResponse。 |
在接入 agent 之前,请注意以下工具输入要求:
- 所有路径输入(
pdf_path、original_pdf_path、modified_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_version、source_hash、page_count、estimated_tokens、root_node_type,以及root_children_count。这些值来自AstDocument模型,其中estimated_tokens是计算属性(大约每四个字符算一个 token)。nextpdf_get_outline会为每个标题返回一条项目,包含id、page_index、text,以及depth(从该节点的attributes["level"]读取,默认为 1),再加上heading_count、total_matches,以及truncated。
附带引用的提取工具会在每条结果上附加一个 CitationAnchor。每个 anchor 都带有 node_id、page_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 | 时机 |
|---|---|---|---|
NextPDFLicenseError | 402 | license/tier-required | 该端点对此操作要求更高的服务器端授权层级。 |
AstNoStructTreeError | 422 | ast/no-struct-tree | 这份 PDF 未加标记,并且服务器未启用启发式回退。 |
QuotaExceededError | 429 | quota/exceeded | 触发了速率限制或配额。会带有 retry_after(单位为秒);前提是服务器发送了 Retry-After 标头。 |
AstBuildTimeoutError | 504 | ast/build-timeout | AST 构建超过了服务器的时间预算。请缩小页面范围。 |
NextPDFAPIError | 其他 4xx/5xx 错误 | 由服务器提供 | 任何其他 API 层级失败。 |
面向 agent 集成的实践建议:
- 超时。 HTTP 客户端使用固定的默认超时:总计 60 秒,连接超时为 10 秒。一份处理较慢或体积较大的文档,会以下列两种形式之一表现出来:
AstBuildTimeoutError(服务器放弃构建 AST),或者,如果是客户端本身超时,则会返回来自传输层的Unexpected error载荷。当你看到ast/build-timeout时,请指示 agent 缩小范围:调低max_pages(位于nextpdf_get_ast上),或在提取工具上设置page_index/page_start与page_end。 - 配额与退避。 遇到 429 时,工具返回的
error_type会是QuotaExceededError,并带有status_code429。retry_after这个值存在于异常对象上。由于服务器只序列化error、error_type与status_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_text、extract_cited_tables、get_document_ast、search_ast_nodes、get_ast_diff)。它不会执行任何绘制、签名、涂黑或文档变更。请发给 agent 一把服务器端范围仅限于这些只读路径的密钥,这样即使 agent 遭入侵,也无法触及写入或更高层级的操作。 - 每个 agent 使用专属密钥。 采用每个 agent 一把密钥,可以让你撤销或轮换某个集成而不影响其他集成,也能让端点日志归因到特定 agent。
- 约束文件系统。 由于每个工具都会从本地磁盘读取一个绝对路径,服务器可以读取主机进程有权限读取的任何文件。请以非特权用户运行它,将其工作目录限制在某个文件夹内,并且绝不要用特权账号运行它。
- 优先采用传输层安全性(TLS)。 在任何非本地部署中,请把
NEXTPDF_BASE_URL指向https://端点。SDK 会把密钥以Bearertoken 形式放在Authorization标头中发送,因此明文传输会在传输途中暴露它。
有关支持这些客户端实践的端点侧控制措施,请参见 Connect 安全性与运维一节。
接入 agent 前先在本地测试服务器
标题为“接入 agent 前先在本地测试服务器”的章节在你连接 agent 之前,先单独验证服务器。最快的检查不需要 PDF,也不需要网络:
python -c "from nextpdf.mcp import _tool_definitions; print(len(_tool_definitions()))"正确安装会打印 8。如果你看到一个 ImportError,且它提及 mcp extra,表示缺少该可选依赖;请用 pip install nextpdf[mcp] 重新安装。
接着,通过 CLI 演练这些工具所使用的相同 SDK 路径。CLI 会使用相同的两个环境变量与你的端点通信。先设置一次:
export NEXTPDF_BASE_URL="https://connect.example.com"export NEXTPDF_API_KEY="$(cat /run/secrets/nextpdf_api_key)"然后确认版本、连接能力,以及一次真正的提取:
nextpdf versionnextpdf info /path/to/sample.pdfnextpdf extract text /path/to/sample.pdf --headings-onlynextpdf version 不需要凭证即可运行,并会确认包可以正常导入。nextpdf info 会演练 get_document_ast,也就是 nextpdf_get_ast 与 nextpdf_info 背后相同的调用。如果两者都成功,说明凭证与端点配置正确,对应的 MCP 工具也可以运行。
若要直接驱动 MCP 协议,请使用上游 MCP Inspector(随 mcp 包一并提供)。把它指向你的 agent 将使用的相同命令与环境,然后手动列出并调用工具。确认 nextpdf_health 报告 status: "ok"。只要 NEXTPDF_BASE_URL 或 NEXTPDF_API_KEY 未设置,它就会返回 misconfigured;这是在 agent 真正调用工具之前,最快发现遗漏环境值的方式。
监控与调试工具调用
标题为“监控与调试工具调用”的章节MCP 服务器通过 stdio 通信,因此它的标准输出承载着协议流,必须保持干净。服务器不会配置自己的应用程序日志,这意味着你主要的可观测性来源是结构化工具错误载荷、CLI,以及端点自身的日志。
- 工具错误载荷就是信号。 每一次失败的调用都会返回一个 JSON 对象,内含
error;对于 SDK 错误,还会包含error_type与status_code(参见 错误处理一节)。请让 agent 主机记录这些载荷;无需在服务器内额外安装任何检测逻辑,就能指出失败的工具与确切原因。 - 通过 CLI 搭配调试日志重现。 MCP 服务器本身不输出任何日志,但 CLI 会演练相同的 SDK 调用并且会记录日志。请用对应的 CLI 命令搭配
--log-level debug重现失败的工具调用。CLI 会把带时间戳的记录写到 stderr,并为非预期错误记录完整回溯;这是不挂接调试器也能看清某个处理程序行为的最直接方式。 - 以健康检查作为探针。 调用
nextpdf_health以确认服务器看得到一个基础 URL 与一把 API 密钥。结果会返回sdk_version、server_url、api_key_configured(一个布尔值,绝不会是密钥本身),以及status。 - 端点端的可观测性。 由于每个工具都对应一个 Connect 请求,请根据 API 密钥与时间戳,把工具活动与端点访问日志相互关联。请让端点在与你用于其他服务客户端相同的验证、配额与可观测性控制下运行。
排查常见的 agent 集成问题
标题为“排查常见的 agent 集成问题”的章节| 症状 | 可能原因 | 解决方式 |
|---|---|---|
服务器无法启动,并出现一个与 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_URL 与 NEXTPDF_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_pages、page_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 概览。