Skip to main content

rmcp/handler/server/
tool.rs

1use std::{
2    borrow::Cow,
3    future::{Future, Ready},
4    marker::PhantomData,
5};
6
7#[cfg(not(feature = "local"))]
8use futures::future::BoxFuture;
9use serde::de::DeserializeOwned;
10
11use super::common::{AsRequestContext, FromContextPart};
12pub use super::{
13    common::{Extension, RequestId, schema_for_input, schema_for_output, schema_for_type},
14    router::tool::{ToolRoute, ToolRouter},
15};
16use crate::{
17    RoleServer,
18    handler::server::wrapper::Parameters,
19    model::{
20        CallToolRequestParams, CallToolResponse, CallToolResult, InputRequiredResult, IntoContents,
21        JsonObject,
22    },
23    service::{MaybeBoxFuture, MaybeSend, MaybeSendFuture, RequestContext},
24};
25
26/// Deserialize a JSON object into a type
27pub fn parse_json_object<T: DeserializeOwned>(input: JsonObject) -> Result<T, crate::ErrorData> {
28    serde_json::from_value(serde_json::Value::Object(input)).map_err(|e| {
29        crate::ErrorData::invalid_params(
30            format!("failed to deserialize parameters: {error}", error = e),
31            None,
32        )
33    })
34}
35#[non_exhaustive]
36pub struct ToolCallContext<'s, S> {
37    /// The request-specific context for this tool call.
38    pub request_context: RequestContext<RoleServer>,
39    /// The server handling this tool call.
40    pub service: &'s S,
41    /// The name of the tool being called.
42    pub name: Cow<'static, str>,
43    /// The arguments supplied for the tool call.
44    pub arguments: Option<JsonObject>,
45    /// Client responses to input requests from the previous MRTR round.
46    pub input_responses: Option<crate::model::InputResponses>,
47    /// Opaque state returned by the server during the previous MRTR round.
48    pub request_state: Option<String>,
49}
50
51impl<'s, S> ToolCallContext<'s, S> {
52    pub fn new(
53        service: &'s S,
54        CallToolRequestParams {
55            meta: _,
56            name,
57            arguments,
58            input_responses,
59            request_state,
60            ..
61        }: CallToolRequestParams,
62        request_context: RequestContext<RoleServer>,
63    ) -> Self {
64        Self {
65            request_context,
66            service,
67            name,
68            arguments,
69            input_responses,
70            request_state,
71        }
72    }
73    pub fn name(&self) -> &str {
74        &self.name
75    }
76    pub fn request_context(&self) -> &RequestContext<RoleServer> {
77        &self.request_context
78    }
79}
80
81impl<S> AsRequestContext for ToolCallContext<'_, S> {
82    fn as_request_context(&self) -> &RequestContext<RoleServer> {
83        &self.request_context
84    }
85
86    fn as_request_context_mut(&mut self) -> &mut RequestContext<RoleServer> {
87        &mut self.request_context
88    }
89}
90
91pub trait IntoCallToolResult {
92    fn into_call_tool_result(self) -> Result<CallToolResponse, crate::ErrorData>;
93}
94
95impl<T: IntoContents> IntoCallToolResult for T {
96    fn into_call_tool_result(self) -> Result<CallToolResponse, crate::ErrorData> {
97        Ok(CallToolResult::success(self.into_contents()).into())
98    }
99}
100
101impl IntoCallToolResult for CallToolResult {
102    fn into_call_tool_result(self) -> Result<CallToolResponse, crate::ErrorData> {
103        Ok(self.into())
104    }
105}
106
107impl IntoCallToolResult for InputRequiredResult {
108    fn into_call_tool_result(self) -> Result<CallToolResponse, crate::ErrorData> {
109        Ok(self.into())
110    }
111}
112
113impl IntoCallToolResult for CallToolResponse {
114    fn into_call_tool_result(self) -> Result<CallToolResponse, crate::ErrorData> {
115        Ok(self)
116    }
117}
118
119impl IntoCallToolResult for crate::ErrorData {
120    fn into_call_tool_result(self) -> Result<CallToolResponse, crate::ErrorData> {
121        Err(self)
122    }
123}
124
125impl<T: IntoCallToolResult, E: IntoCallToolResult> IntoCallToolResult for Result<T, E> {
126    fn into_call_tool_result(self) -> Result<CallToolResponse, crate::ErrorData> {
127        match self {
128            Ok(value) => value.into_call_tool_result(),
129            Err(error) => match error.into_call_tool_result() {
130                Ok(CallToolResponse::Complete(mut result)) => {
131                    result.is_error = Some(true);
132                    Ok(result.into())
133                }
134                Ok(CallToolResponse::InputRequired(_)) => Err(crate::ErrorData::internal_error(
135                    "InputRequiredResult cannot be returned from a tool error branch",
136                    None,
137                )),
138                Ok(CallToolResponse::Task(_)) => Err(crate::ErrorData::internal_error(
139                    "CreateTaskResult cannot be returned from a tool error branch",
140                    None,
141                )),
142                Err(e) => Err(e),
143            },
144        }
145    }
146}
147
148pin_project_lite::pin_project! {
149    #[project = IntoCallToolResultFutProj]
150    #[non_exhaustive]
151    pub enum IntoCallToolResultFut<F, R> {
152        Pending {
153            #[pin]
154            fut: F,
155            _marker: PhantomData<R>,
156        },
157        Ready {
158            #[pin]
159            result: Ready<Result<CallToolResponse, crate::ErrorData>>,
160        }
161    }
162}
163
164impl<F, R> Future for IntoCallToolResultFut<F, R>
165where
166    F: Future<Output = R>,
167    R: IntoCallToolResult,
168{
169    type Output = Result<CallToolResponse, crate::ErrorData>;
170
171    fn poll(
172        self: std::pin::Pin<&mut Self>,
173        cx: &mut std::task::Context<'_>,
174    ) -> std::task::Poll<Self::Output> {
175        match self.project() {
176            IntoCallToolResultFutProj::Pending { fut, _marker } => {
177                fut.poll(cx).map(IntoCallToolResult::into_call_tool_result)
178            }
179            IntoCallToolResultFutProj::Ready { result } => result.poll(cx),
180        }
181    }
182}
183
184pub trait CallToolHandler<S, A> {
185    fn call(
186        self,
187        context: ToolCallContext<'_, S>,
188    ) -> MaybeBoxFuture<'_, Result<CallToolResponse, crate::ErrorData>>;
189}
190
191#[cfg(not(feature = "local"))]
192pub type DynCallToolHandler<S> = dyn for<'s> Fn(ToolCallContext<'s, S>) -> BoxFuture<'s, Result<CallToolResponse, crate::ErrorData>>
193    + Send
194    + Sync;
195
196#[cfg(feature = "local")]
197pub type DynCallToolHandler<S> = dyn for<'s> Fn(
198    ToolCallContext<'s, S>,
199) -> futures::future::LocalBoxFuture<
200    's,
201    Result<CallToolResponse, crate::ErrorData>,
202>;
203
204// Tool-specific extractor for tool name
205#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
206pub struct ToolName(pub Cow<'static, str>);
207
208impl<S> FromContextPart<ToolCallContext<'_, S>> for ToolName {
209    fn from_context_part(context: &mut ToolCallContext<S>) -> Result<Self, crate::ErrorData> {
210        Ok(Self(context.name.clone()))
211    }
212}
213
214/// Extracts the opaque state returned by the server during the previous MRTR round.
215#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
216pub struct RequestState(pub Option<String>);
217
218impl<S> FromContextPart<ToolCallContext<'_, S>> for RequestState {
219    fn from_context_part(context: &mut ToolCallContext<S>) -> Result<Self, crate::ErrorData> {
220        Ok(Self(context.request_state.take()))
221    }
222}
223
224/// Extracts client responses to input requests from the previous MRTR round.
225#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
226pub struct InputResponses(pub Option<crate::model::InputResponses>);
227
228impl<S> FromContextPart<ToolCallContext<'_, S>> for InputResponses {
229    fn from_context_part(context: &mut ToolCallContext<S>) -> Result<Self, crate::ErrorData> {
230        Ok(Self(context.input_responses.take()))
231    }
232}
233
234// Special implementation for Parameters that handles tool arguments
235impl<S, P> FromContextPart<ToolCallContext<'_, S>> for Parameters<P>
236where
237    P: DeserializeOwned,
238{
239    fn from_context_part(context: &mut ToolCallContext<S>) -> Result<Self, crate::ErrorData> {
240        let arguments = context.arguments.take().unwrap_or_default();
241        let value: P =
242            serde_json::from_value(serde_json::Value::Object(arguments)).map_err(|e| {
243                crate::ErrorData::invalid_params(
244                    format!("failed to deserialize parameters: {error}", error = e),
245                    None,
246                )
247            })?;
248        Ok(Parameters(value))
249    }
250}
251
252// Special implementation for JsonObject that takes tool arguments
253impl<S> FromContextPart<ToolCallContext<'_, S>> for JsonObject {
254    fn from_context_part(context: &mut ToolCallContext<S>) -> Result<Self, crate::ErrorData> {
255        let object = context.arguments.take().unwrap_or_default();
256        Ok(object)
257    }
258}
259
260impl<'s, S> ToolCallContext<'s, S> {
261    pub fn invoke<H, A>(
262        self,
263        h: H,
264    ) -> MaybeBoxFuture<'s, Result<CallToolResponse, crate::ErrorData>>
265    where
266        H: CallToolHandler<S, A>,
267    {
268        h.call(self)
269    }
270}
271#[allow(clippy::type_complexity)]
272pub struct AsyncAdapter<P, Fut, R>(PhantomData<fn(P) -> fn(Fut) -> R>);
273pub struct SyncAdapter<P, R>(PhantomData<fn(P) -> R>);
274// #[allow(clippy::type_complexity)]
275pub struct AsyncMethodAdapter<P, R>(PhantomData<fn(P) -> R>);
276pub struct SyncMethodAdapter<P, R>(PhantomData<fn(P) -> R>);
277
278macro_rules! impl_for {
279    ($($T: ident)*) => {
280        impl_for!([] [$($T)*]);
281    };
282    // finished
283    ([$($Tn: ident)*] []) => {
284        impl_for!(@impl $($Tn)*);
285    };
286    ([$($Tn: ident)*] [$Tn_1: ident $($Rest: ident)*]) => {
287        impl_for!(@impl $($Tn)*);
288        impl_for!([$($Tn)* $Tn_1] [$($Rest)*]);
289    };
290    (@impl $($Tn: ident)*) => {
291        impl<$($Tn,)* S, F,  R> CallToolHandler<S, AsyncMethodAdapter<($($Tn,)*), R>> for F
292        where
293            $(
294                $Tn: for<'a> FromContextPart<ToolCallContext<'a, S>> ,
295            )*
296            F: FnOnce(&S, $($Tn,)*) -> MaybeBoxFuture<'_, R>,
297
298            // Need RTN support here(I guess), https://github.com/rust-lang/rust/pull/138424
299            // Fut: Future<Output = R> + Send + 'a,
300            R: IntoCallToolResult + MaybeSendFuture + 'static,
301            S: MaybeSend + 'static,
302        {
303            #[allow(unused_variables, non_snake_case, unused_mut)]
304            fn call(
305                self,
306                mut context: ToolCallContext<'_, S>,
307            ) -> MaybeBoxFuture<'_, Result<CallToolResponse, crate::ErrorData>>{
308                $(
309                    let result = $Tn::from_context_part(&mut context);
310                    let $Tn = match result {
311                        Ok(value) => value,
312                        Err(e) => return Box::pin(std::future::ready(Err(e))),
313                    };
314                )*
315                let service = context.service;
316                let fut = self(service, $($Tn,)*);
317                Box::pin(async move {
318                    let result = fut.await;
319                    result.into_call_tool_result()
320                })
321            }
322        }
323
324        impl<$($Tn,)* S, F, Fut, R> CallToolHandler<S, AsyncAdapter<($($Tn,)*), Fut, R>> for F
325        where
326            $(
327                $Tn: for<'a> FromContextPart<ToolCallContext<'a, S>> ,
328            )*
329            F: FnOnce($($Tn,)*) -> Fut + MaybeSendFuture,
330            Fut: Future<Output = R> + MaybeSendFuture + 'static,
331            R: IntoCallToolResult + MaybeSendFuture + 'static,
332            S: MaybeSend,
333        {
334            #[allow(unused_variables, non_snake_case, unused_mut)]
335            fn call(
336                self,
337                mut context: ToolCallContext<S>,
338            ) -> MaybeBoxFuture<'static, Result<CallToolResponse, crate::ErrorData>>{
339                $(
340                    let result = $Tn::from_context_part(&mut context);
341                    let $Tn = match result {
342                        Ok(value) => value,
343                        Err(e) => return Box::pin(std::future::ready(Err(e))),
344                    };
345                )*
346                let fut = self($($Tn,)*);
347                Box::pin(async move {
348                    let result = fut.await;
349                    result.into_call_tool_result()
350                })
351            }
352        }
353
354        impl<$($Tn,)* S, F, R> CallToolHandler<S, SyncMethodAdapter<($($Tn,)*), R>> for F
355        where
356            $(
357                $Tn: for<'a> FromContextPart<ToolCallContext<'a, S>> + ,
358            )*
359            F: FnOnce(&S, $($Tn,)*) -> R + MaybeSendFuture,
360            R: IntoCallToolResult + MaybeSendFuture,
361            S: MaybeSend,
362        {
363            #[allow(unused_variables, non_snake_case, unused_mut)]
364            fn call(
365                self,
366                mut context: ToolCallContext<S>,
367            ) -> MaybeBoxFuture<'static, Result<CallToolResponse, crate::ErrorData>> {
368                $(
369                    let result = $Tn::from_context_part(&mut context);
370                    let $Tn = match result {
371                        Ok(value) => value,
372                        Err(e) => return Box::pin(std::future::ready(Err(e))),
373                    };
374                )*
375                Box::pin(std::future::ready(self(context.service, $($Tn,)*).into_call_tool_result()))
376            }
377        }
378
379        impl<$($Tn,)* S, F, R> CallToolHandler<S, SyncAdapter<($($Tn,)*), R>> for F
380        where
381            $(
382                $Tn: for<'a> FromContextPart<ToolCallContext<'a, S>> + ,
383            )*
384            F: FnOnce($($Tn,)*) -> R + MaybeSendFuture,
385            R: IntoCallToolResult + MaybeSendFuture,
386            S: MaybeSend,
387        {
388            #[allow(unused_variables, non_snake_case, unused_mut)]
389            fn call(
390                self,
391                mut context: ToolCallContext<S>,
392            ) -> MaybeBoxFuture<'static, Result<CallToolResponse, crate::ErrorData>>  {
393                $(
394                    let result = $Tn::from_context_part(&mut context);
395                    let $Tn = match result {
396                        Ok(value) => value,
397                        Err(e) => return Box::pin(std::future::ready(Err(e))),
398                    };
399                )*
400                Box::pin(std::future::ready(self($($Tn,)*).into_call_tool_result()))
401            }
402        }
403    };
404}
405impl_for!(T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15);