Claude Code を CI や自動化スクリプトから叩いて、結果を JSON で受け取って別ツールに流したい人向け
Claude Code の返答を人が画面で読むのではなく、jq などで値を抜いて別ツール(Slack 通知・DB 投入・GitHub Actions の後続ジョブ)に流したい場面で、claude -p の後ろに --output-format json または --output-format stream-json を足して使う。コストや使用トークンも JSON から抜けるので、月次の費用集計を組みたい時にも叩く。
Claude Code を -p で叩いて返ってくる文字列を、人が読む素のテキストではなく、機械が読める json や stream-json の形で受け取るための起動スイッチです。CI から呼んで結果を別ツールに流したい時に、ここを切り替えないと「テキストを正規表現で切り刻む」みたいな苦しい後処理が始まります。
素のテキストを返すのが既定で、--output-format を書かないと text として動きます。つまりこのスイッチは「機械処理に切り替えたい時だけ明示的に呼び出す」道具という位置づけです。
噛み砕くと
レストランの注文の出し方を3段階で選んでる感じです。text は普通にお皿で出てくる料理、json は完成品が伝票つきで届く弁当、stream-json は厨房で1品ずつ出来た順に運ばれてくる「お任せコース」。同じ料理(Claude の返答)でも、誰がどう受け取るかで皿の形を変えないと、後工程が困ります。
人間が直接読むなら text で十分。スクリプトで jq に食わせて値を抜きたいなら json。長い返答をターミナル画面に流しながら見せたい、途中経過を Slack に WebSocket で投げたい、みたいな「流しっぱなし」用途は stream-json。この3択を間違えると、後ろの処理が組めなくなります。
大事な前提:print mode でしか効かない
この切り替えは、コマンドの後ろに -p を書いた時にだけ意味があります。claude --output-format json だけで叩いても、対話画面が立ち上がるだけで JSON は返ってきません。CI 用の起動スイッチであって、対話画面の表示形式を変えるものではない、と覚えると間違えません。
「料理ブログの新着レシピを CI でレビューさせ、結果を Slack に流す」を例に、実際の手順を見る
料理ブログの GitHub プロジェクト一式に、新しいレシピ記事の Markdown が push されるたびに、Claude Code に「誤字・分量の単位ブレ・手順の抜け」をチェックさせ、結果を Slack の編集部チャンネルに通知する流れを想定します。CI から呼ぶので、返答は JSON で受け取って整形してから Slack に投げます。
ステップ1: まず素のテキストで動かして、返答の中身を目で確認する
いきなり JSON にせず、最初は text で挙動を見ます。何も指定しないと text なので、書く必要すらありません。
$ claude -p "Review the recipe draft in recipes/2026-05-mapo-tofu.md for typos, unit inconsistencies, and missing steps."
返ってくるのは普通の日本語の指摘文。「『大さじ1』と『tbsp1』が混在しています」みたいな指摘がそのまま画面に流れます。CI のログにも残るので、最初の動作確認はこの形で十分です。
ステップ2: --output-format json に切り替えて、機械処理に渡せる形にする
Slack に流すには、本文だけ抜き出したい。そこで JSON 形式に切り替えます。
$ claude -p "Review the recipe draft ..." --output-format json | jq -r '.result'
返答全体は JSON で、result という箱の中にレビュー本文が入っています。jq -r '.result' で囲まれた本文だけを raw 文字列として抜き出せる。これを Slack 通知用の payload に詰めれば、編集部チャンネルに整った形で投げられます。
ステップ3: 同じ JSON から「今回いくら使ったか」も抜く
ここで初心者がやりがちな勘違いがあります。--output-format json で返ってくる JSON には、本文の result 以外にも、セッション識別子・使用トークン量・total_cost_usd(このレビューで使った概算ドル)が一緒に入っています。コストを把握したいなら使用状況のダッシュボードを毎回見に行く必要はなく、ここから抜けば済む。
$ claude -p "Review the recipe draft ..." --output-format json > review.json
$ jq -r '.result' review.json > review.txt
$ jq -r '.total_cost_usd' review.json
0.0247
Slack 通知の末尾に「今回のレビューは $0.0247 でした」と添えると、編集長が月末に「Claude 代いくら?」と聞いてきた時に即答できます。
ステップ4: 指摘のフォーマットを固定したいなら --json-schema を合わせる
レビュー結果を Slack に流すだけでなく、別の集計ツールにも食わせたい。そうなると「自由形式の日本語文」では困るので、構造を強制します。
$ claude -p "Extract issues from the recipe draft as an array." \
--output-format json \
--json-schema '{"type":"object","properties":{"issues":{"type":"array","items":{"type":"string"}}},"required":["issues"]}'
このとき、構造化された出力は result ではなく structured_output という別の箱に入って返ります。jq '.structured_output.issues' で配列を抜き、Slack のリスト形式に整形できる。フォーマットを固定したい時に「結果は .result から取ればいい」と思い込んでスカスカの値しか取れない、というのが典型的な詰まりどころです。
ステップ5: トークンが出来た順に流したいなら stream-json
レビューが長文になる場合、CI の画面で待たされるのが気持ち悪い。トークンが生成された順にリアルタイムで流したい時は stream-json に切り替えます。公式の推奨パターンは --verbose と --include-partial-messages を合わせる形です。
$ claude -p "Review the recipe draft ..." \
--output-format stream-json \
--verbose \
--include-partial-messages
返ってくるのは「改行で区切られた JSON の列」です。1行ごとに system/init、stream_event、system/api_retry のような種別が並びます。JSON 配列ではなく、1行1オブジェクトの「縦に積み上げ」型。jq で扱う時も jq -c で1行ずつ処理するのが普通です。
ステップ6: ターミナルに「打鍵中の文字」をそのまま流したい時の典型コマンド
stream-json のうち、テキストの差分だけを取り出してそのまま画面に垂れ流す形は、公式 docs にそのまま載っています。
$ claude -p "Review the recipe draft ..." \
--output-format stream-json --verbose --include-partial-messages \
| jq -rj 'select(.type == "stream_event" and .event.delta.type? == "text_delta") | .event.delta.text'
これで、生成された文字が出来た順にぬるっと画面に出ます。Slack の Block Kit に流し込むみたいに「途中経過を見せたい」UI を作る時の土台になります。
つまり --output-format は何をしてくれるのか
- やってくれる: 返答を
text(人間用)、json(スクリプト用)、stream-json(流し用)の3形式から選ぶ。jsonには本文・セッション識別子・コストが一緒に入る - やってくれない: 対話画面の表示形式の変更。print mode でしか効かないので、普通に
claudeで立ち上げた画面の出力には一切影響しない - 意味が薄い場面: 自分が画面で読んで終わり、結果を別ツールに渡さない用途。
textのまま動かせばよく、わざわざjsonにしてjqを挟む必要はない
使いどころ3シナリオ(具体題材で再現)
シナリオ1: 料理ブログの新着レシピを GitHub Actions でレビューさせる
新しいレシピ Markdown が main に push されたら、GitHub Actions が claude -p "Review ..." --output-format json --bare を叩き、jq -r '.result' でレビュー本文を抜き、Slack の編集部チャンネルに Webhook で投げる。同時に total_cost_usd を抜いて月次の Google スプレッドシートに行追加する流れ。--bare もつけると、ローカルの ~/.claude 設定に左右されず、GitHub Actions のランナーでも同じ結果が出ます。
シナリオ2: 家計簿アプリの「領収書 OCR 結果から品目を抜く」バッチ処理
領収書を OCR したテキストファイルが ~/receipts/2026-05/*.txt に溜まっていく。これを夜間バッチで claude -p "Extract items as {item, price, qty}" --output-format json --json-schema '...' < receipt.txt に流し込み、structured_output.items を抜いて家計簿 DB に INSERT する。スキーマで形を固定しているので、後段の DB スキーマと食い違わない。フリーフォーマットのレビュー文を正規表現で切るより、はるかに壊れにくい運用になります。
シナリオ3: 長文レポートを Slack スレッドに生成中ストリーミングする社内ツール
営業会議の議事録(30,000字)を要約させて、Slack スレッドに「書き始め〜書き終わりまで」をリアルタイムで流す内製ツール。--output-format stream-json --verbose --include-partial-messages で text_delta イベントを拾い、200文字たまるごとに Slack の chat.update でスレッド本文を上書きする。途中経過が見えるので、参加者は「いま AI が何を要約しているか」を待たずに確認できる。社内ツールの体験を「待ち時間ゼロっぽく」見せる定番パターンです。
初心者が踏みやすい落とし穴
- print mode 専用なのを忘れる。
claude --output-format jsonだけで叩くと対話画面が立ち上がるだけで、JSON は何も返ってこない。必ず-pまたは--printとセットで叩く --json-schemaを使った時、結果が.resultではなく.structured_outputに入る。「jq -r '.result'したら空っぽだった」の8割はこれ。スキーマ準拠の構造化出力は別の箱に入る、と覚える- stream-json は JSON 配列ではない。改行で区切られた JSON が縦に並ぶ形式なので、丸ごと
JSON.parseに渡すと一発で壊れる。1行ずつ読むか、jq -sで配列化してから扱う - stream-json を
--verboseなしで叩いた時の挙動は公式の推奨パターン外。--include-partial-messagesも同様で、トークン単位の流しを使いたい時の推奨はこの3点セット、つまりstream-jsonと--verboseと--include-partial-messagesを全部つける形。組み合わせをサボると期待した粒度のイベントが来ない可能性がある - パイプで stdin から渡す時、Claude Code v2.1.128 以降は 10MB 上限。30,000字の議事録くらいなら余裕だが、PDF テキスト抽出後の大量データを
cat huge.txt | claude -pで流すと、上限を超えてエラー終了する。超える時はファイルの置き場所をプロンプトに書き、Claude 側に読ませる運用に切り替える - JSON の中身が「英語の返答」だと油断する。プロンプトを日本語で書いても、Claude の応答は時々英語になる。Slack に流す前に「日本語で答えて」と明示する、もしくは
--append-system-promptで「Always reply in Japanese」を足しておく total_cost_usdはそのリクエスト1回分。月次集計したいなら、自分でログに溜めて合計する処理が別途必要。Claude 側がダッシュボードと別に「月いくら」を返してくれるわけではない
書き方
claude -p "<prompt>" --output-format <text|json|stream-json>
やってみるとこうなる
入力
claude -p "Summarize this project" --output-format json
出力例
{
"result": "This project is a recipe blog with...",
"session_id": "550e8400-e29b-41d4-a716-446655440000",
"total_cost_usd": 0.0247,
"usage": { "input_tokens": 1234, "output_tokens": 567 }
}
このページに出てきた言葉
- print mode
- Claude Code を <code>-p</code>(または <code>--print</code>)つきで叩いた時の「1問1答」モード。対話画面を開かず、コマンド1回で答えを返して終わる動き方
- text(既定値)
- 返答を素の文章で返す。何も指定しないとこれになる。人が画面で読む用途向け
- json
- 返答を1個の JSON にまとめて返す。本文は <code>result</code>、識別子は <code>session_id</code>、概算費用は <code>total_cost_usd</code> に入る
- stream-json
- 改行で区切られた JSON を縦に並べて返す形式。トークンが生成された順に <code>stream_event</code> が流れる。配列ではない
- --json-schema
- <code>--output-format json</code> と組み合わせて、返答の構造を JSON Schema で固定するための別の起動スイッチ。結果は <code>result</code> ではなく <code>structured_output</code> に入る
- --verbose
- stream-json と組み合わせて使う、内部のやり取りを詳しく出す起動スイッチ。公式の stream-json 推奨パターンに必ず含まれる
- --include-partial-messages
- stream-json の中で「トークン単位の差分」も流すための起動スイッチ。<code>--print</code> と <code>--output-format stream-json</code> が前提
- jq
- 受け取った JSON の中から特定の値だけを抜き出すための小さな別アプリ。<code>jq -r '.result'</code> で生の文字列を取り出せる
- newline-delimited JSON
- 1行に1個ずつ JSON オブジェクトを縦に並べた形式。stream-json の出力はこの形なので、丸ごと JSON.parse すると壊れる