定位:模型 ↔ 工具/資料 的標準協定
Agent 編排篇、Agent Runtime 篇與 Tool Gateway 篇一路把「協定內部」留白給本篇;這裡正式展開——MCP 到底是什麼、不是什麼,以及它解掉的那個爆炸。
《LLM 應用編排與 Agent 模式》在講「為何需要標準協定」的那一節立下一個動機:當你有 M 個 Agent/應用、要接 N 個工具或資料源時,若每一對整合都各自客製,成本會逼近 M × N——每加一個新工具,所有應用都要重接一次。那一篇刻意只講「為什麼需要標準協定」,把角色分工、傳輸方式、訊息格式全都外推給協定專篇。《Agent Runtime 後端服務設計》把 MCP 當成「工具如何被發現與呼叫」的一個抽象邊界;《Tool Gateway、權限與安全邊界》則提到「MCP server 可以是安全強制點的一個落點」。三篇留下的同一塊空白,就是本篇要填的:協定本身長什麼樣。
Model Context Protocol(MCP)是一套開放協定,用來標準化「一個承載 LLM 的應用程式」與「外部工具、資料源」之間的溝通。它由 Anthropic 於 2024 年底提出並開源,隨後被主流 AI 應用與各大廠採用。它的核心承諾只有一句話:
只要每個應用都講 MCP、每個工具都用 MCP server 暴露,那麼任一個 MCP 應用都能接上任一個 MCP server,不必為每一對關係寫一份專屬膠水。整合成本從 M × N 掉到 M + N:寫一個 GitHub 的 MCP server,所有支援 MCP 的應用立刻都能用它;做一個支援 MCP 的應用,整個 server 生態立刻為你所用。官方最愛的類比是「AI 應用的 USB-C」——一個標準接口,取代一堆專屬線材;工程上更貼切的類比是 LSP(Language Server Protocol):在 LSP 之前,M 個編輯器 × N 種語言各自整合;LSP 之後變成 M+N,MCP 對「AI 應用 × 工具」做的是同一件事。
因為名字裡有「Context」、又常和 Agent 一起出現,MCP 很容易被誤解成別的東西。先用一張表把邊界劃清楚,後面各章才好對號入座:
| 常見誤解 | 實際上 | 那是誰的守備 |
|---|---|---|
| MCP 是一個 Agent 框架 | 不是。它不定義 Agent Loop、不編排推理;它只規範「LLM 的宿主如何與工具/資料溝通」 | Agent Loop → Agent 編排篇 |
| MCP 是一種模型 API | 不是。它不是 LLM 推論端點;它連接的是宿主應用與外部能力,與「呼叫哪個模型」無關 | 模型路由 → LLM Gateway 篇 |
| MCP 取代了 Function Calling | 不是。模型端仍然是 function calling;MCP 標準化的是「工具如何被描述、發現、傳輸」那一層(第 6 節詳談) | 本篇第 6 節 |
| 用了 MCP 就安全了 | 不是。MCP 是傳輸與發現協定,不是安全機制;權限、sandbox、審計是宿主與 gateway 的責任 | 安全政策 → Tool Gateway 篇 |
記住這四條「不是」,就抓住了 MCP 的定位:它是工具/資料側的互通協定,一層很薄、但省掉大量重工的標準。
三個角色:Host / Client / Server
MCP 把「應用 ↔ 工具」拆成三個角色。搞懂這三者的拓樸與那條 1:1 的線,後面所有訊息往返才對得上位置。
MCP 的架構刻意分成三個角色,各司其職。最容易搞混的是 Host 與 Client——它們常在同一個行程裡,但職責不同:
| 角色 | 是什麼 | 負責 |
|---|---|---|
| Host | 承載 LLM 的應用程式本體(Claude Desktop、IDE 外掛,或你自己的 Agent Runtime) | 建立並管理多個 Client、控制各連線的權限與生命週期、彙整跨 server 的 context、協調 LLM 推理、執行使用者授權 |
| Client | 由 Host 建立的連線器,與單一 Server 維持 1:1 的有狀態 session | 協定協商、能力交換、雙向訊息路由、管理訂閱與通知;並維持各 server 之間的隔離邊界 |
| Server | 暴露能力的獨立元件,可為本地行程或遠端服務 | 透過 primitives 提供 tools/resources/prompts;聚焦單一職責(如 filesystem、GitHub、Postgres 各一個) |
它們的拓樸是「一個 Host、多個 Client、多個 Server」,而每個 Client 只綁一個 Server——這條 1:1 的線是整個模型的關鍵:
1 Host : N Client : N Server,其中 Client ↔ Server 恆為 1:1。要多接一個工具源,就多開一個 Client 連一個 Server,Host 其餘部分不動。
直覺上「應用直接連工具」不就好了嗎?MCP 硬是插入一個 1:1 的 Client,是為了三件事。隔離:每個 Server 各有一條獨立 session,一個 Server 看不到、也碰不到另一個 Server 的往來——這正是把權限邊界結構化的基礎(安全政策本身見 Tool Gateway 篇,此處只談它的結構前提)。協商:Client 負責和 Server 談妥協定版本與能力(第 5 節),Host 不必關心每個 Server 的協定細節。路由:Host 面對的是「一組 Client」的統一介面,訊息該送去哪個 Server、通知從哪回來,都由 Client 層處理。少了這層,Host 就得自己揉進 N 份連線邏輯與 N 種隔離規則。
對接 Agent Runtime 篇:那篇的 Agent Runtime 就是 Host 的一種形態。那篇把「工具如何被發現與呼叫」留成一個抽象邊界,如今可以填上具體答案——runtime 作為 Host,會為每個要用的工具源建立一個 Client,透過 1:1 session 去發現與呼叫工具。至於角色之間實際傳什麼訊息、怎麼交握,是第 4、5 節的事。
三種能力(Primitives):Tools / Resources / Prompts
Server 能提供的東西不只「工具」。MCP 把能力分成三種 primitive,分野不在功能,而在「誰決定何時用它」。
一個 MCP Server 可以暴露三類能力,稱為 primitives。很多人只知道 tools,但另外兩者同樣重要。三者最本質的差異,是控制權——由誰主導它在什麼時候被用:
inputSchema 描述參數,是本篇 Demo 的主角。URI 定址,通常無副作用。把控制權當成一條光譜就清楚了:tool 由模型主導、resource 由應用主導、prompt 由使用者主導。這條光譜也決定了它們各自的 discovery 與呼叫方法:
| primitive | 控制者 | 用途 | discovery / 取用方法 |
|---|---|---|---|
| tools | 模型(model-controlled) | 執行動作、可有副作用 | tools/list 發現 → tools/call 呼叫 |
| resources | 應用(application-controlled) | 提供唯讀上下文資料 | resources/list 發現 → resources/read 依 URI 讀取 |
| prompts | 使用者(user-controlled) | 模板化訊息、可重用工作流 | prompts/list 發現 → prompts/get 取用 |
三者共用同一個「list → 取用」的動態發現節奏(第 5 節會看到),但控制權不同,因此在產品 UI 上的呈現也不同:工具由模型自動選、資源由應用附掛、prompt 由使用者點選。
上面三種是 Server 端 primitives。MCP 的協商是對稱的,Client 端也能宣告三種能力反向服務 Server,讓你對它們先有個心智模型即可:sampling——Server 反過來請 Client 代為做一次 LLM 推論(因為模型與金鑰握在 Host 手上,Server 不必自帶模型);roots——Client 告訴 Server「你可以在哪些檔案系統/URI 邊界內活動」;elicitation——Server 執行到一半需要補資料時,反向向使用者索取輸入。這三者說明了 MCP 不是單向的 client 拉、server 推,而是一條雙向、可協商的 session。本篇聚焦 Server 三 primitive 與 tools 流程,client 端能力點到為止。
Transport:訊息怎麼傳(stdio / Streamable HTTP)
角色與能力有了,還缺「訊息走哪條線」。MCP 的編碼固定是 JSON-RPC 2.0,傳輸則有兩種標準選擇——外加一個已被淘汰的舊選項。
MCP 所有訊息都以 JSON-RPC 2.0 編碼(UTF-8)。JSON-RPC 只有三種訊息型態,貫穿後面所有交握與呼叫:
| 訊息型態 | 有 id? | 需要回應? | 用途 |
|---|---|---|---|
| Request | 有 | 要(對方以同 id 回 Response) |
initialize、tools/list、tools/call…… |
| Response | 有(對應 Request 的 id) | 本身即回應 | 攜帶 result 或 error |
| Notification | 無 | 不需要、也不會有回應 | notifications/initialized、notifications/tools/list_changed…… |
「有沒有 id」是判讀一則 MCP 訊息的第一個線索:有 id 要配對回應,無 id 是單向通知。第 5 節拆解器裡的「notification」樣本就是一則 initialized,把它的 id 加回去,分類立刻改判。
JSON-RPC 決定了「訊息長什麼樣」,但這些位元組實際透過什麼管道流動,是 transport 的事。截至撰稿時(2026 年中,最新規格版本 2025-11-25),MCP 定義了兩種標準 transport:
| transport | 怎麼運作 | 適合 | 現況 |
|---|---|---|---|
| stdio | Client 把 Server 當子行程啟動,透過 stdin/stdout 收送 JSON-RPC,stderr 記 log |
本地工具、單一使用者、開發期——最簡單、零網路、低延遲 | 標準,本地首選 |
| Streamable HTTP | 遠端服務,單一 HTTP endpoint;Client 用 POST 送訊息,Server 可用 SSE 串流回多筆訊息或主動推送 | 遠端/多使用者/需水平擴展的 server | 標準(2025-03-26 起),遠端首選 |
| 舊設計:一個 endpoint 收 POST、另一個獨立 SSE endpoint 推事件 | — | 已 deprecated(2024-11-05 的舊法,2025-03-26 起被 Streamable HTTP 取代) |
Agent 編排篇講標準協定動機的那一節,當時把 transport 描述成「stdio/HTTP+SSE」,那是 2024 年底 2024-11-05 版的說法。2025-03-26 版起,遠端 transport 已改為 Streamable HTTP(單一 endpoint,把原本分離的 POST 與 SSE 收攏),舊的雙 endpoint HTTP+SSE 被標記為 deprecated。新實作應採 stdio(本地)或 Streamable HTTP(遠端);只有在需要相容舊 server 時,才保留對 HTTP+SSE 的 fallback。這也提醒一件事:MCP 是持續演進中的協定,版本以 YYYY-MM-DD 標示「最後一次破壞相容變更的日期」,讀規格時務必對齊你採用的版本。
一次連線的完整流程:交握 → 探索 → 呼叫 → 回寫
這一章把前面的角色、primitive、transport 串成一條時間線——也就是 Agent Runtime 篇留白的「工具如何被發現與呼叫」,在此正式展開,並由本章的拆解器 Demo 把訊息攤開來看。
一條 MCP session 從建立到用上工具,會依序走過三個階段。三階段用同一套 JSON-RPC 訊息,但目的不同:
① 交握(Initialization)。Client 送出 initialize request,帶上自己支援的 protocolVersion、capabilities 與 clientInfo;Server 回以自己的版本、能力與 serverInfo。雙方就此協商出這條 session 要用哪個協定版本、彼此支援哪些能力(例如 Server 宣告 tools.listChanged 表示工具清單變動時會發通知)。協商成功後,Client 再送一則 notifications/initialized 通知,正式開始正常操作。能力是「談」出來的、不是硬編碼的——這正是協定能向前相容演進的關鍵。
② 探索(Discovery)。Client 送 tools/list(可帶分頁 cursor),Server 回傳一份工具清單,每個工具含 name、description 與 inputSchema。重點是:Client 事前完全不需要知道有哪些工具——是 Server 在執行期把自己「自我描述」出來的。工具清單還可能中途變動,Server 會發 notifications/tools/list_changed,Client 再重新 tools/list。
③ 呼叫與回寫(Invocation)。模型從清單裡挑一個工具、填好參數後,Client 送 tools/call(name + arguments);Server 執行完把結果回寫在 result.content[](可含 text/image/resource 等內容塊),並以 isError 標示成敗。Client 把這份結果交回 Host,餵給模型生成最終回答——這一步正好接上 Agent Runtime 篇那台狀態機的 tool_calls → running 回寫。
整條流程最有價值的一步,是 tools/list 那次執行期發現。傳統做法裡,「有哪些工具、參數長怎樣」是硬編碼在應用程式碼裡的;換一個工具集,就得改應用、重新部署。MCP 把這件事翻轉成執行期協商:應用只要會講 MCP,就能動態問出對方有什麼工具、各自的 schema 是什麼,即插即用。這也是它與「自建 Function Calling」最根本的分野(第 6 節展開)——同一份能力,一邊要寫死、一邊能被發現。
把三個階段放到一條時間線上,就是一條 session 最小可用的往返:
sequenceDiagram
participant C as Client
participant S as Server
C->>S: initialize(版本+capabilities)
S-->>C: result(版本+capabilities)
C->>S: notifications/initialized
C->>S: tools/list
S-->>C: result(name+inputSchema)
C->>S: tools/call(name+arguments)
S-->>C: result.content[] 或 error
看第三條線 notifications/initialized——它沒有回箭頭,因為 notification 無 id、不等回應;其餘每一條請求都恰好配一條同 id 的回應。交握兩則、通知一則、每種 primitive 各一次 list 往返,是這條 session 的固定開銷。
這些訊息長什麼樣、合不合法、能不能用,全都寫在那一小段 JSON 裡。下面的 Demo 讓你直接改那段 JSON,它會真的把訊息拆開判給你看:
載入一則樣本或直接在框裡編輯。Demo 會真的 JSON.parse 這段文字:檢查 JSON-RPC 2.0 信封、分類成 request/response/notification、依交握談出來的能力判斷這個 method 在本 session 能不能用、拿 arguments 對工具的 inputSchema 做必填與型別檢查,最後算出它走兩種 transport 各要包多少框架 bytes。
試著動手破壞它:把 "2.0" 改成 "2"、刪掉 id 那一行、把 location 拿掉、或把 units 填成 "k",看關卡鏈在第幾關擋下、回哪一個 error code。
這張矩陣就是交握的結果:點任一格可切換該能力有沒有被宣告。右欄是它解鎖的 method——沒宣告就沒解鎖,訊息再合法也只會換回一個 -32601。server 側三種 primitive 決定 discovery 要跑幾趟 list,client 側三種則是 Server 反向使用的能力。
同一段 JSON,換條線就換一層外殼:stdio 只多一個換行;HTTP 要先鋪一整組標頭(連 Content-Length 的位數都隨 payload 變)。注意這裡的 HTTP 數字是本 demo 組出來的最小標頭集(request line + Host/Content-Type/Accept/Mcp-Session-Id/Content-Length),真實實作還會帶 Authorization、User-Agent、追蹤標頭等,開銷只會更大。
如果這條流程不用 MCP、改成自建 Function Calling 會怎樣?大致是:沒有 initialize 那次版本/能力協商(版本相容全靠你自己約定)、沒有 tools/list 這一步——工具清單與 schema 被寫死在應用程式碼裡,模型能用什麼在編譯期就定死了;tools/call 則退化成你自訂格式的一次內部函式呼叫,換一個應用就得整套重寫。少掉的那兩步(協商與發現),正是 MCP 換來的「互通性」。完整取捨留待第 6 節。
initialize 協商版本與能力 → initialized)→ 探索(tools/list 動態發現工具與 schema)→ 呼叫回寫(tools/call → result.content[]/isError)。靈魂是 discovery:工具由 Server 自我描述、執行期發現,不必寫死在應用裡。
MCP vs 自建 Function Calling:差異與取捨
既然模型端本來就會 function calling,為什麼還要 MCP?答案不在「功能」,而在「互通性」與「你願不願意背那層抽象」。
先講清楚一件最常被誤會的事:MCP 不取代 function calling,兩者在不同層。模型「決定呼叫某個工具、吐出參數」這件事,仍然是 function calling;MCP 標準化的是它外圍的一切——工具如何被描述、如何被發現、透過什麼管道傳輸、結果如何回寫。可以說:function calling 是模型的能力,MCP 是承載這個能力的互通協定。
把「自建」與「MCP」並排,差異集中在四點:
| 面向 | 自建 Function Calling | MCP |
|---|---|---|
| 工具發現 | schema 硬編碼在應用裡,能用什麼編譯期就定死 | tools/list 執行期動態發現,即插即用 |
| 傳輸 | 自訂格式的內部呼叫,每個 app 各一套 | 標準 JSON-RPC over stdio/Streamable HTTP |
| 互通與重用 | 綁死該 app;換一個應用整套重寫(M×N) | 一個 Server 被任何 MCP host 使用;生態可重用(M+N) |
| 成本 | 單一工具最省事,零額外抽象 | 要跑 server 行程、多一層協定與交握,初期較重 |
MCP 用「一次協定開銷」換「跨應用的互通與生態重用」;自建用「零抽象」換「綁死單一應用」。這不是誰對誰錯,是規模與邊界的取捨。
自建 function calling 更划算:工具數量少、只有你這一個應用會用、都是自家內部 API、且短期內不打算對外開放——這時多一層 MCP 只是徒增行程與協定開銷。MCP 更划算:你要接的是第三方已有的 server(filesystem、GitHub、各家 SaaS)、同一批工具要被多個應用共用、希望工具能熱插拔與獨立演進(改工具不必動應用)、或你想把自家能力對外暴露給整個 MCP 生態。判準一句話:當「互通」的價值超過「多一層抽象」的成本,就上 MCP。
兩個相鄰邊界順帶釐清,避免混淆:其一,MCP server 可以是 gateway 的一個落點——你能把 allowlist、authz、審計實作在 server 這一側,讓它同時是協定端點與強制點;但那是「安全政策擺哪裡」的工程選擇,政策原則本身見 Tool Gateway 篇,本篇只負責協定介面。其二,MCP 是工具/資料側的協定互通,與 LLM Gateway(模型側的路由、fallback、成本、semantic cache)是兩條正交的線——一個管「工具怎麼接」,一個管「模型怎麼選」。
落地與選型:實務與面試
收尾把 MCP 放回整個 Agent 後端版圖:它在系列裡的座標、常見誤區,以及一組能直接搬去面試的選型與問答。
走到這裡,Agent 後端的地圖已經完整。MCP 在其中的座標,正好把 Agent 編排、Agent Runtime、Tool Gateway 三篇串起來:
| 相關篇 | MCP 在這裡的角色 |
|---|---|
| Agent 編排篇 | 立下 M×N 動機;MCP 就是它點名的「標準協定」之一(模型↔工具那條) |
| Agent Runtime 篇 | runtime 就是一種 Host;它為每個工具源開一個 Client,用 MCP 做發現與呼叫 |
| Tool Gateway 篇 | MCP server 可作為安全強制點的落點;協定介面在本篇、政策原則在 Tool Gateway 篇 |
| 本篇 · MCP | 把上面三處留白的「協定內部」——角色、primitives、transport、流程——正式展開 |
真正落地時,第一個要決定的是 transport/部署形態,判準很直接:
| 如果…… | 就用 | 理由 |
|---|---|---|
| 本地工具、單一使用者、開發期 | stdio server | 零網路、低延遲、最好起——Client 直接把 server 當子行程拉起來 |
| 要給多使用者、需水平擴展、遠端部署 | Streamable HTTP server | 單一 HTTP endpoint、可 SSE 串流,能像一般後端服務那樣擴展 |
| 只有一個 app、少數自家內部工具 | 先別上 MCP,自建 function calling | 互通價值還沒出現,多一層協定只是負擔(第 6 節的判準) |
生態現況(截至撰稿時):官方提供多語言 SDK,社群與各大廠已有大量現成 server;要接第三方能力時,先找有沒有現成 MCP server,往往比自己刻更快。
① 把 MCP 當 Agent 框架:它不編排推理、不管 Agent Loop——那是 Agent 編排篇的事。MCP 只負責「工具怎麼被發現與呼叫」。② 以為用了 MCP 就安全:MCP 是傳輸與發現協定,不含權限判斷;把不可信的工具 server 接進來,一樣會踩 Tool Gateway 篇威脅模型那一節的雷,安全得另外做。③ 以為 MCP 取代 function calling:模型端仍是 function calling,MCP 只是標準化它的外圍——兩者不是替代,是分層。
Q:一句話說明 MCP 解決什麼問題?
答:把「M 個應用 × N 個工具」的客製整合,用標準協定收斂成 M+N 的插拔;類比 AI 應用的 USB-C。
Q:Host、Client、Server 各是什麼?
答:Host 承載 LLM 並管理連線(runtime 就是一種 Host)、Client 與單一 Server 維持 1:1 有狀態 session 並負責協商與隔離、Server 暴露 tools/resources/prompts。
Q:MCP 和自建 function calling 差在哪?
答:差在動態 discovery、標準 transport 與跨應用互通;自建把 schema 寫死綁死單一 app,MCP 換來一份 server 到處可用,代價是多一層協定開銷。
Q:stdio 和 Streamable HTTP 怎麼選?
答:本地/單機/開發期用 stdio(子行程、零網路);遠端/多使用者/要擴展用 Streamable HTTP;舊的 HTTP+SSE 已 deprecated 別再新建。
Q:tool call 的錯誤有幾種?
答:兩種——protocol error(JSON-RPC error,如呼叫不存在的工具)代表請求本身不合法;tool execution error(result.isError=true)代表工具跑了但出錯,會帶回可行動訊息讓模型自我修正。