[MCP] Remote MCP Server 新規格筆記(2026-07-28)
MCP Remote Server 新規格筆記(2026-07-28):把自家 API 開放給 AI 工具
前陣子一直在想一件事:自己手上這套 API service,要怎麼讓開發者在 Claude、Cursor 這些 AI 工具裡直接使用?其實答案已經很明顯——MCP(Model Context Protocol)。而且規格最近又大改版了,網路上的文章大多還停在舊版,趁周末把官方文件從頭讀了一遍,這篇就把重點筆記下來,順便站在「想要提供 remote MCP server 給開發者整合」的服務擁有者角度,整理該注意哪些事。
為什麼是現在:規格演進很快
MCP(Model Context Protocol)是 Anthropic 在 2024/11 開源的協定,簡單講就是讓 AI 工具(client)透過統一介面呼叫外部服務(server)的工具、資源、prompt。到目前為止官方發布過的規格 revision 有:
| Revision | 重點 |
|---|---|
| 2024-11-05 | 原始版,遠端傳輸走 HTTP+SSE |
| 2025-03-26 | 引入 Streamable HTTP,取代 HTTP+SSE;加入 tool annotations |
| 2025-06-18 | 新增 elicitation、experimental tasks |
| 2025-11-25 | 版本化政策的雛形 |
| 2026-07-28 | 重大改版:完全 stateless |
整個生態的採用速度也很快:OpenAI(2025/3)、Google DeepMind(2025/5)、Microsoft 都相繼宣布支援,主流工具幾乎都當了 MCP client。也就是說,現在開放自家的 API 給 AI 工具,已經不是「要不要」的問題,而是「跟哪個世代」的問題——因為 2026-07-28 這版真的改很大。
2026-07-28 重點:MCP 變成 stateless
這版最大的變革,就是把過去「先 initialize 握手建立 session」的模型整個拿掉。官方 changelog 引用了三個 SEP(Specification Enhancement Proposal),我拆成三點來看:
1. 沒有 session、沒有 initialize(SEP-2567 / SEP-2575)
過去的流程是 client 先送 initialize,server 回 InitializeResult,然後才開始 tools/list 等呼叫;遠端還會帶 Mcp-Session-Id header 維持 session。2026-07-28 起通通移除:
- 每個 JSON-RPC request 都要在
_meta帶上協定版本、client 能力與身分:
1 | { |
- 對應注意的點:
- server 在 result 的
_meta回io.modelcontextprotocol/serverInfo表明身分 - 版本不符就回
UnsupportedProtocolVersionError,錯誤碼-32022 - 官方的錯誤碼政策:
-32000~-32019留給實作自訂,-32020~-32099是 MCP spec 保留區
- server 在 result 的
我的理解是:這對我們這種「要扛多租戶、要水平擴展」的服務其實是好事——沒有 session 就沒有黏著性(stickiness),load balancer 可以隨便打,每一台 instance 都是無狀態的。真的需要跨 request 的狀態時,官方建議由 server 發出 handle(識別碼)當 tool 參數帶回傳(SEP-2567),不要依賴連線狀態。
2. 新增 server/discover,server 必須實作
stateless 化之後,「宣告能力與身分」這件原本由 initialize 握手做的事,改由一個唯讀 RPC 承接:client 可以在發任何其他 request 之前,先問 server「你支援哪些版本、有什麼能力、你是誰」。
Request 除了標準的 _meta 之外沒有任何參數(client 身分與能力就放在 _meta):
1 | { |
Response 的完整欄位長這樣:
1 | { |
- 對應注意的點:
supportedVersions:列出自 server 支援的協定版本,client 之後每個 request 從中選一個(我們全新服務直接["2026-07-28"]);版本不符時 server 回UnsupportedProtocolVersionError(-32022)並附上supported清單,client 再挑雙方都支援的版本重試——因為 stateless,這個流程發生在「每一個 request」而非連線建立時capabilities:tools / resources / prompts 等能力宣告,client 不用逐個 probe 就能知道 server 提供什麼;Claude Code 這類 client 開場也會自己打tools/list、prompts/list、resources/list再做一次完整發現,discover 比較像「快速總覽」instructions:給 LLM 看的自然語言說明(不是給人類開發者看的文件)——在 Claude Code 裡它 session 開場就載入、作用類似 skill 的觸發條件,決定 agent「何時該搜尋這個 server 的工具」,所以值得花時間寫好ttlMs/cacheScope:caching 提示。ttlMs是快取新鮮度(毫秒,0= 立即失效,語意類似 HTTPCache-Control: max-age);cacheScope分"public"(回應不含使用者特定資料、可跨使用者共用)與"private"(含私有資料,只能同一個授權 context 重用)——工具清單、文件這類 discover 回應幾乎都是public,被 gateway / proxy 快取時是免費的效能優化serverInfo是 server 自報身分,協定不會驗證——官方明確說 client 不該用它做安全決策,只用於顯示、log、除錯
對 client 來說呼叫 server/discover 是 OPTIONAL,主要有兩個用途:一次請求拿完整資訊(不用分開打 tools/list + prompts/list + resources/list);stdio 上的 modern/legacy 相容性探測(我們全新服務用不到)。用官方 go-sdk v1.7.0+ 的話這個 RPC 已內建(mcp/server.go 的 discover handler),ServerOptions.Instructions 填的內容就會出現在這裡,ttlMs/cacheScope 也會自動給預設值,不用自己寫。
3. MRTR:server 不再主動發 request(SEP-2322)
以前 server 想要更多資訊(例如 sampling/createMessage、roots/list、elicitation/create)會主動送 request 給 client。新版改成 Multi Round-Trip Requests 模式:server 在 result 裡回 resultType: "input_required",把需要的請求裝在 inputRequests 欄位,client 再 retry 原 request 並補上 inputResponses。
- 對應注意的點:
- 所有 result 都必須帶
resultType欄位("complete"或"input_required") - 這代表 protocol 的 request/response 流變得非常單純:永遠是 client 發起、server 回應,中間要索取資訊就「卡關」互動一次
- 所有 result 都必須帶
另外兩件也值得知道:ping、logging/setLevel 移除了(log level 改由 _meta 的 io.modelcontextprotocol/logLevel 每個 request 單獨指定),experimental tasks 則移出核心、改為官方 extension(io.modelcontextprotocol/tasks,用 tasks/get polling)。Streamable HTTP 上沒有 client 端 notification,取消就是關閉 SSE stream。
基本款:架一台 Streamable HTTP server
遠端 server 現在就是一個很單純的 HTTP service,官方要求:
- 只提供一個 HTTP endpoint(MCP endpoint),只支援 POST,例如
https://example.com/mcp - 每個 JSON-RPC request 就是一個 HTTP POST
- response 可以是一個 JSON object,或是 scoped 的 SSE stream(
Acceptheader 要同時列application/json和text/event-stream) - request 是 notification 時回
202 Accepted - 每個 POST 都要帶 metadata header(含
MCP-Protocol-Version)
安全面有一個容易忽略但官方強制的要求:server 必須驗證 Origin header,防止 DNS rebinding 攻擊,invalid 就回 403。這在自建的時候很容易漏掉。
以官方 Go SDK(github.com/modelcontextprotocol/go-sdk,需要 v1.7.0 以上才支援 2026-07-28)為例,快速起一個 server 大概長這樣:
1 | package main |
- 對應注意的點:
server/discover、resultType、版本不符回-32022這些新規格細節,官方 go-sdk v1.7.0+ 都已經在內部實作(mcp/server.go裡有 SEP-2575 的 discover handler),你不用自己刻——SDK README 有版本對照表:v1.7.0+ =2026-07-28- HTTP 層的
Accept/MCP-Protocol-Versionheader 處理由 SDK 的 streamable handler 完成,但 Origin 驗證不會自動做,要用 middleware 自己包(上面originCheck) ToolAnnotations的ReadOnlyHint/DestructiveHint是指標欄位、IdempotentHint是普通 bool,第一次寫很容易被這個差異卡一下;annotation 是給 client 的 UX 用,不是權限控制ServerOptions.Instructions會出現在server/discover的回傳——放一段給 AI 看的說明,會明顯改善首次接線的體驗- 動手時照官方
examples/http/main.go抄最快,一支檔案同時示範 Streamable HTTP 的 server 與 client - 舊版教學常見的
initialize/session 流程全部不用寫,全新服務直接以2026-07-28世代實作
進階款:認證(OAuth 2.1)
對 remote server 來說,認證是「服務擁有者」最需要傷腦筋的地方。官方規格基於 OAuth 2.1(draft)的一整套組合:
- resource server = 我們的 MCP server
- MCP client 扮演 OAuth client,代表 resource owner(也就是開發者的使用者)取得 access token
- 用 RFC9728(Protected Resource Metadata)做 authorization server discovery——server 必須實作,client 用它找到 AS 的位址
規格要求的東西不少,我只列服務擁有者一定要知道的:
- MCP server 必須實作 RFC9728:在 401 回應的
WWW-Authenticateheader 帶resource_metadata參數告知 metadata 位置(或提供 well-known URI),client 由此 discover authorization server - authorization server 至少要提供 RFC8414(OAuth AS metadata)或 OIDC discovery 之一
- 動態 client registration(RFC7591)在新版已棄用,改推 Client ID Metadata Documents——對我們來說,意思是「先跟開發者註冊、給一組 client id」的流程會更常態化
- 也可以用 resource indicators(RFC8707)讓同一個 token 綁定特定 MCP server
- stdio(本地)情境不要套 OAuth,直接吃環境變數憑證就好
另一個考量點:OAuth 2.1 + PKCE 這套對「小規模內部服務」其實有點重。如果只是給自己團隊用,或還沒要公開給外部開發者,可以先不開認證(spec 說 authorization 是 OPTIONAL),配你的既有 gateway 擋住就好,等真的要開放再上。
工具設計:靜態 vs 動態
MCP server 主要價值就是工具(tools)。實作上有兩種做法:
- 靜態工具:啟動時就註冊固定清單,適合工具數量固定、所有使用者權限相同的場景
- 動態工具:
tools/list時依目前使用者身分即時回傳不同清單——tools/call前的權限檢查、個人化的工具集,都很適合動態做法
對我們這種多租戶服務,動態工具幾乎是標配:免費方案只給 read-only 工具,付費用戶多幾個寫入工具,彈性很大。官方 Go SDK 的 NewStreamableHTTPHandler 第二個參數就是 factory——每個 request 都可以回傳不同的 server instance,實務上把認證 middleware 解析出的使用者資訊放到 request context,factory 再依它挑選或建立對應工具集的 server,就做到動態工具了。完整範例如下:
1 | // --- 1. 認證 middleware:解析開發者身分,放進 request context --- |
- 對應注意的點:
- factory 每個 HTTP request 都會被呼叫,所以用
map + mutex快取「第一次見到才建立、之後回傳同一個 instance」,避免每次 request 重建 - handler 用 closure 帶住
user(per-user instance 的最大好處),不同使用者的工具與權限天然隔離;tools/list回傳的列表因使用者而異 - 記憶體提醒:這個 map 會隨使用者成長,量大要有 eviction 策略;折衷方案是「共用一個 server + 權限判斷放 handler 內」(回到靜態工具的寫法)
- 認證沿用 RFC6750 的 Bearer token——OAuth 下 client 的 access token 就是放在
Authorization: Bearerheader,middleware 解析後塞進 context
- factory 每個 HTTP request 都會被呼叫,所以用
另外 2025-03-26 起工具可以帶 annotations:
1 | { |
- 對應注意的點:
destructiveHint: true會讓 client 在使用者授權流程上特別謹慎(例如 Claude 會要求確認)readOnlyHint標記純查詢工具,可降低誤用風險- annotation 是給 client 的使用者體驗用的,不是權限控制,真正的權限還是要在 server 端把關
老話一句:工具 description 要寫清楚參數意義、回傳結構、發生錯誤時的表示方式。AI 不會看文件,它只看你給的 schema 跟 description。
開發者視角:coding agent 怎麼用這個 server
前面都是站在「架 server」的角度,這節換成開發者(連線方)的角度,看完比較知道要怎麼設計文件化。開發者把 URL 加進 agent 後,agent 會自動發現你暴露的東西——實際「看懂並整合你的 API」靠 4 個來源:
| 來源 | 在 agent 端的呈現 | 你該怎麼準備 |
|---|---|---|
server instructions(server/discover 回傳) |
session 啟動時就載入,像 skills 的觸發條件,決定 agent「何時該找你的工具」 | 寫清楚:這 server 做什麼、適合哪些任務 |
| tools | 預設 tool search:只有工具名稱先載入,完整定義(description + schema)等需要時才抓;呼叫前會跟使用者要核准 | name / description 精簡準確——instructions 與 tool description 都會被截斷在 2KB,重點放前面 |
| resources | 開發者用 @ 引用:@acme:embedded:guide/api,自動 attach 進對話(跟 @ 檔案一樣,autocomplete 是 fuzzy search) |
指南寫 markdown、命名直覺好搜 |
| prompts | 使用者以 slash command 觸發(user-controlled) | 做法見另一篇筆記:MCP 與 Agent Skills |
以 Claude Code 為例,開發者的流程是:
1 | # 1. 一行指令接上你的 server |
agent 理解你 API 的時機(三個場景):
- 「幫我們接 ACME Analytics API」——agent 會
@引用你的api-guide資源(讀懂認證、錯誤碼、rate limit 規範)再產出整合程式碼 - 「列出最近的報表」——agent 直接呼叫
list_reports,每次呼叫 client 都會先跟開發者核准;有destructiveHint的工具還會多一層確認 - 卡住時——agent 可以再
@引用指南或呼叫資源相關工具補上下文
補充兩個開發者端的小知識:Claude Code 會自動重連掉線的 remote server(指數退避,最多五次);server 送 list_changed 通知時,agent 會自動刷新工具清單,不用斷線重加。
小結
結論先講:MCP 新版規格對「服務擁有者」其實是友善的。stateless 化讓架構變簡單、水平擴展容易;server/discover 讓版本相容有明確的溝通管道;思維上跟 REST API 越來越像,寫過 OpenAPI 服務的人上手會很快。但代價就是「規格變動快」這點必須正視——2026-07-28 直接把 handshake 拿掉,2024、2025 年自建的 server 通通要遷移,決策上建議把「追蹤規格版本」排進例行工作,第一版先鎖定你能掌握的 client 範圍就好。
畢竟 MCP 的核心價值在於:開發者不用再自己寫客製化整合,在 AI 工具裡設定一個 URL 就能用你的服務。對我們這種想降低整合門檻的服務提供者來說,這個投資還算值得。
參考資料
- MCP Specification — Key Changes (2026-07-28)
- MCP Specification — Versioning and Compatibility
- MCP Specification — Streamable HTTP Transport
- MCP Specification — Authorization
- MCP Specification — server/discover
- MCP Go SDK(官方,v1.7.0+ 支援 2026-07-28)
- MCP Go SDK — examples/http(Streamable HTTP server + client 範例)
- MCP Documentation Index