Skip to navigation

Claude Agent SDK (tracing)

The Claude Agent 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.

Create an account at platform.respan.ai and grab an API key.

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

See Claude Agent SDK gateway setup to route this integration through the Respan gateway.

Setup

1

Install packages

pip install claude-agent-sdk respan-ai respan-instrumentation-claude-agent-sdk
2

Set environment variables

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.

3

Initialize and run

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())
4

View your trace

Open the Traces page 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.

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

ParameterTypeDefaultDescription
api_keystr | NoneNoneFalls back to RESPAN_API_KEY env var.
base_urlstr | NoneNoneFalls back to RESPAN_BASE_URL env var.
instrumentationslist[]Plugin instrumentations to activate (e.g. ClaudeAgentSDKInstrumentor(capture_content=True)).
customer_identifierstr | NoneNoneDefault customer identifier for all spans.
metadatadict | NoneNoneDefault metadata attached to all spans.
environmentstr | NoneNoneEnvironment tag (e.g. "production").

ClaudeAgentSDKInstrumentor options

ParameterTypeDefaultDescription
agent_namestr | NoneNoneOverride the agent name attached to emitted Claude Agent SDK spans.
capture_contentboolFalseInclude 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.

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"},
)

With propagate_attributes

Override per-request using a context scope.

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)
AttributeTypeDescription
customer_identifierstrIdentifies the end user in Respan analytics.
thread_identifierstrGroups related messages into a conversation.
metadatadictCustom 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.

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())

Examples

Basic query

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

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())

Streaming message flow

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

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())