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 set_level(
189 &self,
190 request: SetLevelRequestParam,
191 ) -> std::result::Result<(), Self::Error> {
192 let _ = request;
193 Err(BackendError::not_supported("Logging level control not supported").into())
194 }
195
196 async fn on_startup(&self) -> std::result::Result<(), Self::Error> {
200 Ok(())
201 }
202
203 async fn on_shutdown(&self) -> std::result::Result<(), Self::Error> {
205 Ok(())
206 }
207
208 async fn on_client_connect(
210 &self,
211 client_info: &Implementation,
212 ) -> std::result::Result<(), Self::Error> {
213 let _ = client_info;
214 Ok(())
215 }
216
217 async fn on_client_disconnect(
219 &self,
220 client_info: &Implementation,
221 ) -> std::result::Result<(), Self::Error> {
222 let _ = client_info;
223 Ok(())
224 }
225
226 async fn handle_custom_method(
230 &self,
231 method: &str,
232 params: serde_json::Value,
233 ) -> std::result::Result<serde_json::Value, Self::Error> {
234 let _ = (method, params);
235 Err(BackendError::not_supported(format!("Custom method not supported: {method}")).into())
236 }
237}
238
239#[async_trait]
241pub trait SimpleBackend: Send + Sync + Clone {
242 type Error: StdError + Send + Sync + Into<Error> + From<BackendError> + 'static;
243 type Config: Clone + Send + Sync;
244
245 async fn initialize(config: Self::Config) -> std::result::Result<Self, Self::Error>;
246 fn get_server_info(&self) -> ServerInfo;
247 async fn health_check(&self) -> std::result::Result<(), Self::Error>;
248
249 async fn list_tools(
251 &self,
252 request: PaginatedRequestParam,
253 ) -> std::result::Result<ListToolsResult, Self::Error>;
254 async fn call_tool(
255 &self,
256 request: CallToolRequestParam,
257 ) -> std::result::Result<CallToolResult, Self::Error>;
258}
259
260#[async_trait]
262impl<T> McpBackend for T
263where
264 T: SimpleBackend,
265{
266 type Error = T::Error;
267 type Config = T::Config;
268
269 async fn initialize(config: Self::Config) -> std::result::Result<Self, Self::Error> {
270 T::initialize(config).await
271 }
272
273 fn get_server_info(&self) -> ServerInfo {
274 T::get_server_info(self)
275 }
276
277 async fn health_check(&self) -> std::result::Result<(), Self::Error> {
278 T::health_check(self).await
279 }
280
281 async fn list_tools(
282 &self,
283 request: PaginatedRequestParam,
284 ) -> std::result::Result<ListToolsResult, Self::Error> {
285 T::list_tools(self, request).await
286 }
287
288 async fn call_tool(
289 &self,
290 request: CallToolRequestParam,
291 ) -> std::result::Result<CallToolResult, Self::Error> {
292 T::call_tool(self, request).await
293 }
294
295 async fn list_resources(
297 &self,
298 _request: PaginatedRequestParam,
299 ) -> std::result::Result<ListResourcesResult, Self::Error> {
300 Ok(ListResourcesResult {
301 resources: vec![],
302 next_cursor: None,
303 })
304 }
305
306 async fn read_resource(
307 &self,
308 request: ReadResourceRequestParam,
309 ) -> std::result::Result<ReadResourceResult, Self::Error> {
310 Err(BackendError::not_supported(format!("Resource not found: {}", request.uri)).into())
311 }
312
313 async fn list_prompts(
314 &self,
315 _request: PaginatedRequestParam,
316 ) -> std::result::Result<ListPromptsResult, Self::Error> {
317 Ok(ListPromptsResult {
318 prompts: vec![],
319 next_cursor: None,
320 })
321 }
322
323 async fn get_prompt(
324 &self,
325 request: GetPromptRequestParam,
326 ) -> std::result::Result<GetPromptResult, Self::Error> {
327 Err(BackendError::not_supported(format!("Prompt not found: {}", request.name)).into())
328 }
329}