Claude Agent SDK

  1. Sign up — Create an account at platform.respan.ai
  2. Create an API key — Generate one on the API keys page
  3. Add credits or a provider key — Add credits on the Credits page or connect your own provider key on the Integrations page

Add the Docs MCP to your AI coding tool to get help building with Respan. No API key needed.

1{
2 "mcpServers": {
3 "respan-docs": {
4 "url": "https://mcp.respan.ai/mcp/docs"
5 }
6 }
7}

What is Claude Agent SDK?

The Claude Agent SDK (claude-agent-sdk) lets you run Claude-powered agent sessions with tool use, multi-turn reasoning, and streamed events. Respan traces the SDK through OpenInference, so a normal claude_agent_sdk.query() call shows up in the Traces view with agent spans, tool activity, and request metadata.

If you’re new to the wrapper used on this page, start with the OpenInference Instrumentor guide.

Setup

1

Install packages

$pip install claude-agent-sdk respan-ai respan-instrumentation-openinference openinference-instrumentation-claude-agent-sdk opentelemetry-semantic-conventions-ai python-dotenv

This is the tested minimal package set for the example on this page. Use a clean virtual environment so the OpenTelemetry dependencies resolve to this working set.

2

Set environment variables

$export RESPAN_API_KEY="YOUR_RESPAN_API_KEY"
$export ANTHROPIC_API_KEY="YOUR_ANTHROPIC_API_KEY"

RESPAN_API_KEY sends traces to Respan. ANTHROPIC_API_KEY is the simplest way to let Claude Agent SDK authenticate with Anthropic.

If you already use Claude Code locally, claude auth login also works. The environment variable is just the simplest example path.

3

Initialize and run

1import asyncio
2import os
3
4import claude_agent_sdk
5from claude_agent_sdk.types import ClaudeAgentOptions
6from dotenv import load_dotenv
7from openinference.instrumentation.claude_agent_sdk import ClaudeAgentSDKInstrumentor
8from respan import Respan
9from respan_instrumentation_openinference import OpenInferenceInstrumentor
10
11load_dotenv()
12
13respan = Respan(
14 api_key=os.getenv("RESPAN_API_KEY"),
15 app_name="claude-agent-sdk-openinference-example",
16 instrumentations=[
17 OpenInferenceInstrumentor(ClaudeAgentSDKInstrumentor)
18 ]
19)
20
21
22def extract_text(message) -> str:
23 parts = []
24 for block in getattr(message, "content", []) or []:
25 text = getattr(block, "text", None)
26 if isinstance(text, str) and text:
27 parts.append(text)
28 return "\n".join(parts)
29
30
31async def main():
32 env = {}
33 if os.getenv("ANTHROPIC_API_KEY"):
34 env["ANTHROPIC_API_KEY"] = os.environ["ANTHROPIC_API_KEY"]
35
36 options = ClaudeAgentOptions(
37 model=os.getenv("CLAUDE_AGENT_MODEL", "sonnet"),
38 max_turns=1,
39 env=env,
40 )
41
42 assistant_text = ""
43
44 try:
45 async for message in claude_agent_sdk.query(
46 prompt="Explain OpenInference in one sentence.",
47 options=options,
48 ):
49 if type(message).__name__ == "AssistantMessage":
50 assistant_text = extract_text(message)
51
52 print(assistant_text)
53 finally:
54 respan.flush()
55
56
57asyncio.run(main())

This is the same minimal setup used in the example project.

4

View your trace

Open the Traces page to see the Claude Agent SDK run, including the root agent span and any tool calls.

What gets traced

All Claude Agent SDK operations are auto-instrumented through OpenInference:

  • query() runs and final results
  • Agent spans and step metadata emitted by the SDK
  • Tool calls and tool outputs
  • Session and model metadata
  • Token usage when the SDK exposes it

Traces appear in the Traces dashboard.

Configuration

ParameterTypeDefaultDescription
api_keystr | NoneNoneFalls back to RESPAN_API_KEY env var.
base_urlstr | NoneNoneFalls back to RESPAN_BASE_URL env var.
app_namestr"respan"Application name shown in Respan.
instrumentationslist[]Plugin instrumentations to activate, such as OpenInferenceInstrumentor(ClaudeAgentSDKInstrumentor).
is_auto_instrumentbool | NoneNoneAuto-discovers installed instrumentors. Not needed for the explicit setup on this page.
customer_identifierstr | NoneNoneDefault customer identifier for all spans.
thread_identifierstr | NoneNoneDefault conversation or thread identifier for all spans.
metadatadict | NoneNoneDefault metadata attached to all spans.
environmentstr | NoneNoneEnvironment tag such as "production".

Attributes

In Respan()

Set defaults at initialization. These apply to all spans created in the current process.

1from openinference.instrumentation.claude_agent_sdk import ClaudeAgentSDKInstrumentor
2from respan import Respan
3from respan_instrumentation_openinference import OpenInferenceInstrumentor
4
5respan = Respan(
6 instrumentations=[OpenInferenceInstrumentor(ClaudeAgentSDKInstrumentor)],
7 customer_identifier="user_123",
8 thread_identifier="conv_001",
9 metadata={"service": "claude-agent", "plan": "pro"},
10)

With propagate_attributes

Override attributes per request using a context manager.

1import os
2
3import claude_agent_sdk
4from claude_agent_sdk.types import ClaudeAgentOptions
5from respan import propagate_attributes
6
7
8def extract_text(message: object) -> str:
9 parts = []
10 for block in getattr(message, "content", []) or []:
11 text = getattr(block, "text", None)
12 if isinstance(text, str) and text:
13 parts.append(text)
14 return "\n".join(parts)
15
16
17async def handle_request(user_id: str, prompt: str):
18 options = ClaudeAgentOptions(
19 model="sonnet",
20 max_turns=1,
21 env={"ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"]},
22 )
23
24 with propagate_attributes(
25 customer_identifier=user_id,
26 thread_identifier="conv_abc_123",
27 metadata={"plan": "pro"},
28 ):
29 assistant_text = ""
30 async for message in claude_agent_sdk.query(prompt=prompt, options=options):
31 if type(message).__name__ == "AssistantMessage":
32 assistant_text = extract_text(message)
33
34 print(assistant_text)
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. The OpenInference instrumentor already traces Claude Agent SDK calls automatically. Use @workflow and @task when you want to add structure around one or more agent runs.

1import asyncio
2import os
3
4import claude_agent_sdk
5from claude_agent_sdk.types import ClaudeAgentOptions
6from openinference.instrumentation.claude_agent_sdk import ClaudeAgentSDKInstrumentor
7from respan import Respan, task, workflow
8from respan_instrumentation_openinference import OpenInferenceInstrumentor
9
10respan = Respan(
11 instrumentations=[OpenInferenceInstrumentor(ClaudeAgentSDKInstrumentor)]
12)
13
14
15def extract_text(message: object) -> str:
16 parts = []
17 for block in getattr(message, "content", []) or []:
18 text = getattr(block, "text", None)
19 if isinstance(text, str) and text:
20 parts.append(text)
21 return "\n".join(parts)
22
23
24@task(name="draft_answer")
25async def draft_answer(prompt: str) -> str:
26 options = ClaudeAgentOptions(
27 model="sonnet",
28 max_turns=1,
29 env={"ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"]},
30 )
31
32 assistant_text = ""
33 async for message in claude_agent_sdk.query(prompt=prompt, options=options):
34 if type(message).__name__ == "AssistantMessage":
35 assistant_text = extract_text(message)
36
37 return assistant_text
38
39
40@workflow(name="customer_support_flow")
41async def handle_ticket():
42 summary = await draft_answer("Summarize a billing issue in one sentence.")
43 print(summary)
44
45
46asyncio.run(handle_ticket())
47respan.flush()

Examples

Basic

Run a single Claude Agent SDK query and print the assistant reply.

1import asyncio
2import os
3
4import claude_agent_sdk
5from claude_agent_sdk.types import ClaudeAgentOptions
6
7
8def extract_text(message: object) -> str:
9 parts = []
10 for block in getattr(message, "content", []) or []:
11 text = getattr(block, "text", None)
12 if isinstance(text, str) and text:
13 parts.append(text)
14 return "\n".join(parts)
15
16
17async def main():
18 options = ClaudeAgentOptions(
19 model="sonnet",
20 max_turns=1,
21 env={"ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"]},
22 )
23
24 async for message in claude_agent_sdk.query(
25 prompt="Say hello in three languages.",
26 options=options,
27 ):
28 if type(message).__name__ == "AssistantMessage":
29 print(extract_text(message))
30
31
32asyncio.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.

1import asyncio
2import os
3
4import claude_agent_sdk
5from claude_agent_sdk.types import ClaudeAgentOptions
6
7
8async def main():
9 options = ClaudeAgentOptions(
10 model="sonnet",
11 max_turns=1,
12 env={"ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"]},
13 )
14
15 message_types = []
16 async for message in claude_agent_sdk.query(
17 prompt="Explain recursion in one short paragraph.",
18 options=options,
19 ):
20 message_types.append(type(message).__name__)
21
22 print(" -> ".join(message_types))
23
24
25asyncio.run(main())

Learn more