1use std::{sync::Arc, time::Duration};
2
3use async_trait::async_trait;
4use rust_mcp_actix::{ActixServerOptions, create_actix_server};
5use rust_mcp_sdk::{
6 McpServer, StdioTransport, ToMcpServerHandler, TransportOptions,
7 error::McpSdkError,
8 mcp_server::{McpServerOptions, ServerHandler, server_runtime::create_server},
9 schema::{
10 CallToolRequestParams, CallToolResult, Implementation, InitializeResult,
11 LATEST_PROTOCOL_VERSION, ListToolsResult, PaginatedRequestParams, RpcError,
12 ServerCapabilities, ServerCapabilitiesTools, schema_utils::CallToolError,
13 },
14};
15
16use crate::{server_config::ServerConfig, tool_box::ToolBox};
17
18#[derive(Debug, Clone, Default)]
19pub struct ServerBuilder {
20 config: ServerConfig,
21}
22
23impl ServerBuilder {
24 pub fn new() -> Self {
25 Self::default()
26 }
27
28 pub fn with_name(mut self, name: impl Into<String>) -> Self {
29 self.config.name = name.into();
30 self
31 }
32
33 pub fn with_instructions(mut self, instructions: impl Into<String>) -> Self {
34 self.config.instructions = instructions.into();
35 self
36 }
37
38 pub fn with_version(mut self, version: impl Into<String>) -> Self {
39 self.config.version = version.into();
40 self
41 }
42
43 pub fn with_title(mut self, title: impl Into<String>) -> Self {
44 self.config.title = title.into();
45 self
46 }
47
48 pub fn with_timeout(mut self, timeout: Duration) -> Self {
49 self.config.timeout = timeout;
50 self
51 }
52
53 pub fn set_name(&mut self, name: impl Into<String>) {
54 self.config.name = name.into();
55 }
56
57 pub fn set_instructions(&mut self, instructions: impl Into<String>) {
58 self.config.instructions = instructions.into();
59 }
60
61 pub fn set_version(&mut self, version: impl Into<String>) {
62 self.config.version = version.into();
63 }
64
65 pub fn set_title(&mut self, title: impl Into<String>) {
66 self.config.title = title.into();
67 }
68
69 pub fn set_timeout(&mut self, timeout: Duration) {
70 self.config.timeout = timeout;
71 }
72
73 pub fn name(&self) -> &str {
74 &self.config.name
75 }
76
77 pub fn title(&self) -> &str {
78 &self.config.title
79 }
80
81 pub fn version(&self) -> &str {
82 &self.config.version
83 }
84
85 pub fn instructions(&self) -> &str {
86 &self.config.instructions
87 }
88
89 pub async fn start_stdio<T>(self) -> Result<(), McpSdkError>
90 where
91 T: ToolBox + TryFrom<CallToolRequestParams, Error = CallToolError> + Send + Sync + 'static,
92 {
93 let transport_options = TransportOptions {
94 timeout: self.config.timeout,
95 ..Default::default()
96 };
97
98 create_server(McpServerOptions {
99 server_details: self.get_server_details::<T>(),
100 transport: StdioTransport::new(transport_options)?,
101 handler: Handler::<T>::new().to_mcp_server_handler(),
102 task_store: None,
103 client_task_store: None,
104 message_observer: None,
105 })
106 .start()
107 .await
108 }
109
110 pub async fn start_server<T>(
111 self,
112 host: impl Into<String>,
113 port: u16,
114 ) -> Result<(), McpSdkError>
115 where
116 T: ToolBox + TryFrom<CallToolRequestParams, Error = CallToolError> + Send + Sync + 'static,
117 {
118 let transport_options = TransportOptions {
119 timeout: self.config.timeout,
120 ..Default::default()
121 };
122
123 create_actix_server(
124 self.get_server_details::<T>(),
125 Handler::<T>::new().to_mcp_server_handler(),
126 ActixServerOptions {
127 host: Some(host.into())
128 .filter(|host| !host.is_empty())
129 .unwrap_or_else(|| "127.0.0.1".to_string()),
130 port,
131 transport_options: Arc::new(transport_options),
132 ..Default::default()
133 },
134 )
135 .start()
136 .await
137 }
138
139 fn get_server_details<T>(self) -> InitializeResult
140 where
141 T: ToolBox,
142 {
143 InitializeResult {
144 server_info: Implementation {
145 name: self.config.name,
146 version: self.config.version,
147 title: Some(self.config.title).filter(|title| !title.is_empty()),
148 description: Some(self.config.description)
149 .filter(|description| !description.is_empty()),
150 website_url: None,
151 icons: Default::default(),
152 },
153 capabilities: ServerCapabilities {
154 tools: if T::get_tools().is_empty() {
155 None
156 } else {
157 Some(ServerCapabilitiesTools { list_changed: None })
158 },
159 ..Default::default()
160 },
161 meta: None,
162 instructions: Some(self.config.instructions),
163 protocol_version: LATEST_PROTOCOL_VERSION.to_string(),
164 }
165 }
166}
167
168struct Handler<T> {
169 _phantom: std::marker::PhantomData<T>,
170}
171
172impl<T> Handler<T> {
173 pub fn new() -> Self {
174 Self {
175 _phantom: std::marker::PhantomData,
176 }
177 }
178}
179
180#[async_trait]
181#[allow(unused)]
182impl<T> ServerHandler for Handler<T>
183where
184 T: ToolBox + TryFrom<CallToolRequestParams, Error = CallToolError> + Send + Sync + 'static,
185{
186 async fn handle_list_tools_request(
187 &self,
188 params: Option<PaginatedRequestParams>,
189 runtime: Arc<dyn McpServer>,
190 ) -> Result<ListToolsResult, RpcError> {
191 Ok(ListToolsResult {
192 meta: None,
193 next_cursor: None,
194 tools: T::get_tools(),
195 })
196 }
197
198 async fn handle_call_tool_request(
199 &self,
200 params: CallToolRequestParams,
201 runtime: Arc<dyn McpServer>,
202 ) -> Result<CallToolResult, CallToolError> {
203 let custom_tool = T::try_from(params).map_err(CallToolError::new)?;
204
205 custom_tool.get_tool().call().await
206 }
207}