use async_trait::async_trait;
use pulseengine_mcp_protocol::*;
use std::error::Error as StdError;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum BackendError {
#[error("Backend not initialized")]
NotInitialized,
#[error("Configuration error: {0}")]
Configuration(String),
#[error("Connection error: {0}")]
Connection(String),
#[error("Operation not supported: {0}")]
NotSupported(String),
#[error("Internal backend error: {0}")]
Internal(String),
#[error("Custom error: {0}")]
Custom(Box<dyn StdError + Send + Sync>),
}
impl BackendError {
pub fn configuration(msg: impl Into<String>) -> Self {
Self::Configuration(msg.into())
}
pub fn connection(msg: impl Into<String>) -> Self {
Self::Connection(msg.into())
}
pub fn not_supported(msg: impl Into<String>) -> Self {
Self::NotSupported(msg.into())
}
pub fn internal(msg: impl Into<String>) -> Self {
Self::Internal(msg.into())
}
pub fn custom(error: impl StdError + Send + Sync + 'static) -> Self {
Self::Custom(Box::new(error))
}
}
impl From<BackendError> for Error {
fn from(err: BackendError) -> Self {
match err {
BackendError::NotInitialized => Error::internal_error("Backend not initialized"),
BackendError::Configuration(msg) => Error::invalid_params(msg),
BackendError::Connection(msg) => {
Error::internal_error(format!("Connection failed: {msg}"))
}
BackendError::NotSupported(msg) => Error::method_not_found(msg),
BackendError::Internal(msg) => Error::internal_error(msg),
BackendError::Custom(err) => Error::internal_error(err.to_string()),
}
}
}
#[async_trait]
pub trait McpBackend: Send + Sync + Clone {
type Error: StdError + Send + Sync + Into<Error> + From<BackendError> + 'static;
type Config: Clone + Send + Sync;
async fn initialize(config: Self::Config) -> std::result::Result<Self, Self::Error>;
fn get_server_info(&self) -> ServerInfo;
async fn health_check(&self) -> std::result::Result<(), Self::Error>;
async fn list_tools(
&self,
request: PaginatedRequestParam,
) -> std::result::Result<ListToolsResult, Self::Error>;
async fn call_tool(
&self,
request: CallToolRequestParam,
) -> std::result::Result<CallToolResult, Self::Error>;
async fn list_resources(
&self,
request: PaginatedRequestParam,
) -> std::result::Result<ListResourcesResult, Self::Error>;
async fn read_resource(
&self,
request: ReadResourceRequestParam,
) -> std::result::Result<ReadResourceResult, Self::Error>;
async fn list_resource_templates(
&self,
request: PaginatedRequestParam,
) -> std::result::Result<ListResourceTemplatesResult, Self::Error> {
let _ = request;
Ok(ListResourceTemplatesResult {
resource_templates: vec![],
next_cursor: Some(String::new()),
})
}
async fn list_prompts(
&self,
request: PaginatedRequestParam,
) -> std::result::Result<ListPromptsResult, Self::Error>;
async fn get_prompt(
&self,
request: GetPromptRequestParam,
) -> std::result::Result<GetPromptResult, Self::Error>;
async fn subscribe(
&self,
_request: SubscribeRequestParam,
) -> std::result::Result<(), Self::Error> {
Ok(())
}
async fn unsubscribe(
&self,
_request: UnsubscribeRequestParam,
) -> std::result::Result<(), Self::Error> {
Ok(())
}
async fn complete(
&self,
request: CompleteRequestParam,
) -> std::result::Result<CompleteResult, Self::Error> {
let _ = request;
Ok(CompleteResult {
completion: CompletionValues {
values: vec![],
total: None,
has_more: None,
},
})
}
async fn elicit(
&self,
request: ElicitationRequestParam,
) -> std::result::Result<ElicitationResult, Self::Error> {
let _ = request;
Err(BackendError::not_supported("Elicitation not supported").into())
}
async fn set_level(
&self,
request: SetLevelRequestParam,
) -> std::result::Result<(), Self::Error> {
let _ = request;
Ok(())
}
async fn on_startup(&self) -> std::result::Result<(), Self::Error> {
Ok(())
}
async fn on_shutdown(&self) -> std::result::Result<(), Self::Error> {
Ok(())
}
async fn on_client_connect(
&self,
client_info: &Implementation,
) -> std::result::Result<(), Self::Error> {
let _ = client_info;
Ok(())
}
async fn on_client_disconnect(
&self,
client_info: &Implementation,
) -> std::result::Result<(), Self::Error> {
let _ = client_info;
Ok(())
}
async fn handle_custom_method(
&self,
method: &str,
params: serde_json::Value,
) -> std::result::Result<serde_json::Value, Self::Error> {
let _ = (method, params);
Err(BackendError::not_supported(format!("Custom method not supported: {method}")).into())
}
}
#[async_trait]
pub trait SimpleBackend: Send + Sync + Clone {
type Error: StdError + Send + Sync + Into<Error> + From<BackendError> + 'static;
type Config: Clone + Send + Sync;
async fn initialize(config: Self::Config) -> std::result::Result<Self, Self::Error>;
fn get_server_info(&self) -> ServerInfo;
async fn health_check(&self) -> std::result::Result<(), Self::Error>;
async fn list_tools(
&self,
request: PaginatedRequestParam,
) -> std::result::Result<ListToolsResult, Self::Error>;
async fn call_tool(
&self,
request: CallToolRequestParam,
) -> std::result::Result<CallToolResult, Self::Error>;
}
#[async_trait]
impl<T> McpBackend for T
where
T: SimpleBackend,
{
type Error = T::Error;
type Config = T::Config;
async fn initialize(config: Self::Config) -> std::result::Result<Self, Self::Error> {
T::initialize(config).await
}
fn get_server_info(&self) -> ServerInfo {
T::get_server_info(self)
}
async fn health_check(&self) -> std::result::Result<(), Self::Error> {
T::health_check(self).await
}
async fn list_tools(
&self,
request: PaginatedRequestParam,
) -> std::result::Result<ListToolsResult, Self::Error> {
T::list_tools(self, request).await
}
async fn call_tool(
&self,
request: CallToolRequestParam,
) -> std::result::Result<CallToolResult, Self::Error> {
T::call_tool(self, request).await
}
async fn list_resources(
&self,
_request: PaginatedRequestParam,
) -> std::result::Result<ListResourcesResult, Self::Error> {
Ok(ListResourcesResult {
resources: vec![],
next_cursor: None,
})
}
async fn read_resource(
&self,
request: ReadResourceRequestParam,
) -> std::result::Result<ReadResourceResult, Self::Error> {
Err(BackendError::not_supported(format!("Resource not found: {}", request.uri)).into())
}
async fn list_prompts(
&self,
_request: PaginatedRequestParam,
) -> std::result::Result<ListPromptsResult, Self::Error> {
Ok(ListPromptsResult {
prompts: vec![],
next_cursor: None,
})
}
async fn get_prompt(
&self,
request: GetPromptRequestParam,
) -> std::result::Result<GetPromptResult, Self::Error> {
Err(BackendError::not_supported(format!("Prompt not found: {}", request.name)).into())
}
}