axonpush
TypeScript SDKIntegrations

Anthropic

AxonPushAnthropicTracer wraps messages.create and messages.stream, recording tool use, text blocks, stop reason, and token usage including cache hits.

AxonPushAnthropicTracer wraps calls to the Anthropic Messages API. You call the tracer instead of the client, it forwards the call, and it records the turn , the model’s response blocks, the stop reason, and full token usage including cache reads and writes.

Tested against @anthropic-ai/sdk@^0.30.

Install

npm install @axonpush/sdk @anthropic-ai/sdk

Non-streaming

import Anthropic from "@anthropic-ai/sdk";
import { AxonPush, AxonPushAnthropicTracer } from "@axonpush/sdk";

const client = new AxonPush();
const tracer = new AxonPushAnthropicTracer({
  client,
  channelId: process.env.AXONPUSH_CHANNEL_ID!,
});

const anthropic = new Anthropic();
const response = await tracer.createMessage(anthropic, {
  model: "claude-sonnet-4-20250514",
  max_tokens: 1024,
  messages: [{ role: "user", content: "Explain quantum computing." }],
});

createMessage takes the Anthropic client as its first argument and the ordinary messages.create params as its second, and returns the unmodified response. agentId defaults to "claude".

Streaming

streamMessage is an async generator: it yields every raw stream event through to you, recording tokens as they pass.

for await (const event of tracer.streamMessage(anthropic, {
  model: "claude-sonnet-4-20250514",
  max_tokens: 1024,
  messages: [{ role: "user", content: "Explain quantum computing." }],
  stream: true,
})) {
  if (event.type === "content_block_delta") process.stdout.write(event.delta.text ?? "");
}

It prefers anthropic.messages.stream(params) when the client exposes it and falls back to messages.create({ ...params, stream: true }). Usage and stop reason are captured from the terminal message_delta / message_stop events, so the closing conversation.turn.end still carries token counts.

Tool results

The tracer sees the model’s tool_use blocks on the way out, but it cannot see what your code did with them. Report the result back yourself:

tracer.sendToolResult(toolUseId, result);

What gets emitted

Identifieraxonpush eventTypePayload
conversation.turnagent.startmodel, message_count, streaming when applicable
agent.responseagent.messagetext_length, one per text block
tool.{name}.startagent.tool_call.starttool_name, tool_use_id, truncated input
tool.resultagent.tool_call.endtool_use_id, result_preview (first 500 chars)
llm.tokenagent.llm.tokentoken, streaming only
conversation.turn.endagent.endstop_reason and the usage fields below

Usage on conversation.turn.end: input_tokens, output_tokens, cache_creation_input_tokens, cache_read_input_tokens. Fields the API did not return are recorded as null rather than omitted, so a prompt-caching regression is visible as a change in value rather than a missing key.

Assistant text is recorded as a length, not content. Tool inputs are truncated to 500 characters and then pass through the client’s redactor, which under the default metadata_only capture mode replaces content-bearing keys outright. Every event carries metadata.framework: "anthropic".