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}