> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://respan.ai/docs/integrations/claude-agents-sdk/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/gateway/api-keys). Run `npx @respan/cli setup` to set up with your coding agent. #### Use Respan Gateway See [Claude Agent SDK gateway setup](/docs/integrations/gateway/claude-agent-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 **`Python`** ```bash Python pip install claude-agent-sdk respan-ai respan-instrumentation-claude-agent-sdk ``` **`TypeScript`** ```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 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 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. ## Prewarmed TypeScript sessions `prewarm()` starts a Claude Agent SDK session's process before you have a prompt. It's an alpha API in Claude Agent SDK 0.3.282 and later, and tracing it needs `@respan/instrumentation-claude-agent-sdk` 1.3.0 or later. Call `prewarm()` through the same `ClaudeAgentSDK` module you passed to the instrumentor. Tracing starts when `spare.claim()` starts the session, so the wait before the prompt isn't traced, and closing an unused spare creates no trace. ```typescript const spare = await ClaudeAgentSDK.prewarm({ options: { permissionMode: "default", tools: [] }, }); try { const query = spare.claim({ prompt: "Explain tracing in one sentence.", options: { cwd: process.cwd(), model: "sonnet" }, }); for await (const message of query) { if (message.type === "result" && message.subtype === "success") { console.log(message.result); } } } finally { spare.close(); await respan.flush(); } ``` A claimed session gets the same agent, model, and tool spans as `query()`, including failed tool calls, and your existing hooks still run. ## 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 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 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 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 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 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 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 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 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 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 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(" -> ")); ``` > Trace Claude Agent SDK workflows with Respan using the Claude Agent SDK instrumentation plugin for Python and TypeScript.