博客 • 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 要和命令完全一致。

直接結論:只在 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 說的那一種。命令路徑是佔位符,不是要安裝的包。

變量只在 env。2.1.305 之後,允許名單不再把這臺 stdio 服務器濾掉。
{
  "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 不是安全控制,因爲用戶可以給任何服務器起同一個名字。

把這臺服務器停住的拒絕名單。數組和上面的 command、args 相同。
{
  "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. 1
    選對操作

    打開 JSON Schema。操作選「用 Schema 校驗」。第一欄貼拒絕名單,第二欄貼上面的 Schema。serverCommand 必須是字符串數組。

  2. 2
    撞四條壞數據

    serverCommand 寫成一整段字符串,或刪掉 deniedMcpServers,或把參數寫成數字,或把 deniedMcpServers 寫成對象。這四條都會失敗。上面那份拒絕名單會通過。通過只說明形狀對。

  3. 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 的差別。

返回博客