Production-ready MCP TypeScript Server:contract、transport、Auth 與觀測

把 MCP demo server 升級成有 runtime schema、最小工具權限、正確 transport logging、OAuth audience validation、timeout 與 audit 的服務。

MCP server 把模型可以要求的能力轉成真正的 code execution/data access,因此 tool description 不是權限控制。Production server 必須把每個 tool 當外部 API:有明確 input schema、caller identity、authorization、resource limit、side-effect contract、audit 與錯誤模型。只要模型能構造參數,輸入就必須視為不可信。 截至 2026-07-29,MCP 官方 TypeScript 教學以 McpServer 、SDK 與 transport 建立 server,並明確提醒 stdio 的 stdout 是 JSON-RPC channel,不可用 console.log 汙染。現行 MCP Authorization 規範要求 HTTP resource server 驗證 token audience/resource,且禁止把 client token 原樣 passthrough 給下游 API。 實作步驟 先列 inventory:resources 是 read-only context,tools 可能計算或 mutation,prompts 是模板。每個 tool 名稱穩定、描述具體,input 用 Zod/JSON Schema 驗證;輸出只回必要資料。將 search_orders 與 refund_order 分開,不用一個 manage_order(action) 隱藏高風險操作。 server.registerTool( "get_order", { description: "Read one order visible to the authenticated caller", inputSchema: { orderId: z.string().uuid() }, }, async ({ orderId }, context) => { const actor = requireActor(context) const order = await orders.findVisible(actor.id, orderId) if (!order) return toolError("not_found") return { content: [{ type: "text", text: JSON.stringify(redact(order)) }] } }, ) 選 transport 時先看部署邊界。local single-user integration 可用 stdio,credential 從 process environment/OS secret store 取得;stdout 只傳 protocol,log 寫 stderr。Remote multi-user server 使用官方支援的 HTTP transport,HTTPS、request limit、timeout 與 authentication 都要在入口。 HTTP Auth 依 MCP 規範實作 protected resource metadata 與 OAuth 2.1 flow。server 驗證 issuer、signature、expiry、audience/resource 與 scopes;client token 不得直接拿去呼叫第三方。若 server 需要下游 API,使用自己的 OAuth client/credential,將 caller 與 downstream grant 清楚映射。 scope 依 capability 分割,例如 orders:read 、 orders:refund 。每個 tool handler 再做 resource-level authorization,不能只因 client 看得到 tool 就允許操作。多 tenant 查詢必須帶 server-derived tenant/user ID,不接受模型提供 owner ID。 對外 fetch 設 host allowlist、DNS/IP/redirect 驗證、timeout、response size 與 content type,防止 SSRF 到 metadata/internal network。不要提供任意 URL fetch tool。file tool 限制 realpath 在 allowed roots,拒絕 path traversal、symlink escape 與 device files。 side-effect tool 使用 idempotency key、dry-run/preview 與明確 approval metadata。持久層先記 request actor、tool、normalized args digest、decision 與 result ID,但 redaction secret/個資。log 不記 Authorization/token,stdio 只用 console.error 。 部署前 pin SDK/Node、compile、schema snapshot、unit/integration/contract tests。server 啟動檢查 config,無 credential 時 fail closed。健康檢查不執行 tool;readiness 驗證 dependency connectivity 但不洩漏拓撲。 失敗與復原 stdio client 顯示 JSON parse error 時,先查 stdout 是否混入 log/banner;移到 stderr,再抓 protocol trace。不要放寬 parser 忽略任意文字,這會掩蓋 framing corruption。 Remote 401 時比對 protected resource metadata、issuer、audience/resource 與時鐘;403 則檢查 scope/resource authorization。不能為了通過把 audience validation 關掉或接受其他 API token。 下游 timeout 時若 tool read-only 可有限重試;mutation 先查 idempotency/result state,不盲目重送。server 返回 structured error 與 retryable flag,不把 provider stack/token 回給 model。 若 tool 被 prompt injection 誘導執行越權 mutation,立即停用該 capability、撤銷 credential、保存 audit,核對 side effects 並由 source system rollback。修復 handler authorization、schema/allowlist 與 approval gate;只改 tool description 不算修復。 驗證指令 npm ci npm run build npm test node build/index.js 2>mcp-server.log contract tests 必須列出 tools、驗證 invalid schema、未知 tool、無/過期/錯 audience token、缺 scope、跨 tenant ID、path traversal、SSRF redirect、timeout、oversized response、duplicate mutation 與 log redaction。用真 MCP client 做 list/call smoke,stdio 測試需斷言 stdout 每一筆都是合法 JSON-RPC。 官方來源 MCP:Build an MCP server MCP Authorization specification MCP Security Best Practices 延伸閱讀 在 技術文章 查看 AI coding 權限與 Edge Functions。 從 課程總覽 建立第一個 TypeScript MCP contract test。 發現 MCP 安全問題時可由 聯絡頁 提供 redacted trace。