use futures::future::BoxFuture;
use turbomcp_core::McpResult;
use turbomcp_protocol::neutral;
use crate::context::{
CallToolContext, CompleteContext, GetPromptContext, ListPromptsContext,
ListResourceTemplatesContext, ListResourcesContext, ListToolsContext, ReadResourceContext,
};
use crate::traits::{McpServerCore, WithCompletions, WithPrompts, WithResources, WithTools};
macro_rules! handler_slot {
($alias:ident<$s:ident>, $ctx:ty, $params:ty, $result:ty) => {
type $alias<$s> =
Box<dyn Fn($s, $ctx, $params) -> BoxFuture<'static, McpResult<$result>> + Send + Sync>;
};
}
handler_slot!(
ListToolsHandler<S>,
ListToolsContext,
neutral::ListParams,
neutral::ListToolsResult
);
handler_slot!(
CallToolHandler<S>,
CallToolContext,
neutral::CallToolParams,
neutral::CallToolResult
);
handler_slot!(
ListResourcesHandler<S>,
ListResourcesContext,
neutral::ListParams,
neutral::ListResourcesResult
);
handler_slot!(
ReadResourceHandler<S>,
ReadResourceContext,
neutral::ReadResourceParams,
neutral::ReadResourceResult
);
handler_slot!(
ListResourceTemplatesHandler<S>,
ListResourceTemplatesContext,
neutral::ListParams,
neutral::ListResourceTemplatesResult
);
handler_slot!(
ListPromptsHandler<S>,
ListPromptsContext,
neutral::ListParams,
neutral::ListPromptsResult
);
handler_slot!(
GetPromptHandler<S>,
GetPromptContext,
neutral::GetPromptParams,
neutral::GetPromptResult
);
handler_slot!(
CompleteHandler<S>,
CompleteContext,
neutral::CompleteParams,
neutral::CompleteResult
);
pub struct MethodRouter<S> {
list_tools: Option<ListToolsHandler<S>>,
call_tool: Option<CallToolHandler<S>>,
list_resources: Option<ListResourcesHandler<S>>,
read_resource: Option<ReadResourceHandler<S>>,
list_resource_templates: Option<ListResourceTemplatesHandler<S>>,
list_prompts: Option<ListPromptsHandler<S>>,
get_prompt: Option<GetPromptHandler<S>>,
complete: Option<CompleteHandler<S>>,
logging: bool,
}
impl<S> Default for MethodRouter<S> {
fn default() -> Self {
Self {
list_tools: None,
call_tool: None,
list_resources: None,
read_resource: None,
list_resource_templates: None,
list_prompts: None,
get_prompt: None,
complete: None,
logging: false,
}
}
}
macro_rules! dispatch_fn {
($name:ident, $field:ident, $ctx:ty, $params:ty, $result:ty) => {
pub(crate) fn $name(
&self,
server: S,
ctx: $ctx,
params: $params,
) -> Option<BoxFuture<'static, McpResult<$result>>> {
self.$field.as_ref().map(|h| h(server, ctx, params))
}
};
}
impl<S: McpServerCore> MethodRouter<S> {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn with_tools(mut self) -> Self
where
S: WithTools,
{
self.list_tools = Some(Box::new(|server: S, ctx, params| {
Box::pin(async move { server.list_tools(&ctx, params).await })
}));
self.call_tool = Some(Box::new(|server: S, ctx, params| {
Box::pin(async move { server.call_tool(&ctx, params).await })
}));
self
}
#[must_use]
pub fn with_resources(mut self) -> Self
where
S: WithResources,
{
self.list_resources = Some(Box::new(|server: S, ctx, params| {
Box::pin(async move { server.list_resources(&ctx, params).await })
}));
self.read_resource = Some(Box::new(|server: S, ctx, params| {
Box::pin(async move { server.read_resource(&ctx, params).await })
}));
self.list_resource_templates = Some(Box::new(|server: S, ctx, params| {
Box::pin(async move { server.list_resource_templates(&ctx, params).await })
}));
self
}
#[must_use]
pub fn with_prompts(mut self) -> Self
where
S: WithPrompts,
{
self.list_prompts = Some(Box::new(|server: S, ctx, params| {
Box::pin(async move { server.list_prompts(&ctx, params).await })
}));
self.get_prompt = Some(Box::new(|server: S, ctx, params| {
Box::pin(async move { server.get_prompt(&ctx, params).await })
}));
self
}
#[must_use]
pub fn with_completions(mut self) -> Self
where
S: WithCompletions,
{
self.complete = Some(Box::new(|server: S, ctx, params| {
Box::pin(async move { server.complete(&ctx, params).await })
}));
self
}
#[must_use]
pub fn with_logging(mut self) -> Self {
self.logging = true;
self
}
#[must_use]
pub fn has_tools(&self) -> bool {
self.list_tools.is_some()
}
#[must_use]
pub fn has_resources(&self) -> bool {
self.list_resources.is_some()
}
#[must_use]
pub fn has_prompts(&self) -> bool {
self.list_prompts.is_some()
}
#[must_use]
pub fn has_completions(&self) -> bool {
self.complete.is_some()
}
#[must_use]
pub fn has_logging(&self) -> bool {
self.logging
}
dispatch_fn!(
dispatch_list_tools,
list_tools,
ListToolsContext,
neutral::ListParams,
neutral::ListToolsResult
);
dispatch_fn!(
dispatch_call_tool,
call_tool,
CallToolContext,
neutral::CallToolParams,
neutral::CallToolResult
);
dispatch_fn!(
dispatch_list_resources,
list_resources,
ListResourcesContext,
neutral::ListParams,
neutral::ListResourcesResult
);
dispatch_fn!(
dispatch_read_resource,
read_resource,
ReadResourceContext,
neutral::ReadResourceParams,
neutral::ReadResourceResult
);
dispatch_fn!(
dispatch_list_resource_templates,
list_resource_templates,
ListResourceTemplatesContext,
neutral::ListParams,
neutral::ListResourceTemplatesResult
);
dispatch_fn!(
dispatch_list_prompts,
list_prompts,
ListPromptsContext,
neutral::ListParams,
neutral::ListPromptsResult
);
dispatch_fn!(
dispatch_get_prompt,
get_prompt,
GetPromptContext,
neutral::GetPromptParams,
neutral::GetPromptResult
);
dispatch_fn!(
dispatch_complete,
complete,
CompleteContext,
neutral::CompleteParams,
neutral::CompleteResult
);
}