MCP 資安危機:問題不在協定,而在治理
TL;DR: MCP 在一年多內,從 Anthropic 的內部實驗變成 AI 業界共通的介面。但進入 2026 年,資安研究員一個接一個把它拆開:官方 SDK 的 by-design RCE、tool poisoning、rug pull。我的看法是,這些漏洞大多不是協定的 bug,而是「把能力交出去、卻沒把治理一起交出去」的必然結果。現在大家急著補的那些東西,OAuth scope、人工確認、伺服器註冊表,其實就是治理被重新貼回協定上。 2026 年 4 月,資安團隊 OX Security 公布了一個發現:MCP 的官方 SDK(Python、TypeScript、Java、Rust 全中)存在一條從設定檔直接到指令執行的路徑,攻擊者可以在任何跑著有問題實作的機器上執行任意系統指令。根據他們的估算,受影響的套件下載量超過 1.5 億次,潛在波及的伺服器實例上看 20 萬個(The Register 的報導用的標題就是「20 萬台伺服器有風險」)。後續整個生態跟著冒出一連串 CVE,包括 MCP Inspector 的 CVE-2025-49596 和 Cursor 的 CVE-2025-54136。 但真正讓我停下來想的,是 Anthropic 的回應:這是設計如此(by design)。他們不打算改協定,並表示輸入清洗是開發者自己的責任。 這句話可以有兩種讀法,而我認為兩種都對。這正是整件事最值得想的地方。 先講清楚:MCP 為什麼會贏 要評論 MCP 的資安問題,得先承認它解決了一個真的很煩的問題。 在 MCP 之前,每接一個工具到 AI 上,你就得寫一套各自為政的膠水。M 個模型乘上 N 個工具,等於 M×N 種接法。MCP 把它變成 M+N:工具實作一次 server,模型實作一次 client,中間用同一套協定講話。Anthropic 當初的比喻是「AI 的 USB-C」,這個比喻站得住,是因為它真的描述了發生的事。 ...
MCP Security Isn't a Protocol Bug. It's a Governance Problem.
TL;DR: MCP went from an Anthropic side-project to the industry’s default agent-to-tool interface in about a year. Then 2026 brought a steady drip of disclosures: a by-design RCE in the official SDKs, tool poisoning, rug pulls. My read is that almost none of these are protocol bugs. They’re what happens when you ship capability without shipping governance, and the patches now landing (OAuth scopes, human-in-the-loop, registries) are just governance being bolted back on. ...
Skill 邊界設計:從能力到合約
TL;DR: 一個 skill 會多可預測,大概就看它的邊界劃得多清楚。把它當成「能力」(這個 skill 讓 AI 會做 X),它容易亂跑;把它當成「合約」(講好輸入、輸出、以及它不會碰什麼),它就比較像一個設計良好的 API。重點不是寫更多「要小心」,而是把它能碰的範圍框住。 我有個 skill,前一天還用得好好的,隔天就開始亂搞:我要它做 A,它順手把旁邊的 B 也「幫我」改了。我第一個反應是怪模型今天狀況不好。後來才想通,問題在我自己——當初寫這個 skill 的時候,我只說了它「會做什麼」,沒說它「不能碰什麼」。 這是英文版的中文對照,英文那篇用 API 設計的角度談;這篇是我自己怎麼從「能力」這個想法,慢慢搬到「合約」這個想法的過程。 能力清單 vs 合約 我們很習慣用「能力」來描述一個 skill:「這個 skill 讓 AI 會跑測試」「這個會幫我部署」。這種講法很自然,也很容易讓你之後被嚇到。因為「會跑測試」這句話沒有邊。它沒講會讀什麼、會動什麼、遇到不在預期內的狀況時會怎麼處理。 合約天生就有邊。它講清楚什麼東西進去、什麼東西出來、以及哪些地方它不會碰。框架文件裡有一句講得比我直接:skill 應該被當成「有版本、受政策約束、能力被框住的封裝」,而不是「一堆鬆散的 prompt 檔」。這跟一個 AI Skill 和 Prompt 到底差在哪講的是同一件事:重點不是 AI「能」做那件事,而是那件事被「講清楚」了。 合約先行:先想清楚它不該做什麼 我看過一個還算貼切的比喻:skill 像是你交給一位很強的廚師的食譜。食譜提供的是結構:材料、順序、限制;廚師提供的是判斷,什麼時候醬汁該再收一下、什麼時候可以換個材料。你不會因為廚師很強,就把食譜寫成「做一道好吃的菜」。 skill 也一樣。模型本身的判斷力很好,所以你要補的不是判斷,是結構。而結構裡最常被漏掉的,就是那條「不要碰」的線。我現在寫一個 skill,會先問自己一個問題:這個 skill 最不該做的事是什麼? 把那條線寫進去,比再多寫三條「請謹慎處理」都有效。 邊界鬆掉,其實是一次沒講的破壞性變更 框架裡有一個原則我覺得很受用:寧可在邊界上把關,也不要去微管模型怎麼想。一個會亂跑的 skill,你通常修不好它的「想法」;你能做的是把它能碰的範圍(哪些檔案、哪些工具、多少預算)框起來。 一個 skill 的範圍如果隨著時間悄悄變大,那其實是你發佈了一次「破壞性變更」卻沒有改版本號。而呼叫它的人(就是你自己)會用最經典的方式發現這件事:在出事的時候。這也是AI 代理常見痛點裡那個「能力邊界」缺口最貴的一種表現形式。範圍,只是它最容易爆出來的地方。 我把一個 skill 從能力改成合約的前後 回到開頭那個亂改 B 的 skill。它原本的描述大概是「整理這個模組的程式碼」。很開放,聽起來很厲害,結果就是它對「整理」的理解跟我不一樣。 我後來把它改寫成比較像合約的樣子:輸入是「指定的那幾個檔案」,輸出是「格式化後的同一批檔案 + 一份它改了什麼的清單」,然後明確寫上「不要新增或刪除檔案、不要碰指定範圍以外的東西」。改完之後它沒有變笨,只是不再自作主張。差別不在能力,在邊界。 順帶一提,邊界清楚的 skill 通常也比較便宜。skill 是漸進載入的:AI 先讀那一小段 metadata 判斷現在用不用得上,真的要用才載入完整內容。一個邊界小而清楚的 skill,光看它的「合約」就能被快速略過;一個什麼都做的 skill,得把整包拖進 context 才發現其實不該用它。這跟我在 Token 成本那篇講的是同一個方向:清楚的小合約,既好預測也比較省。 ...
Skill Design as Interface Design
TL;DR: An agent skill behaves predictably to about the degree its boundary is specified. Described as a capability (“the agent can now do X”), a skill tends to drift. Described as a contract (declared inputs, declared outputs, a scope it promises not to exceed), it behaves more like a well-designed API. The interface-design habits engineers already have (stable contracts, explicit scope, versioning) seem to transfer directly. The framework’s own direction points the same way: skills as versioned, capability-bounded packages, with boundary enforcement instead of micromanaging how the model reasons. ...
Token 成本的真相:分級,但別分太細
TL;DR: 把 token 當成設計變數,不是月底才看的帳單。沒有治理的任務成本沒有上限;但反過來,把任務切得愈細也不會愈省——subagent 不共享快取、TTL 一過就重建,過度切分反而更貴。真正要找的是「對的顆粒度」:夠細到 AI 不會亂跑,夠粗到能一直讀同一份熱快取。這些數字會隨工具一直變,當概念看就好。 有一陣子我根本不看 token 花多少。直到某次一個自動重構的任務跑了大半個晚上,我隔天看用量才意識到一件事:這東西的成本不是「用完才知道」,是我在開始之前就決定好的。 這篇是英文版的中文對照,但角度不太一樣。英文那篇用比較分析的方式談「為什麼治理的成本是可預測的」;這篇是我自己一路試出來的版本,包括一個我原本以為對、後來發現錯的直覺。 Token 成本是開始任務前就決定的 一個任務會花多少 token,大部分在它「被分類」的那一刻就定了:要載入多少 context、要不要去翻所有技能文件、要不要再開 subagent。等你看到數字,數字早就花掉了。 所以「之後再來省 token」通常沒什麼用。貴的決定都在前面:把整個 codebase 讀一遍而不是先看索引、載入完整的技能定義而不是它的 metadata、把一個本來可以接續的工作當成全新的冷啟動重跑一次。這些事後很難補救,你能做的是下次分類分得好一點。 只用 Prompt 和技能,也能做到基本治理裡我提過,最便宜的那層治理,一個 CLAUDE.md 加一句「commit SHA 是什麼」,幾乎不花錢,原因就在這裡:它把決定提前到任務開始之前。 粗放的成本沒有上限 我後來想通的一件事是,token 成本其實分成兩種,差別不在多寡,在有沒有天花板。 有治理的工作有上限。Agentic OS 的 benchmark 裡最重的情境,一個跨多代理協作的架構變更,大約落在 6 萬 token 上下。你可以嫌它高,但很難說它「沒有上限」:它是個數字,事前就知道,而且你不看它的時候它不會自己長大。 放著不管的工作沒有這個上限。當 AI 說「好了」而你手上沒有任何可以查的東西,真正貴的不是它說那句話的 token,是後面所有建立在這個假完成上的工作。這個系列的第一篇有個具體例子:AI 說它實作了三個模組,其中兩個根本沒動過。它報告完成花的 token 很少,失控的是下游每一步都信了這份假報告。少了 evidence 的失敗反而是便宜的那種,你很快會發現;貴的是那種安靜地累積、等你發現已經繞很遠的。 治理在這個角度下,其實不是為了少花 token。它比較像是把花費從「沒上限」那一欄,搬到「有上限」那一欄。 我原本以為切細一點就省,結果不是 想通上面那點之後,我自然得出一個結論:既然結構能把成本框住,那就把任務切到最細吧。這個直覺在遇到快取之後就破了。 快取的算式大概是這樣(2026 上半的數字,而且它一直在動):讀快取大約是正常 input token 的十分之一,寫快取反而比正常還貴,短窗約 1.25 倍、長窗約 2 倍。也就是說,折扣只有在你「重複讀同一份快取」時才出現;每寫一份新的,你都在付溢價。 這就把「愈小愈省」整個翻過來,因為切分和快取有兩個很不友善的互動: subagent 預設不繼承父代理的快取。 每開一個新的子任務,它通常要為自己需要的前綴重新付一次寫入。一個任務切五份,你可能是買了五次寫入,而不是把一次寫入攤平成很多次便宜的讀取。(現在有些工具開始提供 fork 模式讓子代理重用父快取——這件事本身就說明這個成本真實到值得工程繞過。) 快取有 TTL。 預設窗口很短。如果你把工作切到步驟之間的間隔超過它(某個 subagent 跑太久才回來、某個 fan-out 卡住),快取就過期了,下一步只能用全價重建。 所以一個切太細的任務,最後可能比它取代的那個粗版本更貴:更多寫入、更少讀取、更多重建。同一份 benchmark 也驗證了反方向:一個「載入一次、之後都從快取讀」的接續模型,比每次都整份重讀省了大約一半的執行成本。真正在省的是快取的連續性,不是任務切得多細。 ...
Token Economics of AI Agent Governance
TL;DR: Governance tends to have a token cost you can put a ceiling on. Ungoverned work usually doesn’t; the recovery cost of an undetected error has no obvious upper bound. But the fix isn’t “split everything into the smallest possible pieces.” Caching changes the math: a fresh sub-context pays a cache-write premium and doesn’t inherit its parent’s cached prefix, so over-decomposition can cost more than the coarse version it replaced. The design target, at least with today’s caching, is the granularity that holds error cost and cache cost in check at the same time. It’s a moving target (pricing and model behavior change fast), so treat it as a way of thinking, not a fixed rule. ...
No evidence, no completion
TL;DR: “No evidence, no completion” is a single structural principle: a task isn’t done until the agent produces an artifact that exists outside the conversation and can be checked independently. It sounds trivial. In practice it closes most of the common agent failure modes in one rule, because the act of specifying what evidence looks like, before the task runs, forces you to define what “done” actually means. In the previous post in this series I described an agent that said a feature was done (commit SHA requested, none existed, two of three modules unchanged). The failure had a name: no external completion criterion existed, so the agent supplied its own. That gap has a one-rule fix. ...
Work Log:跨 session 的記憶機制
TL;DR: Work Log 是一個很無聊的東西:一份 markdown 檔案,記錄這個任務做到哪裡、做了哪些決定、下個 session 要從哪裡繼續。它沒有解決 AI 的記憶問題,只是繞過它。但在我們找到更好的方法之前,它有效。 在那篇談治理基礎的文章裡,我說過 AI「只活在那一次的對話框裡」。這個說法的代價,在你真正開始用 AI 代理做持續開發的時候才會變得具體。 你跟 AI 花了一個小時討論這個 feature 要走哪個設計模式、為什麼不用另一個方案、資料庫的 schema 要怎麼調整。全部討論清楚了,開始實作。隔天開新對話,繼續做。AI 從頭來:哪個設計模式?資料庫?我不知道你說的是什麼。 這不是 Claude 的問題,也不是任何特定模型的問題。它就是這樣運作的。上一篇說到記憶檔案(CLAUDE.md、AGENTS.md)可以幫助 AI 記住專案的架構規則。但那解決的是「規則要記住」的問題,不是「這個任務做到哪裡」的問題。Work Log 是後者。 兩層記憶,兩個問題 先說清楚兩個東西的差別,因為我發現自己最初混在一起想。 專案記憶:這個專案的架構是什麼、用了哪些 ADR、活躍的任務清單在哪裡、哪些 skill 可以用。這是全域的、靜態的,跟任何一個具體任務無關。你不常去動它,但每次開新 session,AI 需要讀它才知道自己在什麼脈絡裡。 任務記憶(Work Log):這個任務做到哪個 phase、做了哪些決定、下一個 session 要從哪裡繼續。它是動態的、per-task 的。一個任務一個檔案,在 Agentic OS 裡放在 .agentcortex/context/work/<task-key>.md(完整結構見 repo)。 混在一起的後果是:要麼全域狀態被塞滿具體任務細節(之後沒人看得懂),要麼任務進度沒地方記(每次都從頭)。分開之後,兩個問題各有各的解。 Work Log 長什麼樣子 下面是一個簡化版的實際樣子(來自 github.com/KbWen/agentic-os): # Work Log: feat/email-verification ## Header - Branch: feat/email-verification - Classification: feature - Current Phase: implement - Checkpoint SHA: a3f9c12 ## Task Description 新增 email OTP 驗證流程。使用者第一次登入後需完成驗證, 未驗證帳號只能讀取,不能寫入。 ## Phase Sequence | Phase | Status | Notes | |-----------|-------------|--------------------------| | bootstrap | completed | 分類為 feature | | plan | completed | 確認走 OTP 不走 magic link | | implement | in-progress | auth module 完成,email 發送待測 | | review | pending | | ## Gate Evidence - Gate: plan | Verdict: pass | At: 2026-05-10T14:00Z - Gate: implement | Verdict: FAIL | Reason: email sending untested, scope not complete | At: 2026-05-11T09:00Z ## Phase Summary - plan: 討論了 OTP vs magic link。決定用 OTP,因為我們的 email provider 有速率限制,magic link 的 retry 設計複雜度更高。 這個決定要記住,下個 session 不要再討論。 關鍵不在格式,在那個 Phase Summary。每個完成的 phase,AI 要用一段話說:做了什麼決定、為什麼這樣決定、有什麼取捨。 ...
Prior art: what distributed systems already knows
TL;DR: The governance problems that make AI agents unpredictable (unverified completions, state loss between sessions, unconstrained scope) are structurally identical to problems distributed systems engineering solved with audit logs, delivery acknowledgment, state machines, and least-privilege access. The one genuine difference is non-determinism: an agent given the same open-ended task twice will do something different, which means governance needs to front-load constraints rather than just catch failures after. But the rest of the pattern library applies directly. ...
只用 Prompt 和技能,也能做到基本治理
TL;DR: 在裝任何框架之前,有一層治理是免費的:在專案根目錄放一個 AGENTS.md 或 CLAUDE.md,養成開口要求 evidence 的習慣,開始任務前先說清楚什麼不能動。這三件事不能替代跨 session 的狀態管理,但能擋掉大部分常見問題。這篇說的就是怎麼做、做到什麼程度、在哪裡會失效。 有一段時間我的 Claude Code 工作流裡沒有任何框架,只有對話和一堆臨時 prompt。某天我做了兩個改變:把專案的架構決策寫進一個 CLAUDE.md,還有在每次 AI 說「好了」的時候問一句「commit SHA 是什麼?」 一類問題幾乎消失了:AI 在新 session 裡對著不存在的設計模式寫程式碼的情況,以及我接受了「完成」卻發現什麼都沒變的情況。不是所有問題都解決了。但那兩件事的性價比,讓我後來開始認真想「在裝框架之前,這個層面的治理到底能做多少」。 這篇是AI 代理常見痛點與我們的嘗試的延伸。那篇列了五個反覆出現的問題,這篇專門回答:只靠 prompt 習慣和 skill 選擇,能解決多少? 記憶檔案:解決跨 session 失憶的最低成本方案 AI 代理在每一個新對話都是空白狀態。它不記得上次的架構決策,不記得你說過不要用哪個 pattern,也不記得你已經有一個 utils/auth.ts,所以它再寫一個新的。這個問題在 IEEE Spectrum 的報導裡有量測數據:長 session 後期,AI 重複生成已存在函式、忽視早期建立的 coding convention 的頻率明顯上升。 三個工具在試圖解決同一個問題: AGENTS.md 是 OpenAI Codex 最初設計的慣例,後來被 Cursor、GitHub Copilot 和 Google Antigravity 等主流工具廣泛採納。它的設計邏輯是:在任何工具讀取它之前,先告訴工具「這個專案是怎麼運作的、你可以做什麼、不可以做什麼」。 CLAUDE.md 是 Anthropic 針對 Claude Code 的版本。Claude Code 在每個新 session 開始時自動注入這個檔案的內容,所以你放在這裡的東西就等於是每次都在對話開頭重新說一遍。 .cursor/rules 是 Cursor 的對應物。原理相同。 ...