1use async_trait::async_trait;
4use pulseengine_mcp_protocol::*;
5use std::error::Error as StdError;
6use thiserror::Error;
7
8#[derive(Debug, Error)]
10pub enum BackendError {
11 #[error("Backend not initialized")]
12 NotInitialized,
13
14 #[error("Configuration error: {0}")]
15 Configuration(String),
16
17 #[error("Connection error: {0}")]
18 Connection(String),
19
20 #[error("Operation not supported: {0}")]
21 NotSupported(String),
22
23 #[error("Internal backend error: {0}")]
24 Internal(String),
25
26 #[error("Custom error: {0}")]
27 Custom(Box<dyn StdError + Send + Sync>),
28}
29
30impl BackendError {
31 pub fn configuration(msg: impl Into<String>) -> Self {
32 Self::Configuration(msg.into())
33 }
34
35 pub fn connection(msg: impl Into<String>) -> Self {
36 Self::Connection(msg.into())
37 }
38
39 pub fn not_supported(msg: impl Into<String>) -> Self {
40 Self::NotSupported(msg.into())
41 }
42
43 pub fn internal(msg: impl Into<String>) -> Self {
44 Self::Internal(msg.into())
45 }
46
47 pub fn custom(error: impl StdError + Send + Sync + 'static) -> Self {
48 Self::Custom(Box::new(error))
49 }
50}
51
52impl From<BackendError> for Error {
54 fn from(err: BackendError) -> Self {
55 match err {
56 BackendError::NotInitialized => Error::internal_error("Backend not initialized"),
57 BackendError::Configuration(msg) => Error::invalid_params(msg),
58 BackendError::Connection(msg) => {
59 Error::internal_error(format!("Connection failed: {msg}"))
60 }
61 BackendError::NotSupported(msg) => Error::method_not_found(msg),
62 BackendError::Internal(msg) => Error::internal_error(msg),
63 BackendError::Custom(err) => Error::internal_error(err.to_string()),
64 }
65 }
66}
67
68#[async_trait]
74pub trait McpBackend: Send + Sync + Clone {
75 type Error: StdError + Send + Sync + Into<Error> + From<BackendError> + 'static;
77
78 type Config: Clone + Send + Sync;
80
81 async fn initialize(config: Self::Config) -> std::result::Result<Self, Self::Error>;
87
88 fn get_server_info(&self) -> ServerInfo;
93
94 async fn health_check(&self) -> std::result::Result<(), Self::Error>;
99
100 async fn list_tools(
104 &self,
105 request: PaginatedRequestParam,
106 ) -> std::result::Result<ListToolsResult, Self::Error>;
107
108 async fn call_tool(
110 &self,
111 request: CallToolRequestParam,
112 ) -> std::result::Result<CallToolResult, Self::Error>;
113
114 async fn list_resources(
118 &self,
119 request: PaginatedRequestParam,
120 ) -> std::result::Result<ListResourcesResult, Self::Error>;
121
122 async fn read_resource(
124 &self,
125 request: ReadResourceRequestParam,
126 ) -> std::result::Result<ReadResourceResult, Self::Error>;
127
128 async fn list_resource_templates(
130 &self,
131 request: PaginatedRequestParam,
132 ) -> std::result::Result<ListResourceTemplatesResult, Self::Error> {
133 let _ = request;
134 Ok(ListResourceTemplatesResult {
135 resource_templates: vec![],
136 next_cursor: Some(String::new()),
137 })
138 }
139
140 async fn list_prompts(
144 &self,
145 request: PaginatedRequestParam,
146 ) -> std::result::Result<ListPromptsResult, Self::Error>;
147
148 async fn get_prompt(
150 &self,
151 request: GetPromptRequestParam,
152 ) -> std::result::Result<GetPromptResult, Self::Error>;
153
154 async fn subscribe(
158 &self,
159 request: SubscribeRequestParam,
160 ) -> std::result::Result<(), Self::Error> {
161 let _ = request;
162 Err(BackendError::not_supported("Subscriptions not supported").into())
163 }
164
165 async fn unsubscribe(
167 &self,
168 request: UnsubscribeRequestParam,
169 ) -> std::result::Result<(), Self::Error> {
170 let _ = request;
171 Err(BackendError::not_supported("Subscriptions not supported").into())
172 }
173
174 async fn complete(
178 &self,
179 request: CompleteRequestParam,
180 ) -> std::result::Result<CompleteResult, Self::Error> {
181 let _ = request;
182 Ok(CompleteResult { completion: vec![] })
183 }
184
185 async fn elicit(
189 &self,
190 request: ElicitationRequestParam,
191 ) -> std::result::Result<ElicitationResult, Self::Error> {
192 let _ = request;
193 Err(BackendError::not_supported("Elicitation not supported").into())
194 }
195
196 async fn set_level(
200 &self,
201 request: SetLevelRequestParam,
202 ) -> std::result::Result<(), Self::Error> {
203 let _ = request;
204 Err(BackendError::not_supported("Logging level control not supported").into())
205 }
206
207 async fn on_startup(&self) -> std::result::Result<(), Self::Error> {
211 Ok(())
212 }
213
214 async fn on_shutdown(&self) -> std::result::Result<(), Self::Error> {
216 Ok(())
217 }
218
219 async fn on_client_connect(
221 &self,
222 client_info: &Implementation,
223 ) -> std::result::Result<(), Self::Error> {
224 let _ = client_info;
225 Ok(())
226 }
227
228 async fn on_client_disconnect(
230 &self,
231 client_info: &Implementation,
232 ) -> std::result::Result<(), Self::Error> {
233 let _ = client_info;
234 Ok(())
235 }
236
237 async fn handle_custom_method(
241 &self,
242 method: &str,
243 params: serde_json::Value,
244 ) -> std::result::Result<serde_json::Value, Self::Error> {
245 let _ = (method, params);
246 Err(BackendError::not_supported(format!("Custom method not supported: {method}")).into())
247 }
248}
249
250#[async_trait]
252pub trait SimpleBackend: Send + Sync + Clone {
253 type Error: StdError + Send + Sync + Into<Error> + From<BackendError> + 'static;
254 type Config: Clone + Send + Sync;
255
256 async fn initialize(config: Self::Config) -> std::result::Result<Self, Self::Error>;
257 fn get_server_info(&self) -> ServerInfo;
258 async fn health_check(&self) -> std::result::Result<(), Self::Error>;
259
260 async fn list_tools(
262 &self,
263 request: PaginatedRequestParam,
264 ) -> std::result::Result<ListToolsResult, Self::Error>;
265 async fn call_tool(
266 &self,
267 request: CallToolRequestParam,
268 ) -> std::result::Result<CallToolResult, Self::Error>;
269}
270
271#[async_trait]
273impl<T> McpBackend for T
274where
275 T: SimpleBackend,
276{
277 type Error = T::Error;
278 type Config = T::Config;
279
280 async fn initialize(config: Self::Config) -> std::result::Result<Self, Self::Error> {
281 T::initialize(config).await
282 }
283
284 fn get_server_info(&self) -> ServerInfo {
285 T::get_server_info(self)
286 }
287
288 async fn health_check(&self) -> std::result::Result<(), Self::Error> {
289 T::health_check(self).await
290 }
291
292 async fn list_tools(
293 &self,
294 request: PaginatedRequestParam,
295 ) -> std::result::Result<ListToolsResult, Self::Error> {
296 T::list_tools(self, request).await
297 }
298
299 async fn call_tool(
300 &self,
301 request: CallToolRequestParam,
302 ) -> std::result::Result<CallToolResult, Self::Error> {
303 T::call_tool(self, request).await
304 }
305
306 async fn list_resources(
308 &self,
309 _request: PaginatedRequestParam,
310 ) -> std::result::Result<ListResourcesResult, Self::Error> {
311 Ok(ListResourcesResult {
312 resources: vec![],
313 next_cursor: None,
314 })
315 }
316
317 async fn read_resource(
318 &self,
319 request: ReadResourceRequestParam,
320 ) -> std::result::Result<ReadResourceResult, Self::Error> {
321 Err(BackendError::not_supported(format!("Resource not found: {}", request.uri)).into())
322 }
323
324 async fn list_prompts(
325 &self,
326 _request: PaginatedRequestParam,
327 ) -> std::result::Result<ListPromptsResult, Self::Error> {
328 Ok(ListPromptsResult {
329 prompts: vec![],
330 next_cursor: None,
331 })
332 }
333
334 async fn get_prompt(
335 &self,
336 request: GetPromptRequestParam,
337 ) -> std::result::Result<GetPromptResult, Self::Error> {
338 Err(BackendError::not_supported(format!("Prompt not found: {}", request.name)).into())
339 }
340}