Skip to main content

mcp_client/
main.rs

1// Example demonstrating MCP (Model Context Protocol) client usage with mistral.rs
2//
3// This example shows how to:
4// - Configure MCP servers with different transport protocols (HTTP, WebSocket, Process)
5// - Set up Bearer token authentication for secure connections
6// - Use automatic tool discovery and registration
7// - Integrate MCP tools with model tool calling
8//
9// The MCP client automatically discovers tools from connected servers and makes them
10// available for the model to use during conversations.
11
12use anyhow::Result;
13use mistralrs::{
14    IsqType, McpClientConfig, McpServerConfig, McpServerSource, MemoryGpuConfig,
15    PagedAttentionMetaBuilder, TextMessageRole, TextMessages, TextModelBuilder,
16};
17// use std::collections::HashMap; // Uncomment if using manual headers in examples below
18
19#[tokio::main]
20async fn main() -> Result<()> {
21    // Simple MCP client configuration using defaults
22    // Most fields use sensible defaults (enabled=true, UUID for id/prefix, no timeouts)
23    let mcp_config_simple = McpClientConfig {
24        servers: vec![McpServerConfig {
25            name: "Filesystem Tools".to_string(),
26            source: McpServerSource::Process {
27                command: "npx".to_string(),
28                args: vec![
29                    "@modelcontextprotocol/server-filesystem".to_string(),
30                    ".".to_string(),
31                ],
32                work_dir: None,
33                env: None,
34            },
35            ..Default::default()
36        }],
37        ..Default::default()
38    };
39
40    // Alternative: Full configuration with multiple transport types
41    let _mcp_config_full = McpClientConfig {
42        servers: vec![
43            // Example: Process-based MCP server (enabled by default)
44            McpServerConfig {
45                name: "Filesystem Tools".to_string(),
46                source: McpServerSource::Process {
47                    command: "npx".to_string(),
48                    args: vec![
49                        "@modelcontextprotocol/server-filesystem".to_string(),
50                        ".".to_string(),
51                    ],
52                    work_dir: None,
53                    env: None,
54                },
55                tool_prefix: Some("fs".to_string()),
56                ..Default::default()
57            },
58            // Example: HTTP-based MCP server with Bearer token authentication (disabled by default)
59            McpServerConfig {
60                id: "hf_server".to_string(),
61                name: "Hugging Face MCP".to_string(),
62                source: McpServerSource::Http {
63                    url: "https://hf.co/mcp".to_string(),
64                    timeout_secs: Some(30),
65                    headers: None, // Additional headers can be specified here if needed
66                },
67                enabled: false,                      // Disabled by default
68                tool_prefix: Some("hf".to_string()), // Prefixes tool names to avoid conflicts
69                resources: None,
70                bearer_token: Some("hf_xxx".to_string()), // Replace with your actual Hugging Face token
71            },
72            //
73            // // Example with both Bearer token and additional headers (uncomment HashMap import above)
74            // McpServerConfig {
75            //     id: "authenticated_server".to_string(),
76            //     name: "Authenticated MCP Server".to_string(),
77            //     source: McpServerSource::Http {
78            //         url: "https://api.example.com/mcp".to_string(),
79            //         timeout_secs: Some(60),
80            //         headers: Some({
81            //             let mut headers = HashMap::new();
82            //             headers.insert("X-API-Version".to_string(), "v1".to_string());
83            //             headers.insert("X-Client-ID".to_string(), "mistral-rs".to_string());
84            //             headers
85            //         }),
86            //     },
87            //     enabled: false,
88            //     tool_prefix: Some("auth".to_string()),
89            //     resources: None,
90            //     bearer_token: Some("your-bearer-token".to_string()), // Will be added as Authorization: Bearer <token>
91            // },
92            // Example WebSocket-based MCP server (disabled by default)
93            McpServerConfig {
94                id: "websocket_server".to_string(),
95                name: "WebSocket Example".to_string(),
96                source: McpServerSource::WebSocket {
97                    url: "wss://api.example.com/mcp".to_string(),
98                    timeout_secs: Some(30),
99                    headers: None,
100                },
101                enabled: false, // Disabled by default
102                tool_prefix: Some("ws".to_string()),
103                resources: None,
104                bearer_token: Some("your-websocket-token".to_string()), // WebSocket Bearer token support
105            },
106        ],
107        // Automatically discover and register tools from connected MCP servers
108        auto_register_tools: true,
109        // Timeout for individual tool calls (30 seconds)
110        tool_timeout_secs: Some(30),
111        // Maximum concurrent tool calls across all servers
112        max_concurrent_calls: Some(5),
113    };
114
115    // Use the simple configuration for this example
116    let mcp_config = mcp_config_simple;
117
118    println!("Building model with MCP client support...");
119
120    // Build the model with MCP client configuration
121    // The MCP client will automatically connect to configured servers and discover available tools
122    let model = TextModelBuilder::new("Qwen/Qwen3-4B".to_string())
123        .with_isq(IsqType::Q8_0) // Use 8-bit quantization for efficiency
124        .with_logging()
125        .with_paged_attn(|| {
126            PagedAttentionMetaBuilder::default()
127                .with_gpu_memory(MemoryGpuConfig::ContextSize(8192))
128                .build()
129        })?
130        .with_mcp_client(mcp_config) // This automatically connects to MCP servers and registers tools
131        .build()
132        .await?;
133
134    println!("Model built successfully! MCP servers connected and tools registered.");
135    println!("MCP tools are now available for automatic tool calling during conversations.");
136    println!(
137        "Note: Install filesystem server with: npx @modelcontextprotocol/server-filesystem . -y"
138    );
139
140    // Create a conversation that demonstrates MCP tool usage
141    // The system message informs the model about available external tools
142    let messages = TextMessages::new()
143        .add_message(
144            TextMessageRole::System,
145            "You are an AI assistant with access to external tools via MCP servers. \
146             You can access filesystem operations and other external services \
147             provided by connected MCP servers. Use these tools when appropriate to \
148             help answer user questions. Tools are automatically available and you \
149             can call them as needed.",
150        )
151        .add_message(
152            TextMessageRole::User,
153            "Hello! Can you list the files in the current directory and create a test.txt file?",
154        );
155
156    println!("\nSending chat request...");
157    println!("The model will automatically use MCP tools if needed to answer the question.");
158    let response = model.send_chat_request(messages).await?;
159
160    println!("\nResponse:");
161    println!("{}", response.choices[0].message.content.as_ref().unwrap());
162
163    // Display performance metrics
164    println!("\nPerformance metrics:");
165    println!(
166        "Prompt tokens/sec: {:.2}",
167        response.usage.avg_prompt_tok_per_sec
168    );
169    println!(
170        "Completion tokens/sec: {:.2}",
171        response.usage.avg_compl_tok_per_sec
172    );
173
174    // Display any MCP tool calls that were made during the conversation
175    if let Some(tool_calls) = &response.choices[0].message.tool_calls {
176        println!("\nMCP tool calls made:");
177        for tool_call in tool_calls {
178            println!(
179                "- Tool: {} | Arguments: {}",
180                tool_call.function.name, tool_call.function.arguments
181            );
182        }
183    } else {
184        println!("\nNo tool calls were made for this request.");
185    }
186
187    Ok(())
188}