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}