mcp_core/server.rs
1//! # MCP Server
2//!
3//! This module provides the server-side implementation of the Model Context Protocol (MCP).
4//! It allows creating MCP servers that expose tools for clients to discover and invoke.
5//!
6//! The core components include:
7//! - The `Server` for managing server lifetime
8//! - The `ServerProtocolBuilder` for configuring servers
9//! - Client connection tracking
10//!
11//! Servers expose tools that can be discovered and called by clients, with
12//! customizable capabilities and metadata.
13
14use std::{
15 collections::HashMap,
16 sync::{Arc, RwLock},
17};
18
19use crate::{
20 protocol::Protocol,
21 tools::{ToolHandler, ToolHandlerFn, Tools},
22 types::{
23 CallToolRequest, ListRequest, ProtocolVersion, Tool, ToolsListResponse,
24 LATEST_PROTOCOL_VERSION,
25 },
26};
27
28use super::{
29 protocol::ProtocolBuilder,
30 transport::Transport,
31 types::{
32 ClientCapabilities, Implementation, InitializeRequest, InitializeResponse,
33 ServerCapabilities,
34 },
35};
36use anyhow::Result;
37use std::pin::Pin;
38
39/// Represents a connected MCP client.
40///
41/// Tracks information about a client that has connected to the server,
42/// including its capabilities, info, and initialization state.
43#[derive(Clone)]
44pub struct ClientConnection {
45 /// The capabilities reported by the client
46 pub client_capabilities: Option<ClientCapabilities>,
47 /// Information about the client implementation
48 pub client_info: Option<Implementation>,
49 /// Whether the client has completed initialization
50 pub initialized: bool,
51}
52
53/// The main MCP server type.
54///
55/// Provides static methods for creating and starting MCP servers.
56#[derive(Clone)]
57pub struct Server;
58
59impl Server {
60 /// Creates a new server builder with the specified server information.
61 ///
62 /// # Arguments
63 ///
64 /// * `name` - The server name
65 /// * `version` - The server version
66 /// * `protocol_version` - The protocol version to use
67 ///
68 /// # Returns
69 ///
70 /// A `ServerProtocolBuilder` for configuring the server
71 pub fn builder(
72 name: String,
73 version: String,
74 protocol_version: ProtocolVersion,
75 ) -> ServerProtocolBuilder {
76 ServerProtocolBuilder::new(name, version).set_protocol_version(protocol_version)
77 }
78
79 /// Starts the server with the given transport.
80 ///
81 /// # Arguments
82 ///
83 /// * `transport` - The transport to use for communication with clients
84 ///
85 /// # Returns
86 ///
87 /// A `Result` indicating success or failure
88 pub async fn start<T: Transport>(transport: T) -> Result<()> {
89 transport.open().await
90 }
91}
92
93/// Builder for creating configured server protocols.
94///
95/// The `ServerProtocolBuilder` provides a fluent API for configuring and creating
96/// MCP server protocols with specific settings, tools, and capabilities.
97pub struct ServerProtocolBuilder {
98 protocol_version: ProtocolVersion,
99 protocol_builder: ProtocolBuilder,
100 server_info: Implementation,
101 capabilities: ServerCapabilities,
102 instructions: Option<String>,
103 tools: HashMap<String, ToolHandler>,
104 client_connection: Arc<RwLock<ClientConnection>>,
105}
106
107impl ServerProtocolBuilder {
108 /// Creates a new server protocol builder.
109 ///
110 /// # Arguments
111 ///
112 /// * `name` - The server name
113 /// * `version` - The server version
114 ///
115 /// # Returns
116 ///
117 /// A new `ServerProtocolBuilder` instance
118 pub fn new(name: String, version: String) -> Self {
119 ServerProtocolBuilder {
120 protocol_version: LATEST_PROTOCOL_VERSION,
121 protocol_builder: ProtocolBuilder::new(),
122 server_info: Implementation { name, version },
123 capabilities: ServerCapabilities::default(),
124 instructions: None,
125 tools: HashMap::new(),
126 client_connection: Arc::new(RwLock::new(ClientConnection {
127 client_capabilities: None,
128 client_info: None,
129 initialized: false,
130 })),
131 }
132 }
133
134 /// Sets the protocol version for the server.
135 ///
136 /// # Arguments
137 ///
138 /// * `protocol_version` - The protocol version to use
139 ///
140 /// # Returns
141 ///
142 /// The modified builder instance
143 pub fn set_protocol_version(mut self, protocol_version: ProtocolVersion) -> Self {
144 self.protocol_version = protocol_version;
145 self
146 }
147
148 /// Sets the server capabilities.
149 ///
150 /// # Arguments
151 ///
152 /// * `capabilities` - The server capabilities
153 ///
154 /// # Returns
155 ///
156 /// The modified builder instance
157 pub fn set_capabilities(mut self, capabilities: ServerCapabilities) -> Self {
158 self.capabilities = capabilities;
159 self
160 }
161
162 /// Sets the server instructions.
163 ///
164 /// Instructions provide guidance for AI models on how to use the server's tools.
165 ///
166 /// # Arguments
167 ///
168 /// * `instructions` - The instructions for using the server
169 ///
170 /// # Returns
171 ///
172 /// The modified builder instance
173 pub fn set_instructions(mut self, instructions: String) -> Self {
174 self.instructions = Some(instructions);
175 self
176 }
177
178 /// Removes the server instructions.
179 ///
180 /// # Returns
181 ///
182 /// The modified builder instance
183 pub fn remove_instructions(mut self) -> Self {
184 self.instructions = None;
185 self
186 }
187
188 /// Registers a tool with the server.
189 ///
190 /// # Arguments
191 ///
192 /// * `tool` - The tool definition
193 /// * `f` - The handler function for the tool
194 ///
195 /// # Returns
196 ///
197 /// The modified builder instance
198 pub fn register_tool(mut self, tool: Tool, f: ToolHandlerFn) -> Self {
199 self.tools.insert(
200 tool.name.clone(),
201 ToolHandler {
202 tool,
203 f: Box::new(f),
204 },
205 );
206 self
207 }
208
209 /// Helper function for creating an initialize request handler.
210 ///
211 /// # Arguments
212 ///
213 /// * `protocol_version` - The protocol version to use
214 /// * `state` - The client connection state
215 /// * `server_info` - The server information
216 /// * `capabilities` - The server capabilities
217 /// * `instructions` - Optional server instructions
218 ///
219 /// # Returns
220 ///
221 /// A handler function for initialize requests
222 fn handle_init(
223 protocol_version: ProtocolVersion,
224 state: Arc<RwLock<ClientConnection>>,
225 server_info: Implementation,
226 capabilities: ServerCapabilities,
227 instructions: Option<String>,
228 ) -> impl Fn(
229 InitializeRequest,
230 )
231 -> Pin<Box<dyn std::future::Future<Output = Result<InitializeResponse>> + Send>> {
232 move |req| {
233 let state = state.clone();
234 let server_info = server_info.clone();
235 let capabilities = capabilities.clone();
236 let instructions = instructions.clone();
237 let protocol_version = protocol_version.clone();
238
239 Box::pin(async move {
240 let mut state = state
241 .write()
242 .map_err(|_| anyhow::anyhow!("Lock poisoned"))?;
243 state.client_capabilities = Some(req.capabilities);
244 state.client_info = Some(req.client_info);
245
246 Ok(InitializeResponse {
247 protocol_version: protocol_version.as_str().to_string(),
248 capabilities,
249 server_info,
250 instructions,
251 })
252 })
253 }
254 }
255
256 /// Helper function for creating an initialized notification handler.
257 ///
258 /// # Arguments
259 ///
260 /// * `state` - The client connection state
261 ///
262 /// # Returns
263 ///
264 /// A handler function for initialized notifications
265 fn handle_initialized(
266 state: Arc<RwLock<ClientConnection>>,
267 ) -> impl Fn(()) -> Pin<Box<dyn std::future::Future<Output = Result<()>> + Send>> {
268 move |_| {
269 let state = state.clone();
270 Box::pin(async move {
271 let mut state = state
272 .write()
273 .map_err(|_| anyhow::anyhow!("Lock poisoned"))?;
274 state.initialized = true;
275 Ok(())
276 })
277 }
278 }
279
280 /// Gets the client capabilities, if available.
281 ///
282 /// # Returns
283 ///
284 /// An `Option` containing the client capabilities if available
285 pub fn get_client_capabilities(&self) -> Option<ClientCapabilities> {
286 self.client_connection
287 .read()
288 .ok()?
289 .client_capabilities
290 .clone()
291 }
292
293 /// Gets the client information, if available.
294 ///
295 /// # Returns
296 ///
297 /// An `Option` containing the client information if available
298 pub fn get_client_info(&self) -> Option<Implementation> {
299 self.client_connection.read().ok()?.client_info.clone()
300 }
301
302 /// Checks if the client has completed initialization.
303 ///
304 /// # Returns
305 ///
306 /// `true` if the client is initialized, `false` otherwise
307 pub fn is_initialized(&self) -> bool {
308 self.client_connection
309 .read()
310 .ok()
311 .map(|client_connection| client_connection.initialized)
312 .unwrap_or(false)
313 }
314
315 /// Builds the server protocol.
316 ///
317 /// # Returns
318 ///
319 /// A `Protocol` instance configured with the server's settings
320 pub fn build(self) -> Protocol {
321 let tools = Arc::new(Tools::new(self.tools));
322 let tools_clone = tools.clone();
323 let tools_list = tools.clone();
324 let tools_call = tools_clone.clone();
325
326 let conn_for_list = self.client_connection.clone();
327 let conn_for_call = self.client_connection.clone();
328
329 self.protocol_builder
330 .request_handler(
331 "initialize",
332 Self::handle_init(
333 self.protocol_version.clone(),
334 self.client_connection.clone(),
335 self.server_info,
336 self.capabilities,
337 self.instructions,
338 ),
339 )
340 .notification_handler(
341 "notifications/initialized",
342 Self::handle_initialized(self.client_connection),
343 )
344 .request_handler("tools/list", move |_req: ListRequest| {
345 let tools_list = tools_list.clone();
346 let conn = conn_for_list.clone();
347 Box::pin(async move {
348 match conn.read() {
349 Ok(conn) => {
350 if !conn.initialized {
351 return Err(anyhow::anyhow!("Client not initialized"));
352 }
353 }
354 Err(_) => return Err(anyhow::anyhow!("Lock poisoned")),
355 }
356
357 let tools = tools_list.list_tools();
358
359 Ok(ToolsListResponse {
360 tools,
361 next_cursor: None,
362 meta: None,
363 })
364 })
365 })
366 .request_handler("tools/call", move |req: CallToolRequest| {
367 let tools_call = tools_call.clone();
368 let conn = conn_for_call.clone();
369 Box::pin(async move {
370 match conn.read() {
371 Ok(conn) => {
372 if !conn.initialized {
373 return Err(anyhow::anyhow!("Client not initialized"));
374 }
375 }
376 Err(_) => return Err(anyhow::anyhow!("Lock poisoned")),
377 }
378
379 match tools_call.call_tool(req).await {
380 Ok(resp) => Ok(resp),
381 Err(e) => Err(e),
382 }
383 })
384 })
385 .build()
386 }
387}