研究紹介
MCP のツールは 1 個 15 トークン。ToolSearch で読み込むと 300〜700 トークンになる
· 読了まで約10分
目次
先週起動時のプリフィルを分解した記事で、MCP サーバー 7 台を無効化しても数字は 241 トークンしか動かなかった、つまり測定ノイズの範囲だと書いた。この測定自体は正しい。ただ、あの書き方だと「MCP はもうタダになった」と読めてしまう。
タダではない。従量制になっただけで、その従量部分を自分で測っていなかった。
残り半分をここに書く。手法も環境も先週と同じで、Claude Code 2.1.233 と Haiku 4.5、空のディレクトリ、--strict-mcp-config を付けて設定ファイル以外を読ませない状態で測っている。
起動時のコストは 1 ツールあたり約 15 トークン
サーバーを 1 段階ずつ足しながら claude -p "hi" を実行すると、そのつど usage が返ってくる。差分がそのまま追加分の値段になる。
| 構成 | 追加ツール数 | プリフィル | 差分 |
|---|---|---|---|
| MCP サーバーなし | — | 27,742 | 基準 |
serena | 0(起動せず) | 27,742 | 0 |
+ context7, brave-devtools | 31 | 28,320 | +578 |
+ chrome-devtools, career | 34 | 28,721 | +401 |
65 ツールで 979 トークンなので、1 ツールあたり約 15 トークンになる。これはツールの名前だけの値段だ。mcp__chrome-devtools__take_screenshot というものがどこかに存在する、とモデルが知るためのコストでしかない。
このレートは先週の宿題も片付ける。あのとき無効化した 7 台はほとんどがクラウド連携で、そのうち何台かは OAuth を途中で止めたまま authenticate ツールしか出していなかった。同じ顔ぶれのサーバー群が今このアカウントで登録しているツールは 19 個で、15 を掛ければ 285 になる。ノイズとして切り捨てた 241 は、誤差の範囲で名前リストの正しい値段だったわけだ。ノイズに見えたのは、39,810 トークンのプリフィルと並べて見ていたからで、何も課金されていなかったからではない。
2 行目のゼロは別の話になる。serena はこの環境では起動しない。tools/list を直接叩く探りはタイムアウトし、ヘッドレスセッションに mcp__ で始まるツール名を全部挙げさせても NONE が返る。登録に失敗したサーバーはツールもトークンも 1 つも増やさず、同時に何の仕事もしない。うちの serena は、一覧に並んでいるのが当たり前になるくらい長いあいだ死んでいた。
隠されているスキーマは 1 サーバーで 23KB ある
1 ツール 15 トークンで済むのは、スキーマがサーバー側に留まっているからだ。何が手元に来ていないのかを見るには、サーバーに直接聞けばいい。stdio 越しに 3 通の JSON-RPC を投げると tools/list が返る。1 通が 1 行で、下の折り返しは表示の都合にすぎない。実際に送るときにメッセージを改行で割ってはいけない。
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"probe","version":"0"}}}
{"jsonrpc":"2.0","method":"notifications/initialized"}
{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}
返ってきた配列を 2 通りの長さで測る。全体と、名前だけの場合だ。
| サーバー | ツール数 | スキーマ全体 | 1 ツールあたり | 名前だけ |
|---|---|---|---|---|
context7 | 2 | 4,870 B | 2,435 B | 35 B |
chrome-devtools | 29 | 23,257 B | 802 B | 463 B |
ブラウザ操作のサーバー 1 台で 23KB の JSON スキーマを抱えている。「MCP がコンテキストを食い潰す」と書かれた記事が測っていたのは、この 23KB のほうだ。あれは間違いではなく、起動時にこれを丸ごとシステムプロンプトへ貼り付けるクライアントの話をしていた。いまの Claude Code が送るのは名前だけで、同じ 29 ツールならおよそ 435 トークンになる。
3 列目に注目してほしい。次の節で効いてくる。context7 のツールは chrome-devtools のツールの 3 倍のスキーマを抱えている。
ToolSearch で読み込むと 1 ツール 296 トークン、あるいは 694 トークン
遅延ロードは、必要になった時点で ToolSearch 経由でスキーマを取りに行く仕組みだ。取りに行った分は無料ではない。ヘッドレス実行に --output-format stream-json を付けると、途中のステップごとの usage が見える。2 つのサーバーで 1 回ずつ測った。
| サーバー | 読み込んだツール | 前 | 後 | 1 ツールあたり |
|---|---|---|---|---|
chrome-devtools | 3 | 28,780 | 29,667 | 296 |
context7 | 2 | 28,766 | 30,155 | 694 |
(どちらの起点も最初の表の 28,721 より少し高いのは、プロンプトが hi より長いからだ。)
つまり単一の数字は無い。1 ツールの読み込みは 300〜700 トークンで、どちら寄りになるかはそのサーバーのスキーマの冗長さが決める。2 行の比 2.3 倍は、上の表の 1 ツールあたりスキーマ量の比 3.0 倍とだいたい合っている。実用的には、サーバーの tools/list のバイト数を 3 で割ってトークン数と読めばいい。この換算なら自分のサーバーで 1 分で測れるし、他人の記事に載っている 1 ツールいくらという数字より当てになる。この記事のものも含めて。
296 という値そのものは、多くの解説がツール定義 1 個に見積もっている数百トークンと同じ水準にある。当然で、同じスキーマなのだから小さくなりようがない。変わったのは届くタイミングだけだ。
手元の環境では、触るか触らないかで 20 倍の差が付くことになる。触らないツールは 15 トークン。触った瞬間にさらに 296 トークンが乗り、そのまま居座る。スキーマはもう履歴の中にあるからだ。読み込みの次のステップでも合計は 29,667 のままで、元には戻らなかった。ただし測ったのは DONE で終わる短いヘッドレス実行で、長いセッションで compaction を挟んだあとどうなるかは試していない。
サーバー 1 台分で計算し直すと、以前さんざん言われていた数字がそのまま戻ってくる。chrome-devtools の 29 ツールを 1 セッションで全部読み込めば、読み込んだ 3 つと同じ重さのツールばかりだと仮定すればおよそ 8,600 トークン。23KB という全体量から見ると実際は 6,000 前後だろう。それでも、放っておけば済む 435 トークンとは桁が 1 つ違う。よく引き合いに出される 93 ツールの GitHub サーバーは、公開されている計測値が数える人によって 18,000 から 55,000 まで開いていて、うちのレートを当てるとその中ほどに落ちる。税金は消えていない。従量制になり、置いておくだけで使わないサーバーには気前のいい無料枠が付いた。
サーバーを減らしても意味がない。効くのは別のこと
先週の記事を書いた直後なら、まずサーバーの整理に手を付けていたと思う。それがほぼ無意味だということに、今回やっと数字が付いた。30 ツールを持つ未使用サーバーを消して、プリフィルから戻るのは 450 トークン前後にすぎない。使っているサーバーを消せば、それに読み込み済みスキーマの分が乗るのでずっと大きいが、使っているのだから消せない。消して困らないサーバーはもともとほとんど課金されておらず、まとまったトークンを食うサーバーは食うだけの働きをしている。この整理が割に合う組み合わせは無い。
そのうえで、実際に運用を変えた点が 3 つある。
ToolSearch はまとめて 1 回で呼ぶ。自分の設定に入れてある MCP の指針には、必要なツールを 1 回の select: クエリで全部読め、と以前から書いてある。ずっとレイテンシの話だと思っていた。トークンの話でもあるが、効き方は見た目ほど強くない。1 ツールずつ 3 回呼んでも 1 回あたりのスキーマ代は変わらないはずで、そこは測っていない。まとめて浮くのは呼び出しごとのオーバーヘッドと、その周りに増えるアシスタントのターンだ。
キーワード検索よりも名前指定を選ぶ。select: で正確な名前を指定すれば、返ってくるのは頼んだものだけだ。"browser screenshot" のようなキーワード検索は max_results の上限まで候補を返し、結局呼ばなかったスキーマにも数百トークンずつ払う。2 回外すとツール本体より高くつくこともある。これはスキル側で測ったのと同じ段階的開示の仕組みで、壊れ方も同じだ。余計なものを読み込むまでは安い。
死んだサーバーを定期的に洗い出す。コストのためではない。無料だからこそ厄介なのだ。登録に失敗したサーバーと、ツールをたまたま使っていないだけのサーバーは、プリフィルの上では区別がつかない。どちらも 0 だからだ。怪しいのはたいていクラウド連携で、認証が持ち運べないせいで古いトークンが黙って失効する。確認はヘッドレスのコマンド 1 本で済む。
claude -p "List every tool name starting with mcp__, comma separated, or NONE." \
--model haiku --output-format json
自分の環境で測る
どちらの数字も数分で出る。起動側は条件ごとに設定ファイルを作り、プリフィルの差を取ればいい。
echo '{"mcpServers":{}}' > empty.json
claude -p "hi" --model haiku --output-format json --strict-mcp-config --mcp-config empty.json \
| python3 -c "import json,sys; u=json.load(sys.stdin)['usage']; \
print(u['input_tokens']+u['cache_creation_input_tokens']+u['cache_read_input_tokens'])"
読み込み側は stream-json にして、連続するステップの合計値を読む。プロンプトは 1 行に収めること。select: のリストに改行が入ると、そのままクエリの一部になる。
claude -p "Call ToolSearch once with query 'select:mcp__context7__query-docs' then reply DONE." \
--model haiku --output-format stream-json --verbose \
--strict-mcp-config --mcp-config mcp5.json
ToolSearch を挟んだ前後の差が、そのツール群に対してセッションの残り全体で払い続ける額になる。
エージェントの内部構造を測るたびに学び直しているのは、答えに賞味期限があるということだ。「MCP サーバーはコンテキストを溢れさせる」は事実だった。それが修正され、修正の中身は払うかどうかではなくいつ払うかの変更だった。この件について書かれた解説は、先週の記事も今回のものも含めて、特定のクライアントバージョンを 1 回測った結果でしかない。数字に賞味期限がある以上、持っておくべきは自分の環境で再現するコマンドのほうだ。