MCP のトークン消費を減らす:ツール定義を 60% 削った方法
エージェントが呼び出せる MCP ツールは、どれもモデルがリクエストのたびに読む可能性のある JSON の塊です。私たちのツールは約 95,000 トークンありました。自分のツールを 2 分で測る方法、ツールを 1 つも削除せずに削ったもの、そして再び膨らむのを防ぐ予算チェックを紹介します。
10月27日、有料ユーザーの 1 人が AgentsRoom のサブスクリプションを解約し、理由としてトークンを挙げました。反論する前に、私たちのアプリが起動するエージェントそれぞれに何を上乗せしているのかを測りました。
一番大きな数字は、予想していた場所にはありませんでした。起動時の指示は小さなものでした。すべてのエージェントに渡している MCP ツール(バックログ、プロジェクトメモリー、エージェント間メール、ターミナル、SSH、データベースなど)は 191 個、JSON にして約 380 KB ありました。1 トークンあたり約 4 バイトとすると、ツール定義だけで約 95,000 トークンです。1 週間前は 78,000 でした。誰かがそう決めたわけではありません。新しいツールがそれぞれ自分の段落を足していっただけです。
私たちが作っている AgentsRoom は、Claude Code、Codex などのコーディング CLI を並べて動かし、これらのツールを与えるデスクトップアプリです。ここでは、私たちが測ったもの、ツールを 1 つも削除せずに削ったもの、そしてそれが戻ってこないようにするチェックを紹介します。どれも自分の MCP サーバーにそのまま応用できます。
短い答え
- ツール定義は、モデルがリクエストのたびに読む可能性のある JSON です。名前、説明、入力スキーマ。1 トークンあたり約 4 バイトで計算します。
- 毎ターン払うかどうかは CLI 次第です。 Claude Code はデフォルトで MCP ツールを後回しにし、必要になったときに定義を読み込みます。リクエストのたびにすべてのスキーマを送る CLI もあります。
- 削る前に測る。 Claude Code の
/context、または任意のサーバーにツール一覧を求める 40 行のスクリプトで。 - テキストを短くし、ツールは残す。 説明は「何を」と「いつ」を言うだけ。使い方の詳細は、エージェントが必要なときに読むドキュメントに移します。私たちのツールは約 95,000 から約 38,000 トークンになり、2.5 分の 1 になりました。
- ビルドに上限を入れる。 上限がなければ、定義は勝手にまた膨らみます。
ツール定義のコストと、それを払うのは誰か
MCP クライアントはサーバーに接続すると、ツールの一覧を求めます。各エントリーは、名前、モデル向けに書かれた説明、パラメーターの JSON スキーマです。クライアントはこの一覧をツール定義に変えてモデルに送ります。モデルはそれによって、ツールが存在することと、その呼び出し方を知ります。
つまり説明の一語一語が、モデルの読む入力です。どれだけ頻繁に読むかはクライアントによって違います。
- Claude Code はデフォルトで MCP ツールを後回しにします。 2026年11月3日に Claude Code 2.1.314 をインストールした状態で読んだドキュメントによると、セッション開始時に読み込まれるのはツール名とサーバーの指示だけで、定義は Claude がそのツールを必要としたときにツール検索で読み込まれます。
ENABLE_TOOL_SEARCH=falseのとき、autoでコンテキストウィンドウの 10% というしきい値を下回るとき、Claude Opus 4 と Sonnet 4 より古いモデル、またはalwaysLoadを付けたサーバーでは、定義は最初から読み込まれます。 - ほかの CLI は事情が違います。 10月28日に私たちが行ったテストでは、エージェントに「OK」と答えるよう頼みました。Claude Code は私たちのツールを後回しにした状態で 67,500 トークンから始まり、Codex CLI 0.164 は 25,000 トークンから始まって私たちのスキーマをひとまとめには送らず、OpenCode は接続時に私たちの 175 ツールとブラウザーツール 19 個を読み込み、リクエストのたびにそれを送っていました。同じサーバーでも、請求額は大きく違います。
ツール一覧が変わらなければ、プロンプトキャッシュがコストを和らげます。最初のリクエストのあとは、変わらない先頭部分が入力価格のごく一部でキャッシュから読まれます。ただし空きスペースは戻ってきません。定義は相変わらず、リクエストのたびにコンテキストウィンドウの中で、あなたのコードや会話の隣に居座っています。そして後回しモードにもそれなりのコストがあります。各ツールを初めて使う前に、検索のステップが入ります。
自分のツールを 2 分で測る
Claude Code では、/context を実行します。ウィンドウをソースごとに分解し、後回しにされている MCP ツールや必要時に読み込まれる MCP ツールに印を付けます。プロジェクトで新しいセッションを開き、何も入力する前に実行すると、作業が始まる前の時点で自分の構成がどれだけ使っているかが分かります。
どのサーバー、どの CLI でも、サーバーに直接聞けます。stdio で動く MCP サーバーは JSON-RPC を 1 行 1 メッセージで話します。initialize を送り、次に tools/list を送り、返ってきたものを測ります。このスクリプトはそれだけを行います。Node だけで動き、依存関係はありません。
// Usage: node mcp-tool-cost.mjs <command that starts the server> [args...]
import { spawn } from 'node:child_process';
const [cmd, ...args] = process.argv.slice(2);
const server = spawn(cmd, args, { stdio: ['pipe', 'pipe', 'ignore'], shell: process.platform === 'win32' });
const tools = [];
let buffer = '';
const send = (msg) => server.stdin.write(JSON.stringify({ jsonrpc: '2.0', ...msg }) + '\n');
const listTools = (cursor) => send({ id: 2, method: 'tools/list', params: cursor ? { cursor } : {} });
server.stdout.on('data', (chunk) => {
buffer += chunk;
let end;
while ((end = buffer.indexOf('\n')) >= 0) {
const line = buffer.slice(0, end).trim();
buffer = buffer.slice(end + 1);
if (line) handle(JSON.parse(line));
}
});
function handle(msg) {
if (msg.id === 1) {
send({ method: 'notifications/initialized' });
listTools();
} else if (msg.id === 2) {
tools.push(...msg.result.tools);
if (msg.result.nextCursor) listTools(msg.result.nextCursor);
else { report(); server.kill(); }
}
}
function report() {
const rows = tools
.map((tool) => ({ name: tool.name, bytes: Buffer.byteLength(JSON.stringify(tool)) }))
.sort((a, b) => b.bytes - a.bytes);
const total = rows.reduce((sum, row) => sum + row.bytes, 0);
for (const row of rows.slice(0, 10)) {
console.log(`${String(row.bytes).padStart(7)} bytes ~${Math.round(row.bytes / 4)} tokens ${row.name}`);
}
console.log(`${rows.length} tools, ${total} bytes, about ${Math.round(total / 4)} tokens`);
}
send({
id: 1,
method: 'initialize',
params: { protocolVersion: '2025-06-18', capabilities: {}, clientInfo: { name: 'tool-cost', version: '1.0.0' } },
});
CLI がサーバーを起動するときと同じコマンドを付けて実行します。たとえば node mcp-tool-cost.mjs npx -y @playwright/mcp@latest です。最も重いツール 10 個と合計が表示されます。2026年11月3日の結果は次のとおりです。
| サーバー | ツール数 | JSON | 約 |
|---|---|---|---|
@modelcontextprotocol/server-filesystem 2.1.0 | 14 | 12,554 バイト | 3,100 トークン |
@playwright/mcp 0.0.84 | 25 | 21,034 バイト | 5,300 トークン |
| AgentsRoom ツール、10月28日の削減前 | 191 | 約 380,000 バイト | 95,000 トークン |
| AgentsRoom ツール、10月28日の削減後 | 191 | 約 152,000 バイト | 38,000 トークン |
| AgentsRoom ツール、11月3日 | 203 | 159,957 バイト | 40,000 トークン |
1 トークン 4 バイトは推定値です。サーバー同士を比べたり傾向を見たりするには十分ですが、請求額の予測には使えません。実際のトークナイザーはモデルとテキストによって変わります。私たちが自分たちのトークンを 38 億個数えたときにも、それを確認しました。
上の filesystem 系のサーバーは妥当な大きさです。問題は合計です。この大きさのサーバーが 5 つあれば、私たちのようなサーバーを加える前から 20,000 トークンになります。
削ったもの、手を付けなかったもの
私たちが決めたルールはこうです。ツールは 1 つも削除しない、パラメーターも削除しない、許可された値も必須フィールドも削除しない。 エージェントはすべての機能を保つ必要がありました。変えてよいのはテキストだけです。
- ツールの説明は「何を」と「いつ」を 1〜3 文で言う。 「どうやって」は言いません。バイト数がかさんでいたのは「どうやって」の部分でした。ルール、デフォルト値、例外的なケース、例、そしてエージェントが一度ツールを誤用したあとに書き足された警告です。
- パラメーターには 1 文、または何も書かない。 名前、型、許可された値の一覧ですでにすべて伝わる場合です。ツールをまたいで繰り返し出てくるパラメーター(
project、author、チケット ID)には、どこでも同じ標準の 1 文を付けました。 - 使い方の説明は削除せず、移動した。 ツールのファミリーごとに指示ファイルがあり、エージェントはそのファミリーを初めて呼ぶ前にそれを読みます。最も大きなツール、つまりエージェントのチームを保存するツールでは、フィールドの完全なリファレンスがスキーマに乗って毎回運ばれる代わりに、エージェントが求めたときにツール呼び出しの結果として返されるようになりました。
- 2 回目のパスですべての事実を確認した。 説明から削った文は 1 つずつ指示ファイルの中で探し、見つからなければそこに書き戻しました。
結果の一部です。ツールごとの JSON のバイト数です。
| ツール | 前 | 後 |
|---|---|---|
teams_save | 19,705 | 9,791 |
triggers_save | 16,014 | 4,071 |
backlog_update | 13,150 | 4,253 |
backlog_create | 11,565 | 4,399 |
合計では、380 KB が 152 KB になりました。4 KB を超えるツールは 13 個ありましたが、今も超えているのは 4 個で、持っているフィールドの数が理由です。この作業全体の考え方はこうです。ツールを選ぶためにモデルが知る必要があるのは、ツールが存在することと、いつ呼ぶかです。細部は、実際に使う瞬間まで待てます。
MCP サーバーを書いているなら、Claude Code がもう 1 つ理由をくれます。最初から読み込まれるツールの説明は 4,096 文字で切り詰められ、検索を説明と照らし合わせるときも、その上限内の部分しか読みません。それより後ろにある大事な情報は、一度も読まれないかもしれません。
起動ファイルにも同じ手を
固定コストはツールのスキーマだけではありません。私たちのエージェントは、ツールを使う前にツールについての指示ファイルも読みます。このファイルは 57.7 KB、約 14,000 トークンにまで膨らみ、起動と再開のたびに全文が読まれていました。あるユーザーからは、再開した Codex エージェントが数分で作業用のコンテキスト上限に達したという報告がありました。
対策は同じ考え方です。ファイルは約 11 KB のインデックスになりました。すべてのツールに当てはまるルールと、ツールのファミリーごとに専用ファイルを指す 1 行です。31 個のファミリーファイルは、そのファミリーを初めて呼ぶ前にだけ読まれます。中身のテキストは一字一句変えていません。バックログしか触らないエージェントは、データベースのセクションを一度も読みません。
これは長い CLAUDE.md や AGENTS.md にも使えます。常に読み込まれるファイルには、どのタスクにも必要なものだけを残し、残りはリンクにします。詳しくは AGENTS.md とコンテキストファイルのガイドに書きました。
プッシュを失敗させる予算
私たちが一番気にしていた数字は 95,000 ではありませんでした。1 週間前の 78,000 です。ツールが増えるたびに段落が 1 つ増え、どの段落ももっともなものでしたが、合計は 7 日で 20% 増えていました。
そこでサイズをビルドのチェックにしました。予算は 1 つの JSON ファイルに書きます。
{
"totalBytes": 160000,
"perToolBytes": 4000,
"oversized": {
"teams_save": 9791,
"triggers_save": 4071,
"backlog_update": 4253,
"backlog_create": 4399
}
}
スクリプトがサンドボックスで MCP サーバーを起動し、それぞれにツールを求め、JSON.stringify(tool) をバイト単位で測り、合計が totalBytes を超えるか、どれかのツールが perToolBytes を超えるとエラーで終了します。すでに大きすぎた 4 つのツールは、そのサイズとともに一覧に載っています。一方向にしか動かないラチェットです。小さくなってもよく、大きくなってはいけません。どれかが小さくなると、スクリプトは数字を下げるよう求めます。このチェックは プッシュのたびに、そしてリリースビルドでも実行されます。上限を上げることはできますが、それはファイルに書かれる判断であって、チェックを通すための手段ではありません。
6 日後の 11月3日、同じチェックの結果は 203 ツール、159,957 バイトでした。ツールが 12 個増えて、上限まで残り 43 バイトです。次のツールは、自分で場所を空ける必要があります。それこそがこのチェックの目的です。
使わないものはオフにする
一番安いトークンは、エージェントが一度も目にしないツールのトークンです。
- サーバーは必要なプロジェクトだけに限定する。 ユーザーレベルで宣言したサーバーは、どのリポジトリにもついてきます。Claude Code では、プロジェクトの
.mcp.jsonに書けば、役に立つ場所にとどめておけます。 alwaysLoadは少数のツールだけに使う。 Claude Code のドキュメントもはっきり書いています。最初から読み込まれるツールはどれも、本来なら会話に使えたはずのコンテキストを消費します。- 定義だけでなく、ツールの結果が足すものにも注意する。 ページやテーブルを丸ごと返すツールは、1 回の呼び出しで、定義が 1 日にかかるコストより高くつくことがあります。
AgentsRoom では、設定 > AgentsRoom ツール に私たちのツールのファミリーが重さとともに一覧表示され、ファミリー単位でも 1 つのツール単位でも、すべてのプロジェクト、1 つのプロジェクト、フォルダー、1 体のエージェントに対してオフにできます。オフにしたツールはエージェントに一切提示されないので、コンテキストを消費しません。AgentsRoom MCP のページでツールを紹介しています。またセッショントレースでは、MCP ツールも含め、エージェントのコンテキストを何が埋めたのかがサイズ順に表示されます。
変えなかったもの
同じ計測から、ほかに 2 つのコストが見つかりましたが、あえて手を付けませんでした。
- 起動時のブリーフ:各エージェントに役割と状態の報告方法を伝えるもので、Claude の場合の中央値は約 5,000 トークン、200,000 トークンのウィンドウの約 2.5% で、2 回目のリクエストからはキャッシュから読まれます。
- 各エージェントがターンの終わりに書くステータスファイル:ローカルにある Claude のトランスクリプト 71 件で測ると、コンテキストのトークンの約 2.5% です。
どちらを縮めても、節約できるのはウィンドウの 3% 未満で、エージェントの仕事の質を損なうおそれがありました。ツール定義は 95,000 トークンでした。時間をかけたのはそこです。
実際によく聞かれる質問
MCP ツールはどれくらいトークンを使いますか?
各ツールは定義全体のぶんだけコストがかかります。名前、説明、入力スキーマで、JSON として送られます。目安は 1 トークンあたり 4 バイトです。2026年11月3日の計測では、公式の filesystem サーバーは 14 ツールで約 3,100 トークン、Playwright MCP は 25 ツールで約 5,300 トークンでした。AgentsRoom のツールは短くする前は約 95,000 トークン、今日は約 40,000 トークンです。
Claude Code はすべての MCP ツールをコンテキストウィンドウに読み込みますか?
デフォルトでは読み込みません。Claude Code のツール検索(tool search)は MCP ツールの定義を後回しにします。セッション開始時に読み込まれるのはツール名とサーバーの指示だけで、Claude はツールが必要になったときにその定義を読み込みます。ENABLE_TOOL_SEARCH=false を設定する、Claude Opus 4 と Sonnet 4 より古いモデルを使う、またはサーバーに alwaysLoad を付けると、定義は最初からコンテキストに入ります。
MCP サーバーはトークン消費を減らしますか?
減らせる場合はあります。ツールが短い答えを返し、エージェントがそれなしでは全体を読むことになるものを置き換えるときです。たとえばダンプの代わりにデータベースへのクエリを使う場合です。ただし定義はコンテキストを消費し、結果も同じです。Claude Code はデフォルトで、1 つの MCP の結果が 10,000 トークンを超えると警告し、25,000 トークンで切り詰めます。MCP サーバーがトークンを節約するのは、その結果が置き換えるものより小さいときだけです。
MCP ツールがどれだけコンテキストを使っているか確認するには?
Claude Code では /context を実行します。コンテキストをソースごとに分解し、後回しにされている MCP ツールに印を付けます。どのサーバー、どの CLI でも使える方法は、サーバーにツール一覧を求めて JSON を測ることで、この記事の 40 行のスクリプトがまさにそれをします。
良い MCP ツールの説明とはどんなものですか?
ツールが何をするか、いつ呼ぶかを伝える 1〜3 文で、エージェントが検索しそうな言葉を冒頭近くに置いたものです。パラメーターには 1 文、名前と型と許可された値ですでに伝わるなら何も書きません。ルール、例外的なケース、長い例は、エージェントが必要なときに読むドキュメントに移します。どのみち Claude Code は、最初から読み込まれる説明を 4,096 文字で切り詰めます。
ツール定義が再び膨らむのを防ぐには?
ビルドがチェックできる予算を与えます。私たちは、全体の上限とツールごとの上限を書いた JSON ファイルと、MCP サーバーを起動してすべてのツールを測り、どちらかの上限を超えるとエラーで終了するスクリプトを用意し、プッシュのたびにそれを実行しています。すでに大きすぎたツールは小さくなってもよく、大きくなってはいけません。
まとめ
MCP ツールはモデルが読むテキストですが、そのテキストをコードのようにレビューする人はいません。一度測ってみてください。Claude Code なら /context、どのサーバーでも上のスクリプトで測れます。そのうえで、各説明には「何を」と「いつ」だけを言わせ、「どうやって」はエージェントが必要なときに読むファイルに移し、ビルドに数字を入れて、また膨らまないようにします。
私たちはツールを 1 つも削除せずに、約 95,000 トークンから約 38,000 トークンまで減らしました。同じツールで複数のエージェントを動かしているなら、AgentsRoom は無料でダウンロードできます。また MCP サーバーのページでは、サーバーを一度宣言するだけで、すべての CLI とプロジェクトで使う方法を紹介しています。
AgentsRoomをダウンロード
すべてのAIエージェントを、すべてのプロジェクトで、ひとつのウィンドウから実行。
コンパニオンアプリ:外出先でもエージェントを確認
Claude、Codex、Antigravity CLI、またはその他の AI プロバイダーを使用します。
バグや要望を公開バックログに直接送信できます。
続きを読む
AIコーディングエージェントがサーバーで何を実行しているかを見る方法
エージェントが本番サーバーで作業していて、ターミナルには「ツールを呼び出しています」としか出ていません。実際にどのコマンドを実行し、マシンは何を返したのか。トランスクリプトを読めば後からは分かります。それを実行中に見えるようにした方法、最初に試して失敗したこと、そして私たちのアプリを使わずに同じことを実現する方法を紹介します。
記事を読むプロンプトに書かずにコーディングエージェントへシークレットを渡す方法
エージェントがコマンドを動かすのに Stripe のキーやデータベースのパスワードを必要としています。チャットに貼り付ければ、それはディスク上のトランスクリプトに入り、モデルプロバイダーに届き、ときにはコミットにまで残ります。私たち自身の三つの失敗のあとにたどり着いたルール。シークレットがエージェントに届く経路は環境変数だけで、ほかにはありません。
記事を読むClaudeアカウント1つを2台のパソコンで、または2人で使う:どこまでできるか
1つのClaudeアカウントは手持ちのすべてのデバイスでログインでき、どのデバイスも同じ5時間の上限と週次の上限を、ClaudeとClaude Codeの合計で使います。2人で1つのアカウントを使うのはAnthropicの規約違反で、ファミリープランもありません。2026年10月25日に確認した内容と、どのパソコンが何を使ったかを見る方法をまとめました。
記事を読む