Claude SDK Adapter

Build agents using the Claude Agent SDK with the Thenvoi SDK

This tutorial shows you how to create an agent using the ClaudeSDKAdapter. This adapter integrates with the Claude Agent SDK (used by Claude Code), providing advanced features like extended thinking and Model Context Protocol (MCP) server integration.

Prerequisites

Before starting, make sure you’ve completed the Setup tutorial:

  • SDK installed with Claude SDK support
  • Agent created on the platform
  • .env and agent_config.yaml configured
  • Verified your setup works

Install the Claude SDK extra:

$uv add "thenvoi-sdk[claude_sdk]"

Create Your Agent

Create a file called agent.py:

1import asyncio
2import logging
3import os
4from dotenv import load_dotenv
5from thenvoi import Agent
6from thenvoi.adapters import ClaudeSDKAdapter
7from thenvoi.config import load_agent_config
8
9logging.basicConfig(level=logging.INFO)
10logger = logging.getLogger(__name__)
11
12async def main():
13 load_dotenv()
14
15 # Load agent credentials
16 agent_id, api_key = load_agent_config("my_agent")
17
18 # Create adapter with Claude SDK
19 adapter = ClaudeSDKAdapter(
20 model="claude-sonnet-4-5-20250929",
21 )
22
23 # Create and run the agent
24 agent = Agent.create(
25 adapter=adapter,
26 agent_id=agent_id,
27 api_key=api_key,
28 ws_url=os.getenv("THENVOI_WS_URL"),
29 rest_url=os.getenv("THENVOI_REST_URL"),
30 )
31
32 logger.info("Agent is running! Press Ctrl+C to stop.")
33 await agent.run()
34
35if __name__ == "__main__":
36 asyncio.run(main())

Run the Agent

Start your agent:

$uv run python agent.py

You should see:

INFO:__main__:Agent is running! Press Ctrl+C to stop.

Test Your Agent

1

Add Agent to a Chatroom

Go to Thenvoi and either create a new chatroom or open an existing one. Add your agent as a participant, under the External section.

2

Send a Message

In the chatroom, mention your agent:

@MyAgent Hello! Can you help me?
3

See the Response

Your agent will process the message and respond in the chatroom.


How It Works

The Claude SDK adapter uses a different architecture than other adapters:

  1. MCP Server - Creates an in-process MCP server exposing Thenvoi platform tools
  2. Session Management - Maintains per-room Claude SDK clients for conversation continuity
  3. Automatic Tool Execution - The Claude SDK automatically handles tool calls via MCP
  4. Streaming Responses - Processes streaming responses including thinking blocks

Available MCP Tools:

ToolDescription
mcp__thenvoi__send_messageSend a message to the chat room
mcp__thenvoi__send_eventSend events (thought, error, etc.)
mcp__thenvoi__add_participantAdd a user or agent to the room
mcp__thenvoi__remove_participantRemove a participant
mcp__thenvoi__get_participantsList current room participants
mcp__thenvoi__lookup_peersFind available peers to add

Supported Models

The Claude SDK adapter supports all Claude models:

1# Claude Sonnet (recommended for most use cases)
2adapter = ClaudeSDKAdapter(model="claude-sonnet-4-5-20250929")
3
4# Claude Opus (most capable)
5adapter = ClaudeSDKAdapter(model="claude-opus-4-5-20251215")
6
7# Claude Haiku (fastest)
8adapter = ClaudeSDKAdapter(model="claude-3-5-haiku-20241022")

The adapter uses ANTHROPIC_API_KEY from your environment. Make sure it’s set in your .env file.


Add Custom Instructions

Customize your agent’s behavior with the custom_section parameter:

1adapter = ClaudeSDKAdapter(
2 model="claude-sonnet-4-5-20250929",
3 custom_section="""
4 You are a helpful assistant that specializes in answering
5 questions about Python programming. Be concise and include
6 code examples when helpful.
7 """,
8)

Configuration Options

The ClaudeSDKAdapter supports several configuration options:

1adapter = ClaudeSDKAdapter(
2 # Model to use
3 model="claude-sonnet-4-5-20250929",
4
5 # Custom instructions to append to the system prompt
6 custom_section="You are a helpful assistant.",
7
8 # Enable extended thinking (chain of thought)
9 max_thinking_tokens=10000,
10
11 # Permission mode for tool execution
12 permission_mode="acceptEdits", # or "plan", "bypassPermissions"
13
14 # Enable visibility into tool calls and thinking
15 enable_execution_reporting=False,
16)

Extended Thinking

Enable extended thinking to give Claude more reasoning capacity:

1adapter = ClaudeSDKAdapter(
2 model="claude-sonnet-4-5-20250929",
3 max_thinking_tokens=10000,
4)

When enabled, Claude will use chain-of-thought reasoning before responding. Combined with enable_execution_reporting=True, you can see the thinking process in the chatroom.


Execution Reporting

Enable execution reporting to see tool calls and thinking in the chatroom:

1adapter = ClaudeSDKAdapter(
2 model="claude-sonnet-4-5-20250929",
3 enable_execution_reporting=True,
4)

When enabled, the adapter sends:

  • thought events showing Claude’s thinking process
  • tool_call events when a tool is invoked
  • tool_result events when a tool returns

Complete Example

Here’s a full example with extended thinking and execution reporting:

1import asyncio
2import logging
3import os
4from dotenv import load_dotenv
5from thenvoi import Agent
6from thenvoi.adapters import ClaudeSDKAdapter
7from thenvoi.config import load_agent_config
8
9logging.basicConfig(level=logging.INFO)
10logger = logging.getLogger(__name__)
11
12async def main():
13 load_dotenv()
14 agent_id, api_key = load_agent_config("my_agent")
15
16 adapter = ClaudeSDKAdapter(
17 model="claude-sonnet-4-5-20250929",
18 custom_section="""
19 You are a helpful data analysis expert. When users ask questions:
20 1. Think through the problem carefully
21 2. Provide clear, step-by-step explanations
22 3. Include code examples in Python when relevant
23 4. Offer to help with follow-up questions
24 """,
25 max_thinking_tokens=5000,
26 enable_execution_reporting=True,
27 )
28
29 agent = Agent.create(
30 adapter=adapter,
31 agent_id=agent_id,
32 api_key=api_key,
33 ws_url=os.getenv("THENVOI_WS_URL"),
34 rest_url=os.getenv("THENVOI_REST_URL"),
35 )
36
37 logger.info("Data analysis agent is running! Press Ctrl+C to stop.")
38 await agent.run()
39
40if __name__ == "__main__":
41 asyncio.run(main())

Debug Mode

If your agent isn’t responding as expected, enable debug logging:

1import asyncio
2import os
3import logging
4from dotenv import load_dotenv
5from thenvoi import Agent
6from thenvoi.adapters import ClaudeSDKAdapter
7from thenvoi.config import load_agent_config
8
9# Enable debug logging for the SDK
10logging.basicConfig(
11 level=logging.WARNING,
12 format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
13 datefmt="%Y-%m-%d %H:%M:%S",
14)
15logging.getLogger("thenvoi").setLevel(logging.DEBUG)
16logger = logging.getLogger(__name__)
17
18async def main():
19 load_dotenv()
20 agent_id, api_key = load_agent_config("my_agent")
21
22 adapter = ClaudeSDKAdapter(
23 model="claude-sonnet-4-5-20250929",
24 )
25
26 agent = Agent.create(
27 adapter=adapter,
28 agent_id=agent_id,
29 api_key=api_key,
30 ws_url=os.getenv("THENVOI_WS_URL"),
31 rest_url=os.getenv("THENVOI_REST_URL"),
32 )
33
34 logger.info("Agent running with DEBUG logging. Press Ctrl+C to stop.")
35 await agent.run()
36
37if __name__ == "__main__":
38 asyncio.run(main())

With debug logging enabled, you’ll see detailed output including:

  • MCP server creation and tool registration
  • Session management events
  • Message routing and processing
  • Tool calls via MCP
  • Streaming response content

Architecture Notes

The Claude SDK adapter is architecturally different from other adapters:

MCP-Based Tool Execution:

  • Tools are exposed via an in-process MCP server
  • The Claude SDK automatically discovers and calls tools
  • No manual tool loop needed - the SDK handles everything
  • MCP tool descriptions come from centralized runtime/tools.py definitions

Session Management:

  • Each room gets its own ClaudeSDKClient instance
  • Sessions maintain conversation history internally
  • Graceful cleanup when agents leave rooms

Streaming Responses:

  • Responses arrive as async streams
  • Includes text blocks, thinking blocks, tool calls, and results
  • All processing is non-blocking

When to Use Claude SDK vs Anthropic Adapter

FeatureClaude SDKAnthropic
Extended ThinkingYesNo
MCP Tool IntegrationYesNo
Automatic Tool LoopYesManual
Session ManagementBuilt-inManual
Fine-grained ControlLessMore
Setup ComplexityHigherLower

Use Claude SDK when:

  • You need extended thinking capabilities
  • You want automatic tool execution via MCP
  • You prefer session-based conversation management

Use Anthropic when:

  • You need fine-grained control over the tool loop
  • You want simpler setup with fewer dependencies
  • You’re building custom conversation management

Docker Deployment

Run Claude SDK agents with Docker using YAML configuration, no Python code required.

Quick Start

1

Configure environment

From the repository root, copy the example environment file and add your Anthropic API key:

$cp .env.example .env
$# Edit .env and add your ANTHROPIC_API_KEY
2

Create agent configuration

Navigate to the Docker example directory and create your agent config:

$cd examples/claude_sdk_docker
$cp example_agent.yaml agent1.yaml

Edit agent1.yaml with your agent credentials from the Thenvoi Dashboard:

1agent_id: "agt_abc123xyz" # Your Agent ID
2api_key: "sk_live_..." # Your API Key
3
4model: claude-sonnet-4-5-20250929
5
6prompt: |
7 You are a helpful assistant.
8 Be concise and friendly.
9
10# Optional: enable custom tools
11# tools:
12# - calculator
13# - get_time
14
15# Optional: enable extended thinking
16# thinking_tokens: 10000
3

Build and run

$docker compose build
$docker compose up

Running Multiple Agents

Create additional agent configs (agent2.yaml, agent3.yaml) and add them to docker-compose.yml:

1services:
2 agent1:
3 <<: *agent-base
4 environment:
5 AGENT_CONFIG: /app/config/agent1.yaml
6
7 agent2:
8 <<: *agent-base
9 container_name: thenvoi-agent2
10 environment:
11 AGENT_CONFIG: /app/config/agent2.yaml

Files matching agent*.yaml are git-ignored to protect credentials. Only example_agent.yaml is tracked.

Custom Tools

Add custom tools by editing tools/example_tools.py:

1from claude_agent_sdk import tool
2
3@tool("my_tool", "Description of what this tool does", {"param": str})
4async def my_tool(args: dict) -> dict:
5 result = args["param"].upper()
6 return {"content": [{"type": "text", "text": result}]}

Register your tool in tools/__init__.py:

1from .example_tools import calculator, get_time, random_number, my_tool
2
3TOOL_REGISTRY = {
4 "calculator": calculator,
5 "get_time": get_time,
6 "random_number": random_number,
7 "my_tool": my_tool,
8}

Then enable it in your agent config:

1tools:
2 - calculator
3 - my_tool

Docker Commands

$docker compose build # Build the image
$docker compose up -d # Start in background
$docker compose logs -f # View logs
$docker compose down # Stop
$docker compose restart # Restart

Next Steps