FastMCP 4:把函式變成 AI 工具,
一發請求不用握手
上一課你手寫工具說明書、手組兩回合。MCP 把這件事標準化:你蓋一台工具伺服器, Claude Desktop、Claude Code、Cursor 都能直接接上來用你的函式;FastMCP 讓這台伺服器只剩一個裝飾器。 而 4.0 最大的改版是協定變無狀態——同樣「呼叫一次 add」,舊協定要握手、發 session id、綁死同一台伺服器; 新協定每個請求自帶一切。按下去比較:
舊協定(握手時代)
新協定(4.0 預設)
這堂課的 notebook 不連任何外部服務——它會在 molab 裡真的起一台 HTTP 伺服器並側錄每個請求, 上面這張圖的每一列都是實測結果。本課用 fastmcp==4.0.0b1。
說明書自動生成、參數自動把關
你沒有寫任何 schema。FastMCP 從型別提示、預設值與 docstring 生成說明書: limit 有 default: 3、keyword 在 required。 用 Client(mcp) 直接連同一個 process 裡的伺服器(不開網路)就能 list_tools()、call_tool();故意把 a 給成 "two", pydantic 在進你的函式之前就擋下,回一個 ToolError 說明哪個欄位錯。
工具之外還有兩種東西:resource(給 AI 讀的資料,URI 定址如 menu://today, URI 放 {name} 就是 template)與 prompt(帶參數的話術範本)。 tool 是模型主動呼叫、resource 是應用決定要不要餵、prompt 是使用者挑選。
到 notebook 的 1️⃣–2️⃣ 節:蓋伺服器、連上去、加 resources 與 prompts同一台伺服器、兩種協定,側錄給你看
notebook 用 mcp.http_app()(標準 ASGI app)在背景起一台真的 HTTP 伺服器,前面塞一個只側錄不干擾的中介層。 Client(網址) 自動協商最新協定,mode="legacy" 強制舊的。兩邊各呼叫一次 add:
| 舊協定 2025-11-25 | 新協定 2026-07-28 | |
|---|---|---|
| HTTP 請求數 | 6(initialize → initialized → GET 長連線 → tools/call → tools/list → DELETE) | 3(server/discover → tools/call → tools/list) |
| session id | initialize 後伺服器發 mcp-session-id,之後每發都帶 | 無 |
| 請求自帶什麼 | 只有 JSON-RPC body | header mcp-method/mcp-name+body 的 _meta 信封(協定版本、客戶端能力) |
| 可以落到哪台副本 | 只能回到發 session 的那台 | 任何一台 |
最有感的證明:不用 SDK、不握手,一發 httpx.post 就能呼叫工具——
傳輸無狀態,應用照樣記得你:SessionId
購物車、多步驟流程怎麼辦?4.0 的答案是把狀態綁在一把鑰匙上而不是連線上: 裝上 SessionProvider(),伺服器自動多出 create_session()(發一把猜不到的 uuid) 與 end_session();你的工具宣告 session_id: SessionId,用 get_session(session_id) 拿到可 get/set 的小儲存格。
實測:拿鑰匙加兩樣東西 → 換一條全新連線帶同一把鑰匙 → 購物車還在;亂猜一把 → Invalid or unknown session。 FastMCP 還會在 session_id 的 schema 裡自動寫一段給 AI 看的說明(先建 session、之後每次帶著)。 另一個選項 session: UserSession 自動注入、不進 schema,但要有認證身分——它把狀態綁在登入的使用者上。
到 notebook 的 4️⃣ 節:購物車、換連線、亂猜鑰匙接給真的 AI 客戶端
接上之後對 AI 說「幫我找有茶的飲料」,它會自己呼叫 search_menu。 4.0 還拿掉了 ctx.sample()、ctx.list_roots() 這類需要活連線的反向呼叫、 新增多回合互動工具與背景任務——細節在 notebook 末節,知道有就好。
到 notebook 的 5️⃣ 節:部署方式與 4.0 其他改動換你動手
加一個 place_order(item: str, qty: int = 1) 工具,重跑 list_tools() 確認說明書自動更新。
購物車加 checkout(session_id):算總價、清空、回傳收據。
改裸 POST 去呼叫 tools/list 與 resources/read(讀 menu://today)——查 MCP 規格找出 params 格式。
卡住了?每一題在 notebook 末節都有折疊解答——先自己做,再打開對照。
情境測驗
離開前試試看:下面的情境都真的會遇到。每題選一個你認為的最佳做法,選了馬上看得到解釋。
Q1 情境題
你的茶飲店伺服器已經有 search_menu 工具。現在想把「今日完整菜單」這份資料也提供出去——由接上來的應用程式決定要不要餵進模型的上下文。應該怎麼註冊?
三者的分工就是答案:tool 是模型主動呼叫、resource 是應用程式決定要不要餵、prompt 是使用者挑選——「給 AI 讀的資料、由應用決定餵不餵」正是 resource 的定義,URI 裡放 {name} 還能升級成 template(menu://item/{name})。A 能動,但把資料做成動作,主導權從應用移到模型,模型沒想到呼叫就沒有菜單;C 是給使用者一鍵套用的範本,不是資料通道;D 會污染工具說明書——docstring 是給模型看的使用說明,菜單一改還得改程式碼。
Q2 錯誤診斷
你想試「一發 POST 呼叫工具」,發出下面的請求卻拿到 400。最可能的原因是?
新舊協定走同一個端點,伺服器靠 header 分流:補上 MCP-Protocol-Version: 2026-07-28 與 mcp-method/mcp-name,同一個 body 立刻 200 拿到 42;不宣告就被當舊協定客戶端,而舊協定的規矩是先 initialize 握手拿 mcp-session-id——所以才回 Missing session ID。A 不對:400 是伺服器親口回的 HTTP 狀態,連不上根本不會有回應;B 正好說反——無握手一發就中正是新協定的賣點;C 位置沒錯,params._meta 就是規格寫的位置。
Q3 情境題
你要在 4.0 無狀態協定上做購物車:同一位顧客分多次呼叫加品項、中途可能換連線,而且不同顧客不能看到彼此的購物車。應該怎麼做?
4.0 的口訣是「stateless transport, stateful application」——狀態綁在鑰匙上,不綁在連線上。實測:換一條全新連線帶同一把鑰匙,購物車還在;亂猜一把拿到 Invalid or unknown session,顧客之間自然隔離。B 所有顧客共用同一個 list,甲客加的珍奶會出現在乙客的購物車;C 能動但走回頭路——狀態綁死連線與那台副本,換連線就掉,正是 4.0 要擺脫的;D 部分正確,但 UserSession 把狀態綁在「登入的使用者」上,要有認證身分才能用,本課的伺服器沒有登入機制。
實作在 molab 跑(免費)
molab 的登入狀態進不了內嵌框架(瀏覽器的跨站 cookie 保護), 所以 notebook 要在新分頁執行——把它跟本頁並排開,左邊教學照樣對照。
- 登入 molab(GitHub / Google)
- 開啟課程 notebook,Fork 成自己的副本即可編輯
- 從第一格往下全部執行(首次安裝套件約 1 分鐘)——免費 CPU 環境即可,不需要 GPU
不想用 molab?下載 fastmcp4_ext.py 後在自己電腦
uvx marimo edit --sandbox fastmcp4_ext.py,依賴會自動安裝。