mcpward を作り、契約の変更を検出する仕組みを公開しているAIエージェント周りの話は、正直まだかなり“流動的”です。昨日までうまく動いていたのに、今日はなんだか挙動が変。そんな経験がある人なら、この記事の問題意識はかなり刺さるはずです。
記事の主張をひとことで言うと、MCP serverの契約も、npm dependencyと同じように固定して差分を監視すべきだ、です。
ここでいう「契約」は、APIの名前や引数だけではありません。tools/list で返ってくる tool の名前、description、JSON schema、annotation まで含みます。
このあたり、かなり面白い視点です。ふつう dependency というと、ライブラリのバージョンや lockfile を思い浮かべますよね。でも MCP の世界では、AI が読む「説明文」まで実質的な仕様になっている。つまり、ドキュメントのつもりで書いた文が、そのままモデルの行動指示になっているわけです。怖いけど、たしかにそうです。
記事では、MCP dependency が静かに壊れるパターンを4つ挙げています。
まず一番厄介なのが、description の書き換えです。tool 名も schema も同じなのに、説明文だけ変わる。人間の目には小さな修正に見えても、モデルはその文言を読んで「この tool は何をするのか」「いつ呼ぶべきか」を判断するので、振る舞いが変わる可能性があります。しかも schema diff では見逃します。ここが肝です。
次に、required parameter の追加。これはわかりやすく壊れます。今まで通っていた呼び出しが invalid params で失敗する。しかも本番トラフィックで初めて気づく、という嫌なやつです。
3つ目は、**readOnlyHint が true から false に変わる**こと。readOnlyHint は「この tool は安全そうか」を示すようなヒントです。これが変わると、今まで自由に呼ばせてよかったものが、実は状態を書き換えるかもしれない。安全だと思っていた道具が、急に危険物になる。かなり実務的な恐怖です。
4つ目は、tool の消失。これは派手に壊れるのでまだマシで、少なくとも気づけます。最悪なのは、消えた tool の代わりにエージェントが別の手段で勝手に迂回することだと思います。そうなると、エラーではなく“想定外の成功”が起きるので、もっと見つけにくいです。
この記事の著者は、こうした変更を検出するために mcpward というツールを作っています。役割はかなりシンプルで、
npx mcpward baseline
npx mcpward diff
この2つです。
baseline で、server の contract を lockfile のように保存する。
diff で、CI 上でその契約が変わっていたら build を落とす。
ここがうまいなと思いました。発想としてはとても素直です。npm の依存関係なら lockfile があるし、変更があれば diff を見る。なら MCP の server にも同じ発想を持ち込めばいいじゃないか、というわけです。AI 周辺の議論はどうしても抽象的になりがちですが、この記事はかなり実装寄りで、そこが好感触でした。
baseline に含めるのは、各 tool の名前、description の hash、input/output schema、annotations です。
そして更新後の server に対して diff を取ると、たとえばこんな感じで差分が出ます。
readOnlyHint が変わったしかも build は exit code 1 で止まります。つまり、「壊れてから気づく」のではなく、「壊れそうな変更を先に止める」方向に寄せている。これはかなり DevOps っぽい考え方です。
ここで大事なのが、すべての変更を failure にしないことです。
著者は breaking / non-breaking の線引きをしています。
たとえば、optional parameter の追加は基本的に問題なし。既存の呼び出しはそのまま通るので、普通は non-breaking と見なします。
逆に、required field の追加や、type を狭める変更、enum を厳しくする変更は breaking です。既存クライアントが失敗しやすいからです。
さらに、description の変更は「rug-pull」として扱い、デフォルトで failure にしています。
この “rug-pull” という言い方はかなり印象的でした。絨毯を引き抜く、つまり見た目は同じでも足元を崩す変更という意味です。技術用語というより、かなり率直な比喩です。私はこの感覚、わりと大事だと思います。MCP の description は単なる説明文じゃなくて、実質的な instruction だからです。
個人的には、この線引きがこの手のツールのいちばん難しい部分ではないかと思います。
「どこまでを破壊的変更とみなすか」は、仕様の話であると同時に運用の話でもあるからです。厳しすぎるとノイズだらけになるし、甘すぎると肝心な変更を見逃す。著者もこの判定ロジックを pure function にして、fixture 付きテストで固めていると書いています。ここはかなり真面目で、好感が持てます。
mcpward は、単に diff を見るだけではありません。実際の client として protocol compliance もチェックします。
たとえば、
こういうものを見ます。
中でも地味に重要なのが、エラーの扱いが2層あるという話です。
MCP では、プロトコル自体のエラーと、tool 実行の失敗は別物です。前者は JSON-RPC error object、後者は isError: true を持つ成功レスポンスです。

これ、最初はややこしいですが、実務ではかなり大事です。
たとえば「ファイルが見つからない」は tool の失敗であって、プロトコル全体の破綻ではありません。でも実装を雑にすると、これを JSON-RPC エラーとして返してしまう server がある。すると client 側のエラーハンドリングが全部ずれます。著者が「誰もここをチェックしていない」と言っているのは、かなり納得感があります。
このツールは、security っぽい変な気配も検出します。
説明文に injection 的な文言が混ざっていないか。
hidden unicode や zero-width 文字が入っていないか。
schema が API key や password を要求していないか。
readOnlyHint と見た目の挙動が矛盾していないか。
こういうのは、AI 時代ならではの地雷です。人間から見るとただのテキストでも、モデルにとっては行動を変える入力になり得る。しかも見た目では気づけない細工が混ざることもある。ここは本当にイヤな領域ですが、だからこそ機械的に見る価値があると思います。

出力が SARIF 形式なのも実用的です。GitHub Security tab に流せるので、いかにも「セキュリティ検査しました」感がある。派手ではないけど、現場ではこういう接続の良さが効きます。
こういう検査ツールでいちばん怖いのは、「何となく安心させるだけの置物」になることです。著者もそこをかなり警戒していて、fixture server を複数用意しています。
正しい server、意図的に壊した server、1種類ずつ変更した server、遅い server、poisoned な server。こういう controlled な環境で検査ロジックを育てているそうです。
さらに、各チェックには「ちゃんと赤くなる」negative test を用意している。これ、地味ですが大事です。常に green なテストは、ただの自己満足装置になりがちだからです。

私はこの姿勢がかなり好きです。セキュリティや互換性の検査って、導入した瞬間に安心したくなるんですが、そこが一番危ない。信じたい気持ちをぐっとこらえて、壊れるケースを先に作る。こういう慎重さは、地味だけど強いです。
この話の本質は、MCP の細かい仕様そのものより、AI エージェントの依存先も、ふつうのソフトウェアと同じように固定・監査すべきだという視点だと思います。
私たちは npm dependency には慣れています。lockfile があって、diff を見て、怪しければ止める。
でも AI エージェントが参照する MCP server に対しては、まだその感覚が薄い。そこを「いや、同じでしょ」と持ち込んでいるのが気持ちいいんです。

ただし、コメント欄でも指摘されているように、contract が同じでも実装が同じとは限りません。エンドポイントが同じ名前と schema を返していても、裏で動く binary や container が別物なら、依存関係としては別です。ここはこの記事の限界でもあるし、今後の拡張ポイントでもあるはずです。
個人的には、これはかなり本質的な指摘だと思います。contract の固定は必要。でもそれだけでは足りない。AI 周辺のサプライチェーンは、まだまだ見ないといけない層が多いです。
mcpward は MIT license で、ローカル完結、API call なし、telemetry なし。
このあたりも、内部 server を扱う現場にはありがたい設計です。外に説明文を送らずに済むのは、かなり安心材料でしょう。
参考: Pin your MCP server contracts the way you pin your dependencies