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
26pub 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 pub request_context: RequestContext<RoleServer>,
39 pub service: &'s S,
41 pub name: Cow<'static, str>,
43 pub arguments: Option<JsonObject>,
45 pub input_responses: Option<crate::model::InputResponses>,
47 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#[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#[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#[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
234impl<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
252impl<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>);
274pub 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 ([$($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 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);