pulseengine_mcp_server/
common_backend.rs

1//! Common backend implementations to reduce generated code
2//!
3//! This module provides default implementations that can be used by macro-generated servers
4//! to drastically reduce the amount of generated code per server.
5
6use crate::{BackendError, McpBackend};
7use async_trait::async_trait;
8use pulseengine_mcp_protocol::*;
9
10/// A common error type that can be used by multiple servers to reduce generated code
11#[derive(Debug, thiserror::Error)]
12pub enum CommonMcpError {
13    #[error("Invalid parameters: {0}")]
14    InvalidParams(String),
15
16    #[error("Internal error: {0}")]
17    Internal(String),
18
19    #[error("Backend error: {0}")]
20    Backend(#[from] BackendError),
21
22    #[error("Setup error: {0}")]
23    Setup(String),
24}
25
26impl From<CommonMcpError> for pulseengine_mcp_protocol::Error {
27    fn from(err: CommonMcpError) -> Self {
28        match err {
29            CommonMcpError::InvalidParams(msg) => {
30                pulseengine_mcp_protocol::Error::invalid_params(msg)
31            }
32            CommonMcpError::Internal(msg) => pulseengine_mcp_protocol::Error::internal_error(msg),
33            CommonMcpError::Backend(backend_err) => {
34                pulseengine_mcp_protocol::Error::internal_error(backend_err.to_string())
35            }
36            CommonMcpError::Setup(msg) => pulseengine_mcp_protocol::Error::internal_error(msg),
37        }
38    }
39}
40
41/// Default backend implementation that can be used by macro-generated servers
42#[derive(Clone)]
43pub struct CommonBackendImpl<T>
44where
45    T: Clone,
46{
47    #[allow(dead_code)]
48    inner: T,
49    server_info: ServerInfo,
50}
51
52impl<T> CommonBackendImpl<T>
53where
54    T: Clone,
55{
56    pub fn new(inner: T, server_info: ServerInfo) -> Self {
57        Self { inner, server_info }
58    }
59}
60
61#[async_trait]
62impl<T> McpBackend for CommonBackendImpl<T>
63where
64    T: Send + Sync + Clone + Default + 'static,
65{
66    type Error = CommonMcpError;
67    type Config = T;
68
69    async fn initialize(config: Self::Config) -> std::result::Result<Self, Self::Error> {
70        let server_info = ServerInfo {
71            protocol_version: ProtocolVersion::default(),
72            capabilities: ServerCapabilities {
73                tools: Some(ToolsCapability {
74                    list_changed: Some(false),
75                }),
76                resources: Some(ResourcesCapability {
77                    subscribe: Some(false),
78                    list_changed: Some(false),
79                }),
80                prompts: Some(PromptsCapability {
81                    list_changed: Some(false),
82                }),
83                logging: Some(LoggingCapability {
84                    level: Some("info".to_string()),
85                }),
86                sampling: None,
87                ..Default::default()
88            },
89            server_info: Implementation {
90                name: "MCP Server".to_string(),
91                version: "1.0.0".to_string(),
92            },
93            instructions: Some("Generated MCP Server".to_string()),
94        };
95
96        Ok(Self::new(config, server_info))
97    }
98
99    fn get_server_info(&self) -> ServerInfo {
100        self.server_info.clone()
101    }
102
103    async fn health_check(&self) -> std::result::Result<(), Self::Error> {
104        Ok(())
105    }
106
107    async fn list_tools(
108        &self,
109        _request: PaginatedRequestParam,
110    ) -> std::result::Result<ListToolsResult, Self::Error> {
111        // Try to get tools if the inner type implements tool traits
112        Ok(ListToolsResult {
113            tools: vec![],
114            next_cursor: None,
115        })
116    }
117
118    async fn call_tool(
119        &self,
120        request: CallToolRequestParam,
121    ) -> std::result::Result<CallToolResult, Self::Error> {
122        Err(CommonMcpError::InvalidParams(format!(
123            "Unknown tool: {}",
124            request.name
125        )))
126    }
127
128    async fn list_resources(
129        &self,
130        _request: PaginatedRequestParam,
131    ) -> std::result::Result<ListResourcesResult, Self::Error> {
132        Ok(ListResourcesResult {
133            resources: vec![],
134            next_cursor: None,
135        })
136    }
137
138    async fn read_resource(
139        &self,
140        request: ReadResourceRequestParam,
141    ) -> std::result::Result<ReadResourceResult, Self::Error> {
142        Err(CommonMcpError::InvalidParams(format!(
143            "Unknown resource: {}",
144            request.uri
145        )))
146    }
147
148    async fn list_prompts(
149        &self,
150        _request: PaginatedRequestParam,
151    ) -> std::result::Result<ListPromptsResult, Self::Error> {
152        Ok(ListPromptsResult {
153            prompts: vec![],
154            next_cursor: None,
155        })
156    }
157
158    async fn get_prompt(
159        &self,
160        request: GetPromptRequestParam,
161    ) -> std::result::Result<GetPromptResult, Self::Error> {
162        Err(CommonMcpError::InvalidParams(format!(
163            "Unknown prompt: {}",
164            request.name
165        )))
166    }
167}
168
169/// Helper trait for macro-generated types to provide custom server info
170pub trait HasServerInfo {
171    fn server_info() -> ServerInfo;
172}
173
174/// Helper trait for servers with tools
175pub trait McpToolsProvider {
176    fn get_available_tools(&self) -> Vec<Tool>;
177    fn call_tool_impl(
178        &self,
179        request: CallToolRequestParam,
180    ) -> impl std::future::Future<
181        Output = std::result::Result<CallToolResult, pulseengine_mcp_protocol::Error>,
182    > + Send;
183}
184
185/// Helper trait for servers with resources
186pub trait McpResourcesProvider {
187    fn get_available_resources(&self) -> Vec<Resource>;
188    fn read_resource_impl(
189        &self,
190        request: ReadResourceRequestParam,
191    ) -> impl std::future::Future<
192        Output = std::result::Result<ReadResourceResult, pulseengine_mcp_protocol::Error>,
193    > + Send;
194}
195
196/// Helper trait for servers with prompts
197pub trait McpPromptsProvider {
198    fn get_available_prompts(&self) -> Vec<Prompt>;
199    fn get_prompt_impl(
200        &self,
201        request: GetPromptRequestParam,
202    ) -> impl std::future::Future<
203        Output = std::result::Result<GetPromptResult, pulseengine_mcp_protocol::Error>,
204    > + Send;
205}