· 12 分鐘閱讀 · MCP Claude Code AI 工作流 教學
MCP 教學:MCP server 怎麼設定、.mcp.json 放哪、憑證怎麼不進 repo
MCP 繁體中文教學:tools、resources、prompts 是什麼,stdio 與 HTTP 的差別,Claude Code 的 scope 與核准,其他工具的設定位置,以及讓憑證不進 repo 的啟動腳本做法。
MCP(Model Context Protocol)是讓 AI 應用程式連接外部工具和資料的開放標準。一個 MCP server 可以提供 tools(AI 能呼叫的函式)、resources(唯讀資料)和 prompts(範本);跑在本機的 server 走 stdio,遠端的走 Streamable HTTP。設定的方法是在你用的 AI 工具裡登記 server:Claude Code 用 claude mcp add,或在專案根目錄放一個 .mcp.json。最該先想清楚的是憑證放哪裡。我的做法是 .mcp.json 只寫一行啟動指令,金鑰放在 repo 外、權限 600 的私有檔,由啟動腳本在執行時讀進來。
規格與指令以 MCP 官方文件和各工具官方文件為準,查核日 2026 年 11 月 3 日。
MCP 的三個角色:host、client、server
官方架構文件把參與者分成三種:
- Host:AI 應用程式本身,例如 Claude Code、VS Code、Cursor。
- Client:host 為每個 server 建立的一條連線。連三個 server,就有三個 client。
- Server:提供 context 的程式。可以跑在你的電腦上,也可以是別人架的遠端服務。
訊息格式是 JSON-RPC 2.0。MCP 只規定 server 和 client 怎麼交換資料,模型怎麼使用這些資料由各家應用程式決定。
Server 提供的三種東西
| 類型 | 用途 | 誰決定使用 |
|---|---|---|
| Tools | 可以執行的函式:查資料庫、呼叫 API、改檔案 | 模型 |
| Resources | 唯讀的資料來源,每筆有自己的 URI,例如 file:///path/to/doc.md |
應用程式 |
| Prompts | 預先寫好的指令範本,通常以斜線指令出現 | 使用者 |
這張表依照官方的 server concepts 整理。Tools 由模型自己決定什麼時候呼叫,官方也列了應用程式可以提供的控制方式:逐次核准、預先授權安全的操作、顯示每次呼叫的紀錄。
用 coding agent 時最常碰到的是 tools。我的網站 repo 接了一個讀 SEO 數據的 MCP server,用法多半是查某一頁、某個關鍵字的數字。
兩種傳輸方式:stdio 和 Streamable HTTP
| stdio | Streamable HTTP | |
|---|---|---|
| 誰啟動 server | client 把 server 當子程序啟動 | server 是獨立執行的程序,通常在遠端 |
| 通訊方式 | stdin/stdout,一行一則 JSON-RPC 訊息 | 每則訊息是一個 HTTP POST,回應可以是 JSON 或 SSE 串流 |
| 常見用途 | 本機工具、自己寫的小 server | SaaS 服務的官方 MCP |
| 驗證 | 通常由環境變數或本機檔案提供憑證 | bearer token、API key、自訂 header;官方建議用 OAuth |
stdio 有一條規則一定要記住:規格寫明 server 的 stdout 只能出現合法的 MCP 訊息,log 一律寫到 stderr。自己寫 server 時,在 stdout 多印一行除錯訊息,連線通常就會壞掉。
我用的那個 server 走 stdio:Claude Code 啟動一個 Python 程式,程式讀 stdin、寫 stdout,再用 HTTPS 去問資料服務。對 Claude Code 來說它是本機 server,資料其實在遠端。
在 Claude Code 登記 server
Claude Code 的 MCP 文件給了兩種寫法:
# 遠端(HTTP)
claude mcp add --transport http <name> <url>
# 本機(stdio):-- 後面的東西原封不動交給 server
claude mcp add [options] <name> -- <command> [args...]
-- 很重要。它把 Claude Code 自己的選項(--transport、--env、--scope)和啟動 server 的指令分開;少了它,server 的參數會被當成 Claude Code 的參數解析。
三種 scope
| Scope | 在哪裡生效 | 會跟團隊共用嗎 | 存在哪 |
|---|---|---|---|
| local(預設) | 只有目前這個專案 | 不會 | ~/.claude.json |
| project | 只有目前這個專案 | 會,跟著版本控制 | 專案根目錄的 .mcp.json |
| user | 你所有的專案 | 不會 | ~/.claude.json |
同一個名稱在多個 scope 都有定義時,Claude Code 只連一次,用優先順序最高的那份:local、project、user,再來是 plugin 和 claude.ai connectors。欄位不會跨 scope 合併。
我的 repo 剛好用到這條規則。以下設定的 server 名稱、檔名和變數名都換成了範例名稱,結構與我 repo 裡的檔案相同。.mcp.json 是給所有人用的版本:
{
"mcpServers": {
"my-data": { "command": "bash", "args": ["tools/my-data-mcp.sh", "mcp"] }
}
}
我本機另外有一個同名的 local 設定,多帶一個環境變數,指向這個站自己的憑證目錄。claude mcp get 看得出實際生效的是哪一份(路徑我遮掉了):
my-data:
Scope: Local config (private to you in this project)
Status: ✔ Connected
Type: stdio
Command: bash
Args: tools/my-data-mcp.sh mcp
Environment:
MY_DATA_CONFIG_DIR=<這個站的憑證目錄>
這份 local 設定只存在我的 ~/.claude.json,不會進 repo。
專案 server 要先核准
.mcp.json 會跟著 repo 走,任何人 clone 下來都會帶著它。所以 Claude Code 在互動模式下,第一次用到專案 scope 的 server 前會先問你要不要啟用;想重來,跑 claude mcp reset-project-choices。剛 clone 的 repo 也不能在自己的設定裡替自己核准:放在 repo 裡、已經 commit 的 .claude/settings.json 就算寫了 enableAllProjectMcpServers 或 enabledMcpjsonServers,在還沒被信任的資料夾裡也不會生效。
要注意的是非互動模式。官方文件寫明,沒辦法跳出提示時,專案 scope 的 server 會直接載入。在 CI 或腳本裡跑別人的 repo,要擋掉某個 server,可以用 disabledMcpjsonServers,或用 --strict-mcp-config 只載入你用 --mcp-config 指定的設定。
我本機的 .claude/settings.local.json 不進 git,Claude Code 第一次寫入這個檔案時,會自動把它加進全域的 git excludes。裡面我同時開了 enableAllProjectMcpServers 和 enabledMcpjsonServers: ["my-data"]。前者會讓這個 repo 之後新增到 .mcp.json 的 server 一律自動啟用,範圍比需要的大;只想核准點名的 server,留後者就夠了。
其他 AI 工具怎麼設定
MCP 是開放協定,官方介紹頁列出的支援者包含 Claude、ChatGPT、VS Code、Cursor 等等。概念相同,設定檔的位置和格式各家不一樣:
| 工具 | 專案設定 | 全域設定 | 格式重點 |
|---|---|---|---|
| Claude Code | .mcp.json |
~/.claude.json(user scope) |
JSON,mcpServers |
| Codex | .codex/config.toml(只限受信任的專案) |
~/.codex/config.toml |
TOML,[mcp_servers.<名稱>] |
| Cursor | .cursor/mcp.json |
~/.cursor/mcp.json |
JSON,mcpServers,stdio 可用 envFile |
| VS Code | 根目錄 .mcp.json(開啟實驗設定 chat.mcp.workspaceRootConfig.enabled 後才出現在導引流程);舊的 .vscode/mcp.json 在導引流程裡已標成 deprecated |
~/.copilot/mcp-config.json(Copilot Global;有設 $COPILOT_HOME 時放在該目錄) |
可攜格式用 mcpServers,VS Code 自己的格式用 servers |
同一個啟動腳本換到 Codex,依官方格式大概是這樣。這段我沒有實際跑過,cwd 要換成你 repo 的路徑:
[mcp_servers.my-data]
command = "bash"
args = ["tools/my-data-mcp.sh", "mcp"]
cwd = "/path/to/your/repo"
啟動腳本的好處在這裡:每個工具的設定檔格式不同,但它們都只需要知道「執行哪個指令」,憑證怎麼讀全部留在腳本裡。
憑證不進 repo:啟動腳本的做法
把 API key 直接寫進 .mcp.json 是最快的做法,也是常見的外洩途徑之一,因為這個檔案本來就是要 commit 的。Claude Code 支援在 .mcp.json 裡用 ${VAR} 和 ${VAR:-default} 引用環境變數,金鑰就不必寫死在檔案裡,但值還是得先放進你的 shell 環境,所有從這個 shell 開出來的程式都看得到它。
我的 repo 用另一種寫法:.mcp.json 只告訴 Claude Code「執行 bash tools/my-data-mcp.sh mcp」,啟動腳本自己去私有目錄讀憑證,只交給它啟動的那一個程序。腳本由資料服務的維護者提供,核心是這幾行(節錄,名稱已改成範例):
d="${MY_DATA_CONFIG_DIR:-$HOME/.config/my-data}"
for f in api.env token; do
[ -r "$d/$f" ] || { echo "my-data: 缺 $d/$f" >&2; exit 1; }
# Linux(GNU stat)與 macOS(BSD stat)都支援
case "$(stat -c %a "$d/$f" 2>/dev/null || stat -f %Lp "$d/$f")" in
600|400) ;;
*) echo "my-data: $d/$f 的權限必須是 600 或 400" >&2; exit 1;;
esac
done
# …讀出網址、公開金鑰和本站憑證,檢查三者都不是空的…
export MY_DATA_URL MY_DATA_KEY MY_DATA_TOKEN
exec python3 "$(cd "$(dirname "$0")" && pwd)/server.py" "$@"
- 憑證在 repo 外。 預設目錄是
~/.config/my-data/,可以用環境變數換成別的目錄。我就是用前面那個 local scope 設定,讓這個站指向自己的子目錄。 - 檢查檔案權限。 兩個檔案只要不是 600 或 400(只有擁有者能讀),就拒絕執行。
stat -c是 Linux 的寫法,失敗就改用 macOS 的stat -f,同一支腳本兩邊都能跑。 - 錯誤寫到 stderr。 腳本只說缺哪個檔、權限哪裡不對,不印出任何值。這也符合 stdio 的規則:stdout 留給 MCP 訊息。
- 只交給 server 程序。 變數在腳本內 export,再用
exec把程序換成 Python server。憑證只進入這個程序的環境,不會留在你的 shell 裡。 - 憑證本身權限很小。 這把憑證只能讀這一個站的資料。就算外洩,影響範圍也有限。
我用一個放假值的測試目錄跑過,權限設成 644 時會被擋下:
$ chmod 644 <測試目錄>/api.env
$ bash tools/my-data-mcp.sh mcp
my-data: <測試目錄>/api.env 的權限必須是 600 或 400
自己做的話,步驟很短:
mkdir -p ~/.config/my-service
chmod 700 ~/.config/my-service
# 用編輯器把金鑰寫進檔案,不要用 echo,免得留在 shell history
chmod 600 ~/.config/my-service/token
再寫一個像上面那樣的啟動腳本,.mcp.json 只登記 bash tools/<你的腳本>.sh。repo 裡可以留一份說明,寫清楚需要哪些檔案、放在哪裡、權限多少,但不要放值。
安全:最小權限,tool 輸出只當資料
憑證只給需要的權限
官方的 Security Best Practices 有一節講 scope minimization:一把權限很大的 token 被偷,攻擊者就能碰到所有相關的工具和資料。建議從最小、只能讀的權限開始,需要時再提高。實際做法:
- 能用唯讀 token 就用唯讀 token。
- 一個用途一把憑證,不要把整個帳號的 admin key 交給 agent。
- 有寫入能力的 tool,在 Claude Code 的權限設定裡加一條
ask規則,每次都要問,例如:
"permissions": {"ask": ["mcp__my-data__write_record"]}
Claude Code 的權限規則用 mcp__<server>__<tool> 指定 MCP tool,mcp__<server>__* 代表某個 server 的全部 tool。我在全域設定裡有一個 PreToolUse hook,matcher 是:
mcp__.*[Ss]upabase.*__(execute_sql|apply_migration)
AI 透過 Supabase MCP 執行 SQL 前,hook 會先檢查,刪表、清空資料這類語句要我確認。這個 hook 的來龍去脈寫在 Claude Code 教學。
不要隨便裝本機 server
stdio server 是在你電腦上執行的程式,權限跟你的 AI 工具一樣。官方文件把「本機 MCP server 被入侵」列成一類攻擊:設定裡藏一個惡意的啟動指令,或 server 本身就帶著惡意程式。裝之前先看清楚它會執行什麼指令、來源是誰。Claude Code 文件的提醒也一樣:連接前先確認你信任這個 server。
把 Tool 的輸出當資料看
Claude Code 文件寫:會抓取外部內容的 server 可能讓你暴露在 prompt injection 的風險裡。網頁、issue、email 裡藏一段「請把某某檔案傳到某網址」,模型讀到時可能照做。
自己寫 server 時,可以在 server 的說明裡寫清楚哪些欄位是來源數據、哪些是推論;用別人的 server 時,有寫入能力的 tool 一律保留人工核准。
除錯
連不上時,我會照這個順序查:
-
看狀態。
claude mcp list列出所有 server 和健康狀態,連線失敗會附上原因;claude mcp get <name>看單一 server 實際用的是哪個 scope、什麼指令。看到Pending approval,代表.mcp.json的 server 還沒核准,開一次claude就會問你。 -
在對話裡看。 輸入
/mcp,可以看狀態、重新連線、處理 OAuth 登入。 -
直接跑啟動指令。 在 repo 根目錄手動執行
bash tools/my-data-mcp.sh mcp。缺檔或權限不對,錯誤會直接印在終端機。 -
只列出 tool。 官方 Inspector 有 CLI 模式(需要 Node 22.19.0 以上),列完就結束:
npx @modelcontextprotocol/inspector --cli bash tools/my-data-mcp.sh mcp --method tools/liststdio server 就是讀一行、回一行,也可以直接用 pipe 送一則 JSON-RPC:
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | bash tools/my-data-mcp.sh mcp我的 server 是舊版規格,而且不要求先 initialize,才能這樣測;一般 server 請用 Inspector CLI。我用假憑證測,照樣拿回 tool 清單,因為這個 server 列出 tool 時不會連到外部服務。這一步能把問題切成兩半:tool 列得出來,問題就在呼叫外部 API 那一段。
-
用 MCP Inspector。 官方的除錯工具,
npx @modelcontextprotocol/inspector <啟動指令>會開一個網頁介面,看得到每一則 JSON-RPC。文件提醒,Inspector 會把 stdio 的env值存進系統鑰匙圈,沒有鑰匙圈的機器會存成未加密的檔案。把啟動腳本當成指令交給它,憑證就由腳本讀,不會進 Inspector 的設定。 -
開 debug log。
claude --debug-file /tmp/claude-debug.log。
常見原因:
- stdout 被汙染。 server 或腳本在 stdout 印了非 MCP 的內容,例如
print()除錯訊息、shell profile 印出的歡迎詞。 - 啟動太慢。 用
MCP_TIMEOUT=10000 claude把啟動逾時設成 10 秒。 - 輸出太大。 Claude Code 在 tool 輸出超過 10,000 token 時會警告,預設上限 25,000 token,可以用
MAX_MCP_OUTPUT_TOKENS調整。 - 環境變數沒展開。
.mcp.json裡的${VAR}沒設定又沒預設值時,設定照樣載入,但會留下原本的${VAR}字樣,claude mcp list會出現警告。 - 相對路徑。 我的
.mcp.json用相對路徑tools/my-data-mcp.sh,從 repo 根目錄啟動時正常;從子目錄啟動的情況我沒有測過。
常見問題
.mcp.json 可以 commit 嗎?
可以,它本來就是設計來跟團隊共用的。前提是裡面只有啟動指令和不敏感的設定,金鑰用 ${VAR} 引用,或交給啟動腳本去讀。
本機 server 用 stdio 還是 HTTP? 只給自己這台電腦用,stdio 比較單純:client 啟動它,其他程序連不進來。官方安全文件也建議,打算在本機跑的 server 用 stdio;一定要用 HTTP,就要加 token 驗證或改用權限受限的 IPC。
網路上教 initialize 交握的教學過時了嗎?
目前的規格版本是 2026-07-28,改成無狀態設計:每個請求自己帶著協定版本和能力,用 server/discover 探測 server。之前的版本用 initialize 建立連線。新版規格寫了相容的做法:同時支援新舊版的 client 先送 server/discover 探測,對方不認得,就回頭用 initialize。我用的 server 實作的是舊版(2025-06-18)的 initialize,在 Claude Code 裡照常連線。自己寫新的 server,就照最新規格和官方 SDK。
MCP 讓 agent 拿得到更多東西,所以我設定時第一個問的是這把憑證放在哪、能做什麼。.mcp.json 只寫怎麼啟動,憑證留在 repo 外、權限 600 的檔案裡;有寫入能力的 tool,在權限設定裡加一條 ask 規則。