AI Navigate

Claude Code の API 呼び出しをスニファする: IDE が実際に送信している内容

Dev.to / 2026/3/16

💬 オピニオンDeveloper Stack & InfrastructureTools & Practical Usage

要点

  • この記事は、Claude Code が完全なチャットコンテキスト(システムプロンプト、メッセージ履歴、ツール定義)を Anthropic のサーバへ API 呼び出しとして送信していることを暴露しており、それらは通常ユーザーには隠されています。
  • ANTHROPIC_BASE_URL 環境変数を用いてローカルのスニファ経由でトラフィックをルーティングする、実用的な傍受手法を説明しており、TLS の介入なしに生の API リクエストをログに記録できるようにします。
  • このセットアップは claudetui sniffer および claudetui sniff を用いて Claude Code をプロキシ経由で透過的に実行させ、IDE が通常どおり動作するようにします。
  • ログには各呼び出しの詳細(HTTP メソッド、エンドポイント(例: POST /v1/messages)、ペイロードのサイズ、レイテンシ)が含まれ、IDE が AI サービスとどのように通信しているかを露呈します。

Enterを押すたびに、Claude Codeの裏側で何か興味深いことが起きます。あなたの全会話 — システムプロンプト、メッセージ履歴、ツール定義、すべて — がAPIコールにパックされ、Anthropicのサーバーへ送信されます。

しかし、そんな呼び出しを直接見ることはできません。Claude Code は、行ったことのJSONLトランスクリプト(ツール呼び出し、応答、思考ブロック)をログに残しますが、それを実現した生のAPIトラフィックは表示されません。システムプロンプト、HTTPヘッダー、リクエストパラメータ、呼び出しごとの待機時間、そして1つの完全に隠されたAPI呼び出し — すべてが見えません。

そこで、すべてを見られる方法を作りました。

コツ: 環境変数を1つ

Claude Code は公式に ANTHROPIC_BASE_URL をサポートしています — APIトラフィックをカスタムエンドポイントへリダイレクトする環境変数です。エンタープライズ用プロキシを想定していますが、ローカルでのインターセプションには完璧に機能します:

Claude Code  ──plain HTTP──▶  Sniffer (localhost:7735)  ──HTTPS──▶  api.anthropic.com
                                    │
                                    ▼
                          ~/.claude/api-sniffer/*.jsonl

1つの端末でスニファーを起動し、別の端末で Claude Code を起動します:

# Terminal 1
claudetui sniffer

# Terminal 2
claudetui sniff

claudetui sniff はスニファーポートを自動検出し、プロキシ経由で Claude Code を起動します。スニファーが動作していない場合は、Claude Code を直接起動するフォールバックになり、ConnectionRefused のリトライループに閉じ込められることはありません。

すべてのAPI呼び出しは現在、プロキシを通過してログに記録されます。Claude Codeは同じ動作をします — トラフィックがキャプチャされていることを知っている(あるいは気にする)ことはありません。

TLSの盗聴はありません。証明書もありません。バイナリのパッチもありません。localhostのHTTPサーバーが実APIへHTTPSで転送するだけです。

見えるもの

スニファーはAPI呼び出しが発生するたびに1行を表示します:

  ClaudeTUI API Sniffer — listening on http://127.0.0.1:7735

  Use:  ANTHROPIC_BASE_URL=http://localhost:7735 claude
  Log:  ~/.claude/api-sniffer/sniffer-20260314-103000.jsonl

  #1   POST /v1/messages  opus-4-6  45.2k->1.5k  $0.120  2312ms  740KB/4.2KB  98%c  [Tt]
  #2   POST /v1/messages  opus-4-6  48.1k->0.8k  $0.094  1134ms  741KB/2.1KB  99%c  [TU]  Edit
  #3   POST /v1/messages  opus-4-6  50.3k->52    $0.081  1823ms  742KB/0.3KB  100%c  [U]  Glob,Grep
  #4   POST /v1/messages  opus-4-6  12.3k->2.1k  $0.041  3412ms  42KB/6.8KB   95%c  [Tt]  compaction
  #5   POST /v1/messages  sonnet-4-6  14.3k->2.1k  $0.008  2341ms  42KB/6.8KB  [Tt]  +agent.1

  Summary: 5 requests | $0.344 | 170k in | 5.6k out | 2.3MB sent | 18KB recv | 1 sub-agent

各行には、モデル、入力→出力トークン、推定コスト、レイテンシ、トラフィックサイズ、キャッシュヒット率、コンテンツブロックの種類、ツール名、およびサブエージェントの追跡が表示されます。圧縮イベントは自動的にフラグ付けされます。

コンテンツブロックは Claude が何をしているかを教えてくれます:T = thinking、t = テキスト、U = ツールの使用、S = サーバーサイドツール(WebSearch など)。キャッシュ比率(98%c)はどれだけ節約できているかを示します — 0%c(赤で表示)はキャッシュミスを意味し、12.5倍高くつきます。

一方、すべてのリクエストとレスポンスは、後の分析用に構造化されたJSONLとして記録されます。

転写が教えてくれないこと

Claude Code のJSONL転写は有用ですが、見落としも多いです。スニファーが捉えるが転写には含まれないものを以下に示します:

データ 転写に含まれるか スニファーに含まれるか
トークン使用量(入力/出力/キャッシュ) はい はい
生のシステムプロンプト いいえ はい
リクエスト毎の完全な会話履歴 いいえ はい
リクエストパラメータ(max_tokens、temperature) いいえ はい
HTTP ヘッダー(anthropic-beta、version) いいえ はい
リクエスト/レスポンスのレイテンシ いいえ はい
隠れた圧縮 API 呼び出し いいえ はい
エラー応答本文 部分的 はい
ストリーミング SSE イベント いいえ はい
ツール定義(完全なJSONスキーマ) いいえ はい

このリストの中で最も興味深い項目は、システムプロンプトと隠れた圧縮呼び出しです。

システムプロンプト

Claude Code のシステムプロンプトは、すべてのAPI呼び出しごとに送信されます。含まれる内容は以下のとおりです:

  • Claude Code の内部指示および行動ガイドライン
  • ツール定義(Read, Write, Edit, Bash, Glob, Grep など)と完全なJSONスキーマ
  • あなたの CLAUDE.md プロジェクト指示
  • メモリファイル、フックの出力、その他の埋め込まれたコンテキスト
  • 安全性と許可のガイドライン

--full モードでは、スニファーは完全なシステムプロンプトのテキストをキャプチャします。私たちのセッションでは、常に 約14kトークン を測定します — すべてのAPI呼び出しに対する一定の税です。

これは、Claude Code があなたのプロジェクトについて「知っている」ことを正確に理解するのに役立ちます。あなたの CLAUDE.md、あなたのフック出力、あなたのメモリファイル — それらすべてがシステムプロンプトにあり、今はそれを読むことができます。

隠れた圧縮呼び出し

これは私たちが最も好奇心をそそられたものです。

Claude Code のコンテキストウィンドウがいっぱいになると(約167kトークン/200kトークン)、圧縮が発生します。会話全体が要約に圧縮され、次のターンはシステムプロンプト + 要約だけで新たに始まります。

ただし、圧縮要約を生成するAPI呼び出しは転写には現れません。 Claude Code はそれを実行し、要約を受け取って続行します — しかしJSONL転写には何も表示されません。compact_boundary マーカーは見えますが、実際の要約呼び出しは見えません。

スニファーは、それが単なる別の POST /v1/messages だから検知します:

  #12  POST /v1/messages  opus-4-6  12.3k->2.1k  $0.041  3412ms  compaction

スニファーは連続するリクエストを比較して圧縮を検知します。前のリクエストと比べてメッセージ数が50%以上、または総コンテンツサイズが70%以上低下した場合、それを「ポスト圧縮」とフラグ付けします。167kトークンの会話が約15kの要約へと劇的に縮小する様は、誰の目にも明らかです。

これは、圧縮呼び出しの実際のコスト、レイテンシ、出力トークンを観察する唯一の方法です。私たちのセッションでは、圧縮要約の生成は2〜4秒かかり、11k〜19kトークンの圧縮済みコンテキストを生成します。

ツール使用ループ

スニファーで次のような行を見たら:

  #17  POST /v1/messages  opus-4-6  114.3k->531  $0.217  16047ms  [U]  tool

114kトークン入力にもかかわらず出力は531のみです。どうしてこんなに少ない出力トークンになっているのでしょうか?理由は、Claude が文章を書いているのではなく、ツールを呼び出しているからです。応答はただの小さなJSONブロックです:

{"type": "tool_use", "name": "Read", "input": {"file_path": "/src/app.py"}}

Here's the full cycle for a single tool call:

  1. Claude Code sends the full conversation to the API (114.3k input tokens — system prompt, message history, tool definitions, everything)
  2. API responds with a tool_use block — just the tool name and parameters (531 output tokens)
  3. Claude Code executes the tool locally — reads the file, runs the command, whatever the tool does
  4. Claude Code sends another request with the tool result appended as a tool_result message — now input tokens are higher because the file contents (or command output) are part of the conversation

That's why you see rapid back-to-back requests in the sniffer. A single "read this file and edit it" from the user might generate 5+ API calls:

  #17  POST /v1/messages  opus-4-6  114.3k->531   [U]  ツール     ← ファイルを読む
  #18  POST /v1/messages  opus-4-6  116.1k->204   [U]  ツール     ← ファイルを編集することを決定
  #19  POST /v1/messages  opus-4-6  117.8k->1.2k  [Tt]          ← ユーザーへ応答する

各往復ごとにツールの結果が会話に追加され、入力トークンが増えます。これが、文脈が予想より速く埋まってしまう理由です — ツールの結果(ファイルの内容、コマンド出力、検索結果)は、ツール呼び出し自体よりもはるかに大きな場合が多いです。

SSE ストリーミングの仕組み

Claude Code はストリーミング応答に Server-Sent Events (SSE) を使用します。API は text/event-stream を返し、モデルがトークンを生成するにつれてデータをチャンクで送信します。

スニファはこれを透過的に処理します — 到着した各チャンクを Claude Code にそのまま転送します(遅延を感じません)、同時に全ストリームをロギング用にキャプチャします。

ストリーミングが完了した後、SSE イベントを再構成して、構造化データを抽出します:モデル、使用量、停止理由、コンテンツブロックの種類(テキスト、思考中、ツール使用)。これが、ワンライナーの端末出力を可能にする要因です — 生の SSE データの代わりに 45.2k->1.5k $0.120 2312ms が表示されます。

技術的な重要点: response.read1(8192)response.read(8192) の代わりに使用します。read1() メソッドは、フルバッファが満たされるのを待たずに、現在利用可能なデータを読み取ります — ストリーミングでは部分データを直ちに転送する必要があります。

サブエージェントの追跡

Claude Code がサブエージェントを起動すると(Agent ツール経由)、サブエージェントが独自の API 呼び出しを行います — 多くは別のモデルを使用します。スニファはセッションIDでこれを追跡します:

  #8   POST /v1/messages  opus-4-6    89.1k->3.2k  $0.182  8234ms  99%c  [エージェント
  #9   POST /v1/messages  sonnet-4-6  14.3k->2.1k  $0.008  2341ms         [+エージェント.1
  #10  POST /v1/messages  sonnet-4-6  16.5k->1.2k  $0.006  1823ms         [エージェント] Read  agent.1
  #11  POST /v1/messages  sonnet-4-6  22.8k->0.5k  $0.009  1243ms         [t]   agent.1
  #12  POST /v1/messages  opus-4-6    92.3k->1.5k  $0.152  4312ms  99%c  [Tt]

+agent.1 は新しいサブエージェントの最初のリクエストを示します。同じエージェントからの以降のリクエストは agent.1 を示します。メインセッションにはラベルがありません。

これにより、転写だけからは見えない事柄が明らかになります:サブエージェントは研究タスクにはしばしば Sonnet を使用します(より安価で高速)、メインセッションは Opus で実行されます。各サブエージェントが行う API 呼び出しの数、コスト、メインセッションとの重なり方を正確に見ることができます。

キャッシュミス — 静かなコストの急上昇

キャッシュ比率(98%c100%c)は、入力トークンの何パーセントがキャッシュ読み取りだったかを示します。ほとんどの時間、それはほぼ 100% です — 安価な料金を払っていることを意味します。

ただし、セッションを約5分間アイドル状態にしておくと、何が起こるか観察してください:

  #6   POST /v1/messages  opus-4-6  129.4k->15   $0.199   3336ms  100%c  [t]
  #7   POST /v1/messages  opus-4-6  129.5k->428  $2.460  16108ms  0%c    [Tt]
  #8   POST /v1/messages  opus-4-6  130.0k->600  $0.248  18310ms  100%c  [Tt]

リクエスト #7 のコストは $2.46 — 通常より約12.5倍 — キャッシュが期限切れになったためです。すべての 129.5k トークンは cache_creation を $18.75/1M で処理され、 cache_read の $1.50/1M ではありません。同じデータ、同じトークンでも、価格は大きく異なります。

スニファは、これらのキャッシュミスを見逃せないよう、0%c を赤色で表示します。

Per-Request Cost Tracking

スニファは、レスポンスのトークン内訳を用いて API 呼び出しごとのコストを計算します:

{
  "usage": {
    "input_tokens": 3,
    "cache_read_input_tokens": 45000,
    "cache_creation_input_tokens": 800,
    "output_tokens": 1500
  }
}

モデルごとの価格設定(Opus: 入力/キャッシュ読み取り/キャッシュ書き込み/出力の 1M トークンあたり $15/$1.50/$18.75/$75)では、各行がその呼び出しの正確なコストを示します。推定も平均もなし — リクエストごとの実コストです。

これにより、予想していなかったことが明らかになりました:呼び出し間のばらつきは非常に大きいです。単純な応答は $0.03 程度ですが、長いコード生成は $0.50 以上になることもあり得ます — 同じセッション、同じモデルで。

What We Learned

実際のセッションでスニファを実行した後、いくつか目立つ点がありました:

1. システムプロンプトは非常に安定しています。 セッション内の呼び出し間でほとんど変化しません。約14k トークンは最初の呼び出しの後ほぼ全てがキャッシュされ、安価になります(Opus の $15/M に対して $1.50/M)。ただし、文脈ウィンドウの容量は消費します。

2. コンパクションは遅延に対して高コスト、トークンだけでなく。 概要生成の呼び出しには 2-4 秒かかります — その間 Claude Code は応答しません。3 回のコンパクションが長いセッションで発生すると、6-12 秒の待機時間になります。

3. キャッシュヒット率は驚異的です。 通常のセッションでは、入力トークンの 95-98% がキャッシュ読み取りです。ステートレス API の設計は高価に聞こえますが、キャッシュにより実用的になります。

4. エラー応答は、予想以上に情報量が多いです。 Claude Code が 429(レート制限)や 529(過負荷)に遭遇すると、応答本文に retry-after ヘッダや詳細なエラーメッセージが含まれることがあります。これらは Claude Code のリトライロジックによって隠され、あなたには表示されません。

5. ベータヘッダーは機能フラグを明らかにします。 anthropic-beta ヘッダは、アクティブな実験機能を示します。Claude Code のバージョン間での変化を見守るのは興味深いです。

セキュリティノート

スニファはデフォルトで安全に設計されています:

  • ローカルホストのみ127.0.0.1 にバインドされ、決して 0.0.0.0 には公開されません
  • API キーは伏字x-api-key および authorization ヘッダはログからデフォルトで削除されます(上書きするには --no-redact を使用)
  • 権限を制限 — ログファイルは 0o600 で作成されます(所有者のみ読み取り/書き込み)
  • ローカルプレーンテキスト — API キーはループバックインターフェースを介してのみ平文で伝送され、ローカルプロキシパターンの標準です

Try It

スニファは ClaudeTUI の一部です:
"}

私たちが検討していることの一部:

  • キャプチャ済みセッションの再現 コストモデリングのために(「このセッションは Sonnet と Opus のどちらでどれくらいのコストになりますか?」)
  • システムプロンプトの差分比較 Claude Code のバージョン間で変更を追跡
  • 待機時間(レイテンシ)とコンテキストサイズの相関 — 応答時間は入力トークン数と線形に比例しますか?
  • 圧縮要約の分析 — 何が保持され、何が失われるのか?

APIレベルであなたの Claude Code セッションが実際にどう見えるかが気になる場合は、セッションにスニファーを向けてデータの流れを観察してください。

ClaudeTUI はオープンソースで MIT ライセンスです。標準ライブラリのみの Python、外部依存関係ゼロ。

GitHub: github.com/slima4/claude-tui