1use std::fmt;
8use std::pin::Pin;
9use std::sync::Arc;
10
11use rmcp::model::{
12 CallToolRequestParams, CallToolResponse, Implementation, ListToolsResult, PaginatedRequestParams,
13 ServerCapabilities, ServerConfig, Tool,
14};
15use rmcp::service::RequestContext;
16use rmcp::{ErrorData, RoleServer, ServerHandler};
17
18use crate::tool::ErrorContext;
19
20const NAME: &str = "dbmcp";
22
23const VERSION: &str = env!("CARGO_PKG_VERSION");
25
26const TITLE: &str = "Database MCP Server";
28
29const HOMEPAGE: &str = env!("CARGO_PKG_HOMEPAGE");
31
32#[derive(Clone)]
40pub struct Server {
41 handler: Arc<dyn DynHandler>,
42 shutdown: Arc<dyn Shutdown>,
43 error_context: Option<ErrorContext>,
44}
45
46type HandlerFuture<'a, T> = Pin<Box<dyn Future<Output = Result<T, ErrorData>> + Send + 'a>>;
48
49trait DynHandler: Send + Sync {
55 fn get_info(&self) -> ServerConfig;
56
57 fn call_tool(
58 &self,
59 request: CallToolRequestParams,
60 context: RequestContext<RoleServer>,
61 ) -> HandlerFuture<'_, CallToolResponse>;
62
63 fn list_tools(
64 &self,
65 request: Option<PaginatedRequestParams>,
66 context: RequestContext<RoleServer>,
67 ) -> HandlerFuture<'_, ListToolsResult>;
68
69 fn get_tool(&self, name: &str) -> Option<Tool>;
70}
71
72impl<H: ServerHandler> DynHandler for H {
73 fn get_info(&self) -> ServerConfig {
74 ServerHandler::get_info(self)
75 }
76
77 fn call_tool(
78 &self,
79 request: CallToolRequestParams,
80 context: RequestContext<RoleServer>,
81 ) -> HandlerFuture<'_, CallToolResponse> {
82 Box::pin(ServerHandler::call_tool(self, request, context))
83 }
84
85 fn list_tools(
86 &self,
87 request: Option<PaginatedRequestParams>,
88 context: RequestContext<RoleServer>,
89 ) -> HandlerFuture<'_, ListToolsResult> {
90 Box::pin(ServerHandler::list_tools(self, request, context))
91 }
92
93 fn get_tool(&self, name: &str) -> Option<Tool> {
94 ServerHandler::get_tool(self, name)
95 }
96}
97
98pub type ShutdownFuture<'a> = Pin<Box<dyn Future<Output = ()> + Send + 'a>>;
100
101pub trait Shutdown: Send + Sync {
106 fn shutdown(&self) -> ShutdownFuture<'_>;
108}
109
110impl fmt::Debug for Server {
111 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112 f.debug_struct("Server").finish_non_exhaustive()
113 }
114}
115
116impl Server {
117 pub fn new<S>(handler: S) -> Self
123 where
124 S: ServerHandler + Shutdown,
125 {
126 let handler = Arc::new(handler);
127 Self {
128 handler: handler.clone(),
129 shutdown: handler,
130 error_context: None,
131 }
132 }
133
134 #[must_use]
136 pub fn with_error_context(mut self, context: ErrorContext) -> Self {
137 self.error_context = Some(context);
138 self
139 }
140
141 pub async fn shutdown(&self) {
143 self.shutdown.shutdown().await;
144 }
145}
146
147impl ServerHandler for Server {
148 fn get_info(&self) -> ServerConfig {
149 self.handler.get_info()
150 }
151
152 async fn call_tool(
153 &self,
154 request: CallToolRequestParams,
155 mut context: RequestContext<RoleServer>,
156 ) -> Result<CallToolResponse, ErrorData> {
157 if let Some(error_context) = &self.error_context {
158 context.extensions.insert(error_context.clone());
159 }
160 self.handler.call_tool(request, context).await
161 }
162
163 async fn list_tools(
169 &self,
170 request: Option<PaginatedRequestParams>,
171 context: RequestContext<RoleServer>,
172 ) -> Result<ListToolsResult, ErrorData> {
173 let mut result = self.handler.list_tools(request, context).await?;
174 result.ttl_ms = None;
175 result.cache_scope = None;
176 Ok(result)
177 }
178
179 fn get_tool(&self, name: &str) -> Option<Tool> {
180 self.handler.get_tool(name)
181 }
182}
183
184#[must_use]
190pub fn server_info() -> ServerConfig {
191 let capabilities = ServerCapabilities::builder().enable_tools().build();
192
193 let server_info = Implementation::new(NAME, VERSION)
194 .with_title(TITLE)
195 .with_website_url(HOMEPAGE);
196
197 ServerConfig::new(capabilities).with_server_info(server_info)
198}