Skip to main content

mcp_kit/server/
handler.rs

1use std::{future::Future, pin::Pin, sync::Arc};
2
3use crate::{
4    error::{McpError, McpResult},
5    types::{
6        messages::{CallToolRequest, GetPromptRequest, ReadResourceRequest},
7        prompt::GetPromptResult,
8        resource::ReadResourceResult,
9        tool::CallToolResult,
10    },
11};
12use serde::de::DeserializeOwned;
13
14/// A type-erased, boxed future
15pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
16
17/// Boxed handler function for tool calls
18pub type HandlerFn<Req, Res> =
19    Arc<dyn Fn(Req) -> BoxFuture<'static, McpResult<Res>> + Send + Sync + 'static>;
20
21pub type ToolHandlerFn = HandlerFn<CallToolRequest, CallToolResult>;
22pub type ResourceHandlerFn = HandlerFn<ReadResourceRequest, ReadResourceResult>;
23pub type PromptHandlerFn = HandlerFn<GetPromptRequest, GetPromptResult>;
24
25// ─── IntoToolResult ───────────────────────────────────────────────────────────
26
27/// Anything that can be returned from a tool handler
28pub trait IntoToolResult {
29    fn into_tool_result(self) -> CallToolResult;
30}
31
32impl IntoToolResult for CallToolResult {
33    fn into_tool_result(self) -> CallToolResult {
34        self
35    }
36}
37
38impl IntoToolResult for String {
39    fn into_tool_result(self) -> CallToolResult {
40        CallToolResult::text(self)
41    }
42}
43
44impl IntoToolResult for &str {
45    fn into_tool_result(self) -> CallToolResult {
46        CallToolResult::text(self)
47    }
48}
49
50impl IntoToolResult for serde_json::Value {
51    fn into_tool_result(self) -> CallToolResult {
52        CallToolResult::text(self.to_string())
53    }
54}
55
56impl<T: IntoToolResult, E: std::fmt::Display> IntoToolResult for Result<T, E> {
57    fn into_tool_result(self) -> CallToolResult {
58        match self {
59            Ok(v) => v.into_tool_result(),
60            Err(e) => CallToolResult::error(e.to_string()),
61        }
62    }
63}
64
65// ─── ToolHandler trait ────────────────────────────────────────────────────────
66
67/// Implemented for async functions that can serve as tool handlers.
68///
69/// Supports two calling conventions:
70///   1. `|args: serde_json::Value| async { ... }` – raw JSON args
71///   2. `|params: MyStruct| async { ... }` – typed, deserialized args
72pub trait ToolHandler<Marker>: Clone + Send + Sync + 'static {
73    fn into_handler_fn(self) -> ToolHandlerFn;
74}
75
76/// Marker for typed (deserialised) handlers.
77/// Works for both `|params: MyStruct|` and `|args: serde_json::Value|` since
78/// `Value` implements `DeserializeOwned`.
79pub struct TypedMarker<T>(std::marker::PhantomData<T>);
80
81impl<F, Fut, R, T> ToolHandler<TypedMarker<T>> for F
82where
83    F: Fn(T) -> Fut + Clone + Send + Sync + 'static,
84    Fut: Future<Output = R> + Send + 'static,
85    R: IntoToolResult + Send + 'static,
86    T: DeserializeOwned + Send + 'static,
87{
88    fn into_handler_fn(self) -> ToolHandlerFn {
89        Arc::new(move |req: CallToolRequest| {
90            let f = self.clone();
91            let args = req.arguments.clone();
92            Box::pin(async move {
93                let params: T = serde_json::from_value(args)
94                    .map_err(|e| McpError::InvalidParams(e.to_string()))?;
95                Ok(f(params).await.into_tool_result())
96            })
97        })
98    }
99}
100
101// ─── PromptHandler ───────────────────────────────────────────────────────────
102
103pub trait PromptHandler<Marker>: Clone + Send + Sync + 'static {
104    fn into_handler_fn(self) -> PromptHandlerFn;
105}
106
107pub struct PromptRawMarker;
108
109impl<F, Fut> PromptHandler<PromptRawMarker> for F
110where
111    F: Fn(GetPromptRequest) -> Fut + Clone + Send + Sync + 'static,
112    Fut: Future<Output = McpResult<GetPromptResult>> + Send + 'static,
113{
114    fn into_handler_fn(self) -> PromptHandlerFn {
115        Arc::new(move |req| {
116            let f = self.clone();
117            Box::pin(async move { f(req).await })
118        })
119    }
120}
121
122// ─── ResourceHandler ─────────────────────────────────────────────────────────
123
124pub trait ResourceHandler<Marker>: Clone + Send + Sync + 'static {
125    fn into_handler_fn(self) -> ResourceHandlerFn;
126}
127
128pub struct ResourceRawMarker;
129
130impl<F, Fut> ResourceHandler<ResourceRawMarker> for F
131where
132    F: Fn(ReadResourceRequest) -> Fut + Clone + Send + Sync + 'static,
133    Fut: Future<Output = McpResult<ReadResourceResult>> + Send + 'static,
134{
135    fn into_handler_fn(self) -> ResourceHandlerFn {
136        Arc::new(move |req| {
137            let f = self.clone();
138            Box::pin(async move { f(req).await })
139        })
140    }
141}