NextPDF Connect 开发者指南
快速概览
标题为“快速概览”的章节NextPDF Connect(nextpdf/server)将与 Framework(框架)无关的 NextPDF PDF 2.0 引擎包装为一项服务。它不会重新实现 PDF 生成流程,而是把每一项引擎能力公开为具名且带有 schema 描述的工具,并通过三种传输层提供完整工具目录:基于标准输入输出的 Model Context Protocol(MCP)、一套 Representational State Transfer(REST)应用程序接口(API),以及 gRPC。当你要以这台 server 为基础开发、扩展其工具集,或在生产环境中运维它时,请参阅本指南。
整个设计由三个概念支撑:工具注册(registry)、三个彼此独立的传输层,以及人工介入(HITL)确认 gate。本页说明它们如何协同工作,以及如何在不削弱安全模型的前提下使用它们。如需精确的工具、RPC 与消息符号,请阅读 API 参考。
前置条件:PHP 8.4、Composer 2,以及——若要使用网络传输层——RoadRunner 可执行文件与至少一把 API 密钥。使用 composer require nextpdf/server 安装。
架构边界
标题为“架构边界”的章节请将各项职责放在正确的边界内。工具只是引擎调用的一层薄包装;它不得承载版面解释、文档语义或转换逻辑。
| 层级 | 负责方 | 职责 | 不可放在这里 |
|---|---|---|---|
| 客户端或 AI Agent(代理) | 你的集成程序 | 决定要调用哪个工具;将确认挑战转达给人。 | 引擎逻辑或 tier 检测。 |
| 传输层 | nextpdf/server | 封装请求(JSON-RPC、HTTP 或 Protocol Buffers)、执行验证,并路由到工具执行器。 | 文档语义。 |
| 工具注册(registry) | nextpdf/server | 探索各 tier、在安全允许列表的限制下注册工具、按名称查找工具。 | PDF 生成。 |
| 工具 | nextpdf/server | 按输入 schema 验证参数,并调用引擎。 | 版面解释或多步骤编排。 |
| 确认 gate | nextpdf/server | 暂扣一项 ApprovalRequired 作业,直到有人授权为止。 | 调用端验证。 |
| 引擎 | nextpdf/core(以及 nextpdf/premium) | 生成、检查与转换 PDF 内容。 | 传输层或验证相关事项。 |
运行时生命周期
标题为“运行时生命周期”的章节每个传输层都有自己的入口点与启动工厂(factory),并各自显式构建自己的对象图。这里没有需要注册的依赖注入容器。
- 加载配置。 MCP server 会 resolve(解析)配置,优先级为:环境变量(
NEXTPDF_MCP_*)高于 YAML 文件的nextpdf_mcp区段,再高于内置默认值,最后产出一份readonlyMcpConfig。REST 与 gRPC server 则读取HttpConfig,来源是NEXTPDF_*环境变量。请参阅 组态。 - 建立安全策略。
enabled_tools允许列表会在 registry 之前建立,因此从第一次注册起就能约束探索范围。 - 构建 registry 并探索工具。
ToolRegistry::registerDefaults()会先注册 core tier,接着在 Pro 与 Enterprise 供应器的类可解析时注册它们,最后在环境 gate 允许下注册随附的 AST 与突变供应器。 - 建立共享存储与 gate。 内存中文档存储会按设定的 TTL 与容量建立;
ConfirmationGate则会连同其单次使用令牌存储一起组装。 - 绑定传输层。 MCP 会在 stdio 上进入「读取—处理—写出」循环,直到读到文件结尾为止。REST 与 gRPC 则按检测到的各 tier 建立路由或服务表,并将请求循环交给 RoadRunner。
随后,一次请求的流向是:验证(REST 与 gRPC)、解析工具或作业、对 ApprovalRequired 的工作执行确认 gate、调用引擎,最后返回结果。请参阅 启动与探索。
传输层模型
标题为“传输层模型”的章节这三个传输层在概念上共享 registry、配置与 gate,但它们是彼此独立的进程。启动其中一个,不会连带启动其他传输层。
| 传输层 | 进入点 | 何时选用它 |
|---|---|---|
| MCP | bin/nextpdf-mcp | 将 server 作为受信任子进程启动的本地 AI 客户端。 |
| REST | bin/nextpdf-server | 网络化的 HTTP 客户端;由一份 OpenAPI 3.1 文件描述。 |
| gRPC | bin/nextpdf-grpc | 类型化的流式(stream)客户端;对应 nextpdf.connect.v1.NextPDFConnect 服务。 |
根据你执行的 RoadRunner 配置文件选择传输层:.rr.yaml(仅 REST)、.rr.grpc.yaml(仅 gRPC),或 .rr.full.yaml(两者皆有)。MCP 传输层只是普通子进程,不需要监管程序。各传输层的传输协议细节分别见 MCP 传输层、REST 传输层 和 gRPC 传输层。
建议的部署结构
标题为“建议的部署结构”的章节让网络传输层在 RoadRunner 之下运行,并搭配共享存储与以 secret 挂载的密钥。合并配置文件可让 REST 与 gRPC 共享同一个监管程序。
| 路径或设定 | 用途 |
|---|---|
.rr.full.yaml | 同一个监管程序下的 REST 与 gRPC 合并配置文件。 |
NEXTPDF_API_KEYS_FILE | 指向以 secret 挂载、可热重载的 API 密钥文件路径。 |
NEXTPDF_REDIS_HOST | 为多 worker 池启用以 Redis 为后端的速率限制、幂等性与文档存储。 |
NEXTPDF_WORKER_COUNT / NEXTPDF_GRPC_WORKER_COUNT | HTTP 与 gRPC 池的 worker 池规模设置。 |
| 输出基底目录 | 供文档输出工具使用的专用磁盘卷,并采用最小权限的文件系统权限。 |
以下 shell 示例会使用以 secret 挂载的密钥与共享 Redis 存储启动合并配置文件。文档本身不含任何 secret;密钥挂载在 /run/secrets/api-keys。
export NEXTPDF_API_KEYS_FILE=/run/secrets/api-keysexport NEXTPDF_WORKER_COUNT=8export NEXTPDF_GRPC_WORKER_COUNT=4export NEXTPDF_REDIS_HOST=redis./vendor/bin/rr serve -c .rr.full.yaml若要使用多 worker 池,请设置 Redis 并确认运行中的镜像包含 ext-redis;缺少它时,速率限制、幂等性与文档存储会变成每个 worker 各自独立。请参阅 部署。
工具注册与 tier 解析
标题为“工具注册与 tier 解析”的章节NextPDF\Server\ToolRegistry(src/ToolRegistry.php)会在启动时建立工具目录。tier 是一项声明式不变量:每个工具都会返回自己的 tier() 与 riskLevel();registry 绝不从命名空间或打包方式推断 tier。
- Core tier 会无条件注册:文档与诊断工具,外加在 core 条码编码器存在时注册的
generate_barcode,以及parse_pdf——但仅在NEXTPDF_MCP_TOOL_PARSE_PDF_ENABLED为true或1时才注册。 - Pro 与 Enterprise 供应器 会在其供应器类可解析时注册,并以
class_exists()探测。不存在的 tier 会被静默跳过。 - 随附的 AST 与突变供应器 会注册在 Pro tier 之下,并由
NEXTPDF_AST_TOOLS_ENABLED与NEXTPDF_MUTATION_TOOLS_ENABLED控制(两者默认均启用)。 - 安全策略筛选器 会把每一次注册与
enabled_tools允许列表取交集。允许列表只会做减法,绝不会增加。各 tier 的计数只计入策略放行的工具。
由此产生的各 tier 计数与总数,会回报在 MCP 的 initialize 响应与 REST 的 GET /api/v1/capabilities 端点中。请将文档中任何固定的总数都视为过时信息;以查询正在运行的 server 为准。请参阅 工具目录。
风险 tier 与确认 gate
标题为“风险 tier 与确认 gate”的章节每个工具都通过 RiskLevel 枚举(src/Config/RiskLevel.php)声明四种风险等级之一:Safe(0)、Caution(1)、Review(2)与 ApprovalRequired(3)。审计记录会应用于 Caution 及以上等级。配置覆盖可以提高工具的风险;但绝不能降低一个按设计属于 ApprovalRequired 的工具。配置加载器会在加载时抛出异常,server 会拒绝启动,而不是在 gate 被削弱的情况下运行。
当一个 ApprovalRequired 工具在没有有效令牌的情况下被调用时,ConfirmationGate(src/Mcp/ConfirmationGate.php)会返回一个单次使用的挑战令牌。令牌绑定的是工具名称、一个随机 nonce,以及 300 秒的存活时间(TTL)——而不绑定参数,因为客户端在重试时可能会以不同的键排序重新序列化参数。代理(agent)会把挑战转达给人,再以 _confirmation_token 参数带着该令牌重新调用同一个工具。令牌一经使用即被消耗,恰好放行一次受 gate 管制的调用。
以下 PHP 示例是一个与传输层无关的辅助程序,它驱动一次 MCP 工具调用,并在遇到确认挑战时,先把挑战呈现给人工审批者,再以签发的令牌重试。它声明 strict types、完整标注类型,并且只拦截最具体的异常,而不是吞掉所有错误。
<?php
declare(strict_types=1);
namespace App\Connect;
use JsonException;
/** * Drives one tool call and resolves an ApprovalRequired confirmation * challenge through a human approver before retrying. */final readonly class ConfirmingToolCaller{ public function __construct( private McpClientInterface $client, private HumanApproverInterface $approver, ) {}
/** * @param non-empty-string $toolName * @param array<string, mixed> $arguments * * @return array<string, mixed> The tool result content * * @throws JsonException When a response cannot be decoded * @throws ApprovalDeniedException When the human declines the challenge */ public function call(string $toolName, array $arguments): array { $response = $this->client->callTool($toolName, $arguments);
if (!isset($response['challenge'], $response['token'])) { return $response; }
$challenge = (string) $response['challenge']; $token = (string) $response['token'];
if (!$this->approver->approve($toolName, $challenge)) { throw new ApprovalDeniedException($toolName); }
$arguments['_confirmation_token'] = $token;
return $this->client->callTool($toolName, $arguments); }}请把 McpClientInterface、HumanApproverInterface 与 ApprovalDeniedException 接入你自己的传输层与审批流程。重试会沿用原参数并附加签发的令牌;绝不要在没有人工裁决的情况下自动批准一项挑战。请参阅 HITL 风险 tier。
扩展点
标题为“扩展点”的章节扩展这台 server 的方式是新增工具并提供供应器,而不是编辑 registry。
| 扩展点 | 用于 | 限制 |
|---|---|---|
一个类,实现 ToolInterface | 以工具形式公开一项新的引擎能力。 | 声明 tier()、riskLevel()、category(),以及一份 JSON Schema 形式的 inputSchema();并让它保持为一层薄的引擎包装。 |
一个 ToolProviderInterface 供应器 | 为某个 tier 注册一组工具。 | Pro 与 Enterprise 供应器是通过 class_exists() 探索的;不要让 server 依赖那个专有包。 |
enabled_tools 允许列表 | 以最小权限收缩对外公开的工具目录。 | 允许列表只做减法;它无法注册一个不存在的工具。 |
risk_level_overrides | 通过提高工具风险来强化部署的安全性。 | 仅能升级;降低一个 ApprovalRequired 工具会导致启动失败。 |
| 可注入的传输层与 worker seam | 在隔离环境下测试 server。 | 这些 seam 是为测试而存在,不是用于应用程序装配。 |
运维流程
标题为“运维流程”的章节- 选择一个配置文件。 根据你要公开的传输层,执行
.rr.yaml、.rr.grpc.yaml或.rr.full.yaml。 - 从 secret 挂载密钥。 把
NEXTPDF_API_KEYS_FILE指向一个 secret 文件;建议使用可热重载的文件密钥存储,这样轮换密钥就不需要重启。 - 设置共享存储。 设置
NEXTPDF_REDIS_HOST,并为任何超过一个 worker 的池确认有ext-redis;把 SQLite 工作存储放在所有 worker 都能写入的磁盘卷上。 - 终止 TLS。 让 REST 跑在传输层安全(TLS)终止器后方;在任何不受信任的网络上以双向 TLS 运行 gRPC,并把 server 密钥、server 证书与客户端证书颁发机构以部署 secret 的方式提供。
- 探测健康状态。 使用匿名的
/healthz与/readyz端点(REST),或HealthCheck与ReadinessCheckRPC(gRPC),供编排器探测使用。 - 收缩工具目录。 把
enabled_tools限制为某个集成真正需要的最小工具集。
请验证 Redis 的健康状态,不要想当然:当配置的 Redis 连接失败时,REST server 会退回使用内存中存储。请参阅 部署 与 安全与维运。
失败处理
标题为“失败处理”的章节| 失败 | 发生位置 | 建议的响应 |
|---|---|---|
未知的 document_id | 工具执行 | 向调用端返回一个已定义的错误;指示它先调用 create_pdf。 |
| 修改时 ETag 过时 | AST 突变工具 | 以 get_document_ast 重新读取文档,再以新的 ETag 重试。 |
| API 密钥缺失或无效(REST) | 验证 middleware | 返回 401 并附带 WWW-Authenticate: Bearer 挑战;不要泄漏是哪一部分出错。 |
| tier 未获授权(REST) | 授权 | 返回 403;该密钥的 tier 低于该作业的 tier。 |
| tier 路由不存在(REST) | 路由器 | 返回 404;该包未安装。这是预期内的行为,并非故障。 |
| 令牌错误(gRPC) | gRPC 验证器 | 以 UNAUTHENTICATED 让该调用失败。 |
| 无法连接到 Redis | 启动期或运行时 | 降级为内存中存储;通知运维人员并验证 Redis 的健康状态。 |
| 输出路径落在基底目录之外 | 文档输出工具 | 以失败作为安全默认(fail closed);路径会被规范化,且路径穿越会被拒绝。 |
请把引擎失败呈现为已定义的错误对象,绝不要表现为静默成功。各传输层的错误模型详见 API 参考。
安全的默认值
标题为“安全的默认值”的章节| 事项 | 默认值 | 何时应覆写 |
|---|---|---|
parse_pdf | 停用(通过 NEXTPDF_MCP_TOOL_PARSE_PDF_ENABLED 选择启用)。 | 仅在某个集成需要结构化检查时才启用。 |
enabled_tools | 空(允许所有已探索到的工具)。 | 为最小权限部署设置一份明确的允许列表。 |
| 风险覆盖 | 无。 | 为强化过的部署提高风险;绝不要尝试降低。 |
document_ttl / max_documents | 1800 秒 / 50 份文件。 | 对于数据落盘敏感或内存受限的部署,请调低。 |
allow_file_output | 启用。 | 对于无状态、数据落盘敏感的部署,请设为 false。 |
| worker 数量 | 四(HTTP)、二(gRPC)。 | 根据观察到的延迟与可用核心数来设置规模。 |
| REST 监听器 | 位于 TLS 终止器后方的明文 HTTP。 | 请一律在上游终止 TLS;绝不要在不受信任的网络上公开明文。 |
| 在不受信任网络上的 gRPC | 双向 TLS。 | 必需;绝不要在不受信任的网络上运行明文 gRPC 监听器。 |
测试检查清单
标题为“测试检查清单”的章节- Registry 测试会断言:当 Pro 或 Enterprise tier 不存在时会被静默跳过,而 core 工具目录仍会注册。
- 允许列表测试会断言:
enabled_tools只做减法,绝不会加入一个 registry 未曾探索到的工具。 - 确认 gate 测试会断言:一个
ApprovalRequired工具在首次调用时返回挑战、在持有有效的单次使用令牌时执行一次,且该令牌会在其 TTL 之后过期。 - 降级测试会断言:一个
risk_level_overrides项目若削弱了ApprovalRequired工具,会导致启动失败。 - 验证测试会涵盖 REST 上密钥缺失、格式错误、已停用与已过期的情况(
401加上WWW-Authenticate)与 gRPC(UNAUTHENTICATED),以及 tier 授权遭拒(403)。 - 并行测试会断言:过时的 ETag 会让修改失败,而重复的
idempotency_key会重播已缓存的结果。 - 路径包覆测试会断言:解析后落在基底目录之外的文档输出路径会被拒绝。
- 请让测试 fixture 保持小且不含敏感数据;绝不要提交真实的 API 密钥或文档内容。