自架 MCP Server 接入規格
把自家系統做成 MCP Server 讓 MaiAgent 的 Agent 呼叫:支援的傳輸方式與 URL 要求、驗證(固定標頭、聯絡人憑證、OAuth 動態註冊)、允許的工具、逾時重試與錯誤如何回給模型,以及 MCP Apps 互動卡片。
本頁寫給要「把自家系統包成 MCP Server,讓 MaiAgent 的 Agent 呼叫」的工程師:MaiAgent 這一端怎麼連你的 Server、帶哪些標頭、怎麼做 OAuth、抓工具清單與呼叫工具時的逾時、重試與錯誤處理。
MCP 的概念與運作原理請看 MCP(Model Context Protocol);託管型服務(Composio、Zapier 等)請看 Remote MCP 服務概述。
後台的逐步操作(在哪個畫面新增 MCP 工具、每個欄位填什麼)請看使用者手冊的 建立 MCP 工具,本頁不重複操作步驟。
若你要接的是一般 REST API 而不是 MCP Server,請看 API 工具工程指南。
一次呼叫的流程
MaiAgent 是 MCP Client;Agent 只負責決定呼叫哪個工具與參數,實際連線由平台送出。
傳輸方式與 URL 要求
支援的傳輸
Streamable HTTP 與 SSE。不支援 stdio(不會在平台上啟動 npx、node 等本機指令)。
傳輸判斷
依 URL 自動判斷:路徑結尾為 /sse、路徑中含 /sse/,或 query string 帶 transport=sse 時走 SSE;其餘一律走 Streamable HTTP。
URL scheme
必須是 https://。只有當目標解析到平台允許的內部網段(部署設定的內部 CIDR 白名單)時才接受 http://。
網路位置
網域必須可解析;解析到私有、loopback 等內部位址會被拒絕(位址落在內部 CIDR 白名單內者除外);link-local(如 169.254.x.x)一律拒絕,白名單也無法放行。SaaS 環境下,你的 Server 需要能從公網存取。
TLS
預設使用嚴格的憑證驗證。若憑證鏈使用缺少 Subject Key Identifier 的舊版根憑證而連不上,這個放寬選項只能由 MaiAgent 在系統管理端逐一工具或逐一網域開啟,請聯繫 MaiAgent。
建立工具時的連線驗證
在後台或透過 API 建立/更新 MCP 工具時,平台會先實際連到你的 Server 執行一次 tools/list:
連不上或拿不到工具清單時,建立會失敗,錯誤訊息會附上連線錯誤內容。
若請求沒有指定
mcpAllowedTools或傳空陣列[],平台會把當下tools/list回傳的所有工具名稱存成允許清單。之後 Server 新增的工具不會自動開放,要回到工具設定重新取得清單並勾選。驗證連線只帶
rawMcpHeader中不含{{變數}}的標頭(這時還沒有對話可以代入變數)。若你的 Server 要求每個請求都必須帶某個動態標頭,請讓tools/list在只有固定標頭時也能回應。若這個 URL 對應到設定為 OAuth 的 MCP Registry(見 OAuth 2.0),建立或更新時會略過驗證、允許清單先留空,等授權完成後再取得工具清單。
驗證方式
平台透過 HTTP 標頭把身分帶給你的 Server。帶哪些標頭取決於工具的授權對象(API 欄位 authSubject):
authSubject
後台名稱
誰的身分
帶入的標頭(後者覆蓋前者同名標頭)
shared(預設)
組織共用
全組織一組授權
工具層 OAuth 權杖 → rawMcpHeader → 該聯絡人的 MCP 憑證
contact
聯絡人個別授權
對話中的聯絡人本人
rawMcpHeader → 該聯絡人的 MCP 憑證 → 該聯絡人的 OAuth 權杖
contact 模式下,匿名聯絡人(沒有 sourceId)的對話中完全不會提供這個工具給 Agent。
固定標頭(API Key)
最簡單的做法:在工具的 rawMcpHeader 放一個 JSON 物件,每次連線都會帶上。
rawMcpHeader為唯寫欄位,讀取工具時只會回傳遮罩後的rawMcpHeaderMasked。值支援以下
{{變數}},依當下對話代入:contact_id、source_id、contact_name、conversation_id、inbox_id、organization_id、agent_slug、agent_version。
聯絡人層級 MCP 憑證
要讓 Agent 以終端使用者本人的權限呼叫你的 Server,把使用者的權杖寫進該聯絡人的 MCP 憑證:
每個聯絡人對每個 MCP 工具最多一筆憑證,內容是一組
headers。對話進行時,平台依「目前對話的聯絡人+這個工具」找出憑證,把
headers原樣合併進請求標頭,同名時覆蓋rawMcpHeader。憑證標頭不做{{變數}}代入。寫入方式:
Web Chat 嵌入時呼叫
setup-contact-credentials,在mcpCredentials帶toolId與headers(toolId必須是 MCP 類型工具,否則回 400)。由你的後端呼叫聯絡人 API 的
mcp-credentials端點新增、更新或刪除。
兩種寫法的完整參數、Token 更新與登出撤銷流程請看 聯絡人身分同步與 Token 更新;後台手動設定見使用者手冊的 聯絡人 MCP 憑證設定。
對話識別標頭
只要有對話情境,平台會在每個請求加上 X-Conversation-Id: <對話 ID>,讓有狀態的 Server 能以對話為單位保存工作狀態,不必依賴模型自己傳一個 session token。若你的驗證標頭剛好同名,以驗證標頭為準。
OAuth 2.0
若你的 MCP Server 以 OAuth 保護,平台會自動完成下列流程(依 MCP 授權規格):
探索受保護資源
平台先不帶權杖對 MCP URL 送一個 GET。若回 401 且 WWW-Authenticate: Bearer resource_metadata="...",就讀取這個網址(必須是 https,且主機必須與 MCP URL 相同)。否則依 RFC 9728 預設位置嘗試:先 https://{host}/.well-known/oauth-protected-resource{MCP 路徑},再 https://{host}/.well-known/oauth-protected-resource。
從中取得 resource(權杖的 audience)與 authorization_servers(取第一個,必須是 https)。探索結果快取 5 分鐘。
Callback URL:若你的授權伺服器不支援動態註冊、需要預先登記 redirect URI,下列兩個都要登記,否則對應的流程會在 /authorize 失敗:
成員(組織共用)授權
MaiAgent 後台網域 + /oauth/mcp/callback
聯絡人個別授權
https://api.maiagent.ai/api/mcp/oauth/contacts/callback/(SaaS;私有部署依該環境的 API 網域)
MCP Registry:自架 Server 若需要固定 oauthClientId、指定授權伺服器 URL 或 resourceIdentifier,可由具連接器管理權限的成員建立組織私有的 MCP Registry(/api/v1/mcp/private-registry/)。authType 可選 oauth、service_account(以標頭帶金鑰)或 none。平台以「MCP URL 開頭符合 Registry 的 remoteMcpUrl」判斷工具對應哪個 Registry,留空的欄位才會用自動探索的結果。
允許的工具(mcpAllowedTools)
mcpAllowedTools 是 Server 工具名稱的陣列,決定哪些工具會交給 Agent。執行時,儲存的值依授權對象解讀如下:
值
shared
contact
null
開放 Server 所有工具
開放 Server 所有工具
[](空陣列)
一個都不開放
開放 Server 所有工具
["search_orders", ...]
只開放列出的工具
只開放列出的工具
contact 模式把空陣列視為全部開放,是因為管理員手上沒有聯絡人的權杖、無法事先取得工具清單。shared 模式搭配 OAuth 時,要在管理員完成授權並取得工具清單後,清單才會有內容。
透過 API 建立或更新工具時:
一般 MCP 工具:不帶
mcpAllowedTools或傳[],都會被換成當下取得的全部工具名稱(見建立工具時的連線驗證),因此不能用[]關閉所有工具。對應到 OAuth MCP Registry 的工具:
[]會原樣保存;更新時若傳[]或沒帶這個欄位,允許清單也會被清空,shared模式下就一個工具都不開放。更新這類工具時請一律帶上完整的mcpAllowedTools。
工具清單怎麼交給模型
何時抓:每次組裝 Agent 的工具時都會連到你的 Server 執行
tools/list,再依允許清單過濾。名稱:工具有設定
name時,模型看到的名稱是{name}__{你的工具名}。非英數與底線的字元會換成底線、開頭不是英文字母時補t_,超過 64 字元則截斷並加上 6 碼雜湊尾碼。說明:模型看到的描述就是你在
tools/list回的description,請寫清楚用途與使用時機。參數:
inputSchema會轉成模型的參數定義。平台會自動處理兩種常見寫法:把陣列形式的屬性值轉成anyOf,並把本地$ref內嵌展開(遞迴參照會改成接受任意值)。Schema 仍無法轉換時,只會略過該工具,其他工具照常提供。抓取失敗:連不上、逾時或驗證失敗時,這個 MCP 工具在本次回覆中不會提供任何工具給模型,對話本身不會中斷。
呼叫工具:逾時、重試與錯誤
Session 與逾時
每次
tools/call都會新開一個 MCP session(initialize後再呼叫),不會跨呼叫重用 session。需要跨呼叫保存狀態時,請以X-Conversation-Id或你自己的驗證身分為依據。MCP session 的讀取逾時為 30 秒。
暫時性錯誤的自動重試
連線逾時、連線中斷或被重設、Server 未回應就斷線、session 中途關閉,以及 MCP 錯誤碼為連線關閉或 408 時,平台最多重試 3 次,間隔 1、2、4 秒(上限 8 秒)。你的 Server 回傳的錯誤結果或應用層錯誤不會重試。
只有「重送也安全」的工具才會重試,判斷順序:
工具的
annotations有readOnlyHint: true或idempotentHint: true→ 會重試。其中任一明確為
false→ 不重試。兩者都沒宣告時,看工具名稱的第一個字是否為
get、list、read、fetch、search、find、check、query、describe、show、view、browse、lookup(camelCase、-會先拆字)。
回給模型的內容
呼叫成功
CallToolResult 轉成的文字(包含 content、structuredContent 與 isError 欄位)。你回 isError: true 時,模型一樣會看到這段內容。
呼叫時拋出例外(重試後仍失敗、非暫時性錯誤)
MCP 工具執行錯誤\n MCP Tool Execution Error: <錯誤內容>
結果過長
上限為模型 context window × 工具輸出比例(預設 0.8),以 token 估算。超過時保留開頭與結尾各一半,中間省略,並在最前面加上說明「結果不完整」的提示。
每次呼叫都會寫入工具執行記錄(工具、實際 MCP 工具名稱、輸入參數、輸出或錯誤、耗時、重試次數),除錯方式同 API 工具工程指南的記錄與除錯。
計費分類
每次成功呼叫計 1 次 MCP 工具用量,依工具名稱分為讀取類或寫入類:名稱第一個字符合上述讀取字首就算讀取類,否則名稱中含寫入關鍵字時算寫入類,其餘都算讀取類。組織點數不足時,這次呼叫不會送到你的 Server。
MCP Apps(互動卡片)
MaiAgent 支援 MCP Apps 擴充(SEP-1865):工具除了回傳資料,還可以附上一份 HTML 介面,對話中就會出現一張可操作的卡片,例如圖表、表單或查詢結果清單。使用者看到的效果與限制見使用者手冊的 MCP 互動卡片(MCP Apps);想直接動手,跳到最小可跑範例。
伺服器端:怎麼回傳 UI 資源
連結 UI 資源:在
tools/list的工具定義,或tools/call結果的_meta帶上ui.resourceUri,值必須以ui://開頭,例如{"_meta": {"ui": {"resourceUri": "ui://orders/list"}}}。兩處都有時以呼叫結果的為準;扁平寫法_meta["ui/resourceUri"](@modelcontextprotocol/ext-apps的registerAppTool產生的格式)也接受。工具定義的_meta在每次組裝工具清單時讀取。提供資源內容:工具呼叫成功後,平台在同一個 MCP session 對這個 URI 送出
resources/read,取第一筆內容的text當作卡片 HTML。建議mimeType使用text/html;profile=mcp-app。回傳資料:卡片收到的是這次的工具結果:
content的第一段文字,以及structuredContent(物件)。請讓卡片以structuredContent為資料來源;平台不會把工具輸入參數傳給卡片(ui/notifications/tool-input的arguments為空物件)。
平台的 MCP client 在 initialize 時不會宣告 io.modelcontextprotocol/ui 擴充能力。請不要用 getUiCapability() 之類的判斷來決定要不要附上 _meta,否則平台永遠拿不到 UI 資源。
以下情況不會出現卡片,或改以文字顯示:
工具結果 isError: true
不產生卡片
沒有連結 ui:// 資源
不產生卡片,對話照常顯示工具結果
resources/read 失敗或沒有內容
卡片改顯示 content 的第一段文字
卡片 HTML 會在產生當下隨訊息保存。之後更新伺服器上的 UI,不會改變歷史訊息中已產生的卡片,請讓新版 UI 能處理舊版產生的資料。
前端:卡片怎麼呈現
出現位置:Agent 回覆完成後,卡片出現在該則回覆下方;同一則回覆中,來自同一來源、同一個 UI 資源的卡片只顯示最新一張。
沙盒:卡片在
sandbox="allow-scripts"的 iframe 中執行(沒有allow-same-origin),無法讀取宿主頁面的 Cookie 與儲存空間,也無法自行開新視窗。內容安全政策(CSP):預設只允許內嵌的 script 與 style,圖片、媒體與字型只接受
data:/blob:,connect-src為'none'(不能對外發出請求),也不能送出表單。請把 JS、CSS 與圖片都打包進同一份 HTML。額外允許的來源:卡片確實需要載入外部資源時,在組織私有 MCP Registry(
/api/v1/mcp/private-registry/)的cspAllowedOrigins列出來源。只接受https://開頭、不含路徑與萬用字元的 origin,例如https://viewer.example.com。建立或更新 MCP 工具時,網址開頭符合 RegistryremoteMcpUrl的工具會對應到該 Registry。尺寸與顯示模式:卡片預設高度 220px,依卡片以
ui/notifications/size-changed回報的height調整(上限 2000px),訊息流中的最大高度為min(62vh, 620px),超過時在卡片內捲動。卡片可以請求切換為全螢幕(fullscreen),平台會在右上角加上退出按鈕,也可按 Esc 返回,收合後維持卡片最後回報的高度;請求pip時以一般(inline)模式顯示。串流中不顯示:Agent 回覆串流結束後才掛載卡片;同一則回覆多次更新時,平台以
ui/notifications/tool-result原地推新結果,不重建 iframe。
卡片與宿主的握手
卡片與平台之間以 MCP Apps 的 JSON-RPC 透過 window.parent.postMessage 溝通(宿主端使用 @modelcontextprotocol/ext-apps 1.2 的 AppBridge,協定版本 2026-01-26)。順序:
卡片送
ui/initialize(appInfo、appCapabilities、protocolVersion)。平台回hostInfo、hostCapabilities(serverTools、message.text、openLinks)與hostContext(displayMode、availableDisplayModes: ["inline", "fullscreen"]、theme: { primaryColor, mode })。卡片送
ui/notifications/initialized。平台依序推
ui/notifications/tool-input(arguments為空)與ui/notifications/tool-result(完整CallToolResult)。之後顯示模式或主題改變時推ui/notifications/host-context-changed;卡片卸載前送ui/resource-teardown請求。
平台另外會在卡片 HTML 的 <head> 注入 window.__MCP_INITIAL_RENDER_DATA__(同一份工具結果),讓卡片在握手完成前就能先畫;正式資料仍以 tool-result 通知為準。
卡片可以呼叫的宿主功能
tools/call
由平台代為呼叫,卡片無法直接連到你的伺服器或 MaiAgent 後端。只能呼叫產生這張卡片的同一個 MCP 來源、且在這段對話中已啟用並在允許清單內的工具,驗證標頭與 Agent 呼叫時相同。單次呼叫逾時 30 秒;回給卡片的結果只保留 content 的第一段文字、structuredContent 與 isError。失敗時卡片外會顯示「這個動作沒有完成,請稍後再試一次。」
ui/message
content 需為內容區塊陣列,平台取出其中的文字區塊,當作使用者訊息送進對話並先把卡片收合回 inline,由 Agent 接著回答
ui/open-link
由平台開啟新分頁,只接受 https:、http:、tel:、mailto:,網址中的帳號密碼會被移除;不允許或被瀏覽器擋下時回 { "isError": true }
ui/request-display-mode
在 inline 與 fullscreen 之間切換,回覆實際生效的模式
ui/notifications/size-changed
依 height 調整卡片高度(全螢幕時忽略)
ui/download-file、ui/update-model-context、resources/read
未支援,平台回 -32601 Method not found
適用範圍
卡片顯示在 Web Chat 與 MaiGPT 的對話中;LINE、Messenger 等其他通訊平台只會收到 Agent 的文字回覆。
對 Agent 掛載的 MCP 工具(Agent 模式),以及成員在 MaiGPT 授權使用的連接器都有效,MaiAgent 這一端不需要額外開關。
地端部署的 Web Chat 是否顯示卡片依部署設定而定,請洽 MaiAgent 窗口。
最小可跑範例
一支 Node.js(18 以上)的 MCP Server:一個查訂單狀態的工具(回傳假資料)連結 ui://orders/status 卡片,卡片能重新查詢、也能向 AI 提問。三個檔案放在同一個資料夾。
接進 MaiAgent 的步驟
讓 Server 從公網以
https://存取(本機測試可用cloudflared tunnel --url http://localhost:8787或ngrok http 8787之類的工具取得暫時網址),MCP 端點為https://<你的網域>/mcp。依使用者手冊的建立 MCP 工具在後台新增 MCP 工具,網址填上一步的端點;本範例不需要標頭。「允許的工具」重新取得後應列出
get_order_status。把工具配置給回答模式為 Agent 模式的 Agent,在 Web Chat 或 MaiGPT 問「A1001 這筆訂單到哪了?」。Agent 回覆完成後,卡片會出現在回覆下方;點 重新查詢 走
tools/call、點 問 AI 這筆訂單 走ui/message。使用者端的行為見 MCP 互動卡片(MCP Apps)。
常見錯誤
Agent 有呼叫工具,但沒出卡片
tools/list 的工具定義與 tools/call 結果都沒有 _meta.ui.resourceUri(或值不是 ui:// 開頭)、工具回了 isError: true、或是在 LINE 等不顯示卡片的通路
卡片位置只顯示純文字
resources/read 失敗或回空內容,平台改用 content 第一段文字;用上面第 3 個 curl 確認
卡片一直停在「載入中…」
卡片沒有完成握手(ui/initialize 的 params 缺 appInfo/appCapabilities/protocolVersion,或沒送 ui/notifications/initialized),平台不會推 tool-result;也可能是卡片 HTML 載入了外部 JS 被 CSP 擋下
卡片空白
HTML 依賴外部 CSS/JS/圖片而被沙盒 CSP 擋下,請打包進同一份 HTML,或在 Registry 設定 cspAllowedOrigins
卡片顯示「這個動作沒有完成,請稍後再試一次。」
卡片的 tools/call 被平台拒絕或失敗:工具名稱不在該 MCP 工具的允許清單、工具已從 Agent 移除、授權失效、Server 回錯或超過 30 秒未回應
卡片的「問 AI」沒反應
ui/message 的 content 不是陣列、或沒有 type: "text" 的區塊
上線前檢查清單
最後更新於
這有幫助嗎?