Claude Code SDK Integration
The Claude Code SDK (also known as Claude Agent SDK) provides a powerful framework for building AI agents with Claude. mantisdk provides seamless integration for tracing all Claude agent interactions.
OpenTelemetry Native — Claude Code SDK has built-in OpenTelemetry support, making it easy to send traces to Insight via mantisdk.
Installation
Install Dependencies
pip install mantisdk claude-agent-sdk python-dotenvSet Environment Variables
# Insight credentials
export INSIGHT_HOST="https://insight.withmetis.ai"
export INSIGHT_PUBLIC_KEY="pk-lf-your-public-key"
export INSIGHT_SECRET_KEY="sk-lf-your-secret-key"
# Anthropic API key
export ANTHROPIC_API_KEY="sk-ant-..."Quick Start
#!/usr/bin/env python3
import asyncio
from typing import Any
from dotenv import load_dotenv
load_dotenv()
# Initialize mantisdk tracing FIRST
import mantisdk.tracing as tracing
from mantisdk.tracing.exporters.insight import is_insight_configured
exporters = []
if is_insight_configured():
exporters.append(tracing.insight_exporter())
tracing.init(
service_name="claude-agent",
exporters=exporters if exporters else None,
instrument_default=False,
)
# Instrument Claude Agent SDK
tracing.instrument(names=["claude_agent_sdk"])
# NOW import the SDK
from claude_agent_sdk import (
ClaudeSDKClient,
ClaudeAgentOptions,
tool,
create_sdk_mcp_server,
)
# Define a tool
@tool(
"get_weather",
"Gets the current weather for a given city",
{"city": str},
)
async def get_weather(args: dict[str, Any]) -> dict[str, Any]:
city = args["city"]
# Simulated weather lookup
weather = "Sunny, 72F"
return {
"content": [{"type": "text", "text": f"Weather in {city}: {weather}"}]
}
async def main():
# Create MCP server with tools
weather_server = create_sdk_mcp_server(
name="weather",
version="1.0.0",
tools=[get_weather],
)
# Configure agent options
options = ClaudeAgentOptions(
model="claude-sonnet-4-20250514",
system_prompt="You are a helpful assistant with weather information.",
mcp_servers={"weather": weather_server},
allowed_tools=["mcp__weather__get_weather"],
)
# Use the agent - calls are auto-traced
with tracing.trace("weather-query", input="What's the weather in Paris?") as span:
async with ClaudeSDKClient(options=options) as client:
await client.query("What's the weather like in Paris?")
response_text = []
async for message in client.receive_response():
if hasattr(message, "content"):
for block in message.content:
if hasattr(block, "text"):
response_text.append(block.text)
result = "".join(response_text)
span.set_attribute(tracing.semconv.TRACE_OUTPUT, result)
print(result)
if __name__ == "__main__":
try:
asyncio.run(main())
finally:
tracing.shutdown()What Gets Traced
When you instrument the Claude Agent SDK, mantisdk automatically captures:
| Event | Captured Data |
|---|---|
| Agent Queries | Input prompts, model selection |
| LLM Calls | Tokens used, model response, latency |
| Tool Executions | Tool name, arguments, results |
| MCP Server Calls | Server name, tool invocations |
| Errors | Exception details, stack traces |
Adding Custom Attributes
Enhance your traces with user context and metadata:
async def handle_user_query(user_id: str, session_id: str, query: str):
with tracing.trace("user-query", input=query) as span:
# Add user context
span.set_attribute(tracing.semconv.USER_ID, user_id)
span.set_attribute(tracing.semconv.SESSION_ID, session_id)
# Run the agent
async with ClaudeSDKClient(options=options) as client:
await client.query(query)
async for message in client.receive_response():
# Process response
pass
span.set_attribute(tracing.semconv.TRACE_OUTPUT, result)Multi-Turn Conversations
Track entire conversation sessions:
async def conversation_session(user_id: str):
session_id = f"session-{uuid.uuid4()}"
with tracing.trace("conversation", metadata={"session": session_id}) as session_span:
session_span.set_attribute(tracing.semconv.USER_ID, user_id)
session_span.set_attribute(tracing.semconv.SESSION_ID, session_id)
async with ClaudeSDKClient(options=options) as client:
# First turn
with tracing.span("turn-1", input="Hello!"):
await client.query("Hello!")
# ... handle response
# Second turn
with tracing.span("turn-2", input="What can you help me with?"):
await client.query("What can you help me with?")
# ... handle responseError Handling
Capture and trace errors:
async def safe_agent_call(query: str):
with tracing.trace("agent-call", input=query) as span:
try:
async with ClaudeSDKClient(options=options) as client:
await client.query(query)
async for message in client.receive_response():
if hasattr(message, "is_error") and message.is_error:
span.set_attribute("error", True)
span.set_attribute("error.message", str(message))
raise Exception(f"Agent error: {message}")
# Process successful response
except Exception as e:
span.set_attribute("error", True)
span.set_attribute("error.type", type(e).__name__)
span.set_attribute("error.message", str(e))
raiseBest Practices
⚠️
Import Order Matters — Always initialize mantisdk and call instrument() before importing claude_agent_sdk.
- Initialize Early: Set up tracing before any SDK imports
- Use Sessions: Group related queries with
SESSION_ID - Track Users: Add
USER_IDfor user-level analytics - Handle Errors: Wrap agent calls in try/except with proper tracing
- Shutdown Cleanly: Always call
tracing.shutdown()to flush spans
Viewing Traces in Insight
After running your Claude agent with mantisdk instrumentation:
- Open Insight (opens in a new tab)
- Navigate to your project
- View the trace timeline showing:
- Agent initialization
- Each query/response cycle
- Tool executions with inputs/outputs
- Token usage and latency metrics
Next Steps
- Tracing API Reference — Complete API documentation
- Semantic Conventions — Standard attributes
- Insight Export — Configure trace export