For the complete documentation index, see llms.txt. This page is also available as Markdown.

自架 MCP Server 接入規格

把自家系統做成 MCP Server 讓 MaiAgent 的 Agent 呼叫:支援的傳輸方式與 URL 要求、驗證(固定標頭、聯絡人憑證、OAuth 動態註冊)、允許的工具、逾時重試與錯誤如何回給模型,以及 MCP Apps 互動卡片。

本頁寫給要「把自家系統包成 MCP Server,讓 MaiAgent 的 Agent 呼叫」的工程師:MaiAgent 這一端怎麼連你的 Server、帶哪些標頭、怎麼做 OAuth、抓工具清單與呼叫工具時的逾時、重試與錯誤處理。

一次呼叫的流程

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。

建議使用 Streamable HTTP,並把 MCP 端點放在獨立路徑(例如 https://mcp.example.com/mcp)。若你的 Server 只提供 SSE,URL 請以 /sse 結尾,平台才會用 SSE 連線。

建立工具時的連線驗證

在後台或透過 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 授權規格):

1

探索受保護資源

平台先不帶權杖對 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 分鐘。

2

讀取授權伺服器中繼資料

依 RFC 8414 讀取 /.well-known/oauth-authorization-server。授權伺服器 URL 帶路徑時,會依序嘗試「well-known 插在主機與路徑之間」、「well-known 接在路徑後面」、「主機根目錄」三個位置。

3

動態註冊 Client(RFC 7591)

中繼資料有 registration_endpoint 時,平台送出:

沒有 registration_endpoint 時,需在 MCP Registry 預先設定 oauthClientId(機密 client 另設 oauthClientSecret),否則授權會失敗。

4

授權碼+PKCE

授權請求帶 response_type=code、state、code_challenge(S256)與 resource。scope 的取用順序:MCP Registry 設定的 scope → 受保護資源中繼資料的 scopes_supported → 授權伺服器中繼資料的 scopes_supported。state 有效 10 分鐘。

換權杖時以 grant_type=authorization_code 帶 code_verifier 與 resource,有 client secret 才一併帶上。

5

自動更新權杖

權杖到期前 60 秒內,平台以 grant_type=refresh_token 更新。回應沒有新的 refresh_token 時沿用舊的。

Callback URL:若你的授權伺服器不支援動態註冊、需要預先登記 redirect URI,下列兩個都要登記,否則對應的流程會在 /authorize 失敗:

流程
Callback URL

成員(組織共用)授權

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 時,要在管理員完成授權並取得工具清單後,清單才會有內容。

工具清單怎麼交給模型

  • 何時抓:每次組裝 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 回傳的錯誤結果或應用層錯誤不會重試。

只有「重送也安全」的工具才會重試,判斷順序:

  1. 工具的 annotations 有 readOnlyHint: true 或 idempotentHint: true → 會重試。

  2. 其中任一明確為 false → 不重試。

  3. 兩者都沒宣告時,看工具名稱的第一個字是否為 get、list、read、fetch、search、find、check、query、describe、show、view、browse、lookup(camelCase、- 會先拆字)。

建議在 tools/list 為每個工具明確宣告 readOnlyHint/idempotentHint。會產生副作用的工具(下單、送出表單)請標為 false,避免斷線後被重送。

回給模型的內容

情況
模型看到的內容

呼叫成功

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 資源

  1. 連結 UI 資源:在 tools/list 的工具定義,或 tools/call 結果的 _meta 帶上 ui.resourceUri,值必須以 ui:// 開頭,例如 {"_meta": {"ui": {"resourceUri": "ui://orders/list"}}}。兩處都有時以呼叫結果的為準;扁平寫法 _meta["ui/resourceUri"](@modelcontextprotocol/ext-apps 的 registerAppTool 產生的格式)也接受。工具定義的 _meta 在每次組裝工具清單時讀取。

  2. 提供資源內容:工具呼叫成功後,平台在同一個 MCP session 對這個 URI 送出 resources/read,取第一筆內容的 text 當作卡片 HTML。建議 mimeType 使用 text/html;profile=mcp-app。

  3. 回傳資料:卡片收到的是這次的工具結果:content 的第一段文字,以及 structuredContent(物件)。請讓卡片以 structuredContent 為資料來源;平台不會把工具輸入參數傳給卡片(ui/notifications/tool-input 的 arguments 為空物件)。

以下情況不會出現卡片,或改以文字顯示:

情況
結果

工具結果 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 工具時,網址開頭符合 Registry remoteMcpUrl 的工具會對應到該 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)。順序:

  1. 卡片送 ui/initialize(appInfo、appCapabilities、protocolVersion)。平台回 hostInfo、hostCapabilities(serverTools、message.text、openLinks)與 hostContext(displayMode、availableDisplayModes: ["inline", "fullscreen"]、theme: { primaryColor, mode })。

  2. 卡片送 ui/notifications/initialized。

  3. 平台依序推 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 提問。三個檔案放在同一個資料夾。

1

建立專案

2

server.mjs:工具與 UI 資源

3

card.html:單檔卡片

不引入任何套件,直接用 postMessage 走 JSON-RPC:握手、接收 tool-result、回報高度、tools/call 重新查詢、ui/message 向 AI 提問。JS、CSS 都寫在同一個檔案裡(沙盒 CSP 不允許載入外部資源)。

4

啟動並驗證

另開一個終端機,依平台的順序打三個請求(無狀態模式不需要 session 標頭):

三個都回對了,平台這一端就能產生卡片。

接進 MaiAgent 的步驟

  1. 讓 Server 從公網以 https:// 存取(本機測試可用 cloudflared tunnel --url http://localhost:8787 或 ngrok http 8787 之類的工具取得暫時網址),MCP 端點為 https://<你的網域>/mcp。

  2. 依使用者手冊的建立 MCP 工具在後台新增 MCP 工具,網址填上一步的端點;本範例不需要標頭。「允許的工具」重新取得後應列出 get_order_status。

  3. 把工具配置給回答模式為 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" 的區塊

上線前檢查清單

最後更新於

這有幫助嗎?