導向型 MCP Gateway:無狀態閘道的實戰方法論

重點摘要(一分鐘版)
  • 導向型 MCP Gateway = 一台無狀態路由 server,擺在既有 API 目錄前面:本身零資料、零快取、零登入狀態
  • 只做兩件事:引導(告訴 Agent「這類問題查哪個資料集」)+轉拋(把查詢即時打到上游、回傳當下結果)。
  • 一句話心法:「Agent 問你,你身上沒資料,但你知道資料在哪、幫他導過去查。

當企業內部有上千個各自獨立、各自授權的資料 API時,AI Agent 的最大問題不是「查不到」,而是根本不知道有什麼、也不知道該問誰。這篇拆解一個實際上線的解法——一台導向型 MCP Gateway:它不持有資料,只讓 Agent 找對資料集、把查詢即時轉拋過去。

面向 🚫 直覺做法:持有資料 ✅ 這套做法:導向資料
角色 資料倉儲 / 快取層 路由器 / 導向器
狀態 有 DB、有快取、要同步 無狀態,每次獨立
真相 兩份會分歧 上游唯一真相
風險 PII 落地、同步負擔 資料不落地
🧭 本文導覽

問題:Agent 面對上千個 API 的「無知」

大型組織的資料通常不是乾淨的 data lake,而是幾百上千個各自獨立的 API——各自授權、各自欄位規格。人靠 portal 與口耳相傳慢慢摸;Agent 沒有背景,兩件事都卡:不知道有什麼可查、就算知道也不知道端點長怎樣。導向型 Gateway 的答案是:上游已是權威來源,我不再存一份,只讓 Agent 找得到、問得到。

四件套工具 = Agent 的認知流程

整台 server 對 Agent 只暴露四個工具,剛好走完「探索 → 理解 → 定位 → 查詢」:

① 探索
list_datasets()
「這裡有哪些資料集?」
② 理解
describe_dataset()
「有哪些欄位、要不要身分?」
③ 定位
find_dataset()
「用問題找資料集」=導向核心
④ 查詢
query_dataset()
「真的去查」,回真資料、不落地

八個核心設計決策(為什麼這樣做)

每個決策都是岔路 → 選擇 → 帶走的通則。一表看完:

面向 我們選了什麼 🎯 帶走的通則
A 定位 純導向、零落地 上游是權威來源時,gateway 的職責是降低發現與存取摩擦,不是複製資料。先問「這份狀態非存不可嗎?」
B 範圍 明列「不做什麼」:不落地、不寫入 YAGNI 要「寫下來」才守得住。範圍靠主動排除守住,不是被動不做。
C 實證 拿能跑的真實請求逆推契約 逆向對接以 known-good 呼叫為錨點;宣稱可取的都要一次實際 HTTP 200 背書。
D 推導 目錄只存代碼,URL 用規則推導 能由規則生成的存規則不存結果;新增資料集=丟一個代碼。
E 授權 MCP 只轉拋,永不自己湊身分 畫清「誰負責解析身分」的邊界;寧可回一個明確的「我需要身分」,也不要默默用錯身分。
F 無狀態 身分每次當參數帶入,不記 session gateway 盡量無狀態;上下文由呼叫方每次帶進來 → 可擴充、可重試。
G 誠實 不做假資料 fallback 誠實失敗 > 似是而非;錯誤結構化、給可行動欄位。一次假資料毀掉全部信任。
H 遷就 上游無欄位過濾→本機比對補償 先摸清上游不能做什麼,再決定自己補什麼(補時標成本 + 設上限)。

面向 D 實作(推導取代儲存)——URL 由代碼長出來,catalog 不存 URL:

面向 E 深潛:授權模型的三版演進

這段最值得學——想法愈想愈乾淨:

v1 · 單層
app 級金鑰 + 固定服務身分。夠用但沒分敏感度。
v2 · 敏感度驅動
低敏感度帶服務身分;高敏感度要求呼叫端提供身分,缺則回錯誤。
v3 · 定調
MCP 只是轉接器,永不湊身分;身分解析是呼叫端(Agent / SSO)的責任,AD 永遠留在 client 那側。

身分該從哪來?依資料集敏感度決定——這是 query 打網路前的決策樹:

① 這個資料集是什麼敏感度?
低敏感度 → 預設帶服務身分(呼叫端帶齊三欄則覆蓋)
高敏感度 → 要求呼叫端提供身分
 ├ 三欄齊全 → 用呼叫端身分查
 └ 缺身分 → 擋下,回 identity_required

方法論:由內而外的 checkpoint

大任務切成「離線可測 → 對外可連」,每關綠燈才前進——先讓核心邏輯能離線 mock 測,最後才碰網路與部署,失敗定位最快:

CP1 核心(離線)
代碼推 URL、組 body、分頁、catalog。全 mock 測。
CP2 工具層
四工具契約 + 錯誤轉譯(mock client)。
CP3 整合起服務
live 測(opt-in)對齊 total;MCP client 連得上。
CP4 部署
反代 + systemd 常駐,對外可連。
面向 做法
測試分層 離線 mock 為主(預設全綠、CI 友善);真實網路測試 RUN_LIVE=1 才跑。鐵律:測試 / log 不得出現真實 PII 或金鑰。
部署 backend 綁 localhost、對外走反代;systemd 常駐(Restart=on-failure + 開機自啟);機密走環境變數,不進版控。
單一真相 design 寫「該長怎樣」、git log 寫「怎麼變成的」、留一節「接手看這裡」。三者不重複。

踩過的坑(帶走這些,少走冤枉路)

解法
反代後 DNS-rebinding 421 MCP SDK 擋非白名單 Host。反代後 backend 看到公開域名ALLOWED_HOSTS 必須含正式域名(+ localhost),否則 initialize 回 421。
SSE 卡住 streamable HTTP 走 SSE,nginx 要 proxy_buffering off
金鑰 scope 陷阱 「拿不到」先確認是不是金鑰 scope 太窄,不是程式錯。跨模組要用 scope 較廣那把。
上游沒有欄位過濾 別假設上游能依任意欄位過濾——實測只認日期。要條件查就整表拉下來本機比對(標成本、設上限)。

怎麼複製這套模式到別的資料來源

# 步驟
1找 known-good 呼叫當契約錨點,實測 host / auth / body / 回傳 / 分頁。
2抽最小事實 + 推導規則:能由規則算的(代碼→URL)別存進 catalog。
3列 catalog,每筆要有 200 背書;標 reachable 區分實測可取 vs 聽說。
4畫授權邊界:身分每次當參數帶入,缺就回結構化 identity_required
5四件套工具:list / describe / find / query(含上游限制的本機補償)。
6由內而外做 + 分層測:CP1 離線核心 → … → 部署;離線全綠、live opt-in。
7寫下不做什麼:YAGNI 排除清單放進設計文件。
8單一真相 + 演進留痕:design + git log + 「接手看這裡」一節。

一頁帶走的原則清單

  • 上游是權威來源時,gateway 的價值在降低發現與存取摩擦,不在複製資料。
  • YAGNI 要寫下來才守得住;主動排除 > 被動不做。
  • 逆向對接以 known-good 呼叫為契約錨點;宣稱可取的都要實際 200。
  • 能由規則生成的存規則不存結果
  • 畫清身分解析的責任邊界;gateway 不偷湊身分,寧可明確說「我需要身分」。
  • gateway 盡量無狀態;上下文每次帶進來。
  • 誠實失敗 > 似是而非;錯誤結構化、給可行動欄位;不做假資料 fallback。
  • 先摸清上游不能做什麼,再決定自己這層補什麼。
  • 大任務切由內而外的 checkpoint,每關綠燈才前進。

導向型 MCP Gateway 的程式碼可能只有幾百行,但背後那個轉念很關鍵:MCP server 不一定要持有資料,它可以只是一台幫 Agent 消除無知的路由器。當你手上已有一個權威但難以發現的 API 目錄,這套模式能用最小維運成本,把它變成 Agent 用得動的能力。

留言

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *