深藍背景中的青綠、紫色與暖金光帶交織延伸,象徵 MCP 工具契約、背景工作與可追蹤的結果

mcp-agent 實作指南:非同步工具、JSON Schema 與長任務流程怎麼設計?

把 MCP 工具接上模型後,真正容易出問題的通常不是「能不能呼叫」,而是工具的輸入輸出契約、工作何時才算完成,以及失敗時呼叫端能否採取下一步。若你使用 Python 的 mcp-agent,這些設計會影響模型能否選對工具、前端能否呈現進度,以及團隊日後能否安全除錯。

本文以目前官方文件的設計方向整理:如何把 MCPApp 當成共用執行環境、如何區分一次呼叫可完成的工具與背景工作、JSON Schema 怎麼降低猜測空間,以及為什麼權限和確認機制不能只寫在工具描述裡。

先分清楚:MCP、mcp-agent 與工具契約各自負責什麼

MCP(Model Context Protocol)定義 AI 應用程式如何發現並呼叫外部工具;工具會有名稱、說明與輸入 schema,必要時也可宣告輸出 schema。協定描述的是溝通邊界,不會自動替你的程式處理工作排程、重試、身分授權或業務規則。

mcp-agent 是建立 agent、工作流程與 MCP 工具的 Python 框架。它以 MCPApp 作為中央應用程式環境,集中設定、logging、tracing、MCP 連線與執行器。工具契約則是你對模型和呼叫端的承諾:需要哪些參數、允許什麼值、成功時回傳哪種結果、失敗時能否安全恢復。

一個應用程式共用一個清楚的生命週期

官方文件建議在單一程序中共用 MCPApp,並以 async with app.run() 初始化與清理執行環境。這能讓 server registry、連線、記錄與執行器使用同一個 context,而不是每次工具呼叫都重建資源。

設定可以由 mcp_agent.config.yaml、mcp_agent.secrets.yaml、.env 與環境覆寫組合而成。API 金鑰、token 和其他秘密不應放入工具函式、範例輸出或版本庫;把設定與業務邏輯分開,才能在測試、部署與輪替憑證時維持一致。

區分工具的依據是「何時能交付結果」,不是只有 Python 的 async

Python 函式能不能使用 async,和呼叫端是否要等待一個長任務,是兩件不同的事。若工作可以在一次 MCP 呼叫內可靠完成,例如格式轉換、查詢、參數驗證或小範圍寫入,重點是讓工具直接回傳可使用的結果。即使內部需要短暫的非同步 I/O,呼叫端仍不應被迫輪詢一個其實很快就能完成的工作。

反過來,文件分析、大量批次處理、等待外部服務、需要人工決策或可能長時間重試的工作,應設計成可追蹤的背景流程。mcp-agent 的 @app.async_tool 適合啟動這類 workflow,並回傳 workflow 或 run 識別資訊;客戶端再透過狀態工具取得進度與結果。不要在任務尚未完成時回傳看似成功的文字,也不要把長工作同步卡到逾時。

JSON Schema 的目的,是讓模型少猜一點

一個好 schema 不只是欄位清單。每個參數都應有明確名稱、型別、必要性與合理限制;能列舉的狀態就列舉,能限制數值範圍、字串格式或陣列項目的地方就限制。不要只收一個萬用 payload 字串,再把所有解析與例外推給後端。

在 MCP 2026-07-28 規格中,若未明示 $schema,工具的 inputSchema 與 outputSchema 以 JSON Schema 2020-12 為預設;輸入根節點仍應是 object。輸出 schema 是選用的,但若有宣告,伺服器應回傳符合它的 structuredContent,讓客戶端與模型能更可靠地解析結果。實際採用哪些 schema 特性時,也要考慮你的 MCP client 與 SDK 是否支援。

from mcp_agent.app import MCPApp

app = MCPApp(name="content-tools")

@app.tool(
    description="將標題正規化後以固定欄位回傳",
    structured_output=True,
)
def normalize_title(raw_title: str) -> dict[str, str]:
    title = " ".join(raw_title.split())
    return {
        "status": "ok",
        "title": title[:120],
    }

這段程式展示的是契約概念:輸入名稱可理解、回傳欄位固定、工具說明與行為一致。真正的專案仍應依已安裝的 mcp-agent 版本、SDK 支援與測試規範決定型別、Pydantic 模型與 schema 細節。

背景工作要回傳 handle,狀態不能藏在連線裡

MCP 沒有可依賴的協定層 session。若一個工作需要跨多次呼叫保存狀態,伺服器應回傳明確的識別資訊,並要求後續呼叫帶回該 handle;不要假設同一條連線、同一個模型對話或同一個瀏覽器分頁永遠存在。

handle 應該是不透明且有期限的識別值。每次查詢狀態或取得結果時,伺服器都必須重新驗證呼叫者是否有權存取該工作。這能避免把工作 ID 誤當成永久授權,也讓逾期、遺失或無效的工作能回傳可理解的錯誤。

錯誤訊息要能讓下一步更正,而不是只說失敗

工具輸入不符合 schema、缺少必要欄位或名稱不存在,屬於請求結構問題;外部 API 暫時失敗、資料不存在或業務規則不允許,則是工具執行中的不同情況。對模型和前端而言,這些情況需要不同處理方式。能修正的輸入錯誤應指出哪個欄位不合規;可重試的外部錯誤應表明是否建議重試;不可恢復的錯誤則不應假裝稍後一定會成功。

同時要注意記錄範圍:追蹤 ID、操作類型與安全的錯誤分類通常足夠;密碼、access token、原始私密內容或完整授權標頭不應出現在模型可見的工具結果或一般日誌中。

工具描述能幫模型選擇,不能取代伺服器端授權

清楚的 description 能讓模型知道何時使用工具、需要哪些輸入、有哪些限制;但它不是安全控制。任何會修改資料、發送訊息、付款、刪除資源或讀取敏感資訊的工具,都要在伺服器端驗證身分、權限與輸入,並為重要動作保留人類確認機會。

MCP 規格也建議讓人能看見目前暴露給模型的工具、辨識工具被呼叫的時機,並在需要時拒絕操作。把這些保護放在應用程式與後端邊界,才不會因模型誤解描述或提示詞遭操控而失效。

可維護的開發順序

  1. 先選一個範圍很小、能明確驗證的工作,不要一開始就做萬用代理人。
  2. 定義輸入、輸出、錯誤與權限邊界,再寫實作;正常、缺欄位與外部失敗都要有測試。
  3. 依完成時間選擇直接結果或可追蹤背景 workflow,明確設計 status 與結果取得方式。
  4. 把連線、秘密、logging 與 executor 放在應用程式層管理,避免散落於工具函式。
  5. 用真實模型做小範圍驗收:它是否選對工具、填對欄位、讀懂錯誤,且不會越權操作?

常見問題

所有工具都要做成背景 workflow 嗎?

不需要。能在一次呼叫內可靠完成的工作,直接回傳結果通常更簡單。只有當工作真的需要長時間、排隊、重試、跨步驟狀態或人工介入時,才值得增加 handle、狀態查詢與背景執行的複雜度。

輸出 schema 是不是越細越好?

只細到足以消除下一步的歧義即可。過度拆分會讓工具難以演進;太少欄位則讓模型和前端自行猜測。先固定核心欄位,再以選填欄位或版本化方式安全擴充。

工具描述可以當作安全規則嗎?

不可以。工具描述屬於引導資訊,真正的驗證仍要在伺服器端完成,包括身分、授權、輸入驗證、速率限制與敏感操作的確認。

延伸閱讀:以官方文件為準

Similar Posts