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,並以部署 secret 提供 server 金鑰、server 憑證與用戶端憑證授權單位。
- 探測健康狀態。 使用匿名的
/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會重播已快取的結果。 - 路徑包覆測試會斷言:解析後落在基底目錄之外的檔案輸出路徑會被拒絕。
- 請讓測試夾具維持精簡且不含敏感資料;絕不要提交真實的 API 金鑰或文件內容。