MCP 開發實戰手冊:SSE、STDIO、Tool、Resource 一次搞懂
MCP 到底是什麼
最近 MCP 超級火,都被吹到天上去了,有人說 MCP 是 AI 領域的「USB-C 標準」,甚至有人預測它將引領下一個 AI 應用時代的到來。不過,乍看之下,MCP 不就跟原本的 function calling 幹的是同一件事嗎?
其實不然,MCP 不是想取代 function calling,MCP 實際上是把 Tool Execution 的部份從原本 function calling 的流程中抽離出來,並統一了接口。 另外,不只是 Tool,MCP 總共提出了三個核心概念:Tool、Resource、Prompt,目的是把 AI Agent 常見的操作邏輯抽象成一套標準化的介面。
Tool
其中大家最關心的核心功能就是 Tool,Tool 是為了 function calling 所設計的通訊協議。它的重點在於,把原本寫在每個 LLM Client 裡的「怎麼呼叫工具」這段邏輯,抽離成一個獨立的通訊協議。這樣開發者只需要專注在工具本身的實作,不需要再管怎麼塞給每個模型怎麼用。
為了更清楚理解這件事,我們來看看原本的 function calling 過程:
sequenceDiagram
participant LLM Client
participant LLM
LLM Client->>LLM Client: Generate prompt (tools & usage)
LLM Client->>LLM: Send prompt to LLM
LLM->>LLM: Decide which tool to use
LLM-->>LLM Client: Instruct to execute tool
LLM Client->>LLM Client: Execute the tool
LLM Client-->>LLM: Return execution result
而 MCP 的出現,最大的變化就是把 執行工具的階段 抽離出來,改由 MCP Server 來執行,如下圖:
sequenceDiagram
participant MCP Server
participant MCP Client / LLM Client
participant LLM
MCP Server-->>MCP Client / LLM Client: Provide available tools
MCP Client / LLM Client->>MCP Client / LLM Client: Generate prompt (tools & usage)
MCP Client / LLM Client->>LLM: Send prompt to LLM
LLM->>LLM: Decide which tool to use
LLM-->>MCP Client / LLM Client: Instruct to execute tool
MCP Client / LLM Client->>MCP Server: Request tool execution
MCP Server-->>MCP Client / LLM Client: Return execution result
MCP Client / LLM Client-->>LLM: Return execution result
Resource
再來講 Resource。每個 Resource 都有一個唯一的 URI,MCP Client 可以透過這些 URI 存取對應的資源內容。重點來了,Resource 的使用方式是由 MCP Client 決定的,MCP Server 只是提供資源清單和資料本體,怎麼用、什麼時候用,完全交給 Client 自己發揮。舉例來說,像 Cursor 這類工具就得自己決定要怎麼把 Resource 的內容餵給 LLM 當作 context,或是透過 UI 顯示給使用者看,讓使用者選,都可以。等於是讓 MCP Client 自由發揮,這也難怪目前支援 Resource 的 MCP Client 還不多 😅
Prompt
最後是 Prompt。Prompt 並不是像 Tool 一樣「讓模型主動呼叫」的東西,它比較像是 Prompt Template,由 MCP Client 在 UI 上顯示給使用者,讓使用者決定什麼時候套用。你可以把它想像成是一些常用的提示語句封裝,例如「問答模板」、「摘要格式」、「重寫句子」這種固定用途的提示,方便重複使用。
MCP 設計邏輯
可以看出 MCP 的設計邏輯其實很清楚:
- Tool 是給 模型 決定什麼時候用的
- Resource 是給 應用(MCP Client) 決定怎麼使用的
- Prompt 則是讓 使用者 自己挑要不要用、什麼時候用
三種能力剛好對應三個角色 —— 模型、應用、使用者 —— 各自有不同的控制權限與靈活度。
如果還不清楚的話,建議直接參閱 官方的 spec,比各種介紹文寫的清楚、直接,問就是 RTFM!
2025-03-26 MCP 新標準
我才寫好文章,MCP 就出了新版標準,主要改動 4 個部分
- 加入 OAuth 2.1 驗證機制
- 把 SSE 換成 Streamable HTTP (Server 可以選擇要不要使用 SSE,SSE 用完就關,不需要保持一個一直在線的 SSE,靈活性更高)
- 支援 JSON-RPC Batch
- 新增 Tool Annotations,能更好的描述 Tool
更多請參考 https://modelcontextprotocol.io/specification/2025-03-26/changelog
MCP Server 模式
初步理解 MCP 到底是在做什麼之後,我們來看 MCP 通訊協議的部分。
目前 MCP 協議支援 STDIO
和 SSE
兩種傳輸模式。
STDIO
在 STDIO 模式下,當 LLM 決定要使用某個工具時,LLM Client 會直接執行對應的 MCP Server,將輸入資料餵進該 Process 的 STDIN,然後從 STDOUT 讀取結果並回傳給模型。雖然名稱叫 MCP Server,但在 STDIO 模式下它其實就是個本地 CLI 程式,可以參考下面這個簡化的 Process Tree:
Process Tree:
-----
claude desktop (MCP Client)
└─ npx main.js (MCP Server)
MCP Client 的設定檔會指定一個 "command",想跑什麼就跑什麼,可以包成一個 shell script,甚至可以用 docker 跑。不過一般 python 是用 uvx
跑,而 node.js 則是用 npx
跑,這我們待會再來講講。
比如 mcp-server-git 的設定檔就長這樣:
{
"mcpServers": {
"git": {
"command": "uv",
"args": [
"--directory",
"/<path to mcp-servers>/mcp-servers/src/git",
"run",
"mcp-server-git"
]
}
}
}
SSE
另一種模式是 SSE,當 LLM 決定要使用某個工具時,MCP Client 會發送 HTTP 請求連接到 MCP Server,這時的 Server 通常是常駐在遠端、開了一個 port 的 HTTP 服務,也就是我們傳統認知的 "Server"。
SSE 連不上請看這裡
填寫設定檔的時候,記得不只要填 domain,url 後面要加上 /sse
用官方的 python-sdk 開發,預設是開在 /sse
路徑
{
"mcpServers": {
"git": {
"url": "http://192.168.1.200:8000/sse"
}
}
}
另外這邊補充一下:SSE(Server-Sent Events) 並不是 MCP 發明的新概念,它是 Web 世界中既有的標準,用來實現一種「單向的、持續的資料推送」機制。Server 可以透過一條長連線,主動推送訊息給 Client,而 Client 不需要一直 Long Polling。SSE 和 WebSocket 不同,它是單向的(只有 Server → Client),也不像 Long Polling 需要頻繁建立連線。對於 MCP 這種「工具執行完之後,要把結果推回去」的場景來說,使用 SSE 可謂恰到好處。
在 MCP 的實作裡,Client 會先對 /sse
發出一個連線請求,建立一個長時間不中斷的 stream。之後 MCP Server 執行工具時,不會立刻把結果塞進 HTTP 回應,而是走這個 SSE stream 將資料慢慢傳回去。
比如當你呼叫一個 List Tool
的操作時,可能會先收到一個 HTTP Response 顯示 "status": "Accepted"
(打開 F12 的你這時候可能滿頭問號,阿我的結果呢?),但實際的執行結果則是透過最一開始就建立的 /sse
stream 陸續推回來的。
常見坑
這邊要特別注意的是:如果 MCP Server 中途重啟了,而 MCP Client 沒有重新建立 /sse
stream,這時候當 Client 再送請求給 Server,就會發生錯誤。記得去 MCP Client 點一下重整之類的就好了。
mcp-test | ERROR: Exception in ASGI application
mcp-test | + Exception Group Traceback (most recent call last):
mcp-test | | File "/root/.venv/lib/python3.11/site-packages/mcp/server/sse.py", line 131, in connect_sse
mcp-test | | yield (read_stream, write_stream)
...
mcp-test | | RuntimeError: Received request before initialization was complete
MCP Server 開發語言與執行方式
目前開發 MCP Server 最主流的語言是 Python 和 TypeScript (Node.js)。官方的 SDK 也已經支援多種語言,像是 C#、Java、Kotlin 等,未來生態可能會越來越廣。
Python
官方提供的 Python SDK 為 modelcontextprotocol/python-sdk,使用方式相當直觀,基本上照著範例就可以跑起來。SDK 預設是使用 STDIO 模式,想改用 SSE 模式,需要在啟動時加上 mcp.run(transport="sse")
的參數來啟用。
另一個大家常用的套件是 FastMCP,他其實已經 merge 進去官方的 python-sdk,也就是官方的 python-sdk 用的就是 FastMCP v1。但最近 FastMCP 又推出 v2,感覺要搞事了,可以持續觀察。
在不到一年前 astral-sh/uv 橫空出世,迅速在 Python 開發圈掀起一波浪潮,現在新的專案,比如 MCP Server 們都是使用 uv
作為套件與專案管理器。uv
本身是 Rust 開發的,所以標榜速度超快(現在什麼都要用 Rust 重寫就對了)。以前要用 pyenv
, poetry
, pipenv
等一堆工具才能做到的事,現在裝一個 uv
就全搞定了,超方便,用過都說讚,我也跳槽啦 xD
對 MCP 開發者來說,uv
的其中一個特別實用的工具就是 uvx
,它的定位等同於隔壁 js 陣營的 npx
,可以直接執行一個外部套件,免安裝、免額外環境設定,非常適合用來啟動 MCP Server。比如以下就是一個使用 uvx
啟動 mcp-server-git
的範例設定:
{
"mcpServers": {
"git": {
"command": "uvx",
"args": ["mcp-server-git", "--repository", "path/to/git/repo"]
}
}
}
這裡示範一個簡單的範例,將以下程式碼儲存為 server.py
,然後執行 uv run server.py
,預設就會聽在 8000 port,要在 MCP Client 上連線時填上 http://your-ip:8000/sse
。
from mcp.server.fastmcp import FastMCP
# Create an MCP server
mcp = FastMCP("Demo")
# Add an addition tool
@mcp.tool()
def add(a: int, b: int) -> int:
"""Add two numbers"""
return a + b
if __name__ == "__main__":
mcp.run(transport="sse")
我們可以透過 MCP Inspector 去戳 List Tool,接著右鍵檢查封包,就可以看到 MCP 回傳的 json 資訊。其實,MCP Server 本質上就是一個 REST API,跟 LLM 是完全解耦、獨立運作的。最後,是 Cursor 這類的 MCP Client 要負責把這些拿回來的 JSON 資訊組裝成 prompt 餵給 LLM。
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"tools": [
{
"name": "add",
"description": "Add two numbers",
"inputSchema": {
"properties": {
"a": { "title": "A", "type": "integer" },
"b": { "title": "B", "type": "integer" }
},
"required": ["a", "b"],
"title": "addArguments",
"type": "object"
}
}
]
}
}
Typescript
另一個常見的選擇是 TypeScript,官方 SDK 為 modelcontextprotocol/typescript-sdk。開發方式與 Python 相似,不過這邊我自己是用 Python 開發的,TypeScript SDK 我就沒實際用過。
Node.js 的套件管理還是照舊使用 npm
, pnpm
或 yarn
,而 Server 的啟動方式則通常使用 npx
指令,這樣可以不需要預先安裝套件,直接執行遠端的 MCP Server。例如下面這段設定,就是用 npx
啟動 server-puppeteer
的範例設定:
{
"mcpServers": {
"puppeteer": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-puppeteer"]
}
}
}
MCP Server Debug
官方提供了一套非常實用的測試工具 modelcontextprotocol/inspector。這個工具可以讓你直接與 MCP Server 互動,手動發送請求、查看回應,方便快速 debug。這是純戳 MCP Server 拿到結果,沒有任何 LLM 的部分。
MCP Server 列表
官方提供的
第三方列表
這些第三方網站要格外注意阿,這個沒有經過驗證的,小心載到有毒的,尤其是 smithery 和 mcp-get 多幫你包一層,你都不知道你跑了什麼。最好是有 github 原始碼可以看的,自己裝。
- punkpeye/awesome-mcp-servers
- mcp.so
- smithery
- 他自己多包了一層
@smithery/cli
幫你安裝的腳本工具
- 他自己多包了一層
- mcp-get
- 他自己多包了一層
@michaellatman/mcp-get
幫你安裝的腳本工具
- 他自己多包了一層
MCP Client 有哪些
可以參見這個表:Example Clients - Model Context Protocol
目前支援 SSE 的 MCP Client 還不多。像 Cursor 是有支援的,但 Claude Desktop 本身還不支援 SSE,只能用 STDIO 模式,也就是要把工具裝在本地執行。
至於 Resource 和 Prompt 的功能,各家 Client 的支援狀況也不太一致,不一定都有實作。但 Tool 幾乎是大家都有支援的,只要有 Tool,其實就夠用了。
載入評論區...