use claude_agent_sdk::{
ClaudeAgentOptions, ClaudeClient, ContentBlock, McpServers, McpToolResultContent, Message,
ToolResult, create_sdk_mcp_server, tool,
};
use futures::StreamExt;
use serde_json::json;
use std::collections::HashMap;
async fn calculator_handler(args: serde_json::Value) -> anyhow::Result<ToolResult> {
let operation = args["operation"]
.as_str()
.ok_or_else(|| anyhow::anyhow!("Missing operation"))?;
let a = args["a"]
.as_f64()
.ok_or_else(|| anyhow::anyhow!("Missing or invalid parameter 'a'"))?;
let b = args["b"]
.as_f64()
.ok_or_else(|| anyhow::anyhow!("Missing or invalid parameter 'b'"))?;
let result = match operation {
"add" => a + b,
"subtract" => a - b,
"multiply" => a * b,
"divide" => {
if b == 0.0 {
return Ok(ToolResult {
content: vec![McpToolResultContent::Text {
text: "Error: Division by zero".to_string(),
}],
is_error: true,
});
}
a / b
},
_ => {
return Ok(ToolResult {
content: vec![McpToolResultContent::Text {
text: format!("Error: Unknown operation '{}'", operation),
}],
is_error: true,
});
},
};
Ok(ToolResult {
content: vec![McpToolResultContent::Text {
text: format!("Result: {} {} {} = {}", a, operation, b, result),
}],
is_error: false,
})
}
async fn statistics_handler(args: serde_json::Value) -> anyhow::Result<ToolResult> {
let numbers = args["numbers"]
.as_array()
.ok_or_else(|| anyhow::anyhow!("Missing or invalid 'numbers' array"))?;
let nums: Vec<f64> = numbers.iter().filter_map(|v| v.as_f64()).collect();
if nums.is_empty() {
return Ok(ToolResult {
content: vec![McpToolResultContent::Text {
text: "Error: No valid numbers provided".to_string(),
}],
is_error: true,
});
}
let sum: f64 = nums.iter().sum();
let mean = sum / nums.len() as f64;
let max = nums.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
let min = nums.iter().cloned().fold(f64::INFINITY, f64::min);
let variance = nums.iter().map(|&x| (x - mean).powi(2)).sum::<f64>() / nums.len() as f64;
let std_dev = variance.sqrt();
let stats = json!({
"count": nums.len(),
"sum": sum,
"mean": mean,
"min": min,
"max": max,
"variance": variance,
"std_dev": std_dev
});
Ok(ToolResult {
content: vec![McpToolResultContent::Text {
text: format!("Statistics: {}", serde_json::to_string_pretty(&stats)?),
}],
is_error: false,
})
}
async fn random_handler(args: serde_json::Value) -> anyhow::Result<ToolResult> {
let min = args["min"].as_i64().unwrap_or(0);
let max = args["max"].as_i64().unwrap_or(100);
if min >= max {
return Ok(ToolResult {
content: vec![McpToolResultContent::Text {
text: "Error: min must be less than max".to_string(),
}],
is_error: true,
});
}
use std::time::{SystemTime, UNIX_EPOCH};
let seed = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos();
let random = (seed % (max - min) as u128) as i64 + min;
Ok(ToolResult {
content: vec![McpToolResultContent::Text {
text: format!("Random number between {} and {}: {}", min, max, random),
}],
is_error: false,
})
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("=== MCP Server Integration Example ===\n");
println!("Step 1: Creating custom MCP tools...\n");
let calculator_tool = tool!(
"calculator",
"Perform basic arithmetic operations (add, subtract, multiply, divide)",
json!({
"type": "object",
"properties": {
"operation": {
"type": "string",
"enum": ["add", "subtract", "multiply", "divide"],
"description": "The arithmetic operation to perform"
},
"a": {
"type": "number",
"description": "First operand"
},
"b": {
"type": "number",
"description": "Second operand"
}
},
"required": ["operation", "a", "b"]
}),
calculator_handler
);
let statistics_tool = tool!(
"statistics",
"Calculate statistical measures (mean, median, std dev, etc.) for a list of numbers",
json!({
"type": "object",
"properties": {
"numbers": {
"type": "array",
"items": { "type": "number" },
"description": "Array of numbers to analyze"
}
},
"required": ["numbers"]
}),
statistics_handler
);
let random_tool = tool!(
"random_number",
"Generate a random number within a specified range",
json!({
"type": "object",
"properties": {
"min": {
"type": "integer",
"description": "Minimum value (inclusive)"
},
"max": {
"type": "integer",
"description": "Maximum value (exclusive)"
}
},
"required": ["min", "max"]
}),
random_handler
);
println!("✓ Created 3 tools: calculator, statistics, random_number\n");
println!("Step 2: Creating SDK MCP server...\n");
let math_server = create_sdk_mcp_server(
"math-tools",
"1.0.0",
vec![calculator_tool, statistics_tool, random_tool],
);
println!("✓ Created 'math-tools' MCP server v1.0.0\n");
println!("Step 3: Configuring Claude client with MCP server...\n");
let mut mcp_servers = HashMap::new();
mcp_servers.insert(
"math-tools".to_string(),
claude_agent_sdk::McpServerConfig::Sdk(math_server),
);
let options = ClaudeAgentOptions {
mcp_servers: McpServers::Dict(mcp_servers),
allowed_tools: vec![
"mcp__math-tools__calculator".to_string(),
"mcp__math-tools__statistics".to_string(),
"mcp__math-tools__random_number".to_string(),
],
max_turns: Some(10),
permission_mode: Some(claude_agent_sdk::PermissionMode::AcceptEdits),
..Default::default()
};
let mut client = ClaudeClient::new(options);
println!("Step 4: Connecting to Claude...\n");
client.connect().await?;
println!("✓ Connected!\n");
println!("=== Query 1: Calculator Tool ===");
println!("User: Calculate 42 multiplied by 7\n");
client.query("Calculate 42 multiplied by 7").await?;
let mut stream = client.receive_response();
while let Some(message) = stream.next().await {
match message? {
Message::Assistant(msg) => {
for block in msg.message.content {
match block {
ContentBlock::Text(text) => {
println!("Claude: {}", text.text);
},
ContentBlock::ToolUse(tool) => {
println!("🔧 Using tool: {} ({})", tool.name, tool.id);
println!(" Input: {}", serde_json::to_string_pretty(&tool.input)?);
},
_ => {},
}
}
},
Message::User(user_msg) => {
if let Some(content_blocks) = &user_msg.content {
for content in content_blocks {
if let ContentBlock::ToolResult(result) = content
&& let Some(claude_agent_sdk::ToolResultContent::Text(text)) =
&result.content
{
println!("📊 Tool result: {}", text);
}
}
}
},
Message::Result(result) => {
println!(
"\n[Result] Turns: {}, Duration: {}ms\n",
result.num_turns, result.duration_ms
);
},
_ => {},
}
}
drop(stream);
println!("=== Query 2: Statistics Tool ===");
println!("User: Calculate statistics for the numbers: 10, 20, 30, 40, 50\n");
client
.query("Calculate statistics for the numbers: 10, 20, 30, 40, 50")
.await?;
let mut stream = client.receive_response();
while let Some(message) = stream.next().await {
match message? {
Message::Assistant(msg) => {
for block in msg.message.content {
match block {
ContentBlock::Text(text) => {
println!("Claude: {}", text.text);
},
ContentBlock::ToolUse(tool) => {
println!("🔧 Using tool: {} ({})", tool.name, tool.id);
println!(" Input: {}", serde_json::to_string_pretty(&tool.input)?);
},
_ => {},
}
}
},
Message::User(user_msg) => {
if let Some(content_blocks) = &user_msg.content {
for content in content_blocks {
if let ContentBlock::ToolResult(result) = content
&& let Some(claude_agent_sdk::ToolResultContent::Text(text)) =
&result.content
{
println!("📊 Tool result: {}", text);
}
}
}
},
Message::Result(result) => {
println!(
"\n[Result] Turns: {}, Duration: {}ms\n",
result.num_turns, result.duration_ms
);
},
_ => {},
}
}
drop(stream);
println!("=== Query 3: Random Number Tool ===");
println!("User: Generate a random number between 1 and 100\n");
client
.query("Generate a random number between 1 and 100")
.await?;
let mut stream = client.receive_response();
while let Some(message) = stream.next().await {
match message? {
Message::Assistant(msg) => {
for block in msg.message.content {
match block {
ContentBlock::Text(text) => {
println!("Claude: {}", text.text);
},
ContentBlock::ToolUse(tool) => {
println!("🔧 Using tool: {} ({})", tool.name, tool.id);
println!(" Input: {}", serde_json::to_string_pretty(&tool.input)?);
},
_ => {},
}
}
},
Message::User(user_msg) => {
if let Some(content_blocks) = &user_msg.content {
for content in content_blocks {
if let ContentBlock::ToolResult(result) = content
&& let Some(claude_agent_sdk::ToolResultContent::Text(text)) =
&result.content
{
println!("📊 Tool result: {}", text);
}
}
}
},
Message::Result(result) => {
println!(
"\n[Result] Turns: {}, Duration: {}ms\n",
result.num_turns, result.duration_ms
);
},
_ => {},
}
}
drop(stream);
println!("=== Query 4: Multiple Tools ===");
println!("User: Calculate the average of 15, 25, and 35, then multiply it by 3\n");
client
.query("Calculate the average of 15, 25, and 35, then multiply the result by 3")
.await?;
let mut stream = client.receive_response();
while let Some(message) = stream.next().await {
match message? {
Message::Assistant(msg) => {
for block in msg.message.content {
match block {
ContentBlock::Text(text) => {
println!("Claude: {}", text.text);
},
ContentBlock::ToolUse(tool) => {
println!("🔧 Using tool: {} ({})", tool.name, tool.id);
println!(" Input: {}", serde_json::to_string_pretty(&tool.input)?);
},
_ => {},
}
}
},
Message::User(user_msg) => {
if let Some(content_blocks) = &user_msg.content {
for content in content_blocks {
if let ContentBlock::ToolResult(result) = content
&& let Some(claude_agent_sdk::ToolResultContent::Text(text)) =
&result.content
{
println!("📊 Tool result: {}", text);
}
}
}
},
Message::Result(result) => {
println!(
"\n[Result] Turns: {}, Duration: {}ms\n",
result.num_turns, result.duration_ms
);
},
_ => {},
}
}
drop(stream);
println!("Disconnecting...");
client.disconnect().await?;
println!("✓ Disconnected!\n");
println!("=== Example Complete ===");
println!("\nKey Takeaways:");
println!("• Created custom tools with the tool! macro");
println!("• Built an SDK MCP server with multiple tools");
println!("• Integrated MCP server with ClaudeClient");
println!("• Claude automatically selected and used appropriate tools");
println!("• Tools can be composed to solve complex tasks");
Ok(())
}