OCR 可以作為 Model Context Protocol(MCP)client。你把它指向一個或多個外部 MCP server,這些 server 暴露的工具就會提供給評審 agent——與 file_read、code_search 等內建工具並列。
當評審器需要 diff 之外的脈絡時,就該引入 MCP server:
如果只需要讀 repo 本身,內建工具就夠了——MCP 是為了觸達 checkout 之外的東西。
# 最小配置:只給命令
ocr config set mcp_servers.docs.command npx
# 參數
ocr config set mcp_servers.docs.args '["-y", "@acme/docs-mcp-server"]'
# 限制暴露給評審器的工具
ocr config set mcp_servers.docs.tools '["search_docs", "get_page"]'
# server 啟動前跑的 setup 命令
ocr config set mcp_servers.docs.setup "npm install -g @acme/docs-mcp-server"
# 環境變數(KEY=VALUE 條目)
ocr config set mcp_servers.docs.env '["DOCS_TOKEN=secret", "DOCS_REGION=eu"]'
# 移除
ocr config unset mcp_servers.docs
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
command | string | ✓ | 啟動 server 的可執行檔(npx、uvx、絕對路徑) |
args | string[] | 傳給 command 的參數 | |
tools | string[] | 工具名白名單;空 = 註冊全部 | |
setup | string | server 啟動前跑一次的 shell 命令(5 分鐘超時) | |
env | string[] | 額外環境變數(KEY=VALUE) |
預設註冊 server 宣告的每個工具。當工具太多時用 tools 白名單——更少、更精準的工具讓 agent 更專注、降低 token 成本。白名單裡 server 沒有的名字會被跳過並警告(拼寫錯誤會顯示在 stderr,而不是默默無聲)。
MCP 工具名與內建工具共享同一命名空間。若與內建/保留工具(file_read、code_search…)或其他 server 的工具衝突,OCR 會跳過並記錄警告。先註冊者勝出。
所有 MCP 診斷都輸出到 stderr([ocr] 前綴),絕不污染 stdout 的 JSON 輸出:
Running setup for MCP server "x": …——正在跑 setupfailed to start MCP server "x": …——30 秒初始化超時,或 command 不在 PATHtool "y" conflicts with built-in tool, skipping——改名或從 tools 去掉allowed tool "y" not found in server's tool list——檢查拼寫這是最常被誤解的:OCR 消費 MCP server 暴露的工具,而不是暴露自己的工具給別人。你把外部 MCP server 指向 OCR,它的工具就成為評審 Agent 的一部分——與 file_read、code_search 並列。
判斷標準:評審需要 diff 之外的脈絡嗎?
如果只需要讀 repo 本身,內建工具就夠了。
當 MCP server 暴露 20+ 工具時,Agent 會在每次請求中都看到所有工具定義——消耗 token。用 tools 白名單只暴露你真正需要的 2-3 個,Agent 更專注、token 更省。拼寫錯誤會在 stderr 顯示警告而不是默默無聲。
Step 1 — 設定 MCP server 命令
ocr config set mcp_servers.docs.command npx
Step 2 — 設定參數
ocr config set mcp_servers.docs.args '["-y", "@acme/docs-mcp-server"]'
Step 3 — 設定工具白名單
ocr config set mcp_servers.docs.tools '["search_docs", "get_page"]'
Step 4 — 設定環境變數
ocr config set mcp_servers.docs.env '["DOCS_TOKEN=secret"]'
Step 5 — 跑評審(MCP 工具自動可用)
ocr review
預期產出
✓ MCP server "docs" started
✓ Tools registered: search_docs, get_page
✓ Reviewing 4 files...
(agent used search_docs to verify API usage)
| 錯誤訊息 | 診斷 | 修復 |
|---|---|---|
failed to start MCP server | 30 秒初始化超時或 command 不在 PATH | 檢查 setup 命令、確認 command 可執行 |
tool conflicts with built-in, skipping | MCP 工具名與內建衝突 | 改名或從 tools 白名單去掉 |
allowed tool not found in server | tools 白名單拼寫錯誤 | 檢查拼寫,stderr 有警告 |
tools 白名單與 setup 的用途、以及名稱衝突的「先註冊者勝出」規則。延伸閱讀:6 個內建工具 · 設定 · 程式碼:internal/mcp
你們公司有套內部的 acme-lint(檢查公司特有的 API 使用規範,例如「不得直接呼叫 legacy payment 端點」)。你想讓 OCR 的評審 agent 在 Main 迴圈裡直接跑 acme-lint 拿結果當脈絡,而不是靠模型瞎猜。
# acme-lint-mcp/server.js
const { Server } = require('@modelcontextprotocol/sdk/server/index.js')
const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js')
const server = new Server({ name: 'acme-lint', version: '1.0.0' })
server.setRequestHandler(server.listTools, async () => ({
tools: [{
name: 'run_acme_lint',
description: '對指定檔案跑 acme-lint,回傳違規清單',
inputSchema: { type: 'object', properties: { path: { type: 'string' } } }
}]
}))
server.setRequestHandler(server.callTool, async (req) => {
const { path } = req.params.arguments
const out = execSync(`acme-lint --json ${path}`)
return { content: [{ type: 'text', text: out.toString() }] }
})
server.connect(new StdioServerTransport())
ocr config set mcp_servers.acmelint.command node
ocr config set mcp_servers.acmelint.args '["/path/to/acme-lint-mcp/server.js"]'
ocr config set mcp_servers.acmelint.tools '["run_acme_lint"]'
ocr review --format json --audience agent
# session JSONL 中可看到 tool_call: run_acme_lint
開 viewer,找 main_task 泳道,看 tool call 的結果有沒有影響模型的 code_comment(例如「此檔違反 acme-lint 的 legacy payment 規則」)。
公司特有規範(legacy 端點禁用的黑白名單)是模型不可能憑常識知道的——不給它工具,它就只能「猜」。把 acme-lint 包成 MCP 工具,讓 Agent 在需要時以確定性工具取得確定性事實,評論就從「猜測」升級成「引用公司規則」。這正是 MCP 頁開頭說的「評審需要 diff 之外的脈絡」的最佳實例。
預期產出
✓ MCP server "acmelint" started
✓ Tools registered: run_acme_lint
Reviewing 4 files...
agent called run_acme_lint on payment/gateway.go
comment: "此檔呼叫 legacy payment 端點,違反公司規範 (acme-lint rule LP-001)"
setup 若有)+ 30 秒超時等它回應。tools 白名單,檢查名稱衝突(先註冊者勝出)。tool_call 事件寫進 session JSONL。工具結果對 Agent 而言只是「一段文字脈絡」——它不會自動成為評論。就像 file_read 發現的問題「按設計被忽略」,MCP 工具結果也只在「目前檔 diff 可觀察」時才會進入評論。
# acme-lint server 對不存在/失敗的檔案回傳:
{"content":[{"type":"text","text":"[]"}]} # 空清單 = "沒違規"
# 但其實是路徑不存在,acme-lint 失敗被 server 吞掉、回傳空
# Agent 收到 "[]" → 解讀成「此檔乾淨」→ 評論零條
工具本身沒被設計成「回報錯誤」——失敗時回傳空陣列,Agent 無法分辨「乾淨」與「執行失敗」。隱患:看似完整整合,實際上 linter 在關鍵檔上根本沒跑。防禦:server 對失敗應回傳錯誤內容(isError: true 或明確錯誤文字),讓 Agent 知道「工具失敗,不是乾淨」;並在 viewer 的 tool call 結果檢查是否有「被吞掉的錯誤」。
| 症狀 | 可能原因 | 解決方案 |
|---|---|---|
failed to start MCP server | 30 秒初始化超時、command 不在 PATH、setup 失敗 | 檢查 stderr 的 [ocr] 診斷;先手動跑 node server.js 確認可啟動 |
| 工具被註冊但 agent 從沒呼叫 | Agent 不知道何時該用;或工具名描述不清 | 改 server 的 description,明確寫「當發現 legacy payment 呼叫時」 |
| 工具名與內建衝突被跳過 | 「先註冊者勝出」 | 改名或從 tools 白名單去掉衝突者 |
| 工具回傳「乾淨」但其實失敗 | server 吞掉錯誤回傳空 | 讓 server 回傳 isError 或明確錯誤文字 |
| 工具結果沒進評論 | 脈絡工具發現的問題按設計被忽略 | 確認問題在目前檔 diff 可觀察;這是設計而非 bug |
callTool handler,讓它對「檔案不存在」回傳 isError: true 而非空清單。code_search 包成 MCP 工具」有意義嗎?為什麼(提示:內建已存在,衝突規則)?