Claude Media
Agent SDKのgetContextUsage()で/context内訳を取る — fullとsummary

Agent SDKのgetContextUsage()で/context内訳を取る — fullとsummary

getContextUsage()は/contextと同じ内訳をコードから取る手段です。fullとsummaryの違い、請求とコスト集計への影響、返り値の読み方を説明します。

Agent SDKのgetContextUsage()は、対話セッションの/contextコマンドが描く内訳を、コードから取り出すメソッドです。カテゴリ別・MCPツール別・スキル別のトークン量が、型付きのオブジェクトで返ります。

この記事では、TypeScriptとPythonでの呼び出し方、detail引数のfullとsummaryの違い、そして「メッセージストリームに出ない」ことがコスト集計に与える影響を順に扱います。

getContextUsage()は何を返すメソッドか

getContextUsage()は、TypeScript SDKのquery()が返すQueryオブジェクトのメソッドです。戻り値はSDKControlGetContextUsageResponse型で、セッションのコンテキストウィンドウ使用量をカテゴリ・スキル・ツール別に分解したものです。

既定のdetailで呼ぶと、Claude Codeが対話セッションで/contextを表示するときと同じペイロードが返ります。そのため、トークン数に加えてcolorやgridRowsといった、使用状況のグリッドを描くための表示用フィールドも入っています。

Python SDKではClaudeSDKClient.get_context_usage()が対応するメソッドで、戻り値はContextUsageResponseです。同じ/contextのデータを返します。ClaudeSDKClientの位置づけはquery() vs ClaudeSDKClientにまとめています。

TypeScriptとPythonでの呼び出し方

TypeScriptの型定義は次のとおりです。detailは省略でき、省略するとfullになります。

getContextUsage(opts?: {
  detail?: 'summary' | 'full';
}): Promise<SDKControlGetContextUsageResponse>;

例えば、query()の戻り値を保持しておき、メッセージを読み終えた時点で呼ぶ形が書けます。次は公式のメソッド定義に沿った使用例です。

import { query } from "@anthropic-ai/claude-agent-sdk";
 
const q = query({ prompt: "リポジトリの構成を要約して" });
 
for await (const message of q) {
  if (message.type === "result") {
    const usage = await q.getContextUsage({ detail: "summary" });
    console.log(`${usage.totalTokens} / ${usage.maxTokens} (${usage.percentage}%)`);
  }
}

呼び出せるタイミングの制約は、該当節に記載がありません。上の位置は一例として、自分の環境で動作を確かめてから使ってください。

Pythonは引数なしです。

from claude_agent_sdk import ClaudeSDKClient
 
async with ClaudeSDKClient() as client:
    await client.query("リポジトリの構成を要約して")
    async for _ in client.receive_response():
        pass
    usage = await client.get_context_usage()
    print(usage["totalTokens"], usage["maxTokens"], usage["percentage"])

Pythonのリファレンスにはdetail引数の記載がありません。TypeScriptのfull / summaryの切り替えは、現状TypeScript側の仕様として読むのが安全です。

fullとsummaryは何が違うのか

違いは、各カテゴリのトークン数をどう数えるかにあります。

項目full(既定)summary
数え方full(既定)カテゴリごとにtoken-counting APIへリクエストsummary直近の応答のusageとローカル推定
追加のAPIリクエストfull(既定)ありsummaryなし
メッセージストリームへの表示full(既定)出ないsummary該当リクエスト自体がない
精度full(既定)カテゴリごとのAPI計測値summaryカテゴリ別の数値は概算
必要なSDKfull(既定)引数なしの呼び出しは従来どおりsummaryv0.3.257以降

fullは、Claude Codeが各カテゴリをtoken-counting APIで数えます。精度を取るときの選択肢です。

summaryは{ detail: 'summary' }を渡したときの挙動で、token-countのリクエストは外へ出ません。その分カテゴリ別の数値は近似値です。

detail引数そのものがv0.3.257以降の機能です。それより古いSDKを使っているなら、先にバージョンを上げます。

fullの追加リクエストがコスト集計に乗らない理由

fullで数えるときのtoken-countingリクエストは、メッセージストリームに現れません。公式の説明はこうです。ストリームを読んでコストを集計している実装には、これらのリクエストは見えません。

Anthropic APIでは、token countingは課金されません。したがって、集計に乗らないことと、請求が増えることは別の話です。

押さえておく点は2つあります。

  • 集計がストリームのusageとtotal_cost_usdだけで組まれているなら、getContextUsage()を呼んでも数字は動かない
  • 課金されないと書かれているのは「Anthropic APIでは」の範囲で、他のプロバイダー経由の扱いはこの節に記載がない

ストリームから金額を読む方法は、Agent SDK Cost Trackingで扱っています。getContextUsage()の結果はその金額とは別の系統で、混ぜて合算しないのが前提です。

返り値のどこを読めばトークンの出どころが分かるか

返り値は大きく、全体の数字と、出どころ別の配列に分かれます。

全体の数字は次の4つです。

  • totalTokens: セッションの現在のコンテキスト使用量
  • maxTokens: その使用量を測る窓の大きさ。モデルのコンテキストウィンドウ、または適用されている低めの自動コンパクション窓
  • rawMaxTokens: maxTokensと同じ値
  • percentage: totalTokensを窓に対する整数の割合にしたもの

apiUsageは、直近のAPI応答のusageです。セッション累計ではありません。累計の代わりに使わないでください。

出どころ別の配列は次のとおりです。

フィールド何が分かるか
categories何が分かるかカテゴリごとの合計。kindはused / free / buffer / deferred
mcpTools何が分かるかMCPツール1本ごと(nameとserverName付き)のトークン
agents何が分かるかサブエージェント定義ごとのトークン
memoryFiles何が分かるか読み込まれたメモリファイルごとのパスとトークン
skills何が分かるかskillFrontmatterでスキルごとの一覧エントリのトークン
messageBreakdown何が分かるかツール呼び出し・結果・添付・アシスタント・ユーザー発言の内訳

categoriesの行を判定するときは、表示名のnameではなくkindで見ます。nameは/contextの表示名で、変わりうるためです。このkindフィールドはAgent SDK v0.3.268以降です。

deferredの行は、Claude Codeがウィンドウの外に保留しているツールスキーマで、使用量の計算から除かれています。MCPツールの定義が膨らむ仕組みはMCPのツール定義とコンテキスト圧迫に詳しく書いています。

スキルについては、skills.totalSkillsとskills.includedSkillsを比べると、見つかったスキルが全部一覧に入ったかが分かります。skillFrontmatterの各数値は、Claude Codeが実際に送る一覧エントリ単位の値で、フロントマター全体より短くなることがあります。

型に書いてあるのに返ってこないフィールド

型定義にはdeferredBuiltinTools、systemTools、systemPromptSectionsが載っています。Claude Codeはこの3つを未設定のままにするため、値は入っていません。コードでは存在チェックを前提にします。

const tools = usage.systemTools ?? [];
if (tools.length === 0) {
  // 型にはあるが、Claude Codeは埋めない
}

「システムプロンプトの内訳が取れる」と読める型ですが、そのつもりで集計を組むと空になります。

次のターンへの余裕を測る使い方

実務での使いどころは、しきい値の監視です。percentageやtotalTokensとautoCompactThresholdを並べると、自動コンパクトまでの距離が分かります。

const usage = await q.getContextUsage({ detail: "summary" });
 
if (usage.isAutoCompactEnabled && usage.autoCompactThreshold) {
  const room = usage.autoCompactThreshold - usage.totalTokens;
  if (room < 20_000) {
    console.warn(`自動コンパクトまで残り約${room}トークン`);
  }
}

監視を毎ターン走らせるなら、summaryが向きます。token-countのリクエストが出ないためです。数値は概算なので、しきい値は余裕を持って決めます。

内訳を調べる調査用途ならfullです。MCPツールが数十本ある構成で、どのサーバーのツールが多く食っているかをmcpToolsから降順に並べる、といった使い方になります。

const usage = await q.getContextUsage();
const top = [...usage.mcpTools]
  .sort((a, b) => b.tokens - a.tokens)
  .slice(0, 5);
for (const t of top) console.log(t.serverName, t.name, t.tokens);

どの対策を選ぶかはコンテキスト管理の使い分け基準が参考になります。数字で出どころを突き止めたあとの判断材料です。

/contextをプロンプトとして送る場合との違い

getContextUsage()を呼ぶ代わりに、/contextをプロンプトとして送る方法もあります。この場合、結果を届けるアシスタントメッセージのcontext_usageフィールドに、SDKContextUsage型のペイロードが付きます。Agent SDK v0.3.232以降の機能です。

SDKContextUsageはgetContextUsage()の返り値と形が違います。レンダリング用のcolorやgridRowsを持たず、キーもtotal_tokensやraw_max_tokensのようにスネークケースです。

比較点getContextUsage()/contextをプロンプトで送る
型getContextUsage()SDKControlGetContextUsageResponse/contextをプロンプトで送るSDKContextUsage
キーの表記getContextUsage()totalTokensなどキャメルケース/contextをプロンプトで送るtotal_tokensなどスネークケース
表示用フィールドgetContextUsage()color、gridRowsあり/contextをプロンプトで送るなし
上限超過の表現getContextUsage()該当フィールドなし/contextをプロンプトで送るover_limit(tokens_overとkind)
取り方getContextUsage()メソッド呼び出し/contextをプロンプトで送るメッセージストリームから読む

over_limitは、total_tokensがraw_max_tokensを超えたときだけ付きます。kindがhard_limitなら、APIが拒否する窓を指します。compaction_windowは圧縮方針上の窓で、モデルの限界と一致するとは限りません。

どちらの方法でも、Claude Code内部のtoken-countingリクエストはストリームに出ません。この性質は共通です。

categoriesを集計するときに外す行

categoriesには、実際にウィンドウを占めている行だけが入っているわけではありません。kindがfree(残りの窓)、buffer(コンパクション用の予備)、deferred(窓の外に保留したツールスキーマ)の行も混ざります。

「いま何がどれだけ詰まっているか」を知りたいなら、kind === "used"の行だけを足し合わせます。deferredの行は使用量の計算から除外されているので、合計に含めるとtotalTokensと食い違います。

const used = usage.categories.filter((c) => c.kind === "used");
const rows = used
  .map((c) => ({ name: c.name, tokens: c.tokens }))
  .sort((a, b) => b.tokens - a.tokens);
console.table(rows);

行が0トークンのこともあります。SDKContextUsageCategoryの説明にも「行が0トークンを持ちうる」とあるため、割り算で比率を出すときは0除算を避けます。

messageBreakdownで会話の中身を見る

セッションが長くなったとき、固定費(システム側のツール定義やメモリ)が重いのか、会話そのものが重いのかで打ち手が変わります。messageBreakdownは後者の内訳です。

  • toolCallTokensとtoolResultTokens: ツール呼び出しと、その結果が占める量
  • attachmentTokens: 添付
  • assistantMessageTokensとuserMessageTokens: 発言そのもの
  • redirectedContextTokensとunattributedTokens: 上の項目とは別に集計される分(公式に個別の説明なし)

さらにtoolCallsByTypeで、ツール名ごとの呼び出しトークンと結果トークンが並びます。toolResultTokensが突出していて、その大半が特定のツール1種に偏っているなら、結果の取り込み方を見直す候補になります。古いツール結果を自動で外す設定は、tool result clearingの設定オプションで扱っています。

messageBreakdownは省略可能なフィールドです。取れなかったときは存在しない前提で読みます。

メモリファイルとスキルの肥大を見つける

固定費の側では、memoryFilesが読み込まれたメモリファイルをpath、type、tokensの3つで並べます。typeにはProjectやUserのような出どころのラベルが入ります。CLAUDE.mdが太って毎回の入力を押し上げていないかは、ここを降順に見れば分かります。

スキルは、skills.skillFrontmatterで1件ずつの一覧エントリを確認できます。totalSkillsとincludedSkillsに差があれば、発見されたスキルのうち一覧に入らなかったものがあります。スキルを増やしたのに呼ばれない、という症状の切り分けの入口になります。

サブエージェントはagentsに、agentType、source、tokensの形で載ります。組み込みのサブエージェントは含まれず、カスタム定義だけが対象です。

fullとsummaryを使い分ける運用の組み方

2つのモードは排他ではなく、頻度で役割を分けると扱いやすくなります。

手順

監視から調査までの流れ

  1. 1

    毎ターンはsummaryで見る

    percentageとautoCompactThresholdだけを読み、余裕の目安にします。token-countのリクエストは出ません。

  2. 2

    しきい値を越えたらfullで取り直す

    超えたターンだけfullで内訳を取り、mcpToolsやmessageBreakdownから原因の行を探します。

  3. 3

    結果を自前のログへ書く

    戻り値は自分で保存しない限り残りません。調査結果はログに書きます。コスト集計とは別の記録として持ちます。

summaryは概算なので、summaryの数字とfullの数字は一致しません。しきい値判定はsummaryで、金額や上限の議論に使う数字はfullで取る、と決めておくと混乱しません。

つまずきやすい点

最後に、読み違えやすい点をまとめます。

  • detailの指定はv0.3.257以降
  • categories[].kindはv0.3.268以降。表示名のnameで判定しない
  • apiUsageは直近1回の値で、累計ではない
  • /contextをプロンプトで送った場合のpercentageは、上限超過時に100を超えうる
  • 型にあってもsystemToolsなどの3つは空

対話セッションで/contextを見慣れている人ほど、コードから取るときに「同じ数字が出る」と期待しがちです。fullなら同じ種類のデータですが、summaryは概算です。値を比べるときは、どちらで取ったかを先に決めておくと食い違いの原因を切り分けられます。

この記事を共有:XはてブLinkedIn