AI 開發

Claude Code、Codex 等 AI Coding Agent 如何呼叫 MCP Tools?JSON-RPC、Tool Schema 與執行權限完整解析

閱讀約 16 分鐘

協議對比文寫過 Schema 和 Function Calling 的分工。本篇只落到 Coding Agent:模型填參數,宿主發 JSON-RPC。接伺服器、列工具、過 Schema、再評權限,是四步,不是一句「連上 MCP」。

閱讀前速覽

步 鎖什麼 誰執行
配置 .mcp.json / config.toml stdio 或 http;url 必須帶 type
发现 tools/list + inputSchema 宿主列工具,模型看不見傳輸
调用 tools/call 的 arguments 模型填 JSON,宿主發 RPC
权限 mcp__server__tool / enabled_tools 連上 ≠ 自動執行

MCP 是宿主和伺服器的協議。模型只看見工具名和參數 Schema。

調 MCP 的是宿主,不是模型

Claude Code、Codex、Cursor 都是 MCP 用戶端。會話開始後,宿主連上伺服器,發 JSON-RPC 2.0 的 tools/list,把每條工具的 inputSchema 交給模型。模型只產出一段參數 JSON。真正發 tools/call、跑副作用的是宿主。沒有宿主這一跳,MCP 只是設定檔。

  • 設定:伺服器怎麼啟動。stdio 跑本地行程,http 連遠端。兩邊都是物件,不要口頭約定。
  • Schema:order_id 必須過 inputSchema。寬 object 等於請模型發明鍵。
  • 權限:Claude 用 mcp__伺服器__工具 寫規則。Codex 用 enabled_tools 和審批檔。Cursor 用 mcpAllowlist。連上伺服器,不等於這些門已經開。

協議層的 Schema 和 Function Calling,見 MCP Tool Schema vs Function Calling。Coding Agent 自己的工具 JSON,見 AI Coding Agent 與 JSON 工具呼叫。Schema 為什麼是門,見 AI 為什麼需要 JSON Schema。本篇不再定義那三個詞,只寫宿主怎麼把 MCP 跑起來。

第一張物件:伺服器設定

Claude Code 的專案設定是倉庫根的 .mcp.json,可進版本庫。本機私有的寫進 ~/.claude.json。有 url 就必須寫 type:http(streamable-http 是別名)或 sse / ws。只寫 url、不寫 type,官方會當成壞掉的 stdio,伺服器被跳過。虛構訂單服務,不要貼真實金鑰:

{
  "mcpServers": {
    "orders": {
      "type": "stdio",
      "command": "node",
      "args": ["./servers/orders-mcp.js"]
    }
  }
}

Codex 把同一件事寫進 ~/.codex/config.toml 或可信專案的 .codex/config.toml,表名是 [mcp_servers.orders]。stdio 用 command / args,遠端用 url。Cursor 用 .cursor/mcp.json 或使用者級檔案,編輯器和 CLI 共用。專案裡新出現的伺服器,Claude Code 會停在待批准,不是自動連。

第二張物件:tools/list 和 inputSchema

宿主連上之後,先問有哪些工具。這是 JSON-RPC,不是模型自己發的 HTTP:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/list"
}

伺服器回一張表。每一行是名字、說明、一份 JSON Schema。規範要求宣告 tools 能力。列表可以隨授權變,但不應隨連線抖動。Coding Agent 把這張表轉成模型能看的工具。Claude Code 裡,可呼叫名是 mcp__orders__lookup_order。外掛帶來的伺服器還要嵌外掛名。工具太多時,官方會延後載入,用工具搜尋按需展開,不要預設把整表塞進上下文。

{
  "name": "lookup_order",
  "description": "Look up an order by ID. Confirm before any refund.",
  "inputSchema": {
    "type": "object",
    "properties": {
      "order_id": { "type": "string", "pattern": "^[A-Z]-\\d{4}$" }
    },
    "required": ["order_id"],
    "additionalProperties": false
  }
}

第三張物件:模型填參,宿主 tools/call

模型看見的是宿主給的工具名。Claude Code 會話裡,一次呼叫長這樣:

{
  "type": "tool_use",
  "id": "call_3c91",
  "name": "mcp__orders__lookup_order",
  "input": { "order_id": "A-1042" }
}

宿主校驗 input 對上 inputSchema,再翻譯成發給伺服器的 RPC。名字用伺服器註冊名 lookup_order,不再帶 mcp__ 前綴:

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "lookup_order",
    "arguments": { "order_id": "A-1042" }
  }
}

成功就回 content。失敗把 isError 設真,原因寫成物件,不要只丟一句散文。模型讀的是回寫,不是通訊端。

{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "content": [
      { "type": "text", "text": "{\"order_id\":\"A-1042\",\"status\":\"paid\"}" }
    ],
    "isError": false
  }
}

第四張物件:執行權限

MCP 規範寫明:工具呼叫應當能被拒絕。Coding Agent 把拒絕寫成規則,不寫成提示。Claude Code 用完整可呼叫名:

{
  "permissions": {
    "allow": ["mcp__orders__lookup_order"],
    "ask": ["mcp__orders__create_note"],
    "deny": ["mcp__orders__refund_order"]
  }
}

官方順序仍是 deny、ask、allow。光桿 mcp__* 作 deny,會把 MCP 從上下文摘掉。Codex 用 enabled_tools 做白名單,disabled_tools 做隨後的黑名單,預設審批是 auto / prompt / approve,也可按工具覆蓋。Cursor 預設每次問;permissions.json 的 mcpAllowlist 寫成 orders:lookup_order。Shell / Git / 檔案那三道門,見 Coding Agent 權限模型。MCP 是第四張表,不是那三道門的替代。

五步把 MCP 收成物件

不要從「幫我接一個 MCP」開始。按物件寫:

  1. 先寫伺服器設定。stdio 或 http 選一個。url 帶 type。金鑰走環境變數,不進倉庫。
  2. 自己跑一遍 tools/list。記下名字和 required。沒有 Schema 的工具不要交給模型。
  3. 收緊 inputSchema。pattern、enum、additionalProperties: false。查單不要帶退款鍵。
  4. 按工具寫權限。唯讀查單 allow。寫盤 ask。退款 deny。Codex 用 enabled_tools 先裁表。
  5. 把一次成功呼叫存成三份 JSON:tool_use、tools/call、result。能對上,才算接通。

Schema 怎麼當門,見 Agents API + JSON Schema。本篇只要求:Coding Agent 上,MCP 的每一步都有物件,而且由宿主發。

複盤 MCP,先攤開三份 JSON

一次失敗,多半是名字、Schema、權限沒對齊,不是模型不會調。

  • 把 inputSchema 和 arguments 格式化,看缺欄位和多餘鍵。
  • 用 JSONPath 提取 抽出 $.params.name、$.input.order_id。
  • 一次過門和一次被拒 JSON Diff。先改規則和 Schema,再改提示。
  • 核對宿主名 mcp__orders__lookup_order 和 RPC 名 lookup_order。外掛伺服器還要帶外掛段。
  • 設定、權杖、會話日誌不要貼進公共格式化站。JSONSort 在瀏覽器本地跑。

三份軌跡都能按 Schema 重建,你才是在收 MCP,不是在收「我點過連線」。

常見誤區

  • 以為連上就等於會調。連上只是行程或 HTTP 通了。沒有 list、沒有 Schema、沒有權限,模型仍調不成。
  • 把 MCP 當成 Function Calling。對模型都是填 JSON。對宿主,MCP 多一跳 JSON-RPC。協議對比文寫過分工,這裡只要求你看見那一跳。
  • url 不寫 type。Claude Code 會按壞 stdio 跳過。遠端伺服器就此失蹤。
  • 只配伺服器,不裁工具。一張大表進上下文。官方用延後載入和 enabled_tools,就是怕這個。
  • 用一句話「請自動批准所有 MCP」當預設。Cursor 的一鍵全批、Claude 的 bypass、Codex 的 auto 全開,官方留給隔離環境。日常按工具寫。
  • 專案 .mcp.json 當成本機私貨。它會進版本庫,別人打開倉庫會看到待批准。金鑰放使用者級設定。

FAQ

Coding Agent 會自己發 JSON-RPC 嗎?

不會。模型只填工具名和 arguments。JSON-RPC 的 id、method、params 由宿主拼。你在日誌裡看到 tools/call,那是用戶端,不是模型。

inputSchema 和權限哪個先評?

先形狀,再執行。Schema 不過,不要發 RPC。過了 Schema,仍可能 ask 或 deny。兩道門鎖的不是同一件事。

stdio 和 http 怎麼選?

要碰本機檔案或本機行程,用 stdio。要連託管服務,用 http。Claude Code 推薦遠端走 http;SSE 已標過時。WebSocket 適合伺服器主動推事件。

工具太多怎麼辦?

Claude Code 可延後 MCP 工具,按搜尋再展開。Codex 用 enabled_tools 裁表。不要把五十個工具一次塞進會話。

Subagent 能用這些 MCP 嗎?

看產品。Claude Code 的權限和 allowed-tools 要用完整可呼叫名。Agents API 的子代理繼承 MCP、不繼承你的 function。不要假設一張表自動傳給所有子會話。

結論

Coding Agent 調 MCP,不是模型直接打伺服器。宿主列工具,模型填 JSON,宿主再 tools/call。Schema 和權限是兩道門。

先寫清 A-1042 會走哪一個工具名、哪一份 Schema、哪一條權限。能指到三份物件,MCP 才算接通。模型會換。這三份應該先穩定。

延伸阅读

更新紀錄: 首次發布