MCP / Plugin 實作
我為什麼自己建立 MCP 與 Plugin:從工具分散到真實驗收
三個 Agent 的能力與規則不同步,促使我建立共用 MCP 與外掛入口。記錄 OAuth 真實瀏覽器踩坑、459 次工具呼叫的失控探索,以及 0.4.2 的範圍修正與驗收數據。
實作紀錄期間:2026-08-03 至 2026-10-14
我開始建立 MCP,是因為一個很日常的問題:要查工時時,我想不起來哪個 Agent 會做。 有的工具只有 Claude Code 能用,Codex 沒有;Hermes 又有另一份操作方法。當三個 Agent 跨電腦使用,複製過去的 Skill 和 SOP 逐漸出現差異,修改一次並不代表每個入口都更新。
後來,需求延伸到個人知識庫與工作訊息:我希望在不同 AI 入口搜尋同一份資料,保留來源,也能限制哪些操作可以執行。這讓我同時處理 MCP 服務、Plugin 接入、OAuth、權限和驗收,而不只是寫出一個能回傳 JSON 的工具。
這篇整理我與 AI 開發工具協作的實作。需求、範圍與驗收取捨由我決定;程式修正、測試與排障由 AI 工具協作完成。數據是指定日期的歷史紀錄,不代表服務今天的即時狀態,也不是所有模型或外掛的通用表現。
起點:把能力與規則集中,減少每個入口各做一份
最初的能力中心把能力目錄、SOP、參數對照、核心執行與稽核放在共用服務。Agent 端保留薄的路由規則:先找到能力,再取得操作要求。改動核心行為時,盡量讓各入口共用同一份實作。
我對 MCP 和 Plugin 的分工,是從這個維護問題長出來的。
| 層級 | 我放進去的責任 | 原因 |
|---|---|---|
| MCP 服務 | 查詢、原文讀取、受限寫入、來源範圍與權限檢查 | 不同 Agent 要取得一致的能力與結果 |
| Plugin/connector | 讓特定平台發現服務、完成授權與載入工具 | 各平台有自己的安裝與連線生命週期 |
| Skill/使用規則 | 何時呼叫、如何呈現來源、何時停下等使用者確認 | 讓操作流程符合實際工作需求 |
| Server 端強制檢查 | scope、允許的資料範圍、寫入預覽與執行約束 | 使用規則不能代替真正的授權檢查 |
如果 Plugin 只把一個 search 轉呼叫成另一個 search,沒有增加平台接入或使用規則,我不會為了多一層包裝而建立它。私人知識庫能直接接 MCP,就直接接;需要特定平台入口時,再做相應的封裝。
哪些是自己建立,哪些是重用
| 實作 | 自己處理的部分 | 重用的部分 |
|---|---|---|
| 共用能力中心 | 能力目錄、操作規則、核心執行與驗收流程 | 既有業務系統 API |
| Vault RAG & Inbox MCP | search/fetch、受限筆記寫入、OAuth 與來源政策接合 | 既有 VaultRAG 檢索服務 |
| Google Chat Reader 外掛 | 外掛連線設定、OAuth 範圍、使用規則與實際讀取驗收 | Google 官方 remote MCP;沒有重寫 Google Chat 服務 |
| DSH 接入 | 自建 MCP 的桌面 OAuth 相容性、描述檔、問題修復與驗收 | 第三方 dsh-mcp-connector 0.2.70;這個 connector 並非我開發 |
Google Chat 的例子尤其能說明取捨:既然已有可接入的官方服務,我做的是讓它進入自己的工作流程。當時選了 4 個 Chat 唯讀 scope,另加登入身分所需範圍。但「唯讀」只能限制操作種類,並不自動限制成指定工作群組;那次實作尚無群組白名單技術隔離。
2026-10-02 的外掛修正版能完成 search_conversations → list_messages,讀回訊息;同時,授權是否能在一小時後持續刷新,仍列為待驗收。修正版加入 access_type=offline 與 prompt=consent,並重新授權;Google 文件說明離線存取需在授權要求設定 access_type=offline,才能取得更新權杖。參數改對與長期運作通過,是兩項不同證據。 參考:Google OAuth 網路伺服器流程。
第一個實際故障:程式測試通過,瀏覽器 OAuth 卻失敗
2026-10-13,我把同一個 Vault MCP 接到 DSH。服務端先補上桌面 public client、PKCE 與受限制的 loopback callback。程式化探測已通過授權、search、fetch、refresh、revoke,真正的 Chrome 授權頁卻一直顯示「拒絕跨站請求」。
初期曾把方向放在批准碼錯誤或過期。後來追到服務端處理順序才發現:Origin 檢查發生在批准碼檢查之前。 請求根本還沒走到批准碼驗證。
| 同一授權頁的觀察 | 修復前 | 修復後 |
|---|---|---|
| Referrer-Policy | no-referrer | same-origin |
| Chrome 表單 POST Origin | null | 正確的同站 Origin |
| 使用無效測試值送出 | 403,拒絕跨站 | 400,批准失敗或逾時 |
| 真實授權 | 無法完成 | 完成連線 |
上表的 400 不是授權成功;它表示請求已通過 Origin 檢查,進到預期的無效值驗證。之後另用真實流程完成授權,才算接通。
修正是調整這個服務授權頁的 Referrer-Policy,保留嚴格的同站檢查。這是本案瀏覽器與頁面設定的實測,不代表所有 OAuth 錯誤都應套同一修法。
為什麼先前測不到?因為 probe 自行帶了正確 Origin,繞過了瀏覽器實際產生標頭的那段行為。0.4.1 修復時,第一組 29 項測試通過;加入 Origin 回歸後,transport 組 11 項通過。兩組有重疊,不能相加成 40 項。
第二個實際故障:插件已安裝,host 還沒載入
另一個卡點發生在註冊設定。DSH 的 cordis.yml 是空清單佔位,註解指向 cordis.patch.yml;把條目寫進前者,套件雖然存在,工具仍不生效。後續修 patch 時,又曾用正規表示式改壞無關 YAML 區塊,必須還原再追加。
這次讓我把驗收拆成七關:安裝 → host 載入 → 描述檔匯入 → OAuth → search → fetch → 重啟後複測。每一關都要留下實際結果,不能靠模型說「完成」跨過下一關。
2026-10-13 最終驗收取得 1 個 server、7 個工具;實際 fetch 回 2,144 字元、103 行,has_more=false,帶來源路徑、雜湊與行號。重啟後不需重新授權,搜尋仍成功。這證明該次接入與重啟持久性,沒有涵蓋全部查詢品質或長期穩定性。
459 次工具呼叫,沒有換來等比例的進展
這次接入也留下不漂亮、但值得公開的數據。從安裝要求到重啟後複測,牆鐘約 6 小時 10 分鐘,包含人工介入、等待批准、停機與回合間隔,並非模型連續推理時間。
下面只統計當日 12:08 的第一份日誌快照,不是整場總量。
| 快照指標 | 數值 | 計算範圍 |
|---|---|---|
| 自安裝要求起的牆鐘時間 | 4 小時 19 分 29 秒 | 到第一份快照 |
| 工具呼叫 | 459 次 | 其中 pwsh 439 次 |
| 完全相同呼叫的額外重複 | 152 次 | 同組第一筆之外的重複 |
| 已完成上下文壓縮 | 11 次 | 該快照內 |
| 額外重複占工具呼叫 | 約 33.1% | 152 ÷ 459,本文換算 |
約三分之一的呼叫是完全相同輸入的額外重複,但不能直接換算成三分之一時間可省:每次呼叫的執行與模型等待成本不同。日誌還顯示,後續至少 28 次同類瀏覽器歷史查詢,並沒有定位真正的 Origin 問題。
我後來把指導縮成有停止條件的關卡:同方法失敗兩次就交付證據;OAuth 只 connect 一次,逾時後查一次 status;不再反覆查歷史網址,也不讓 Agent 終止承載自己的主程序。限制流程之後,Agent 能停下來回報,但當時授權仍失敗。停止無效探索是改善流程,修復 Origin 才是解決故障。
這不是模型排名測試。當時模型標示為 strata-local/qwen3.8-flash-next-iq3_s,沒有固定任務換模型的對照組,因此無法把責任全部歸給模型、量化或 DSH。實際完成也有 Codex 修復與人工操作,不能宣稱 Agent 自主完成整段建置。
第三個實際故障:後端新增來源,MCP 還守著舊名單
2026-10-14,聯合搜尋出現 source_not_allowed。後端已擴大核准專案,但 MCP adapter 還寫死最初 4 個專案。候選結果一旦碰到新增專案,整次 search 就拋錯,連合法結果也不回傳。
這與 OAuth 是兩個不同問題。連線已成功,壞的是前後端重複保存的範圍政策。只改問某個舊專案會成功,也不能據此判斷其他搜尋全部正常。
0.4.2 將 adapter 的工作收斂為 ID 安全格式檢查;可查哪些專案由後端 published_projects() 決定。fetch 仍檢查 scope、search 登錄的不透明 ID、來源版本與核准範圍。部分候選不合法時保留有效結果,並回 coverage.partial 與 failures,讓呼叫端知道結果不完整。
| 2026-10-14 驗收 | 實際結果 | 可以支持的結論 |
|---|---|---|
| adapter 回歸 | 16 項通過 | 覆蓋新來源、撤權後舊 ID、版本、分頁、惡意路徑與部分失敗等情境 |
| Inbox 回歸 | 26 項通過 | OAuth、安全、audit 與 transport 回歸通過 |
| 原本失敗的問句 | 回 8 筆;partial=false,failures=[] | 該問句不再整批失敗 |
| 另一個限定來源問句 | 回 7 筆,無部分失敗 | 原有來源的一次回歸成功 |
| 精確 ID 查詢 | 回 1 筆 | 該議題可召回 |
| 原文 fetch | 5 個證據區塊、1,358 字元;has_more=false | 該來源可讀完 |
| 部署回執 | completed=true,rollback=false;health 版本 0.4.2 | 該次正式部署通過 |
16 與 26 是這次兩組測試的項數,不能拿來當回答正確率。8 筆是該次搜尋結果數,不是完整資料總量。這次正式驗收使用 OAuth helper 與 connector 呼叫,沒有重新操作 DSH Desktop UI;不能借用前一天的 UI 驗收替代這一點。
給要做同類整合的人:先決定如何證明完成
這幾次實作之後,我會先把驗收表寫出來,再開始接線。下面是依本案整理的方法,並非另一輪已執行的測試結果。
| 要驗證的事 | 留下的證據 | 必須另外測的失敗情境 |
|---|---|---|
| host 真的載入外掛 | 可呼叫的工具清單與版本 | 套件存在但未註冊、改錯 profile |
| 使用者真的能授權 | 真實瀏覽器表單、callback、連線狀態 | 錯誤 Origin、過期 request、取消授權 |
| 查詢走對資料來源 | search 的來源類型與可 fetch 的 ID | 新增來源、來源撤權、部分候選失敗 |
| 回答能追溯 | fetch 的原文、行號或證據區塊、版本 | 舊 ID、截斷內容、來源版本變動 |
| 寫入符合預期 | 預覽內容、使用者確認、執行後回讀 | 過期預覽、重複執行、未授權路徑 |
| 能持續使用 | 重啟後實際查詢、到期後 refresh 測試 | 憑證失效、服務中斷;各自記錄結果 |
對我而言,建立 MCP 和 Plugin 的成果,是讓原本分散的工作能力有共同入口,並能說清楚每個入口的權限、來源與失敗原因。這些事需要需求整理、架構取捨、實作和驗收一起做;工具數量只是其中很小的一部分。
數據來源與公開界線
本文依據三組自己的實作紀錄:2026-08-03 至 08-04 能力中心架構與執行紀錄、2026-10-01 至 10-02 Google Chat Reader 外掛設定與讀取紀錄、2026-10-13 至 10-14 DSH 軌跡分析與 MCP 0.4.1/0.4.2 部署驗收。量化數據來自日誌分析、測試結果與部署回執;公開稿已移除客戶名稱、內部單號、主機位置與憑證。
原始工作資料與 session 未公開,讀者無法由本文獨立重跑我的私人資料測試。本文提供的是明確的條件、結果與驗收方法,沒有宣稱節省多少人力、提高多少正確率或保證長期可用。Google 文件支持 OAuth 參數說明,並不替我的個案數據背書。