> ## Documentation Index
> Fetch the complete documentation index at: https://arize-ax.mintlify.site/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Claude Agent SDK

> Trace Claude Agent SDK query() and ClaudeSDKClient runs with OpenInference and send spans to Arize AX for LLM observability.

The [Claude Agent SDK](https://platform.claude.com/docs/en/agent-sdk/overview) is Anthropic's framework for building agents on the same harness that powers Claude Code — tools, subagents, hooks, and skills, driven by the `query()` function or the `ClaudeSDKClient` class. Arize AX captures every Agent SDK run — agent invocations and the tool calls they make — via the [`openinference-instrumentation-claude-agent-sdk`](https://github.com/Arize-ai/openinference/tree/main/python/instrumentation/openinference-instrumentation-claude-agent-sdk) package.

<Note>
  **Use this if you are building an application with the Claude Agents SDK.** This page uses the OpenInference `ClaudeAgentSDKInstrumentor` to emit standard agent and tool spans into your application's project — the right choice when the SDK is part of an app you're instrumenting. If you instead want to trace **Claude Code CLI (or SDK) *sessions*** — the coding tool's activity, tool usage, and token costs — via a plugin you enable through a settings file with no in-code instrumentor, use [Claude Code](/docs/ax/integrations/platforms/claude-code/claude-code-tracing) instead.
</Note>

## Prerequisites

* Python 3.10+
* [Node.js](https://nodejs.org/) and the Claude Code CLI (`npm install -g @anthropic-ai/claude-code`), which the Agent SDK runs as a subprocess
* An Arize AX account ([sign up](https://arize.com/sign-up/))
* An `ANTHROPIC_API_KEY` from the [Claude Console](https://console.anthropic.com/)

## Launch Arize AX

1. Sign in to your [Arize AX account](https://app.arize.com/).
2. From **Space Settings**, copy your **Space ID** and **API Key**. You will set them as `ARIZE_SPACE_ID` and `ARIZE_API_KEY` below.

## Install

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
pip install arize-otel \
  openinference-instrumentation-claude-agent-sdk \
  claude-agent-sdk
```

## Configure credentials

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
export ARIZE_SPACE_ID="<your-space-id>"
export ARIZE_API_KEY="<your-api-key>"
export ARIZE_PROJECT_NAME="claude-agent-sdk-tracing-example"
export ANTHROPIC_API_KEY="<your-anthropic-api-key>"
```

## Setup tracing

<Note>
  Instrument the Agent SDK with `ClaudeAgentSDKInstrumentor`. If your app serves the SDK from a web framework (FastAPI, Flask, etc.), do **not** rely on web-server instrumentation like `FastAPIInstrumentor` for agent observability — it records HTTP requests, not the SDK's agent and tool spans. Add `ClaudeAgentSDKInstrumentor` alongside any transport instrumentation you keep.
</Note>

```python theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
# instrumentation.py
import os

from arize.otel import register
from openinference.instrumentation.claude_agent_sdk import (
    ClaudeAgentSDKInstrumentor,
)

tracer_provider = register(
    space_id=os.environ["ARIZE_SPACE_ID"],
    api_key=os.environ["ARIZE_API_KEY"],
    project_name=os.environ["ARIZE_PROJECT_NAME"],
)

ClaudeAgentSDKInstrumentor().instrument(tracer_provider=tracer_provider)
print("Arize AX tracing initialized for Claude Agent SDK.")
```

## Run the Claude Agent SDK

The instrumentor traces both entry points:

* `query()` — one AGENT span per call, for one-off tasks.
* `ClaudeSDKClient` — one AGENT span per response turn (each `receive_response()` iteration), for multi-turn conversations that keep session context.

<Tabs>
  <Tab title="query()">
    ```python theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
    # example.py

    # Importing instrumentation first ensures tracing is set up
    # before the SDK is used.
    from instrumentation import tracer_provider

    import asyncio

    from claude_agent_sdk import query, ClaudeAgentOptions


    async def main():
        async for message in query(
            prompt="What files are in this directory?",
            options=ClaudeAgentOptions(allowed_tools=["Bash", "Glob"]),
        ):
            if hasattr(message, "result"):
                print(message.result)


    asyncio.run(main())
    ```
  </Tab>

  <Tab title="ClaudeSDKClient">
    ```python theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
    # example_client.py

    # Importing instrumentation first ensures tracing is set up
    # before the SDK is used.
    from instrumentation import tracer_provider

    import asyncio

    from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions


    async def main():
        options = ClaudeAgentOptions(allowed_tools=["Bash", "Glob"])
        async with ClaudeSDKClient(options=options) as client:
            await client.query("What files are in this directory?")
            async for message in client.receive_response():
                if hasattr(message, "result"):
                    print(message.result)


    asyncio.run(main())
    ```
  </Tab>
</Tabs>

## Verify in Arize AX

1. Open your Arize AX space and select project **`claude-agent-sdk-tracing-example`**.
2. Within \~30 seconds you should see a new trace with an AGENT root span (`ClaudeAgentSDK.query` or `ClaudeAgentSDK.ClaudeSDKClient.receive_response`) carrying the prompt as input, the SDK result as output, and `session.id`, `llm.model_name`, and token-count metadata. Any tools the agent invokes appear as child TOOL spans with their inputs and outputs.
3. If no traces appear, see [Troubleshooting](#troubleshooting).

## Span coverage

The instrumentor emits **AGENT** spans (one per `query()` call or `ClaudeSDKClient` response turn) and child **TOOL** spans for each tool the agent invokes. It does **not** emit separate LLM spans: the Agent SDK runs the Claude Code binary as a subprocess and makes its model calls there, so an in-process instrumentor like `AnthropicInstrumentor` never sees them. Model, token-count, and cost detail is captured as attributes on the AGENT span (`llm.model_name`, `llm.token_count.*`, `llm.cost.total`).

## Capture LLM spans

If you want per-generation `LLM` spans in addition to the AGENT span, synthesize them yourself from the `AssistantMessage` objects the SDK streams back — each one carries the model and its output. Start the LLM span while the assistant message streams (so it nests under the AGENT span) and set the token counts from the final `ResultMessage`, whose `usage` holds the accurate totals (the per-`AssistantMessage` `usage` is a partial, streaming value):

```python theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
# example.py
from instrumentation import tracer_provider

import asyncio

from claude_agent_sdk import (
    query,
    ClaudeAgentOptions,
    AssistantMessage,
    ResultMessage,
    TextBlock,
)
from openinference.semconv.trace import SpanAttributes, OpenInferenceSpanKindValues

tracer = tracer_provider.get_tracer("claude-agent-sdk-llm-spans")


async def main():
    llm_spans = []
    async for message in query(
        prompt="Why is the ocean salty? Answer in one sentence.",
        options=ClaudeAgentOptions(max_turns=1),
    ):
        # Start an LLM span for each assistant turn (nests under the AGENT span).
        if isinstance(message, AssistantMessage):
            text = "".join(b.text for b in message.content if isinstance(b, TextBlock))
            span = tracer.start_span(f"{message.model} generation")
            span.set_attribute(
                SpanAttributes.OPENINFERENCE_SPAN_KIND,
                OpenInferenceSpanKindValues.LLM.value,
            )
            span.set_attribute(SpanAttributes.LLM_MODEL_NAME, message.model)
            span.set_attribute(SpanAttributes.LLM_PROVIDER, "anthropic")
            span.set_attribute(SpanAttributes.OUTPUT_VALUE, text)
            llm_spans.append(span)
        # Accurate token counts arrive only on the final result message.
        if isinstance(message, ResultMessage):
            usage = message.usage or {}
            if llm_spans and usage:
                last = llm_spans[-1]
                last.set_attribute(
                    SpanAttributes.LLM_TOKEN_COUNT_PROMPT, usage.get("input_tokens", 0)
                )
                last.set_attribute(
                    SpanAttributes.LLM_TOKEN_COUNT_COMPLETION,
                    usage.get("output_tokens", 0),
                )
            print(message.result)

    for span in llm_spans:
        span.end()


asyncio.run(main())
```

The LLM span nests under the AGENT span in the same trace. The token counts are exact for single-turn runs; for multi-turn runs they apply to the final turn.

## Troubleshooting

* **No traces in Arize AX.** Confirm `ARIZE_SPACE_ID` and `ARIZE_API_KEY` are set in the same shell that runs your script. Enable OpenTelemetry debug logs with `export OTEL_LOG_LEVEL=debug` and re-run.
* **Agent spans missing.** `ClaudeAgentSDKInstrumentor().instrument(...)` must run before `query()` or `ClaudeSDKClient` is called. Make sure `instrumentation.py` is the first import in your entry point.
* **`claude` executable not found.** The Agent SDK runs the Claude Code CLI as a subprocess. Install it with `npm install -g @anthropic-ai/claude-code`, or point the SDK at an existing binary with `CLAUDE_CODE_EXECUTABLE`.
* **`401` from Anthropic.** Verify `ANTHROPIC_API_KEY` is set and valid.
* **Tool spans expected but not present.** TOOL spans only emit when the agent actually invokes a tool. Grant tools via `ClaudeAgentOptions(allowed_tools=[...])` and give a prompt that requires them.

## Resources

<CardGroup>
  <Card icon="book-open" href="https://platform.claude.com/docs/en/agent-sdk/overview" title="Claude Agent SDK Documentation" horizontal />

  <Card icon="terminal" href="https://github.com/Arize-ai/openinference/tree/main/python/instrumentation/openinference-instrumentation-claude-agent-sdk" title="OpenInference Claude Agent SDK Instrumentor" horizontal />

  <Card icon="github" href="https://github.com/anthropics/claude-agent-sdk-python" title="Claude Agent SDK (Python) GitHub" horizontal />
</CardGroup>
