mirror of
https://github.com/anthropics/claude-code-sdk-python.git
synced 2025-12-23 09:19:52 +00:00
feat: Add in-process SDK MCP server support (#142)
## Summary Adds in-process SDK MCP server support to the Python SDK, building on the control protocol from #139. **Note: Targets `dickson/control` branch (PR #139), not `main`.** ## Key Changes - Added `@tool` decorator and `create_sdk_mcp_server()` API for defining in-process MCP servers - SDK MCP servers run directly in the Python process (no subprocess overhead) - Moved SDK MCP handling from Transport to Query class for proper architectural layering - Added `McpSdkServerConfig` type and integrated with control protocol ## Example ```python from claude_code_sdk import tool, create_sdk_mcp_server @tool("greet", "Greet a user", {"name": str}) async def greet_user(args): return {"content": [{"type": "text", "text": f"Hello, {args['name']}!"}]} server = create_sdk_mcp_server(name="my-tools", tools=[greet_user]) options = ClaudeCodeOptions(mcp_servers={"tools": server}) ``` ## Testing - Added integration tests in `test_sdk_mcp_integration.py` - Added example calculator server in `examples/mcp_calculator.py` --------- Co-authored-by: Dickson Tsai <dickson@anthropic.com> Co-authored-by: Ashwin Bhat <ashwin@anthropic.com> Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
parent
22fa9f473e
commit
9ef57859af
11 changed files with 879 additions and 18 deletions
181
examples/mcp_calculator.py
Normal file
181
examples/mcp_calculator.py
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Example: Calculator MCP Server.
|
||||
|
||||
This example demonstrates how to create an in-process MCP server with
|
||||
calculator tools using the Claude Code Python SDK.
|
||||
|
||||
Unlike external MCP servers that require separate processes, this server
|
||||
runs directly within your Python application, providing better performance
|
||||
and simpler deployment.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from typing import Any
|
||||
|
||||
from claude_code_sdk import (
|
||||
ClaudeCodeOptions,
|
||||
create_sdk_mcp_server,
|
||||
query,
|
||||
tool,
|
||||
)
|
||||
|
||||
# Define calculator tools using the @tool decorator
|
||||
|
||||
@tool("add", "Add two numbers", {"a": float, "b": float})
|
||||
async def add_numbers(args: dict[str, Any]) -> dict[str, Any]:
|
||||
"""Add two numbers together."""
|
||||
result = args["a"] + args["b"]
|
||||
return {
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": f"{args['a']} + {args['b']} = {result}"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@tool("subtract", "Subtract one number from another", {"a": float, "b": float})
|
||||
async def subtract_numbers(args: dict[str, Any]) -> dict[str, Any]:
|
||||
"""Subtract b from a."""
|
||||
result = args["a"] - args["b"]
|
||||
return {
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": f"{args['a']} - {args['b']} = {result}"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@tool("multiply", "Multiply two numbers", {"a": float, "b": float})
|
||||
async def multiply_numbers(args: dict[str, Any]) -> dict[str, Any]:
|
||||
"""Multiply two numbers."""
|
||||
result = args["a"] * args["b"]
|
||||
return {
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": f"{args['a']} × {args['b']} = {result}"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@tool("divide", "Divide one number by another", {"a": float, "b": float})
|
||||
async def divide_numbers(args: dict[str, Any]) -> dict[str, Any]:
|
||||
"""Divide a by b."""
|
||||
if args["b"] == 0:
|
||||
return {
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Error: Division by zero is not allowed"
|
||||
}
|
||||
],
|
||||
"is_error": True
|
||||
}
|
||||
|
||||
result = args["a"] / args["b"]
|
||||
return {
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": f"{args['a']} ÷ {args['b']} = {result}"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@tool("sqrt", "Calculate square root", {"n": float})
|
||||
async def square_root(args: dict[str, Any]) -> dict[str, Any]:
|
||||
"""Calculate the square root of a number."""
|
||||
n = args["n"]
|
||||
if n < 0:
|
||||
return {
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": f"Error: Cannot calculate square root of negative number {n}"
|
||||
}
|
||||
],
|
||||
"is_error": True
|
||||
}
|
||||
|
||||
import math
|
||||
result = math.sqrt(n)
|
||||
return {
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": f"√{n} = {result}"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@tool("power", "Raise a number to a power", {"base": float, "exponent": float})
|
||||
async def power(args: dict[str, Any]) -> dict[str, Any]:
|
||||
"""Raise base to the exponent power."""
|
||||
result = args["base"] ** args["exponent"]
|
||||
return {
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": f"{args['base']}^{args['exponent']} = {result}"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
async def main():
|
||||
"""Run example calculations using the SDK MCP server."""
|
||||
|
||||
# Create the calculator server with all tools
|
||||
calculator = create_sdk_mcp_server(
|
||||
name="calculator",
|
||||
version="2.0.0",
|
||||
tools=[
|
||||
add_numbers,
|
||||
subtract_numbers,
|
||||
multiply_numbers,
|
||||
divide_numbers,
|
||||
square_root,
|
||||
power
|
||||
]
|
||||
)
|
||||
|
||||
# Configure Claude to use the calculator server
|
||||
options = ClaudeCodeOptions(
|
||||
mcp_servers={"calc": calculator},
|
||||
# Allow Claude to use calculator tools without permission prompts
|
||||
permission_mode="bypassPermissions"
|
||||
)
|
||||
|
||||
# Example prompts to demonstrate calculator usage
|
||||
prompts = [
|
||||
"Calculate 15 + 27",
|
||||
"What is 100 divided by 7?",
|
||||
"Calculate the square root of 144",
|
||||
"What is 2 raised to the power of 8?",
|
||||
"Calculate (12 + 8) * 3 - 10" # Complex calculation
|
||||
]
|
||||
|
||||
for prompt in prompts:
|
||||
print(f"\n{'='*50}")
|
||||
print(f"Prompt: {prompt}")
|
||||
print(f"{'='*50}")
|
||||
|
||||
async for message in query(prompt=prompt, options=options):
|
||||
# Print the message content
|
||||
if hasattr(message, 'content'):
|
||||
for content_block in message.content:
|
||||
if hasattr(content_block, 'text'):
|
||||
print(f"Claude: {content_block.text}")
|
||||
elif hasattr(content_block, 'name'):
|
||||
print(f"Using tool: {content_block.name}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
|
|
@ -14,7 +14,7 @@ bash commands, edit files, search the web, fetch web content) to accomplish.
|
|||
# BASIC STREAMING
|
||||
# ============================================================================
|
||||
|
||||
from claude_code_sdk import ClaudeSDKClient, AssistantMessage, TextBlock, ResultMessage
|
||||
from claude_code_sdk import AssistantMessage, ClaudeSDKClient, ResultMessage, TextBlock
|
||||
|
||||
async with ClaudeSDKClient() as client:
|
||||
print("User: What is 2+2?")
|
||||
|
|
@ -32,7 +32,8 @@ async with ClaudeSDKClient() as client:
|
|||
# ============================================================================
|
||||
|
||||
import asyncio
|
||||
from claude_code_sdk import ClaudeSDKClient, AssistantMessage, TextBlock
|
||||
|
||||
from claude_code_sdk import AssistantMessage, ClaudeSDKClient, TextBlock
|
||||
|
||||
async with ClaudeSDKClient() as client:
|
||||
async def send_and_receive(prompt):
|
||||
|
|
@ -53,7 +54,7 @@ async with ClaudeSDKClient() as client:
|
|||
# PERSISTENT CLIENT FOR MULTIPLE QUESTIONS
|
||||
# ============================================================================
|
||||
|
||||
from claude_code_sdk import ClaudeSDKClient, AssistantMessage, TextBlock
|
||||
from claude_code_sdk import AssistantMessage, ClaudeSDKClient, TextBlock
|
||||
|
||||
# Create client
|
||||
client = ClaudeSDKClient()
|
||||
|
|
@ -88,8 +89,7 @@ await client.disconnect()
|
|||
# IMPORTANT: Interrupts require active message consumption. You must be
|
||||
# consuming messages from the client for the interrupt to be processed.
|
||||
|
||||
import asyncio
|
||||
from claude_code_sdk import ClaudeSDKClient, AssistantMessage, TextBlock, ResultMessage
|
||||
from claude_code_sdk import AssistantMessage, ClaudeSDKClient, TextBlock
|
||||
|
||||
async with ClaudeSDKClient() as client:
|
||||
print("\n--- Sending initial message ---\n")
|
||||
|
|
@ -141,7 +141,7 @@ async with ClaudeSDKClient() as client:
|
|||
# ERROR HANDLING PATTERN
|
||||
# ============================================================================
|
||||
|
||||
from claude_code_sdk import ClaudeSDKClient, AssistantMessage, TextBlock
|
||||
from claude_code_sdk import AssistantMessage, ClaudeSDKClient, TextBlock
|
||||
|
||||
try:
|
||||
async with ClaudeSDKClient() as client:
|
||||
|
|
@ -168,7 +168,8 @@ except Exception as e:
|
|||
# SENDING ASYNC ITERABLE OF MESSAGES
|
||||
# ============================================================================
|
||||
|
||||
from claude_code_sdk import ClaudeSDKClient, AssistantMessage, TextBlock
|
||||
from claude_code_sdk import AssistantMessage, ClaudeSDKClient, TextBlock
|
||||
|
||||
|
||||
async def message_generator():
|
||||
"""Generate multiple messages as an async iterable."""
|
||||
|
|
@ -209,7 +210,7 @@ async with ClaudeSDKClient() as client:
|
|||
# COLLECTING ALL MESSAGES INTO A LIST
|
||||
# ============================================================================
|
||||
|
||||
from claude_code_sdk import ClaudeSDKClient, AssistantMessage, TextBlock, ResultMessage
|
||||
from claude_code_sdk import AssistantMessage, ClaudeSDKClient, TextBlock
|
||||
|
||||
async with ClaudeSDKClient() as client:
|
||||
print("User: What are the primary colors?")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue