Skip to main content
Claude Agent SDK is Anthropic’s Python SDK for building AI agents powered by Claude Code. It gives agents access to built-in tools like Bash, Read, Write, and Glob for autonomous task execution. HoneyHive integrates with the Claude Agent SDK via the OpenInference instrumentor, automatically capturing agent runs, tool calls, and multi-turn conversations.

Quick Start

Add HoneyHive tracing in just 4 lines of code. Initialize the tracer and instrumentor, and all agent activity is automatically traced.
To see where to initialize the tracer for your environment, including AWS Lambda and long-running servers, see Tracer Initialization.
pip install "honeyhive>=1.0.0rc0" openinference-instrumentation-claude-agent-sdk claude-agent-sdk
import os
from honeyhive import HoneyHiveTracer
from openinference.instrumentation.claude_agent_sdk import ClaudeAgentSDKInstrumentor

tracer = HoneyHiveTracer.init(
    api_key=os.getenv("HH_API_KEY"),
    project=os.getenv("HH_PROJECT"),
)
ClaudeAgentSDKInstrumentor().instrument(tracer_provider=tracer.provider)

# Import claude_agent_sdk AFTER this setup block (see examples below)
# Call tracer.force_flush() before your process exits

Compatibility

RequirementVersion
Python3.10+
claude-agent-sdk0.1.45+

What Gets Traced

The instrumentor automatically captures:
  • Agent runs - Every agent invocation with inputs and outputs
  • Tool executions - Built-in tool calls (Bash, Read, Write, Glob) with arguments and results
  • Multi-turn conversations - Session continuity across turns with ClaudeSDKClient
  • Token usage and cost - Model token counts and cost per request
No manual instrumentation required.

Example: Single Query with Tools

The query() function runs a one-off agent session. The agent can use built-in tools to complete tasks autonomously:
import asyncio
import os
from openinference.instrumentation.claude_agent_sdk import ClaudeAgentSDKInstrumentor
from honeyhive import HoneyHiveTracer

tracer = HoneyHiveTracer.init(
    api_key=os.getenv("HH_API_KEY"),
    project=os.getenv("HH_PROJECT"),
)
ClaudeAgentSDKInstrumentor().instrument(tracer_provider=tracer.provider)

# Import AFTER instrument() so references resolve to the patched versions
from claude_agent_sdk import AssistantMessage, ClaudeAgentOptions, TextBlock, query

async def main():
    async for message in query(
        prompt="Read the file config.json and summarize its contents.",
        options=ClaudeAgentOptions(
            system_prompt="You are a helpful assistant. Complete tasks concisely.",
            allowed_tools=["Read"],
            max_turns=3,
            permission_mode="bypassPermissions",
        ),
    ):
        if isinstance(message, AssistantMessage):
            for block in message.content:
                if isinstance(block, TextBlock):
                    print(block.text)

    tracer.force_flush()

asyncio.run(main())
permission_mode="bypassPermissions" allows unrestricted tool use without interactive prompts. Use only in sandboxed or non-interactive environments.

Example: Multi-Turn Conversation

ClaudeSDKClient maintains session continuity across multiple turns. The agent remembers context from previous interactions:
import asyncio
import os
from openinference.instrumentation.claude_agent_sdk import ClaudeAgentSDKInstrumentor
from honeyhive import HoneyHiveTracer

tracer = HoneyHiveTracer.init(
    api_key=os.getenv("HH_API_KEY"),
    project=os.getenv("HH_PROJECT"),
)
ClaudeAgentSDKInstrumentor().instrument(tracer_provider=tracer.provider)

# Import AFTER instrument() so references resolve to the patched versions
from claude_agent_sdk import (
    AssistantMessage,
    ClaudeAgentOptions,
    ClaudeSDKClient,
    TextBlock,
)

async def main():
    client = ClaudeSDKClient(
        options=ClaudeAgentOptions(
            system_prompt="You are a customer support agent. Keep responses concise.",
            allowed_tools=["Read"],
            max_turns=3,
            permission_mode="bypassPermissions",
        )
    )

    await client.connect()
    try:
        # Turn 1
        await client.query(prompt="Read orders.json and tell me the status of ORD-1002.")
        async for message in client.receive_response():
            if isinstance(message, AssistantMessage):
                for block in message.content:
                    if isinstance(block, TextBlock):
                        print(block.text)

        # Turn 2 - agent remembers context from Turn 1
        await client.query(prompt="What about ORD-1003? Is it delayed?")
        async for message in client.receive_response():
            if isinstance(message, AssistantMessage):
                for block in message.content:
                    if isinstance(block, TextBlock):
                        print(block.text)
    finally:
        await client.disconnect()
        tracer.force_flush()

asyncio.run(main())
In HoneyHive, you’ll see each turn as a separate agent span with tool calls nested underneath.

Troubleshooting

Traces not appearing

  1. Check environment variables - Ensure HH_API_KEY and HH_PROJECT are set
  2. Pass the tracer provider - The instrumentor must receive tracer_provider=tracer.provider:
from honeyhive import HoneyHiveTracer
from openinference.instrumentation.claude_agent_sdk import ClaudeAgentSDKInstrumentor

tracer = HoneyHiveTracer.init(project="your-project")

# Correct - pass tracer_provider
ClaudeAgentSDKInstrumentor().instrument(tracer_provider=tracer.provider)

# Wrong - missing tracer_provider
ClaudeAgentSDKInstrumentor().instrument()
  1. Import after instrumenting - Import claude_agent_sdk after calling instrument(). The instrumentor patches module-level functions, so importing before patching captures the original unpatched references:
# Correct - import after instrument()
ClaudeAgentSDKInstrumentor().instrument(tracer_provider=tracer.provider)
from claude_agent_sdk import query

# Wrong - import before instrument()
from claude_agent_sdk import query
ClaudeAgentSDKInstrumentor().instrument(tracer_provider=tracer.provider)
  1. Flush before exit - Call tracer.force_flush() before your process exits. Without this, buffered spans may be silently dropped in short-lived scripts:
async def main():
    # ... your agent code ...
    tracer.force_flush()

asyncio.run(main())
  1. Check Anthropic credentials - Ensure ANTHROPIC_API_KEY is set

Enrich Your Traces

Add user IDs and custom metadata to Claude Agent SDK traces

Custom Spans

Create spans for business logic around agent calls

Distributed Tracing

Trace agents across service boundaries

Resources