某個週末,系統的 WebSocket 又斷線了(Day 20 提過的已知問題)。
這類故障的處置,在我家經歷過三個時代。最早是純手工:拿出筆電 → SSH 進 NAS → 打 docker restart → 等它起來 → 確認 log,大概三分鐘,不難,但瑣碎。後來進入自癒時代(Day 20 那套機制)——結果多了一種新劇情:它修著修著把自己搞到當機,負責自癒的那隻手跟著一起陣亡,最後還是我在外面用手機或筆電遠端連回去救它。自動化沒有消滅手工,只是把手工從「例行」變成「救援」——而救援永遠發生在最不方便的時候。
現在是第三個時代:在桌面的 Claude 對話框輸入「幫我重啟 CLI 容器」。它呼叫一個工具、NAS 上的 docker restart 跑完、回我一句「已重啟,服務恢復正常」。全程我沒離開沙發,也沒打開終端機——連救援都不再需要終端機了。
不過第三個時代也有它的邊界,先誠實講:MCP Server 自己就住在同一台 NAS 上。它能救的是「別的容器死了」,救不了「它自己所在的那一層」——整台宿主出事的那種夜晚,一句話喚不醒任何東西,最後一哩還是 SSH。單機系統的救援鏈,最底層永遠要留一條不依賴系統本身的路。
中間那層魔法叫 MCP(Model Context Protocol)。先補一個 2026 年的背景:這個協定已經不是小眾規格了——包括 Anthropic、Google DeepMind、OpenAI 的工具框架在內,多家 AI 廠商都把 MCP 列為原生支援,它實質上成了「LLM 呼叫外部工具」的業界標準。我動手蓋自己的 MCP Server 時它還沒這麼熱,所以今天這篇也算是一次「押對了業界標準」的復盤:我怎麼幫自己的 NAS 系統蓋一個 MCP Server,12 個工具的設計取捨,部署時吃過的兩個悶虧,以及比「能做什麼」更重要的問題:哪些操作我刻意不做成工具。
補記(發文當日):寫這篇時是 12 個工具,現在是 26 個。多出來的十四個沒有一個是規劃出來的——每一個都對應一次「我又手動做了同樣的事第三遍」。下面講的設計取捨在 26 個工具的規模下依然成立,尤其是最後那節「刻意不給的能力」:工具數量翻倍,負空間反而一項都沒有放寬。
一句話版本:MCP 是一個開放協定,讓 LLM 應用(桌面助手、IDE、Agent)能用統一的方式呼叫外部工具。你把自己的服務包成 MCP Server、宣告「我有這些工具、參數長這樣」,任何支援 MCP 的 LLM 客戶端就能發現並呼叫它們——不用為每個客戶端寫一次整合。
當初選它的理由很樸素:就是不想為每個客戶端重寫整合。事後看,這個選擇搭上了標準化的順風車——當協定變成業界共識,你當年包好的工具插座,新出的客戶端一接上就能用,整合成本一次攤提。而這在我家已經不是理論:同一台 MCP Server,現在同時接著三種客戶端——桌面的 Claude(互動與排程分析)、開發用的 Claude Code(改系統時直接查生產狀態)、連框架自己(OpenClaw)也接上來用。三個大腦,共用同一排插座,整合各寫一次的世界線被省掉了兩條。自架系統選協定的心法大概就是這樣:選「中立的開放規格」而不是「某家的私有接口」,賭的是生態,不是廠商。
對我的場景,它解決的是「兩個大腦共用一雙手」的問題:我家有常駐 NAS 的 Agent,桌面上還有互動用的 Claude(Day 11 講過雙 AI 協作)。這位桌面搭檔很聰明,但它原本碰不到我的 NAS——不能讀 workspace、不能查行情快取、不能重啟容器。MCP Server 一架,NAS 的能力變成一排工具插座,桌面 AI 隨插即用。

工具清單不是一次設計出來的,是照「我實際會叫它做什麼」長出來的,最後收斂成 12 個,五大類:
| 類別 | 工具例 | 設計重點 |
|---|---|---|
| Workspace 讀 | 讀檔、列目錄、全文搜尋 | 最常用,零風險 |
| Workspace 寫 | 寫檔、附加內容 | 限 workspace 路徑內,擋路徑跳脫 |
| 狀態查詢 | 系統狀態、記憶體用量、行情快取、持倉快照 | 唯讀,聚合好再回傳 |
| 維運操作 | 重啟容器(指定目標)、跑白名單腳本 | 枚舉型參數,不收自由字串 |
| 排程管理 | 列出 job、查執行歷史、觸發單次執行 | 觸發可以,改設定要過閘道 |
兩個設計原則值得展開:
**一、工具的粒度要「對齊意圖」,不是對齊 API。**初版我做了一個萬用工具 exec(command)——什麼都能跑,等於把 shell 直接交給 LLM。後來全部改成意圖級工具:restart_containers(target) 的 target 只接受枚舉值(cli / gateway / md-server),run_script(name) 只能跑白名單裡的腳本。**LLM 是機率引擎,你給它自由字串的權力,它遲早組出一句你沒料到的指令。**參數空間越窄,行為越可預測。
**二、回傳值幫 LLM 消化好。**工具回傳原始 JSON 一大坨,LLM 要嘛摘錯重點要嘛燒 token。我的工具在 server 端先聚合:持倉快照回傳的是算好的損益摘要,不是原始持倉檔全文。把確定性的整理工作留在程式端——這跟 Day 17 晨報的分工哲學一脈相承。
清單之外的「負空間」才是安全設計的主體。這些操作我刻意不提供:
一條經驗法則:**工具的破壞半徑 × LLM 的誤觸機率 = 你的風險。**誤觸機率你控制不了(它是機率模型),能控制的只有破壞半徑。
**坑一:build cache 餵我吃舊程式碼。**MCP Server 改完原始碼要重建 Docker image。有一次修了一個 bug、docker compose up -d --build、測試——bug 還在。改碼、重建、測試,bug 還在。我盯著那段「明明已經改掉」的程式碼懷疑人生半小時,最後發現:**build 用了快取層,src 根本沒被重新複製進 image。**解法是老實分兩步:docker compose build --no-cache 再 up -d。從此我的部署筆記多了一條粗體字:懷疑自己瘋掉之前,先懷疑 cache。
**坑二:SSE 404 的假警報。**客戶端接上後,log 裡持續出現對 /mcp 的 GET 請求收到 404。查了半天,結論是:某些 MCP 客戶端會嘗試建立 SSE 持久連線,而我的 server 是無狀態實作,不支援該端點——404 是預期行為,工具呼叫本身完全正常。這個坑的教訓不是技術的:接協定生態的東西,要分清「規範的可選功能」和「壞掉」,不然你會花一晚上修一個不是 bug 的 bug。後來這兩個坑都進了部署筆記,部署流程的最後也多了一步驗證:發一個真實的工具呼叫,確認新版程式碼真的生效——而不是只看容器有沒有起來。
上面那句「確認新版真的生效」,聽起來像廢話,但它其實是我後來被迫養成的習慣——因為這套系統裡,「改完存檔」在不同的地方代表三件完全不同的事:
| 你改的東西 | 改完之後 | 為什麼不一樣 |
|---|---|---|
| Agent 的 Markdown(人格、規則、記憶) | 存檔就生效 | 同步工具即時送到 NAS,Agent 下次醒來自然讀到新的 |
| Web 儀表(我自己寫的瀏覽頁面) | 自動重載 | 它跑在檔案監看模式,偵測到變動自己重啟 |
| MCP Server | 要重建映像檔 | 原始碼不在同步範圍內,而且要編譯後打包進容器 |
在編輯器裡,這三種檔案長得一模一樣。危險就在這裡:你改了 MCP 的程式碼、存檔、切去測試——什麼都沒變。你會開始懷疑邏輯寫錯、懷疑快取、懷疑自己。但真正的原因是它根本還沒被部署,而沒有任何東西會告訴你這件事。
更陰險的是中間那一列。檔案監看偶爾會失靈:檔案確實送到了、行程也還活著,但它就是沒重載,頁面停在舊版——而且不會有錯誤訊息。我遇過幾次,每次都是先懷疑自己的程式碼,最後才想到去重啟。
所以我在每個服務都放了一個看得到的版本號。它不是給使用者看的、也不是為了發版,它只回答一個問題:
我現在看到的這個東西,是不是我剛才改的那一份?
這個問題沒有版本號就答不了,而且它比「服務有沒有活著」重要得多——一個跑著舊程式碼的健康服務,比一個掛掉的服務更難發現。
後來我把儀表和腳本接上了 CI:推上主線就自動送到 NAS。省了很多手動步驟,但它有一個容易被忽略的前提——
同步工具同步的是「工作目錄裡的檔案」,它不看 git 分支。
意思是:如果我在那個會被同步的資料夾裡切到一個做到一半的分支,那些未完成、未驗證的檔案會立刻被推上生產。不需要部署、不需要合併,存檔就到了。
我是理解了這件事之後,才把開發全部移到同步資料夾之外的獨立工作目錄——同步的那份永遠停在主線。這條規矩看起來很囉唆,但它擋掉的是一整類「我只是先改改看」造成的生產事故。
這也是自架跟一般開發最不一樣的地方:在公司,你的開發環境跟生產環境之間隔著好幾道閘門;在家裡,它們可能只隔著一個資料夾。
順帶一提,框架本身也有一個控制台,可以看每個 session 的用量。但它給的是 token 數,不是金額——沒有牌價表,成本欄位一律是零。這是我後來得自己做一層成本計算的原因(完整的成本拆解見 Day 22)。能看到用量跟能看到花費,中間差了一整套定價邏輯。
MCP 工具層的三個心法:工具粒度對齊意圖、參數空間收窄到枚舉、負空間(不做什麼)比正空間更重要。做完之後,NAS 從「要 SSH 進去的機器」變成「能對話的服務」——而且因為押在開放標準上,這排工具插座對未來的新客戶端一樣有效。
而部署那三列表格的教訓可以濃縮成一句:「我改了」跟「它生效了」是兩件事,中間那段沒有人會通知你。
明天是第四週小結:把這週的成本閘道、品質監控、安全紅線收攏成一份可以直接抄走的檢查清單。順便講一件讓我很難堪的事——我有一支合規掃描腳本,跑了兩個月,每天回報「零違規」,而它其實一直在比對空值。
🔑 這篇的關鍵字
MCP(Model Context Protocol)· stateless MCP server · 工具描述要對齊意圖不是對齊 API
參數空間收到最窄:能用 enum 就不要用自由字串(LLM 的輸出是機率分布,縮小它能觸碰的範圍才是安全設計)
負空間優先:哪些能力刻意不給,比給了什麼更重要(例:沒有任意 shell、沒有刪檔、憑證連查詢介面都不存在)
工具是踩坑長出來的:每個工具都對應一次「我又手動做了第三遍」
我是一名金融業資訊工程師,這是我半年來在家自架 AI Agent 系統的實錄。