大師篇 · 第 21 章
MCP 進階與客製化(contrarian)
第 9 章帶你接好第一個 MCP、學會它是什麼。這一章換個角度,講一件可能跟你直覺相反的事——達人其實不愛接 MCP。Boris Cherny(Claude Code 的創造者)公開說自己「六個多月沒寫過一行 SQL」,靠的不是哪個資料庫 MCP,而是直接叫 bq 這個命令列工具。這章收的是 Shankar 和 Boris 這類資深玩家的「反主流」心法:MCP 留給少數真的需要它的場景當安全閘道,沒狀態的工具能用 CLI 就用 CLI;後半段再講怎麼用 Skills 取代過時的自訂指令、怎麼把終端機調成自己順手的樣子。這章料源大量來自具名達人的個人偏好,不是官方硬規範——我會逐處用徽章標清楚,你照自己的專案斟酌採用。
這章特別多「達人個人偏好」,每段就地標明
本章的料源比前面幾章更偏「個人實踐」,正文會用 source-tag 徽章逐處標清楚,讓你一眼分辨「官方規範」和「某位達人的個人主張」:官方 出自 Anthropic 官方文件或工程部落格;達人 是具名實踐者(如 Boris Cherny、Shrivu Shankar)的個人做法,非官方背書;社群 是社群整理的最佳實踐;實驗性 表示前沿、尚未定型、未來可能改。本章很多「少接 MCP」「只留兩個自訂指令」這類主張都是 👤 達人傾向——它代表「有人這樣用得很順、而且講得出道理」,不是「你一定要這樣」。
21.1 把 MCP 當安全閘道,別把每個 API 都包成工具
第 9 章你學會接 MCP,很自然會冒出一個念頭:「那我把公司每個服務、每支 API 都接成 MCP,Claude 不就無所不能了?」資深玩家會勸你慢一點。Shrivu Shankar(一位以「反主流」著稱的獨立實踐者)提出一個很值得參考的原則 達人:MCP server 不該是「每個 API endpoint 的鏡子」,而該收斂成少數幾個「高階能力」。
他的判準很實用——分成兩種情況:
| 工具性質 | 代表例子 | 建議怎麼接 |
|---|---|---|
| 無狀態(每次呼叫互不相干) | Jira、AWS、GitHub 這類「下個指令、拿個結果」的操作 | 直接用它們的命令列工具(CLI),讓 Claude 經 Bash 叫用就好,不必接 MCP |
| 有狀態(需要維持一個工作階段) | 像 Playwright 操控瀏覽器,要記住「現在開到哪一頁」 | 留給 MCP——這正是 MCP 擅長、CLI 難取代的場景 |
換句話說,MCP 最值得用的地方有兩類:把大批原始資料一次倒給 Claude(raw data dump),以及需要層層把關的高風險動作(gated action,例如「部署到正式環境」這種要審批的操作)。除此之外,能用 CLI 解決的,就別多接一層 MCP。
這是「設計取捨」,不是非遵守不可的規矩
提醒你:這條原則是 Shankar 個人推薦的安全設計取向 達人,不是 Anthropic 官方規定的「必須這樣」。如果你的團隊已經有一套接好接滿的 MCP 跑得很順,沒必要為了這條原則打掉重練。把它當成「下次要新增整合時,先問自己這個真的需要 MCP 嗎」的思考框架就好。
21.2 CLI 優先:能跑現成指令,就別接 MCP
承上一節,這節把「少接 MCP」最具代表性的一招單獨拿出來講,因為它太好用、也太反直覺。Boris Cherny 的做法是 達人:讓 Claude 直接在你真實環境裡跑命令列工具,而不是為每個服務都接一個 MCP。最有名的一句話是他說自己「六個多月沒寫過一行 SQL」——不是因為接了什麼資料庫 MCP,而是因為他叫 Claude 用 bq(Google BigQuery 的命令列工具)直接去查資料。
道理在哪?這些工具(bq 查 SQL、gh 操作 GitHub)你的電腦本來就裝了、本來就會用,Claude 只要透過 Bash 叫它們就好。比起「把整個 API 重新包成 MCP」,這種做法有兩個實在的好處:吃的 token 更少(不必把一大包工具定義塞進 context),而且通常更可靠(CLI 是官方維護的成熟工具,比你自己鏡像一層 API 穩)。
對照:同一件事——查資料庫——兩種接法
# ✅ CLI 優先:直接叫 Claude 用現成的 bq 工具
# (你只要在 prompt 裡這樣講,Claude 自己組指令去跑)
請用 bq 查上週每天的新註冊使用者數,照日期排序
# Claude 會自己生成並執行類似這樣的指令:
bq query --use_legacy_sql=false \
'SELECT DATE(created_at) d, COUNT(*) n FROM users
WHERE created_at > ... GROUP BY d ORDER BY d'
# ✗ 另一條路:為了查資料庫,特地接一個資料庫 MCP server
# → 多一層要維護的東西、多吃 token,多數情況其實沒必要
心法:「能用 CLI 就別接 MCP」
下次想接新整合前,先問一句:這個服務有沒有命令列工具?有的話,先試試讓 Claude 直接用它(記得搭配第 9 章學的權限控制,別給它無限制的 Bash)。真的沒 CLI、或需要維持工作階段狀態,再考慮 MCP。這是 Boris 的個人偏好 達人、不是硬規則——兩種做法都能動,只是達人實測下來覺得 CLI 這條路更省、更穩。
21.3 Slack 修 bug 迴圈:把 thread 貼給它、說一句「修一下」
MCP 真正發光的場景,是「跨系統的高階動作」。Boris 有一個很生活化的工作流 達人:在 .mcp.json 裡接上 Slack 的搜尋與發文能力後,他可以不離開 Claude Code,就完成「從 Slack 看到 bug 回報 → 修好 → 回貼結果」一整圈。
流程大致是這樣:團隊在 Slack 某個 thread 回報了一個 bug,他直接叫 Claude 去把那個 thread 抓回來讀、理解問題、動手改 code,改完再請它把處理摘要貼回原本那個 Slack thread。整個過程他不用在「Slack、終端機、瀏覽器」之間切來切去——這就是 MCP 當「高階能力閘道」的價值:它接的不是某支零散 API,而是「讀對話、發訊息」這種完整能力。
-
動手做
在專案的 .mcp.json 接上 Slack 能力
參照第 9 章接 MCP 的做法,把 Slack 的 MCP server 加進專案根目錄的
.mcp.json。重點是給它「搜尋訊息」和「發文」這兩種能力就夠了。 -
動手做
把 bug thread 交給它、講清楚要它做什麼
直接用講的,例如:「去讀 #bug-report 裡關於登入失敗那個 thread,找出原因、修好,然後把你做了什麼回貼到那個 thread。」
-
動手做
驗收它的修改、確認回貼內容
預期會看到Claude 讀完 thread、改完 code(你照常 review 它的 diff),並把一段「問題出在哪、怎麼修的」摘要貼回原 Slack thread——你全程沒離開終端機。
再往外延伸,你還能接 Sentry(錯誤追蹤)、或讀 Docker 容器日誌的 MCP,讓 Claude 在排查分散式系統問題時,自己去把錯誤紀錄、日誌調出來看。共同點都一樣:MCP 接的是「完整能力」,不是「一支支零碎 API」。
21.4 連官方都承認的天花板:用「寫程式呼叫」取代「一步步呼叫工具」
上一節的 Slack 修 bug 迴圈,是 MCP「直接呼叫工具」這個核心設計用得很道地的例子——工具呼叫本身沒有錯。但這裡要講一件更犀利的事:連「直接呼叫工具」這個 MCP 從一開始就採用的預設做法,Anthropic 自己的工程團隊都在 2026 年的官方工程部落格〈Code execution with MCP〉裡承認它有結構性問題 官方。這不是外部評論者的酸言酸語,是官方自己在自家部落格上寫的,也是整章「反主流」料源裡分量最重的一則。
9.9 教過的 Tool Search,解決的是「工具接得越多,定義塞爆 context」這個問題的一半;官方這篇部落格點名的,是 Tool Search 治不了的另一半——中間結果的問題。傳統工具呼叫的運作方式是:每一步的輸出都要完整流回模型的 context,模型看過一輪、決定下一步要傳什麼參數,那筆資料才又流出去繼續下一個工具呼叫。工具鏈拉得越長,同一批資料在同一個任務裡經過模型的次數就越多,而模型的 context 是照 token 計費的——等於同一筆資料,你可能付了不只一次錢。
官方舉的例子很具體:假設你要它讀一份兩小時會議的完整逐字稿、整理成摘要、再轉寄出去。逐字稿要先完整流進模型一次(讀取這步),模型消化後的內容又要再流出去給下一個工具(轉寄這步)——同一批文字跑了兩趟,官方估計光是這種「重複流經模型」,一次任務就可能多燒上看 5 萬 token,而這些原始文字多數時候模型根本不需要真的「看過一遍」才能處理,只是傳統工具呼叫的設計逼它非看不可。
官方提出的解法,方向跟 Tool Search 一致,但做得更徹底:與其把每個 MCP 伺服器攤成一長串要塞進 context 的工具定義,不如把它們攤成一個檔案系統——每個伺服器變成一個資料夾,底下每支工具是一支可以被程式「引用」呼叫的檔案。Claude 不用先讀懂一整包工具規格才動手,而是直接寫一段程式碼去呼叫這些函式,原始資料留在執行環境裡先做過濾、轉換,只有真正需要的部分才回傳進模型的 context:
官方示範的目錄結構:把 MCP 伺服器攤成程式碼 API
servers/
├── google-drive/
│ ├── getDocument.ts
│ └── index.ts
└── salesforce/
├── updateRecord.ts
└── index.ts
官方拿一個真實情境驗證這套做法:把一份文件從 Google Drive 讀出來、寫進 Salesforce 的一筆紀錄。傳統直接工具呼叫的做法燒了大約 150,000 tokens;換成寫程式呼叫、在執行環境內先過濾好才回傳,同樣的任務只花了約 2,000 tokens——官方標出的節省幅度是 98.7%。這不是零頭最佳化,是量級上的差異。
為什麼這段特別值得放進「反主流」這章
本章前面幾節的「反主流」,講的是達人的個人偏好——你可以不同意 Boris 或 Shankar,兩種做法通常都能動。但這段不一樣:第一個承認「傳統 MCP 工具呼叫有結構性問題」的,是 Anthropic 官方自己的工程部落格,不是外部評論者 官方。意思是:就算你完全照官方文件把 MCP 接好接滿、一步都沒走錯,一旦工具鏈拉長、中間資料變大,你還是會撞上這道天花板——不是你設定錯了,是設計本身在那個規模下就是這樣。
這個方向在 2026 年進一步落實為一個正式功能:Programmatic Tool Calling(PTC,目前是 beta) 官方 實驗。概念是同一條路線的延伸:讓 Claude 寫一段程式碼,一次協調 20 支以上的工具呼叫(還能平行執行),而不是一步一步等模型決定下一步要呼叫誰。官方在一個含 75 支工具的專案管理 agent benchmark 上測到,PTC 讓計費的 input token 用量減少約 38%;更值得注意的是準確率沒有因為「少讓模型每步都看一遍」而下降,反而在部分 benchmark 項目往上升(例如某一項從 25.6% 提升到 28.5%,另一項從 46.5% 提升到 51.2%)。
對你來說,實際的意義是:如果你已經照第 9 章的建議開好 Tool Search、也用了 alwaysLoad 精簡常駐工具,對話還是覺得又貴又慢,那很可能不是哪個環境變數沒調對,而是撞到了這一節講的結構性天花板——這種情況,關注官方 Programmatic Tool Calling 的後續進展,會比繼續在 Tool Search 的旋鈕上打轉更對症。它目前還是 beta,介面和涵蓋範圍可能持續變動,實際可用性與語法請以官方文件當下版本為準。
21.5 團隊共享 .mcp.json 與權限白名單,當成版控資產
前面講的 MCP 和權限設定,如果只活在你自己電腦裡,團隊其他人就享受不到。Boris 與官方的 power-user 建議是 達人 官方:把 .mcp.json 和 /permissions 的白名單一起放進 git,讓全團隊跑同一組 MCP server、共用同一份「預先核准的安全指令清單」。
白名單可以用萬用字元(wildcard)把「一整類安全操作」一次放行,不必每次都跳出來問你。例如:
/permissions 白名單範例(這幾類操作預先放行、不再逐次詢問)
# 允許跑任何 bun run 開頭的指令(測試、建置等)
Bash(bun run *)
# 允許編輯 /docs 底下的任何檔案(文件隨便改)
Edit(/docs/**)
# 允許任何 gh pr 開頭的 GitHub PR 操作
Bash(gh pr *)
這麼做的好處是:把「安全」變成團隊的預設值。新成員 clone 專案下來,就自動帶著「哪些操作安全、可以放手做」的共識,而且這份清單跟著程式碼一起被 review、被版控。
「比 skip-permissions 好」是達人的價值判斷
達人推這套做法時,常說它「優於 --dangerously-skip-permissions(跳過所有權限確認)」達人。這句是價值判斷、不是官方規定,但背後的道理很站得住腳:與其用 --dangerously-skip-permissions 把所有確認一次關掉(那等於把油門焊死),不如用白名單明確列出哪幾類安全,其餘照樣攔下來問。前者圖一時方便、風險敞開;後者多花一點設定,但安全網還在。能用白名單就別整個跳過。
團隊自律不夠、要變成公司政策時:企業治理的兩條路
上面講的是團隊層級的「自律」——大家說好用同一份白名單,但技術上任何人隨時都能自己再加一個伺服器、自己再開一條 allow 規則。如果你的角色要對整個組織負責(IT、平台團隊),自律不夠,需要的是機器強制的公司政策。Claude Code 針對這個情境提供兩條路,擇一使用即可 官方:
| 管控方式 | 效果 | 適用情境 |
|---|---|---|
| managed-mcp.json(獨佔式) | 部署到系統層級路徑,使用者完全無法自行新增或覆蓋任何伺服器,連外掛(plugin)提供的伺服器也一併被排除 | 高度管制環境,MCP 清單要百分之百由 IT 決定,不留使用者自行擴充的空間 |
| allowedMcpServers/deniedMcpServers(政策式白/黑名單) | 使用者仍可以自己 claude mcp add,但只有落在白名單內、且沒被黑名單擋下的伺服器才真正生效 |
想保留一定彈性,但要對「哪些伺服器/指令能連」設一道機器強制的底線 |
兩條路的細節都值得注意:黑名單(deny)的優先權永遠贏過白名單(allow),就算某個伺服器被列在 allow 清單裡,只要同時撞到 deny 規則就是被擋;而白名單本身還有一個容易漏掉的開關——allowManagedMcpServersOnly: true,沒開這個的話,使用者自己在個人 settings.json 裡加的 allow 規則會跟公司政策的白名單合併,等於變相放寬了原本要鎖住的範圍,不是你想像中「公司說了算」的獨佔效果。
拿 serverName 當管控依據,等於沒管
設定 allow/deny 規則時,可以用 serverUrl(支援萬用字元 *)、serverCommand(逐字精準比對,包含每一個參數)、或 serverName 三種欄位比對。這裡有一個容易誤踩的陷阱:serverName不是安全控制——它只是使用者自己在 claude mcp add 時隨手取的標籤,任何人都能把一個惡意伺服器取名叫 github,混過只用名字比對的規則。真的要管控,請用 serverUrl 或 serverCommand,不要只靠 serverName。
managed-mcp.json 範例,與政策白名單的萬用字元用法
// managed-mcp.json(獨佔式,部署到系統層級路徑;不同作業系統路徑不同,
// 例如 Linux/WSL 上大致是 /etc/claude-code/managed-mcp.json 這類位置,
// 確切位置請以官方文件當下版本為準)
{
"mcpServers": {
"github": { "type": "http", "url": "https://api.githubcopilot.com/mcp/" },
"company-internal": {
"type": "stdio",
"command": "/usr/local/bin/company-mcp-server",
"args": ["--config", "/etc/company/mcp-config.json"]
}
}
}
// 政策白名單:允許某內部網域下所有子網域,萬用字元只在 serverUrl 這個欄位適用
{ "serverUrl": "https://*.internal.example.com/*" }
被鎖死的時候,畫面會直接告訴你原因:獨佔式 managed-mcp.json 生效中會看到 Cannot add MCP server: enterprise MCP configuration is active and has exclusive control over MCP servers;撞到黑名單則是 Cannot add MCP server "<名字>": server is explicitly blocked by enterprise policy——這兩句都不是你操作有誤,是公司政策擋下你,要調整得找 IT,不是回頭檢查自己的指令打錯字。
想知道組織裡實際在用哪些 MCP?打開稽核
白名單擋得住「新加的」,擋不住「已經在跑、你不知道」的。想稽核整個組織實際連了哪些伺服器、用了哪些工具(抓出沒申報、卻被員工偷偷接上的「影子 MCP」),開啟 OpenTelemetry 匯出、設定 OTEL_LOG_TOOL_DETAILS=1,工具呼叫事件裡就會帶上伺服器與工具名稱,可以在你的 collector 端聚合統計——這是官方建議的稽核路徑 官方。
21.6 用 Skills 取代舊式自訂指令(別再學過時寫法)
如果你看過比較舊的教學,可能讀到「在 .claude/commands/ 放一個檔案、用 ! 嵌入 shell 指令、用 $ARGUMENTS 接參數」這套自訂斜線指令的寫法。請直接跳過那套——它已經過時了。
舊式 .claude/commands/ 已被 Skills 取代,別再學
2026 年 2 月起,舊的 .claude/commands/ 自訂指令(搭配 ! 行內 bash、$ARGUMENTS 變數)已被 Skills 機制取代。舊寫法目前可能還能跑,但它不是現行做法,網路上很多文章還停在這套,看到請略過。現在要寫自訂能力,一律改用 Skills:在 .claude/skills/<名稱>/SKILL.md 放一個檔案。
Skills 的設計比舊式指令強在哪?最關鍵的是它是雙模式(dual-mode) 官方 達人:你既可以像斜線指令一樣手動叫用(打 /名稱),Claude 也會在判斷相關時自動載入它。它還能在檔案裡嵌入一小段 Bash,把即時資訊(例如目前的 git status)一併帶進來,不必為此多花一次模型呼叫。
一個最小的 SKILL.md 長這樣(放在 .claude/skills/pr-summary/SKILL.md)
# 開頭這段 frontmatter 告訴 Claude 這個 skill 是做什麼的、何時該自動用
---
name: pr-summary
description: 整理目前分支相對 main 的變更,產生一段 PR 摘要
---
# 下面是給 Claude 的指示(純文字,可夾雜步驟)
請比較目前分支與 main 的差異,用台灣繁體中文寫一段適合放進 PR 的摘要,
列出:改了什麼、為什麼、有沒有要 reviewer 特別注意的地方。
Shankar 甚至認為 Skills「可能比 MCP 更重要」達人——因為它把「如何指揮 agent 做一件複雜事」這件事,正式變成一個可以版控、可以分享、可以自動觸發的東西。這個觀點留給你參考;至少現在你知道:要寫自訂能力,找 Skills,不要找舊的 commands。
21.7 Skills 不夠用時:自己動手寫一個 MCP server
上一節的 Skills,補的是「怎麼指揮 Claude 做一件事」這個層次。但回頭看 21.1 那條判準——真正需要 MCP 的,是有狀態、需要維持工作階段的場景。如果你發現自己想要的不是「一份指示文字」,而是「一個會記得狀態、能被多個對話重複呼叫、甚至團隊裡每個人都能連上的服務」,那已經超出 Skills 的範圍,該考慮自己寫一個 MCP server 了。這節帶你認識起步的幾條路,不是要你現在就動手,是讓你知道「原來這條路沒有想像中難走」。
起手式:讓 Claude 自己幫你 scaffold
最快的起點,是官方維護的一個 plugin——mcp-server-dev 官方。它會直接問你這個伺服器的使用情境,自動判斷該生一個 remote HTTP 骨架還是 local stdio 骨架,省下你自己從零讀 SDK 文件的時間:
# 如果 marketplace 還沒加過,先加一次
/plugin marketplace add anthropics/claude-plugins-official
# 安裝 mcp-server-dev plugin
/plugin install mcp-server-dev@claude-plugins-official
# 開始互動式 scaffold,它會問你要做什麼,自動生對應骨架
/mcp-server-dev:build-mcp-server
兩條開發路線:Python 快,TypeScript 穩
骨架生出來之後,實際往下寫,主要有兩條慣用路線,適合的場景不太一樣:
FastMCP(Python)
用 decorator 直接把一個 function 標記成工具,型別註記和 docstring 會被自動轉成 schema,不用手寫規格。適合內部小工具、快速迭代、想法還在變動的階段。
官方 low-level TypeScript SDK
要自己手寫 Zod schema 定義每個工具的輸入輸出格式,換來的是完整型別檢查和乾淨的 Node host 部署路徑。適合要正式對外發佈、給別人連的伺服器。
寫完先別急著接進 Claude Code:用 Inspector 單獨測
伺服器寫好第一件事,不是馬上塞進 .mcp.json。官方提供一個獨立的測試介面,叫 MCP Inspector,讓你把每支工具逐一點開、餵測試參數、看回傳結果對不對:
# 把你的啟動指令接在後面,Inspector 會幫你把它跑起來並開一個測試介面
npx @modelcontextprotocol/inspector <你的執行指令>
這一步的價值在於排除混淆變因:如果你直接把新寫好的伺服器接進 Claude Code 才發現不對勁,你分不清是「我的伺服器邏輯寫錯」還是「Claude Code 這端接線設定錯」;先用 Inspector 單獨驗證邏輯沒問題,之後真的接進 .mcp.json(見 9.2、9.4)出狀況,就能直接把嫌疑鎖定在設定,不用回頭懷疑自己的程式碼。
當作者的兩項特權:協定層級的輸出上限與強制確認
9.9 提過,單一工具的輸出可以用 _meta["anthropic/maxResultSizeChars"] 宣告一個更高的上限。這件事只有伺服器作者做得到——身為使用者,你只能調全域的 MAX_MCP_OUTPUT_TOKENS,會連帶拖累其他工具的 context 預算;但作為作者,你可以精準地只放寬「本來就該回傳大量資料」的那一支工具(像完整資料庫 schema),其餘工具維持保守上限不受影響:
// 在 tools/list 回應裡,針對單一工具宣告更高的輸出上限(最高 500,000 字元)
{
"name": "get_schema",
"description": "Returns the full database schema",
"_meta": { "anthropic/maxResultSizeChars": 200000 }
}
另一項特權更關鍵:如果你的工具會做危險、難以復原的操作(撤銷授權、刪除資源),可以在協定層級標記 _meta["anthropic/requiresUserInteraction"]: true 官方。這個標記的強制力比你想像的高——即使呼叫端開了 auto 模式或 --dangerously-skip-permissions,這支工具每次被呼叫都還是會強制跳出許可提示,而且不提供「不再詢問」的選項;在完全不允許互動的 dontAsk 模式下,則是直接拒絕呼叫,而不是靜靜放行。換句話說,「這個操作務必每次都要人看過」這件事,你可以寫進協定本身去保證,不必只在文件裡寫一句「請小心使用」然後祈禱使用者真的會看。
// 強制每次呼叫都要人核可,即使呼叫端開了自動放行也一樣
{
"name": "grant_access",
"description": "Requests access to a protected resource",
"_meta": { "anthropic/requiresUserInteraction": true }
}
不確定值不值得自己寫?回頭套 21.1 的判準
寫一個 MCP server 是本章「客製化」裡投入最重的一條路,動手前先套一次 21.1 的判準:要接的東西是不是真的有狀態?能不能用現成 CLI(21.2)或一個 Skill(上一節)打發?三題都問過,還是覺得非寫不可,再開始不遲。
21.8 MCP 的供應鏈風險:接的、寫的,安全都是你的責任
9.7 提醒過,惡意伺服器可能在工具回傳的內容裡偷藏指令,誘導 Claude 做出不該做的事。這節把這件事再往下挖一層——這不只是「小心來路不明的伺服器」這種個人習慣層次的提醒,業界已經把它整理成一套有名字的攻擊手法,而且部分弱點是設計層級的,不是哪個伺服器的實作沒寫好。你自己動手寫伺服器(上一節)之後,這些也變成你自己的責任。
工具下毒:模型看得到、你看不到的那一段
工具下毒(tool poisoning)指的是攻擊者把惡意指令藏在工具描述或工具回傳結果裡——這些欄位模型會讀進 context 當成可信資料處理,但你在對話畫面上未必看得到完整內容。跟一般的提示注入(prompt injection)比,它多一個特別討厭的性質:持久性。一般提示注入通常來自你這次餵給它的某個網頁或檔案,下次不碰那個來源就沒事;工具下毒的毒描述卻是包在一個套件、一份設定檔、或一個遠端伺服器裡,只要你連著這個伺服器,每一次呼叫都中招,直到有人發現、通報、你把它移除為止。資安圈在 2026 年一次針對這個手法的揭露報告中,估計波及的 MCP 服務規模上看 20 萬個,橫跨 IDE 外掛、企業內部工具和雲端服務 社群。這也是 9.6 教的「別人專案帶來的工具,先卡在等你核准」那道關卡格外重要的原因——不核准的當下,你擋下的可能就是這種長期潛伏的東西。
想知道原理:stdio 指令注入,問題到底出在哪
資安研究團隊 OX Security 在 2026 年的一份揭露報告指出一個更根本的問題:官方 MCP SDK(不分 Python、TypeScript、Java、Rust,全語言通用)在 stdio 這種傳輸方式下,存在一個架構層級的缺陷——設定檔裡 command 欄位填的值,不管它最後有沒有真的啟動一個合法的 MCP 伺服器,都會被當成系統指令直接執行。這不是某個實作團隊沒寫好防呆的 bug,是 stdio 這個傳輸方式打從設計上就是這麼運作的。Anthropic 對這份揭露的公開回應是「這是預期行為」,沒有打算修改協定本身 官方。實務上的意思是:防線得靠下游各自在應用層自己補——例如 LiteLLM 在修補一個相關漏洞(CVE-2026-30623)時,做法是把 command 欄位限制在一組固定白名單裡(npx、uvx、python、python3、node、docker、deno),其餘一律拒絕。如果你自己的環境會動態組出 .mcp.json 的內容(例如讓使用者貼上一段設定就自動加入),這條白名單思路值得直接抄。
permission 攔不到「整類」工具,改用 hook
9.7 教過權限規則要精準寫成 mcp__伺服器名__工具名,allow 清單不支援萬用字元。這裡有一個官方文件不太強調、但實測確認可行的差異化用法:PreToolUse hook 的 matcher 支援 regex,而 permission 規則不行。想一次攔下「這個伺服器所有寫入類的工具」,permission 只能一個個列名字很累人,但 hook 用一條 regex 就搞定:
用 hook matcher 攔截整類 MCP 工具(PreToolUse,可回傳 deny)
// 攔下任何伺服器裡「工具名含 write」的呼叫,一條 regex 涵蓋全部,
// 不用一個個列 mcp__github__write_file、mcp__notion__write_page……
{ "matcher": "mcp__.*__write.*" }
// 或鎖定單一伺服器的全部工具
{ "matcher": "mcp__memory__.*" }
這招對「一整類危險操作」特別好用,例如你想確保不管接了幾個雲端硬碟類的伺服器,任何名字帶 delete 的工具一律先過一手你寫的檢查腳本再放行。Hooks 的完整寫法留給第 19 章,這裡先記住這個「permission 管不到、hook 管得到」的分工就好。
自架伺服器的最低限度防護:把它關進容器
如果上一節你真的動手寫了、或部署了一個自架的 MCP 伺服器,它多半會處理真實的憑證、碰到真實的資料。資安社群針對「MCP 伺服器該怎麼跑」整理出一套偏保守但實用的最低設定組合 社群:
- 拿掉所有 Linux capability(
cap_drop: ALL)、檔案系統設唯讀(read_only: true)、禁止提權(no-new-privileges)——伺服器需要寫的路徑,額外掛一個可寫的 volume 就好,不要讓整個容器都能寫。 - 預設不連外網(
internal: true),只讓它連同一個網段裡真正需要的容器,而不是預設能連整個網際網路。 - 憑證走 Docker Secrets 或 vault,不要在 image 的 build 階段存取——build 階段碰過的任何變數,都可能被烙進 image 的某一層,之後誰都能從 image 裡把它挖出來,事後才想到要刪已經沒用。
- 有餘力的話,把容器 runtime 從原生
runc換成 gVisor 或 Kata Containers——一般容器仍然共用 host 的 kernel,對「會動態產生、執行外部輸入內容」的 agent 場景來說,這層隔離偏薄弱。
伺服器悄悄掛掉:stdio 不會自動重連
最後一個容易被忽略的細節,跟惡意行為無關,是可靠度本身的落差:stdio 伺服器的行程如果中途掛掉,Claude Code不會自動幫你重連;這點跟 HTTP/SSE 不一樣——那兩種連線斷了會自動用指數型退避重試(從 1 秒開始,每次間隔倍增,最多五次)。stdio 掛掉多半要等到你下一次呼叫失敗,才會發現剛剛那個工具其實已經不在線上,而且錯誤訊息未必指得出真正原因。這種時候,回頭用 9.8 教的那招——把設定檔裡的指令原封不動貼到終端機手動跑一次——通常最快能看到底層真正的錯誤(缺 Node.js、缺瀏覽器之類),不用對著一句語焉不詳的「disconnected」瞎猜。
這節的重點不是要你恐慌,是要你知道責任在誰
接一個知名度高、star 數多的官方或大廠伺服器,風險遠低於接一個來路不明、你在某個論壇貼文裡看到連結就手動貼進 .mcp.json 的伺服器。9.6 教的「陌生專案先別核准」是第一道、也是最重要的一道防線。這節講的下毒手法、指令注入設計缺陷、容器隔離,是給你在接得更深、自己動手寫伺服器、或要對團隊的 MCP 環境負責時,知道再往下一層可以做什麼——不是要你從此不敢接任何 MCP。
21.9 自訂輸出風格:讓它「邊做邊解釋」或「陪你學」
Claude Code 預設的回話風格是「精簡、直接做事」。但有些情境你會希望它換個講法。官方提供了幾種內建的輸出風格(output style) 官方,用 /config 就能切換,它會重塑 Claude 的語氣和側重點:
| 風格 | 它會變成怎樣 | 什麼時候好用 |
|---|---|---|
| Explanatory(解說型) | 一邊做事,一邊解釋它在用的框架、設計模式、為什麼這樣選 | 你在摸一個陌生的 codebase,想順便搞懂它的脈絡 |
| Learning(學習型) | 用協作、帶你一起想的方式,像在陪你學 | 你想透過實作來學東西,而不只是要它把結果丟給你 |
Boris 自己也用這類設定來調整 Claude 的講話方式 達人。切換很簡單,打開 /config 找到 output style 選項換一下即可,不影響它實際做事的能力,只影響「它怎麼跟你說」。
「寫一個自己的風格檔」——這部分先別當定論
坊間有些說法是「你還可以寫一個完全自訂的輸出風格檔」。內建的 Explanatory/Learning 是官方明確有的 官方;但「自己寫一個風格檔」這件事,官方文件目前沒有明確列出,引用前請先以你當下的 /config 選項和官方文件為準,別把它當成一定做得到的功能寫進你的流程。
21.10 把終端機調成你順手的樣子
最後是一組「生活品質」客製。Boris 會花點心思把終端機環境調順 達人,這些都是錦上添花、不影響核心功能,但用久了很有感:
- 狀態列(
/statusline):自訂底部那條狀態列要顯示什麼——目前用哪個模型、在哪個 git 分支、context 用了多少、花了多少錢,一眼掌握。 - 提示色(
/color):給每個 session 設不同的提示顏色。當你同時開好幾個 Claude(這正是第 18 章平行 session 的場景),不同顏色幫你一眼分辨「現在這個是哪個」。 - 快捷鍵(
/keybindings):重新對應鍵盤快捷鍵,設定檔在~/.claude/keybindings.json,改了會熱重載(不用重開就生效)。 - 等待動畫的字(spinner verb):連 Claude 思考時顯示的那個動詞都能換——Boris 用的是星艦迷航(Star Trek)主題的詞,純粹好玩。
一個實用建議:把 settings.json 也放進 git,這樣團隊成員能共用同一套終端機設定,環境一致、少踩「在我這台就好好的」那種坑。
先別急著全部改,挑一兩個最有感的就好
這些客製不是必做功課。如果你還在熟悉 Claude Code,建議先放著用預設;等你開始同時開多個 session,再回來設「提示色」——那時你會立刻體會到它的價值。其餘的等你覺得「這裡如果能改就好了」再動手,不必一次調到滿。
21.11 極簡指令紀律:只留 /catchup 和 /pr 的哲學
收尾講一個跟「客製」唱反調、但很有意思的觀點。前面教你怎麼接 MCP、寫 Skills、調終端機,Shankar 卻刻意反過來——他主張自訂指令越少越好 達人。他自己只留兩個:
/catchup:在/clear清空對話之後,讀一下有變動的 git 檔案,幫 Claude 快速「重新進入狀況」。/pr:準備一個 pull request。
他的理由很犀利:當你發現自己越來越依賴一堆自訂指令,這往往是個「氣味」(smell)——代表你的 CLAUDE.md(也就是專案的「憲法」)寫得不夠好。與其不斷加各種「魔法指令」去補洞,不如回頭把 CLAUDE.md 這份根本規則寫對。
這條跟本章前半「教你客製」看似矛盾,其實是一體兩面:客製是工具,紀律是品味。能用 Skills 解決的就寫 Skill,但別把「該寫進 CLAUDE.md 的通則」拆成十幾個零碎指令來記。把它當成 Shankar 的個人哲學參考 達人——不是要你真的砍到只剩兩個,而是提醒你:加指令之前,先想想是不是 CLAUDE.md 沒寫好。
21.12 小結
這一章的主軸是「反主流」:別一頭熱把什麼都接成 MCP,達人的做法更克制、也更省;而且這股克制不只來自具名達人的個人偏好,連 Anthropic 官方自己都在承認傳統工具呼叫的結構性天花板。把它收成幾個帶得走的重點:
- MCP 當閘道,不當鏡子:無狀態工具(Jira/AWS/GitHub)走 CLI,有狀態的(如 Playwright)才留 MCP;MCP 最值得用在「倒原始資料」和「高風險把關動作」達人。
- CLI 優先:能叫現成命令列工具(
bq、gh)就別接 MCP,更省 token、更可靠——Boris「六個多月沒寫 SQL」就靠這招 達人。 - 用 MCP 做高階動作:像「Slack 看 bug → 修 → 回貼」這種跨系統迴圈,是 MCP 真正發光的場景 達人。
- 連官方都承認工具呼叫有天花板:中間結果重複流經模型會多燒 token,官方的解法是把 MCP 攤成程式碼 API 讓 Claude 寫程式呼叫,Programmatic Tool Calling(beta)是這條路線目前的正式產品化 官方 實驗。
- 共享 .mcp.json + 白名單,重要就升級成公司政策:團隊層級把 MCP 設定和
/permissions白名單放進 git;真的要機器強制,才進一步用managed-mcp.json或 allow/deny policy,記得serverName不是安全控制 達人 官方。 - Skills 取代舊指令:寫自訂能力用
.claude/skills/<名稱>/SKILL.md(雙模式:手動/名稱+ 自動觸發),別再學過時的.claude/commands/+!+$ARGUMENTS官方。 - Skills 還不夠時,自己寫一個 MCP server:官方
mcp-server-devplugin 起手、FastMCP 或 TypeScript SDK 動手、Inspector 單獨測;身為作者,你還能用_meta宣告更高輸出上限或強制人工核可 官方。 - 安全是接的人和寫的人共同的責任:工具下毒、stdio 指令注入是設計層級的風險,防線要靠 hook 攔整類工具、容器沙盒化自己補上 社群。
- 客製有節制:輸出風格、狀態列、提示色、快捷鍵都能調,但挑最有感的就好;指令越加越多,往往是 CLAUDE.md 沒寫好的氣味 達人。
下一章談「成本、模型路由與長跑自動化」——當你讓 Claude 跑得久、跑得多,怎麼用 /effort 控預算、把輕量活路由到便宜模型,還不燒爆荷包。
動手試試
- 盤點你(或你團隊)目前接的 MCP,逐一問「這個有沒有現成 CLI 可以取代」。挑一個無狀態的(例如 GitHub 操作),改試用
gh讓 Claude 直接跑,比較看看順不順。 - 把專案的
.mcp.json和/permissions白名單放進 git,用 wildcard 放行一兩類你最常用的安全操作(如Bash(git status)),體會「不再被逐次詢問」的順暢。 - 把你手邊一個常用的小流程(例如「整理 PR 摘要」),寫成一個
.claude/skills/<名稱>/SKILL.md,試試手動打/名稱叫用它。 - 打開
/config切到 Explanatory 風格,叫 Claude 解釋你專案裡某段你看不太懂的程式碼,感受一下「邊做邊解釋」跟預設風格的差別。 - 找一個你手上工具鏈最長、中間資料量最大的流程(例如「讀一份長文件、整理、再送出去」),對照 21.4 的說法算算看:這份資料在傳統工具呼叫裡,實際上流經模型幾次?光想這一步,通常就能感受到「寫程式呼叫」省的是什麼。
- 如果你要對團隊或組織的 MCP 環境負責,找一次時間盤點現有設定:有沒有來路不明的伺服器、
.mcp.json有沒有版控、要不要導入managed-mcp.json或白/黑名單。
總提醒:本章料源比前面幾章雜,別照單全收
這章標 達人 的主張特別多(「少接 MCP」「CLI 優先」「只留兩個自訂指令」等),它們是 Boris Cherny、Shrivu Shankar 等具名實踐者的個人經驗與取向,非 Anthropic 官方硬性規範——兩種做法通常都能動,達人只是分享「實測下來這樣更順」。「比 skip-permissions 好」「Skills 可能比 MCP 更重要」這類是價值判斷,採用時依你的專案斟酌。「連官方都承認的天花板」那段雖然出自 Anthropic 官方部落格,但 Programmatic Tool Calling 目前仍是 實驗 beta 功能,介面和涵蓋範圍可能持續變動;供應鏈風險那節引用的是資安研究社群的揭露報告與觀察 社群,具體數字是特定報告的估計值,不是恆常不變的統計。另外「自己寫輸出風格檔」官方未明列,引用前以你當下的 /config 與官方文件為最終真相。