Mastra’s agent HTTP API returns a JSON structure with steps, each containing content items typed as reasoning, tool-call, tool-result, and text. The raw output is dense. Start by exploring it:

# Hit the API and see raw structure
http localhost:4111/api/agents/weather-agent/generate \
  messages[0]="what's the weather in montreal?" | jq .

# Get just the final answer
http localhost:4111/api/agents/weather-agent/generate \
  messages[0]="what's the weather in montreal?" | jq -r '.text'

# Explore what's inside steps
http localhost:4111/api/agents/weather-agent/generate \
  messages[0]="what's the weather in montreal?" | jq '.steps[].content[] | .type'
# "reasoning"
# "tool-call"
# "tool-result"
# "text"
# "reasoning"
# "text"

# See what fields each type has
http localhost:4111/api/agents/weather-agent/generate \
  messages[0]="what's the weather in montreal?" | jq '.steps[].content[] | select(.type == "tool-call")'

Once the structure is clear, pipe through jq -r with inline ANSI escape sequences to colorize each piece:

http localhost:4111/api/agents/weather-agent/generate \
  messages[0]="what's the weather in montreal?" | jq -r '
  (
    .steps[]
    | .content[]
    | select(.type != "tool-result" and .type != "text")
    | if .type == "tool-call" then
        "\u001b[35m<\(.type)>\(.text // .toolName)</\(.type)>\u001b[0m"
      else
        "\u001b[90m<\(.type)>\(.text // .toolName)</\(.type)>\u001b[0m"
      end
  ),
  "\n\n\u001b[32mAssistant: \(.text)\u001b[0m"
'

The \u001b[Xm sequences are ANSI color codes: 35 is magenta for tool calls, 90 is dark gray for reasoning, 32 is green for the final answer. jq outputs them literally in raw mode (-r), and the terminal renders them as colors. The filter uses .text // .toolName to handle content items that store their label in different fields depending on type.