Streaming JSON

Amp’s CLI can print each message as a JSON object on its own line. Use this output for programmatic integrations or to monitor a conversation as it runs.

Basic Usage

Use --stream-json with --execute to output streaming JSON instead of plain text.

Pass a prompt as an argument:

$ amp --execute "what is 3 + 5?" --stream-json

Continue an existing thread:

$ amp threads continue --execute "now add 8 to that" --stream-json

Provide the prompt through stdin:

$ echo "analyze this code" | amp --execute --stream-json

Streaming JSON input mode (see below for more information):

$ echo '{"type":"user","message":{"role":"user","content":[{"type":"text","text":"what is 2+2?"}]}}' | amp --execute --stream-json --stream-json-input

The --stream-json flag requires --execute mode. Add --stream-json-thinking to include assistant thinking blocks. This extends the schema and is not compatible with Claude Code.

Each conversation starts with an init system message. User and assistant messages follow, and a final result message contains the result and run statistics. Amp emits each message as a separate JSON object.

Example Output

Simple math query:

$ amp --execute "what is 3 + 5?" --stream-json
{"type":"system","subtype":"init","cwd":"/Users/orb","session_id":"T-f9941a55-3765-421e-972f-05dc1138c3a3","tools":["Bash","finder","create_file","edit_file","glob","Grep","mcp__postgres__query","oracle","Read","read_mcp_resource","read_web_page","Task","todo_read","todo_write","undo_edit","web_search"],"mcp_servers":[{"name":"postgres","status":"connected"}]}
{"type":"user","message":{"role":"user","content":[{"type":"text","text":"what is 3 + 5?"}]},"parent_tool_use_id":null,"session_id":"T-f9941a55-3765-421e-972f-05dc1138c3a3"}
{"type":"assistant","message":{"type":"message","role":"assistant","content":[{"type":"text","text":"8"}],"stop_reason":"end_turn","usage":{"input_tokens":10,"cache_creation_input_tokens":16256,"cache_read_input_tokens":0,"output_tokens":99,"max_tokens":968000,"service_tier":"standard"}},"parent_tool_use_id":null,"session_id":"T-f9941a55-3765-421e-972f-05dc1138c3a3"}
{"type":"result","subtype":"success","duration_ms":5400,"is_error":false,"num_turns":1,"result":"8","session_id":"T-f9941a55-3765-421e-972f-05dc1138c3a3"}

Tool usage example:

$ amp --execute "list files using a tool" --stream-json
{"type":"system","subtype":"init","cwd":"/Users/orb/project","session_id":"T-d2fc4acc-dd1d-497f-9609-ed0da22a7c95","tools":["Bash","finder" ,"create_file","edit_file","glob","Grep","mcp__postgres__query","oracle","Read","read_mcp_resource","read_web_page","Task","todo_rea d","todo_write","undo_edit","web_search"],"mcp_servers":[{"name":"postgres","status":"connected"}]}
{"type":"user","message":{"role":"user","content":[{"type":"text","text":"list files using a tool"}]},"parent_tool_use_id":null,"session_id":"T-d2fc4acc-dd1d-4 97f-9609-ed0da22a7c95"}
{"type":"assistant","message":{"type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_019cyniPYrSgaJitUSMyxyNV","name": "read", "input":{"path":"/Users/orb/project"}}],"stop_reason":"tool_use","usage":{"input_tokens":10,"cache_creation_input_tokens":13150,"cache_read_input_tokens": 0,"output_tokens":111,"max_tokens":968000,"service_tier":"standard"}},"parent_tool_use_id":null,"session_id":"T-d2fc4acc-dd1d-497f-9609-ed0da22a7c95"}
{"type":"user","message":{"role":"user","content":[{"type":"tool_result","tool_use_id":"toolu_019cyniPYrSgaJitUSMyxyNV","content":"[\"index.js\",\"README.md\"] ","is_error":false}]},"parent_tool_use_id":null,"session_id":"T-d2fc4acc-dd1d-497f-9609-ed0da22a7c95"}
{"type":"assistant","message":{"type":"message","role":"assistant","content":[{"type":"text","text":"Two files: index.js and README.md"}],"stop_reason":"end_tu rn","usage":{"input_tokens":7,"cache_creation_input_tokens":133,"cache_read_input_tokens":13150,"output_tokens":13,"max_tokens":968000,"service_tier":"standard "}},"parent_tool_use_id":null,"session_id":"T-d2fc4acc-dd1d-497f-9609-ed0da22a7c95"}
{"type":"result","subtype":"success","duration_ms":7363,"is_error":false,"num_turns":2,"result":"Two files: index.js and README.md","session_id":"T-d2fc4acc-dd1d-497f-9609-ed0da22a7c95"}

Message Schema

When --stream-json-thinking is enabled, assistant content may include thinking and redacted_thinking blocks.

Messages returned from the streaming JSON API use this schema:

type StreamJSONMessage =
  // An assistant message
  | {
      type: "assistant";
      message: {
        type: "message";
        role: "assistant";
        content: Array<{
          type: "text";
          text: string;
        } | {
          type: "tool_use";
          id: string;
          name: string;
          input: Record<string, unknown>;
        } | {
          type: "thinking";
          thinking: string;
        } | {
          type: "redacted_thinking";
          data: string;
        }>;
        stop_reason: "end_turn" | "max_tokens" | "stop_sequence" | "tool_use" | "pause_turn" | "refusal" | null;
        usage?: {
          input_tokens: number;
          cache_creation_input_tokens?: number;
          cache_read_input_tokens?: number;
          cache_creation?: {
            ephemeral_5m_input_tokens: number;
            ephemeral_1h_input_tokens: number;
          };
          output_tokens: number;
          max_tokens?: number;
          service_tier?: "standard" | "enterprise";
        };
      };
      parent_tool_use_id: string | null;
      session_id: string;
    }

  // A user message in streaming JSON output
  | {
      type: "user";
      message: {
        role: "user";
        content: Array<{
          type: "text";
          text: string;
        } | {
          type: "tool_result";
          tool_use_id: string;
          content: string;
          is_error: boolean;
        }>;
      };
      parent_tool_use_id: string | null;
      session_id: string;
    }

  // The last message when the run succeeds
  | {
      type: "result";
      subtype: "success";
      duration_ms: number;
      duration_api_ms?: number;
      is_error: false;
      num_turns: number;
      result: string;
      session_id: string;
      usage?: {
        input_tokens: number;
        cache_creation_input_tokens?: number;
        cache_read_input_tokens?: number;
        cache_creation?: {
          ephemeral_5m_input_tokens: number;
          ephemeral_1h_input_tokens: number;
        };
        output_tokens: number;
        max_tokens?: number;
        service_tier?: "standard" | "enterprise";
      };
      permission_denials?: string[];
    }

  // The last message when the run fails
  | {
      type: "result";
      subtype: "error_during_execution" | "error_max_turns";
      duration_ms: number;
      duration_api_ms?: number;
      is_error: true;
      num_turns: number;
      error: string;
      session_id: string;
      usage?: {
        input_tokens: number;
        cache_creation_input_tokens?: number;
        cache_read_input_tokens?: number;
        cache_creation?: {
          ephemeral_5m_input_tokens: number;
          ephemeral_1h_input_tokens: number;
        };
        output_tokens: number;
        max_tokens?: number;
        service_tier?: "standard" | "enterprise";
      };
      permission_denials?: string[];
    }

  // The first message in a conversation
  | {
      type: "system";
      subtype: "init";
      cwd: string;
      session_id: string;
      tools: string[];
      mcp_servers: {
        name: string;
        status: "awaiting-approval" | "authenticating" | "connecting" | "reconnecting" | "connected" | "denied" | "failed" | "blocked-by-registry";
      }[];
      agent_mode?: string;
    }

  // A system error
  | {
      type: "system";
      subtype: "error_max_turns" | "error_during_execution";
      error: string;
      session_id: string;
    };

Streaming JSON Input

Use --stream-json-input to let Amp read messages from stdin until it closes. Each line must be a complete JSON object that uses this schema:

type StreamJSONInputMessage = {
  type: "user";
  steer?: boolean;
  message: {
    role: "user";
    content: Array<{
      type: "text";
      text: string;
    } | {
      type: "image";
      source_path?: string;
      source: {
        type: "base64";
        media_type: "image/jpeg" | "image/png" | "image/gif" | "image/webp";
        data: string;
      };
    }>;
  };
};

For example, use jq -c to emit a text and image message as one line:

$ jq -c . <<'EOF' | amp -x --stream-json --stream-json-input
{
  "type": "user",
  "message": {
    "role": "user",
    "content": [
      {
        "type": "text",
        "text": "what do you see?"
      },
      {
        "type": "image",
        "source_path": "file:///Users/alice/images/example.jpg",
        "source": {
          "type": "base64",
          "media_type": "image/jpeg",
          "data": "..."
        }
      }
    ]
  }
}
EOF

source_path is optional. If you omit it, Amp generates one. The declared media_type must match the decoded image bytes. User image blocks are accepted as input but omitted from streamed user messages on stdout so the output remains compatible with Claude Code.

The --stream-json-input flag requires --stream-json.

When you use --stream-json-input, Amp exits only after the assistant is done and stdin has closed. This allows a program to send several user messages during one conversation:

#!/usr/bin/env bash

send_message() {
  local text="$1"
  echo '{"type":"user","message":{"role":"user","content":[{"type":"text","text":"'$text'"}]}}'
}

{
  send_message "what's 2+2?"
  sleep 10

  send_message "now add 8 to that"
  sleep 10

  send_message "now add 5 to that"
} | amp --execute --stream-json --stream-json-input

This script produces the following output:

$ ./script.sh
{"type":"system","subtype":"init","cwd":"/Users/orb","session_id":"T-addfb7a4-61d9-41e1-890b-7330aa54087a","tools":["Bash","finder","create_file","edit_file","glob","Grep","mcp__postgres__query","oracle","Read","read_mcp_resource","read_web_page","Task","todo_read","todo_write","undo_edit","web_search"],"mcp_servers":[{"name":"postgres","status":"connected"}]}
{"type":"user","message":{"role":"user","content":[{"type":"text","text":"what's 2+2?"}]},"parent_tool_use_id":null,"session_id":"T-addfb7a4-61d9-41e1-890b-7330aa54087a"}
{"type":"assistant","message":{"type":"message","role":"assistant","content":[{"type":"text","text":"4"}],"stop_reason":"end_turn","usage":{"input_tokens":10,"cache_creation_input_tokens":13993,"cache_read_input_tokens":0,"output_tokens":67,"max_tokens":968000,"service_tier":"standard"}},"parent_tool_use_id":null,"session_id":"T-addfb7a4-61d9-41e1-890b-7330aa54087a"}
{"type":"user","message":{"role":"user","content":[{"type":"text","text":"now add 8 to that"}]},"parent_tool_use_id":null,"session_id":"T-addfb7a4-61d9-41e1-890b-7330aa54087a"}
{"type":"assistant","message":{"type":"message","role":"assistant","content":[{"type":"text","text":"12"}],"stop_reason":"end_turn","usage":{"input_tokens":10,"cache_creation_input_tokens":36,"cache_read_input_tokens":13993,"output_tokens":76,"max_tokens":968000,"service_tier":"standard"}},"parent_tool_use_id":null,"session_id":"T-addfb7a4-61d9-41e1-890b-7330aa54087a"}
{"type":"user","message":{"role":"user","content":[{"type":"text","text":"now add 5 to that"}]},"parent_tool_use_id":null,"session_id":"T-addfb7a4-61d9-41e1-890b-7330aa54087a"}
{"type":"assistant","message":{"type":"message","role":"assistant","content":[{"type":"text","text":"17"}],"stop_reason":"end_turn","usage":{"input_tokens":10,"cache_creation_input_tokens":36,"cache_read_input_tokens":14029,"output_tokens":43,"max_tokens":968000,"service_tier":"standard"}},"parent_tool_use_id":null,"session_id":"T-addfb7a4-61d9-41e1-890b-7330aa54087a"}
{"type":"result","subtype":"success","duration_ms":21639,"is_error":false,"num_turns":3,"result":"17","session_id":"T-addfb7a4-61d9-41e1-890b-7330aa54087a"}

Set the top-level steer field to true if Amp should handle a queued message at the next interruption point while the agent is busy.

Claude Code Compatibility

Amp’s stream JSON output tries to be compatible with Claude Code’s format as much as possible. When --stream-json-thinking is enabled, the output includes extra content block types that are not part of Claude Code’s public schema.