博客 • MCP
升級到 Claude Code 2.1.305 之後,managed-mcp.json 裏只在 env 寫了變量的 stdio 服務器又加載了,deniedMcpServers 要怎麼寫?
Claude Code 2.1.305 的更新說明只有一句,但升級之後會看見服務器自己出現:allowedMcpServers 不再過濾 managed-mcp.json 裏那些只在 env 使用 ${VAR} 的 stdio 服務器。允許名單原來沒寫它,升級後它會加載。要繼續停住,改的是 deniedMcpServers,不是把允許名單再抄一遍。
誰負責 MCP 的權限,寫在MCP 安全那篇。OAuth 憑證交給錯誤授權服務器時日誌停在哪一步,寫在上一篇 OAuth 文。本篇只寫 2.1.305 這一處:允許名單不再擋住只在 env 使用變量的 stdio 服務器,拒絕名單的 JSON 要和命令完全一致。
- 對象是 managed-mcp.json 裏的 stdio 服務器,而且 ${VAR} 只出現在 env。
- 2.1.305 起,允許名單不再把它濾掉。允許名單裏沒有它,升級後會加載。
- 要繼續停住,deniedMcpServers 裏的 serverCommand 必須是命令加參數的數組,順序也要一致。
- 空的拒絕名單形狀合法,但什麼也不擋。serverName 不是安全控制。
直接結論:只在 env 使用 ${VAR} 的 stdio 服務器,2.1.305 之後不再靠允許名單停住。拒絕名單裏寫下與 command 和 args 完全相同的 serverCommand。
2.1.305 改了允許名單的哪一處?
更新說明寫的是:allowedMcpServers 不再過濾 managed-mcp.json 裏那些只在 env 使用 ${VAR} 的 stdio 服務器。允許名單原來沒寫上的那一臺,升級後會加載。要繼續停住,用 deniedMcpServers。原文在Claude Code 更新說明。
組織級 MCP 文檔仍寫着更早的規則:managed-mcp.json 裏的服務器本來跳過允許名單,但定義裏只要有 ${VAR},就還要過允許名單。2.1.259 之前,只要設了允許名單,每臺託管服務器都要過。2.1.305 把其中一窄條拿掉了:stdio,而且變量只在 env。更新說明沒有說命令、參數、URL 或 headers 裏的 ${VAR} 也免檢。文檔在組織級 MCP 文檔。
怎樣看出變量只寫在 env?
文檔裏的 stdio 例子有 type、command、args 和 env。密鑰不要寫進 env 的值裏。每人自己的憑證用 ${VAR} 從該用戶的環境讀取。下面這份把變量只放在 env,命令和參數都是字面量。這就是 2.1.305 說的那一種。命令路徑是佔位符,不是要安裝的包。
{
"mcpServers": {
"notes": {
"type": "stdio",
"command": "/usr/local/bin/notes-server",
"args": ["--stdio"],
"env": {
"NOTES_TOKEN": "${NOTES_TOKEN}"
}
}
}
}
如果 ${VAR} 寫在 command、args、URL 或 headers 裏,更新說明沒有把它們算進這次放行。那種服務器仍按文檔裏的允許名單檢查來對待,直到更新說明自己改口。本篇的拒絕名單例子只覆蓋 env 這一種。
deniedMcpServers 要和哪一條命令一致?
文檔裏,serverCommand 匹配的是啓動 stdio 服務器的命令和參數,而且要完全一致。上面的 command 加上 args,就是這一條數組。少一個參數、多一個參數、或把順序換掉,都對不上。serverName 只是標籤。文檔寫明,允許名單或拒絕名單裏的 serverName 不是安全控制,因爲用戶可以給任何服務器起同一個名字。
{
"deniedMcpServers": [
{
"serverCommand": ["/usr/local/bin/notes-server", "--stdio"]
}
]
}
拒絕名單對託管服務器同樣生效,用戶自己的 deniedMcpServers 也會並進來,所以用戶可以只爲自己擋住一臺託管服務器。允許名單是空數組時,文檔說除了跳過允許名單檢查的服務器以外,都不允許加載。2.1.305 之後,只在 env 使用變量的這臺 stdio 服務器正是跳過的那一種,空的允許名單留不住它。
這份拒絕名單用什麼 Schema 卡住形狀?
用 JSON Schema 卡住類型。deniedMcpServers 必須是數組。每一項的 serverCommand 必須是字符串數組。空數組形狀仍然合法。它不證明這臺服務器會被停住。停住它的是應用規則:數組要和 command 加 args 完全相同。
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["deniedMcpServers"],
"properties": {
"deniedMcpServers": {
"type": "array",
"items": {
"type": "object",
"required": ["serverCommand"],
"properties": {
"serverCommand": {
"type": "array",
"items": { "type": "string" }
}
}
}
}
}
}
| 看到的記錄 | Schema | 要停住這臺服務器時 |
|---|---|---|
| serverCommand 寫成一整段字符串 | 失敗。這裏要字符串數組。 | 先拆成命令和參數兩個字符串。 |
| 參數寫成數字 | 失敗。數組裏的每一項都是字符串。 | 改成和 args 裏相同的字符串。 |
| deniedMcpServers 是空數組,形狀正確 | 通過。空數組仍是數組。 | 補上與 command 和 args 相同的 serverCommand。空名單什麼也不擋。 |
加載前的三步,哪一步真正停住它?
文檔把加載前的檢查寫成三步,並且在用戶於 /mcp 裏重新連接或重新打開時再跑一遍。進程內的 sdk 類型跳過這三步。拒絕名單命中之後,沒有東西能推翻。
| 步驟 | 這臺 env-only 服務器 |
|---|---|
| 合併各層名單 | 各設置層的允許名單和拒絕名單合成一份。allowManagedMcpServersOnly 爲 true 時,只留託管來源的允許名單。拒絕名單始終從每一層合併。 |
| 先查拒絕名單 | serverCommand 完全一致就停住。這一步之後,允許名單不能把它放回來。 |
| 再查允許名單 | 2.1.305 之後,這臺只在 env 使用變量的 stdio 服務器跳過這一步。允許名單裏沒有它,也不再因此停住。 |
在 JSONNote 裏怎樣覈對這份拒絕名單?
- 1選對操作
打開 JSON Schema。操作選「用 Schema 校驗」。第一欄貼拒絕名單,第二欄貼上面的 Schema。serverCommand 必須是字符串數組。
- 2撞四條壞數據
serverCommand 寫成一整段字符串,或刪掉 deniedMcpServers,或把參數寫成數字,或把 deniedMcpServers 寫成對象。這四條都會失敗。上面那份拒絕名單會通過。通過只說明形狀對。
- 3對比空名單和那條命令
打開 JSON 對比 Diff。第一欄貼上面的拒絕名單。第二欄把 deniedMcpServers 改成空數組。兩份都能通過 Schema。擋住這臺服務器的是那條 serverCommand,不是空數組。
常見問題
允許名單裏補上同一條命令,能不能代替拒絕名單?
不能。2.1.305 之後,這臺服務器跳過允許名單。允許名單裏有沒有這條命令,都不再決定它加載不加載。要停住,必須有一條命中的拒絕名單。拒絕名單命中之後,允許名單不能把它放回來。
用 serverName 寫成 notes,夠不夠?
不夠。文檔寫明,serverName 是用戶起的標籤,通配符也不展開。用戶可以把任意服務器叫成同一個名字。要停住這臺 stdio 服務器,用 serverCommand,並且和 command、args 完全一致。
命令裏也有 ${VAR},這次還會加載嗎?
更新說明沒有把這種情況算進放行。它只點名 stdio、而且變量只在 env。命令、參數、URL 或 headers 裏還有 ${VAR} 時,仍按組織級文檔裏的允許名單檢查來對待。那種服務器如果允許名單對不上,不要假設 2.1.305 會讓它加載。
用戶自己的設置能不能擋住組織下發的這一臺?
可以。文檔寫明,deniedMcpServers 對 managed-mcp.json 裏的服務器也生效,用戶自己的拒絕名單會並進來。所以用戶可以只爲自己擋住一臺。允許名單不能代替這一步。
env 裏要寫下 token 的值嗎?
不要。文檔寫明,這臺機器上的任何人都能讀 managed-mcp.json,所以不要把 API key 或其他憑證寫進 env。每人自己的憑證用 ${VAR},從該用戶的環境讀取。拒絕名單裏也不要出現 token,只保留命令和參數。
總結
2.1.305 只放行一種:managed-mcp.json 裏的 stdio 服務器,${VAR} 只在 env。允許名單不再把它濾掉。命令、參數、URL 或 headers 裏的變量,更新說明沒有一起放行。
直接結論:要繼續停住,就在 deniedMcpServers 裏寫與 command 和 args 完全相同的 serverCommand;空的允許名單留不住它;serverName 不夠;env 裏不寫 token 的值。
下一步:用 Schema 卡住拒絕名單的數組,再用 Diff 看出空數組和那條 serverCommand 的差別。