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 概觀。