AgentSkillsCN

claude-code-stream-json

在 stream-json 模式下使用 Claude Code CLI,实现实时双向 JSON 通信。使代理能够将 JSON 消息传递给 Claude,并接收带有完整会话可观测性的流式 JSONL 输出,包括工具调用、增量变化以及最终结果。

SKILL.md
--- frontmatter
name: claude-code-stream-json
description: Use Claude Code CLI in stream-json mode for real-time bidirectional JSON communication. Enables agents to pipe JSON messages to Claude and receive streaming JSONL output with full session observability including tool calls, deltas, and results.

Claude Code Stream-JSON Mode

[Created by Opus: 134bdda1-e4e8-45b4-ab3a-666a2d89509c]

TL;DR - Quick Start

bash
echo '{"type":"user","message":{"role":"user","content":"Hello, what is 2+2?"}}' | \
  claude --print --input-format=stream-json --output-format=stream-json \
    --verbose --dangerously-skip-permissions | jq

Required flags:

  • --print (-p) - Non-interactive mode
  • --input-format=stream-json - Accept JSON input from stdin
  • --output-format=stream-json - Emit JSONL output
  • --verbose - Required when using stream-json output

Common optional flags:

  • --include-partial-messages - Emit assistant message snapshots as content accumulates
  • --replay-user-messages - Echo user messages back to stdout for acknowledgment
  • --dangerously-skip-permissions - Skip permission dialogs (use in trusted environments)

Full Sample Command

This example pipes a user prompt requesting project analysis and report generation:

bash
echo '{"type":"user","message":{"role":"user","content":"Tell me about this project. '\
  'Tell me what the nearest commit implemented. '\
  'Also, are there committed code? What are they for? '\
  'Write a report to /tmp/throwaway/"}}' | \
     ~/symlinks/claude --print --input-format=stream-json --output-format=stream-json \
    --verbose --include-partial-messages --replay-user-messages \
    --dangerously-skip-permissions | tee /tmp/sample_output.jsonl | jq

Input Format

Send newline-delimited JSON objects to stdin. Each message must have this structure:

json
{"type":"user","message":{"role":"user","content":"Your prompt here"}}

Multi-turn Conversations

Send multiple user messages sequentially. Each becomes a new turn:

bash
cat << 'EOF' | claude -p --input-format=stream-json --output-format=stream-json --verbose
{"type":"user","message":{"role":"user","content":"What is Python?"}}
{"type":"user","message":{"role":"user","content":"Show me a hello world example"}}
EOF

Output Format (JSONL)

Claude emits newline-delimited JSON. Each line is a complete JSON object with a type field.

Message Types

TypeDescription
systemSession initialization with metadata (subtype: init)
userUser message (echoed back when --replay-user-messages)
assistantComplete or partial assistant message snapshot
stream_eventRaw streaming event from Claude API
resultFinal session result (subtype: success or error)

1. System Init Message

First message emitted, contains session metadata:

json
{
  "type": "system",
  "subtype": "init",
  "cwd": "/Users/sotola",
  "session_id": "d4b6edfb-892e-419d-b725-7fbf0fc2fc30",
  "tools": ["Task", "Bash", "Read", "Edit", "Write", ...],
  "mcp_servers": [{"name": "browser", "status": "connected"}],
  "model": "claude-opus-4-5-20251101",
  "permissionMode": "bypassPermissions",
  "claude_code_version": "2.1.2",
  "uuid": "08d0607a-64c0-446c-be61-4e5a741792bf"
}

2. Stream Events

Raw API streaming events. Useful for real-time UI updates:

json
{"type":"stream_event","event":{"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Hello"}},"session_id":"...","uuid":"..."}

Event subtypes:

  • message_start - New message begins (includes model, usage)
  • content_block_start - New content block (text or tool_use)
  • content_block_delta - Incremental content (text_delta or input_json_delta)
  • content_block_stop - Block completed
  • message_delta - Message-level update (stop_reason)
  • message_stop - Message completed

3. Assistant Messages

Complete assistant message snapshots (emitted when --include-partial-messages):

json
{
  "type": "assistant",
  "message": {
    "model": "claude-opus-4-5-20251101",
    "id": "msg_01MTUH7myRjkMoTtTtuvPWUF",
    "type": "message",
    "role": "assistant",
    "content": [{"type": "text", "text": "I'll help you with that."}],
    "stop_reason": null,
    "usage": {"input_tokens": 2, "output_tokens": 15}
  },
  "session_id": "...",
  "uuid": "..."
}

4. User Messages (Tool Results)

Tool results appear as user messages with tool_result content:

json
{
  "type": "user",
  "message": {
    "role": "user",
    "content": [
      {
        "tool_use_id": "toolu_01ChoXFfg2mf7vJisCeFMd7T",
        "type": "tool_result",
        "content": "file contents here",
        "is_error": false
      }
    ]
  },
  "tool_use_result": {
    "stdout": "file contents here",
    "stderr": "",
    "interrupted": false
  },
  "session_id": "...",
  "uuid": "..."
}

5. Result Message

Final message indicating session completion:

json
{
  "type": "result",
  "subtype": "success",
  "duration_ms": 12345,
  "cost_usd": 0.05,
  "session_id": "...",
  "uuid": "..."
}

Control Messages (Advanced)

For interactive streaming control, you can send control requests:

Interrupt Generation

json
{"type":"control_request","request_id":"abc123","request":{"subtype":"interrupt"}}

Cancel Request

json
{"type":"control_cancel_request","request_id":"abc123"}

Responses come back as control_response messages.

Flag Reference

FlagDescription
--print / -pRequired. Non-interactive print mode
--input-format=stream-jsonAccept streaming JSON input
--output-format=stream-jsonEmit streaming JSONL output
--verboseRequired with stream-json output
--include-partial-messagesEmit assistant message snapshots during streaming
--replay-user-messagesEcho user messages back with isReplay: true
--dangerously-skip-permissionsBypass all permission dialogs
--json-schema <schema>Validate structured output against JSON schema

Constraints & Validation Rules

  1. --output-format=stream-json requires --verbose

    • Error: --output-format=stream-json requires --verbose
  2. --input-format=stream-json requires --output-format=stream-json

    • Error: --input-format=stream-json requires output-format=stream-json
  3. --include-partial-messages requires both --print and --output-format=stream-json

    • Error: --include-partial-messages requires --print and --output-format=stream-json
  4. --replay-user-messages requires both stream-json formats

    • Error: --replay-user-messages requires both --input-format=stream-json and --output-format=stream-json

Use Cases

1. Agent Orchestration

Parent agents can spawn Claude Code as a subprocess and communicate via JSON:

python
import subprocess
import json

proc = subprocess.Popen(
    ['claude', '-p', '--input-format=stream-json', '--output-format=stream-json', '--verbose'],
    stdin=subprocess.PIPE,
    stdout=subprocess.PIPE,
    text=True
)

# Send prompt
msg = {"type": "user", "message": {"role": "user", "content": "List files"}}
proc.stdin.write(json.dumps(msg) + '\n')
proc.stdin.flush()

# Read streaming output
for line in proc.stdout:
    event = json.loads(line)
    if event['type'] == 'stream_event':
        # Handle real-time updates
        pass
    elif event['type'] == 'result':
        break

2. Real-time UI Updates

Stream text deltas to a web UI:

bash
claude -p --input-format=stream-json --output-format=stream-json --verbose | \
  while read -r line; do
    echo "$line" | jq -r 'select(.type=="stream_event") | .event.delta.text // empty'
  done

3. Session Logging & Observability

Capture full session for replay/analysis:

bash
echo '{"type":"user","message":{"role":"user","content":"..."}}' | \
  claude -p --input-format=stream-json --output-format=stream-json \
    --verbose --include-partial-messages \
  | tee session.jsonl | jq

4. Daemon Mode (SDK Integration)

The --daemon flag automatically enables stream-json mode:

bash
claude --daemon  # Implies: --output-format=stream-json --verbose

Extracting Specific Data

Get Final Text Output

bash
cat session.jsonl | jq -r 'select(.type=="assistant") | .message.content[]? | select(.type=="text") | .text' | tail -1

Get All Tool Calls

bash
cat session.jsonl | jq 'select(.type=="assistant") | .message.content[]? | select(.type=="tool_use")'

Get Session ID

bash
cat session.jsonl | jq -r 'select(.type=="system" and .subtype=="init") | .session_id' | head -1

Calculate Total Cost

bash
cat session.jsonl | jq -r 'select(.type=="result") | .cost_usd'