pmcp 2.6.0

High-quality Rust SDK for Model Context Protocol (MCP) with full TypeScript SDK compatibility
Documentation
//! Example: Client tool discovery and invocation
//!
//! This example demonstrates:
//! - Listing available tools from a server
//! - Calling tools with arguments
//! - Handling tool responses
//! - Error handling for tool calls
//!
//! Phase 73 update: now demonstrates `Client::call_tool_typed` with a
//! `#[derive(Serialize)]` struct, replacing the earlier hand-rolled
//! `json!({...})` pattern for the calculator invocation.

use pmcp::{Client, ClientCapabilities, StdioTransport};
use serde::Serialize;
use serde_json::json;

/// Typed arguments for the calculator tool — replaces the old `json!({...})` pattern
/// with a compile-checked struct that is serialized by `call_tool_typed`.
#[derive(Serialize)]
struct CalculatorArgs {
    operation: &'static str,
    a: f64,
    b: f64,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Initialize logging
    tracing_subscriber::fmt()
        .with_env_filter("pmcp=info")
        .init();

    println!("=== MCP Client Tools Example ===\n");

    // Create and initialize client
    let transport = StdioTransport::new();
    let mut client = Client::new(transport);

    // Use minimal capabilities - client doesn't need to advertise any special features
    // to call tools on a server
    let capabilities = ClientCapabilities::minimal();

    println!("Connecting to server...");
    let _server_info = client.initialize(capabilities).await?;
    println!("✅ Connected!\n");

    // List available tools
    println!("📋 Listing available tools:");
    let tools_result = client.list_tools(None).await?;

    for tool in &tools_result.tools {
        println!("\n🔧 Tool: {}", tool.name);
        if let Some(desc) = &tool.description {
            println!("   Description: {}", desc);
        }

        // Print input schema if available
        if !tool.input_schema.is_null() {
            println!(
                "   Input schema: {}",
                serde_json::to_string_pretty(&tool.input_schema)?
            );
        }
    }

    // Example: Call a calculator tool using the Phase 73 typed helper.
    println!("\n\n📐 Calling calculator tool (typed):");
    let calc_args = CalculatorArgs {
        operation: "multiply",
        a: 42.0,
        b: std::f64::consts::PI,
    };

    println!(
        "   Arguments: {}",
        serde_json::to_string_pretty(&calc_args)?
    );

    match client.call_tool_typed("calculator", &calc_args).await {
        Ok(result) => {
            println!(
                "   ✅ Result: {}",
                serde_json::to_string_pretty(&result.content)?
            );
        },
        Err(e) => {
            println!("   ❌ Error: {}", e);
        },
    }

    // Example: Call a string manipulation tool
    println!("\n\n📝 Calling string manipulation tool:");
    let string_args = json!({
        "text": "Hello, MCP!",
        "operation": "reverse"
    });

    println!(
        "   Arguments: {}",
        serde_json::to_string_pretty(&string_args)?
    );

    match client
        .call_tool("string_manipulator".to_string(), string_args)
        .await
    {
        Ok(result) => {
            println!(
                "   ✅ Result: {}",
                serde_json::to_string_pretty(&result.content)?
            );
        },
        Err(e) => {
            println!("   ❌ Error: {}", e);
        },
    }

    // Example: Handle tool errors
    println!("\n\n⚠️  Testing error handling:");
    let bad_args = json!({
        "operation": "divide",
        "a": 10,
        "b": 0  // Division by zero
    });

    println!("   Arguments: {}", serde_json::to_string_pretty(&bad_args)?);

    match client.call_tool("calculator".to_string(), bad_args).await {
        Ok(result) => {
            println!(
                "   Result: {}",
                serde_json::to_string_pretty(&result.content)?
            );
        },
        Err(e) => {
            println!("   ✅ Error caught: {}", e);
            // Check error type
            if let Some(code) = e.error_code() {
                println!("   Error code: {:?}", code);
            }
        },
    }

    Ok(())
}