> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://respan.ai/docs/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://respan.ai/_mcp/server.

# Claude Agent SDK (tracing)

> Trace Claude Agent SDK workflows with Respan using the Claude Agent SDK instrumentation plugin for Python and TypeScript.

The [Claude Agent SDK](https://docs.anthropic.com/en/docs/claude-code/sdk) (`claude-agent-sdk`) lets you run Claude-powered agent sessions with tool use, multi-turn reasoning, and streamed events. Respan gives you full observability over every SDK run, streamed response, and tool call — and gateway routing for Claude models through the Anthropic-compatible Respan endpoint.

#### Set up Respan

Create an account at [platform.respan.ai](https://platform.respan.ai) and grab an [API key](https://platform.respan.ai/platform/api/api-keys).

Run `npx @respan/cli setup` to set up with your coding agent.

#### Use Respan Gateway

See [Claude Agent SDK gateway setup](/docs/gateway/claude-agents-sdk) to route this integration through the Respan gateway.

#### Example projects

* [Python examples](https://github.com/respanai/respan-example-projects/tree/main/python/tracing/anthropic-agents-sdk)
* [TypeScript examples](https://github.com/respanai/respan-example-projects/tree/main/typescript/tracing/claude-agent-sdk)

## Setup

#### Install packages

```bash Python
pip install claude-agent-sdk respan-ai respan-instrumentation-claude-agent-sdk
```

```bash TypeScript
npm install @anthropic-ai/claude-agent-sdk @respan/respan @respan/instrumentation-claude-agent-sdk
```

#### Set environment variables

```bash
export ANTHROPIC_API_KEY="YOUR_ANTHROPIC_API_KEY"
export RESPAN_API_KEY="YOUR_RESPAN_API_KEY"
```

`ANTHROPIC_API_KEY` is used for Claude requests. `RESPAN_API_KEY` is used to export traces to Respan.

#### Initialize and run

```python Python
import asyncio

import claude_agent_sdk
from claude_agent_sdk import ClaudeAgentOptions, ResultMessage
from respan import Respan
from respan_instrumentation_claude_agent_sdk import ClaudeAgentSDKInstrumentor

respan = Respan(
    instrumentations=[ClaudeAgentSDKInstrumentor(capture_content=True)],
)

async def main():
    async for message in claude_agent_sdk.query(
        prompt="Explain tracing in one sentence.",
        options=ClaudeAgentOptions(model="sonnet", max_turns=1),
    ):
        if isinstance(message, ResultMessage):
            print(message.result)

asyncio.run(main())
```

```typescript TypeScript
import * as _ClaudeAgentSDK from "@anthropic-ai/claude-agent-sdk";
import { ClaudeAgentSDKInstrumentor } from "@respan/instrumentation-claude-agent-sdk";
import { Respan } from "@respan/respan";

// ESM namespace objects are read-only. Patch a mutable copy instead.
const ClaudeAgentSDK = { ..._ClaudeAgentSDK };

const respan = new Respan({
  apiKey: process.env.RESPAN_API_KEY,
  baseURL: process.env.RESPAN_BASE_URL,
  instrumentations: [
    new ClaudeAgentSDKInstrumentor({
      sdkModule: ClaudeAgentSDK,
    }),
  ],
});
await respan.initialize();

const response = await ClaudeAgentSDK.query({
  prompt: "Explain tracing in one sentence.",
  options: {
    model: "sonnet",
    maxTurns: 1,
  },
});

for await (const message of response) {
  if (message.type === "result") {
    console.log(message.result);
  }
}

```

#### View your trace

Open the [Traces page](https://platform.respan.ai/platform/traces) to see your workflow with Claude Agent SDK spans, streamed responses, and tool activity.

## Configuration

| Parameter             | Type           | Default | Description                                                                                    |
| --------------------- | -------------- | ------- | ---------------------------------------------------------------------------------------------- |
| `api_key`             | `str \| None`  | `None`  | Falls back to `RESPAN_API_KEY` env var.                                                        |
| `base_url`            | `str \| None`  | `None`  | Falls back to `RESPAN_BASE_URL` env var.                                                       |
| `instrumentations`    | `list`         | `[]`    | Plugin instrumentations to activate (e.g. `ClaudeAgentSDKInstrumentor(capture_content=True)`). |
| `customer_identifier` | `str \| None`  | `None`  | Default customer identifier for all spans.                                                     |
| `metadata`            | `dict \| None` | `None`  | Default metadata attached to all spans.                                                        |
| `environment`         | `str \| None`  | `None`  | Environment tag (e.g. `"production"`).                                                         |

### ClaudeAgentSDKInstrumentor options

| Parameter         | Type          | Default | Description                                                                                                             |
| ----------------- | ------------- | ------- | ----------------------------------------------------------------------------------------------------------------------- |
| `agent_name`      | `str \| None` | `None`  | Override the agent name attached to emitted Claude Agent SDK spans.                                                     |
| `capture_content` | `bool`        | `False` | Include prompt, response, and tool content in telemetry. Set to `True` when you want full dashboard payload visibility. |

## Attributes

### In Respan()

Set defaults at initialization — these apply to all spans.

```python Python
from respan import Respan
from respan_instrumentation_claude_agent_sdk import ClaudeAgentSDKInstrumentor

respan = Respan(
    instrumentations=[ClaudeAgentSDKInstrumentor(capture_content=True)],
    customer_identifier="user_123",
    metadata={"service": "claude-agent-api", "version": "1.0.0"},
)
```

```typescript TypeScript
import * as _ClaudeAgentSDK from "@anthropic-ai/claude-agent-sdk";
import { ClaudeAgentSDKInstrumentor } from "@respan/instrumentation-claude-agent-sdk";
import { Respan } from "@respan/respan";

const ClaudeAgentSDK = { ..._ClaudeAgentSDK };

// Note: default attributes are set via propagateAttributes() in TypeScript
const respan = new Respan({
  instrumentations: [
    new ClaudeAgentSDKInstrumentor({
      sdkModule: ClaudeAgentSDK,
    }),
  ],
});
await respan.initialize();
```

### With propagate\_attributes

Override per-request using a context scope.

```python Python
import claude_agent_sdk
from claude_agent_sdk import ClaudeAgentOptions, ResultMessage
from respan import Respan, propagate_attributes
from respan_instrumentation_claude_agent_sdk import ClaudeAgentSDKInstrumentor

respan = Respan(
    instrumentations=[ClaudeAgentSDKInstrumentor(capture_content=True)],
)

async def handle_request(user_id: str, prompt: str):
    with propagate_attributes(
        customer_identifier=user_id,
        thread_identifier="conv_abc_123",
        metadata={"plan": "pro"},
    ):
        async for message in claude_agent_sdk.query(
            prompt=prompt,
            options=ClaudeAgentOptions(model="sonnet", max_turns=1),
        ):
            if isinstance(message, ResultMessage):
                print(message.result)
```

```typescript TypeScript
import * as _ClaudeAgentSDK from "@anthropic-ai/claude-agent-sdk";
import { ClaudeAgentSDKInstrumentor } from "@respan/instrumentation-claude-agent-sdk";
import { Respan } from "@respan/respan";

const ClaudeAgentSDK = { ..._ClaudeAgentSDK };

const respan = new Respan({
  instrumentations: [
    new ClaudeAgentSDKInstrumentor({
      sdkModule: ClaudeAgentSDK,
    }),
  ],
});
await respan.initialize();

async function handleRequest(userId: string, prompt: string) {
  await respan.propagateAttributes(
    {
      customer_identifier: userId,
      thread_identifier: "conv_abc_123",
      metadata: { plan: "pro" },
    },
    async () => {
      const response = await ClaudeAgentSDK.query({
        prompt,
        options: {
          model: "sonnet",
          maxTurns: 1,
        },
      });

      for await (const message of response) {
        if (message.type === "result") {
          console.log(message.result);
        }
      }
    }
  );
}
```

| Attribute             | Type   | Description                                           |
| --------------------- | ------ | ----------------------------------------------------- |
| `customer_identifier` | `str`  | Identifies the end user in Respan analytics.          |
| `thread_identifier`   | `str`  | Groups related messages into a conversation.          |
| `metadata`            | `dict` | Custom key-value pairs. Merged with default metadata. |

## Decorators (optional)

Decorators are not required. All Claude Agent SDK runs and streamed responses are auto-traced by the instrumentor. Use `@workflow` and `@task` (Python) or `withWorkflow` and `withTask` (TypeScript) to add structure when you want to group related agent runs into a named workflow with nested tasks.

```python Python
import asyncio

import claude_agent_sdk
from claude_agent_sdk import ClaudeAgentOptions, ResultMessage
from respan import Respan, task, workflow
from respan_instrumentation_claude_agent_sdk import ClaudeAgentSDKInstrumentor

respan = Respan(
    instrumentations=[ClaudeAgentSDKInstrumentor(capture_content=True)],
)

@task(name="draft_answer")
async def draft_answer(prompt: str) -> str:
    async for message in claude_agent_sdk.query(
        prompt=prompt,
        options=ClaudeAgentOptions(model="sonnet", max_turns=1),
    ):
        if isinstance(message, ResultMessage):
            return message.result
    return ""

@workflow(name="customer_support_flow")
async def handle_ticket():
    summary = await draft_answer("Summarize a billing issue in one sentence.")
    print(summary)

asyncio.run(handle_ticket())
```

```typescript TypeScript
import * as _ClaudeAgentSDK from "@anthropic-ai/claude-agent-sdk";
import { ClaudeAgentSDKInstrumentor } from "@respan/instrumentation-claude-agent-sdk";
import { Respan } from "@respan/respan";

const ClaudeAgentSDK = { ..._ClaudeAgentSDK };

const respan = new Respan({
  apiKey: process.env.RESPAN_API_KEY,
  baseURL: process.env.RESPAN_BASE_URL,
  instrumentations: [
    new ClaudeAgentSDKInstrumentor({
      sdkModule: ClaudeAgentSDK,
    }),
  ],
});
await respan.initialize();

async function draftAnswer(prompt: string) {
  return respan.withTask({ name: "draft_answer" }, async () => {
    const response = await ClaudeAgentSDK.query({
      prompt,
      options: {
        model: "sonnet",
        maxTurns: 1,
      },
    });

    for await (const message of response) {
      if (message.type === "result") {
        return message.result;
      }
    }
  });
}

async function handleTicket() {
  return respan.withWorkflow({ name: "customer_support_flow" }, async () => {
    const summary = await draftAnswer(
      "Summarize a billing issue in one sentence."
    );
    console.log(summary);
  });
}

await handleTicket();
```

## Examples

### Basic query

Run a single Claude Agent SDK query and print the final result.

```python Python
import asyncio

import claude_agent_sdk
from claude_agent_sdk import ClaudeAgentOptions, ResultMessage

async def main():
    async for message in claude_agent_sdk.query(
        prompt="Say hello in three languages.",
        options=ClaudeAgentOptions(model="sonnet", max_turns=1),
    ):
        if isinstance(message, ResultMessage):
            print(message.result)

asyncio.run(main())
```

```typescript TypeScript
import * as _ClaudeAgentSDK from "@anthropic-ai/claude-agent-sdk";

const ClaudeAgentSDK = { ..._ClaudeAgentSDK };

const response = await ClaudeAgentSDK.query({
  prompt: "Say hello in three languages.",
  options: {
    model: "sonnet",
    maxTurns: 1,
  },
});

for await (const message of response) {
  if (message.type === "result") {
    console.log(message.result);
  }
}
```

### Streaming message flow

The SDK emits multiple message objects during a run. You can inspect the flow while Respan traces the full session.

```python Python
import asyncio

import claude_agent_sdk
from claude_agent_sdk import ClaudeAgentOptions

async def main():
    message_types = []
    async for message in claude_agent_sdk.query(
        prompt="Explain recursion in one short paragraph.",
        options=ClaudeAgentOptions(model="sonnet", max_turns=1),
    ):
        message_types.append(type(message).__name__)

    print(" -> ".join(message_types))

asyncio.run(main())
```

```typescript TypeScript
import * as _ClaudeAgentSDK from "@anthropic-ai/claude-agent-sdk";

const ClaudeAgentSDK = { ..._ClaudeAgentSDK };

const response = await ClaudeAgentSDK.query({
  prompt: "Explain recursion in one short paragraph.",
  options: {
    model: "sonnet",
    maxTurns: 1,
  },
});

const messageTypes: string[] = [];
for await (const message of response) {
  messageTypes.push(message.type);
}

console.log(messageTypes.join(" -> "));
```