1use std::future::Future;
84use std::marker::PhantomData;
85use std::ops::Deref;
86use std::pin::Pin;
87
88use schemars::JsonSchema;
89use serde::de::DeserializeOwned;
90use serde_json::Value;
91
92use crate::context::RequestContext;
93use crate::error::{Error, Result};
94use crate::protocol::CallToolResult;
95use crate::tool::SchemaValidation;
96
97#[derive(Debug, Clone)]
107pub struct Rejection {
108 message: String,
109}
110
111impl Rejection {
112 pub fn new(message: impl Into<String>) -> Self {
114 Self {
115 message: message.into(),
116 }
117 }
118
119 pub fn message(&self) -> &str {
121 &self.message
122 }
123}
124
125impl std::fmt::Display for Rejection {
126 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
127 write!(f, "{}", self.message)
128 }
129}
130
131impl std::error::Error for Rejection {}
132
133impl From<Rejection> for Error {
134 fn from(rejection: Rejection) -> Self {
135 Error::tool(rejection.message)
136 }
137}
138
139#[derive(Debug, Clone)]
153pub struct JsonRejection {
154 message: String,
155 path: Option<String>,
157}
158
159impl JsonRejection {
160 pub fn new(message: impl Into<String>) -> Self {
162 Self {
163 message: message.into(),
164 path: None,
165 }
166 }
167
168 pub fn with_path(message: impl Into<String>, path: impl Into<String>) -> Self {
170 Self {
171 message: message.into(),
172 path: Some(path.into()),
173 }
174 }
175
176 pub fn message(&self) -> &str {
178 &self.message
179 }
180
181 pub fn path(&self) -> Option<&str> {
183 self.path.as_deref()
184 }
185}
186
187impl std::fmt::Display for JsonRejection {
188 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
189 if let Some(path) = &self.path {
190 write!(f, "Invalid input at `{}`: {}", path, self.message)
191 } else {
192 write!(f, "Invalid input: {}", self.message)
193 }
194 }
195}
196
197impl std::error::Error for JsonRejection {}
198
199impl From<JsonRejection> for Error {
200 fn from(rejection: JsonRejection) -> Self {
201 Error::tool(rejection.to_string())
202 }
203}
204
205impl From<serde_json::Error> for JsonRejection {
206 fn from(err: serde_json::Error) -> Self {
207 let path = if err.is_data() {
209 None
212 } else {
213 None
214 };
215
216 Self {
217 message: err.to_string(),
218 path,
219 }
220 }
221}
222
223#[derive(Debug, Clone)]
237pub struct ExtensionRejection {
238 type_name: &'static str,
239}
240
241impl ExtensionRejection {
242 pub fn not_found<T>() -> Self {
244 Self {
245 type_name: std::any::type_name::<T>(),
246 }
247 }
248
249 pub fn type_name(&self) -> &'static str {
251 self.type_name
252 }
253}
254
255impl std::fmt::Display for ExtensionRejection {
256 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
257 write!(
258 f,
259 "Extension of type `{name}` not found. Did you call `router.with_state()`, \
260 `router.with_extension()`, or `transport.bridge_extension::<{name}>()` for a \
261 type a tower layer inserts into the request?",
262 name = self.type_name
263 )
264 }
265}
266
267impl std::error::Error for ExtensionRejection {}
268
269impl From<ExtensionRejection> for Error {
270 fn from(rejection: ExtensionRejection) -> Self {
271 Error::tool(rejection.to_string())
272 }
273}
274
275pub trait FromToolRequest<S = ()>: Sized {
306 type Rejection: Into<Error>;
308
309 fn from_tool_request(
317 ctx: &RequestContext,
318 state: &S,
319 args: &Value,
320 ) -> std::result::Result<Self, Self::Rejection>;
321}
322
323#[derive(Debug, Clone, Copy)]
354pub struct Json<T>(pub T);
355
356impl<T> Deref for Json<T> {
357 type Target = T;
358
359 fn deref(&self) -> &Self::Target {
360 &self.0
361 }
362}
363
364impl<S, T> FromToolRequest<S> for Json<T>
365where
366 T: DeserializeOwned,
367{
368 type Rejection = JsonRejection;
369
370 fn from_tool_request(
371 _ctx: &RequestContext,
372 _state: &S,
373 args: &Value,
374 ) -> std::result::Result<Self, Self::Rejection> {
375 serde_json::from_value(args.clone())
376 .map(Json)
377 .map_err(JsonRejection::from)
378 }
379}
380
381#[derive(Debug, Clone, Copy)]
406pub struct State<T>(pub T);
407
408impl<T> Deref for State<T> {
409 type Target = T;
410
411 fn deref(&self) -> &Self::Target {
412 &self.0
413 }
414}
415
416impl<S: Clone> FromToolRequest<S> for State<S> {
417 type Rejection = Rejection;
418
419 fn from_tool_request(
420 _ctx: &RequestContext,
421 state: &S,
422 _args: &Value,
423 ) -> std::result::Result<Self, Self::Rejection> {
424 Ok(State(state.clone()))
425 }
426}
427
428#[derive(Debug, Clone)]
449pub struct Context(RequestContext);
450
451impl Context {
452 pub fn into_inner(self) -> RequestContext {
454 self.0
455 }
456}
457
458impl Deref for Context {
459 type Target = RequestContext;
460
461 fn deref(&self) -> &Self::Target {
462 &self.0
463 }
464}
465
466impl<S> FromToolRequest<S> for Context {
467 type Rejection = Rejection;
468
469 fn from_tool_request(
470 ctx: &RequestContext,
471 _state: &S,
472 _args: &Value,
473 ) -> std::result::Result<Self, Self::Rejection> {
474 Ok(Context(ctx.clone()))
475 }
476}
477
478#[derive(Debug, Clone)]
496pub struct RawArgs(pub Value);
497
498impl Deref for RawArgs {
499 type Target = Value;
500
501 fn deref(&self) -> &Self::Target {
502 &self.0
503 }
504}
505
506impl<S> FromToolRequest<S> for RawArgs {
507 type Rejection = Rejection;
508
509 fn from_tool_request(
510 _ctx: &RequestContext,
511 _state: &S,
512 args: &Value,
513 ) -> std::result::Result<Self, Self::Rejection> {
514 Ok(RawArgs(args.clone()))
515 }
516}
517
518#[derive(Debug, Clone)]
571pub struct Extension<T>(pub T);
572
573impl<T> Deref for Extension<T> {
574 type Target = T;
575
576 fn deref(&self) -> &Self::Target {
577 &self.0
578 }
579}
580
581impl<S, T> FromToolRequest<S> for Extension<T>
582where
583 T: Clone + Send + Sync + 'static,
584{
585 type Rejection = ExtensionRejection;
586
587 fn from_tool_request(
588 ctx: &RequestContext,
589 _state: &S,
590 _args: &Value,
591 ) -> std::result::Result<Self, Self::Rejection> {
592 ctx.extension::<T>()
593 .cloned()
594 .map(Extension)
595 .ok_or_else(ExtensionRejection::not_found::<T>)
596 }
597}
598
599#[diagnostic::on_unimplemented(
609 message = "`{Self}` is not a valid extractor handler",
610 note = "each closure argument must be an extractor (`Json<T>`, `State<S>`, `Context`, `Extension<T>`, `RawArgs`) and the return type must be `Result<impl Into<CallToolResult>, ToolError>`",
611 note = "for a `Json<T>` argument, `T` must implement `serde::Deserialize` and `schemars::JsonSchema`",
612 note = "if `T` derives `JsonSchema` but this still fails, check for a `schemars` major-version mismatch: the derive must come from the same `schemars` version tower-mcp uses (>=1). Depend on it via the `tower_mcp::schemars` re-export to stay aligned"
613)]
614pub trait ExtractorHandler<S, T>: Clone + Send + Sync + 'static {
615 type Future: Future<Output = Result<CallToolResult>> + Send;
617
618 fn call(self, ctx: RequestContext, state: S, args: Value) -> Self::Future;
620
621 fn input_schema() -> Value;
625}
626
627impl<S, F, Fut, T1> ExtractorHandler<S, (T1,)> for F
629where
630 S: Clone + Send + Sync + 'static,
631 F: Fn(T1) -> Fut + Clone + Send + Sync + 'static,
632 Fut: Future<Output = Result<CallToolResult>> + Send,
633 T1: FromToolRequest<S> + HasSchema + Send,
634{
635 type Future = Pin<Box<dyn Future<Output = Result<CallToolResult>> + Send>>;
636
637 fn call(self, ctx: RequestContext, state: S, args: Value) -> Self::Future {
638 Box::pin(async move {
639 let t1 = T1::from_tool_request(&ctx, &state, &args).map_err(Into::into)?;
640 self(t1).await
641 })
642 }
643
644 fn input_schema() -> Value {
645 if let Some(schema) = T1::schema() {
646 return schema;
647 }
648 serde_json::json!({
649 "type": "object",
650 "additionalProperties": true
651 })
652 }
653}
654
655impl<S, F, Fut, T1, T2> ExtractorHandler<S, (T1, T2)> for F
657where
658 S: Clone + Send + Sync + 'static,
659 F: Fn(T1, T2) -> Fut + Clone + Send + Sync + 'static,
660 Fut: Future<Output = Result<CallToolResult>> + Send,
661 T1: FromToolRequest<S> + HasSchema + Send,
662 T2: FromToolRequest<S> + HasSchema + Send,
663{
664 type Future = Pin<Box<dyn Future<Output = Result<CallToolResult>> + Send>>;
665
666 fn call(self, ctx: RequestContext, state: S, args: Value) -> Self::Future {
667 Box::pin(async move {
668 let t1 = T1::from_tool_request(&ctx, &state, &args).map_err(Into::into)?;
669 let t2 = T2::from_tool_request(&ctx, &state, &args).map_err(Into::into)?;
670 self(t1, t2).await
671 })
672 }
673
674 fn input_schema() -> Value {
675 if let Some(schema) = T2::schema() {
676 return schema;
677 }
678 if let Some(schema) = T1::schema() {
679 return schema;
680 }
681 serde_json::json!({
682 "type": "object",
683 "additionalProperties": true
684 })
685 }
686}
687
688impl<S, F, Fut, T1, T2, T3> ExtractorHandler<S, (T1, T2, T3)> for F
690where
691 S: Clone + Send + Sync + 'static,
692 F: Fn(T1, T2, T3) -> Fut + Clone + Send + Sync + 'static,
693 Fut: Future<Output = Result<CallToolResult>> + Send,
694 T1: FromToolRequest<S> + HasSchema + Send,
695 T2: FromToolRequest<S> + HasSchema + Send,
696 T3: FromToolRequest<S> + HasSchema + Send,
697{
698 type Future = Pin<Box<dyn Future<Output = Result<CallToolResult>> + Send>>;
699
700 fn call(self, ctx: RequestContext, state: S, args: Value) -> Self::Future {
701 Box::pin(async move {
702 let t1 = T1::from_tool_request(&ctx, &state, &args).map_err(Into::into)?;
703 let t2 = T2::from_tool_request(&ctx, &state, &args).map_err(Into::into)?;
704 let t3 = T3::from_tool_request(&ctx, &state, &args).map_err(Into::into)?;
705 self(t1, t2, t3).await
706 })
707 }
708
709 fn input_schema() -> Value {
710 if let Some(schema) = T3::schema() {
711 return schema;
712 }
713 if let Some(schema) = T2::schema() {
714 return schema;
715 }
716 if let Some(schema) = T1::schema() {
717 return schema;
718 }
719 serde_json::json!({
720 "type": "object",
721 "additionalProperties": true
722 })
723 }
724}
725
726impl<S, F, Fut, T1, T2, T3, T4> ExtractorHandler<S, (T1, T2, T3, T4)> for F
728where
729 S: Clone + Send + Sync + 'static,
730 F: Fn(T1, T2, T3, T4) -> Fut + Clone + Send + Sync + 'static,
731 Fut: Future<Output = Result<CallToolResult>> + Send,
732 T1: FromToolRequest<S> + HasSchema + Send,
733 T2: FromToolRequest<S> + HasSchema + Send,
734 T3: FromToolRequest<S> + HasSchema + Send,
735 T4: FromToolRequest<S> + HasSchema + Send,
736{
737 type Future = Pin<Box<dyn Future<Output = Result<CallToolResult>> + Send>>;
738
739 fn call(self, ctx: RequestContext, state: S, args: Value) -> Self::Future {
740 Box::pin(async move {
741 let t1 = T1::from_tool_request(&ctx, &state, &args).map_err(Into::into)?;
742 let t2 = T2::from_tool_request(&ctx, &state, &args).map_err(Into::into)?;
743 let t3 = T3::from_tool_request(&ctx, &state, &args).map_err(Into::into)?;
744 let t4 = T4::from_tool_request(&ctx, &state, &args).map_err(Into::into)?;
745 self(t1, t2, t3, t4).await
746 })
747 }
748
749 fn input_schema() -> Value {
750 if let Some(schema) = T4::schema() {
751 return schema;
752 }
753 if let Some(schema) = T3::schema() {
754 return schema;
755 }
756 if let Some(schema) = T2::schema() {
757 return schema;
758 }
759 if let Some(schema) = T1::schema() {
760 return schema;
761 }
762 serde_json::json!({
763 "type": "object",
764 "additionalProperties": true
765 })
766 }
767}
768
769impl<S, F, Fut, T1, T2, T3, T4, T5> ExtractorHandler<S, (T1, T2, T3, T4, T5)> for F
771where
772 S: Clone + Send + Sync + 'static,
773 F: Fn(T1, T2, T3, T4, T5) -> Fut + Clone + Send + Sync + 'static,
774 Fut: Future<Output = Result<CallToolResult>> + Send,
775 T1: FromToolRequest<S> + HasSchema + Send,
776 T2: FromToolRequest<S> + HasSchema + Send,
777 T3: FromToolRequest<S> + HasSchema + Send,
778 T4: FromToolRequest<S> + HasSchema + Send,
779 T5: FromToolRequest<S> + HasSchema + Send,
780{
781 type Future = Pin<Box<dyn Future<Output = Result<CallToolResult>> + Send>>;
782
783 fn call(self, ctx: RequestContext, state: S, args: Value) -> Self::Future {
784 Box::pin(async move {
785 let t1 = T1::from_tool_request(&ctx, &state, &args).map_err(Into::into)?;
786 let t2 = T2::from_tool_request(&ctx, &state, &args).map_err(Into::into)?;
787 let t3 = T3::from_tool_request(&ctx, &state, &args).map_err(Into::into)?;
788 let t4 = T4::from_tool_request(&ctx, &state, &args).map_err(Into::into)?;
789 let t5 = T5::from_tool_request(&ctx, &state, &args).map_err(Into::into)?;
790 self(t1, t2, t3, t4, t5).await
791 })
792 }
793
794 fn input_schema() -> Value {
795 if let Some(schema) = T5::schema() {
796 return schema;
797 }
798 if let Some(schema) = T4::schema() {
799 return schema;
800 }
801 if let Some(schema) = T3::schema() {
802 return schema;
803 }
804 if let Some(schema) = T2::schema() {
805 return schema;
806 }
807 if let Some(schema) = T1::schema() {
808 return schema;
809 }
810 serde_json::json!({
811 "type": "object",
812 "additionalProperties": true
813 })
814 }
815}
816
817#[diagnostic::on_unimplemented(
823 message = "`{Self}` does not implement `HasSchema`",
824 note = "for `Json<T>` this means `T: schemars::JsonSchema` is not satisfied",
825 note = "a common cause is a `schemars` major-version mismatch: the derive on `T` must come from the same `schemars` version tower-mcp uses (>=1)",
826 note = "depend on `schemars` via the `tower_mcp::schemars` re-export to keep the versions aligned"
827)]
828pub trait HasSchema {
829 fn schema() -> Option<Value>;
831}
832
833impl<T: JsonSchema> HasSchema for Json<T> {
834 fn schema() -> Option<Value> {
835 let schema = schemars::schema_for!(T);
836 serde_json::to_value(schema)
837 .ok()
838 .map(crate::tool::ensure_object_schema)
839 }
840}
841
842impl HasSchema for Context {
844 fn schema() -> Option<Value> {
845 None
846 }
847}
848
849impl HasSchema for RawArgs {
850 fn schema() -> Option<Value> {
851 None
852 }
853}
854
855impl<T> HasSchema for State<T> {
856 fn schema() -> Option<Value> {
857 None
858 }
859}
860
861impl<T> HasSchema for Extension<T> {
862 fn schema() -> Option<Value> {
863 None
864 }
865}
866
867#[deprecated(
876 since = "0.8.0",
877 note = "Use `ExtractorHandler` instead -- `extractor_handler` auto-detects JSON schema from `Json<T>` extractors"
878)]
879pub trait TypedExtractorHandler<S, T, I>: Clone + Send + Sync + 'static
880where
881 I: JsonSchema,
882{
883 type Future: Future<Output = Result<CallToolResult>> + Send;
885
886 fn call(self, ctx: RequestContext, state: S, args: Value) -> Self::Future;
888}
889
890#[allow(deprecated)]
892impl<S, F, Fut, T> TypedExtractorHandler<S, (Json<T>,), T> for F
893where
894 S: Clone + Send + Sync + 'static,
895 F: Fn(Json<T>) -> Fut + Clone + Send + Sync + 'static,
896 Fut: Future<Output = Result<CallToolResult>> + Send,
897 T: DeserializeOwned + JsonSchema + Send,
898{
899 type Future = Pin<Box<dyn Future<Output = Result<CallToolResult>> + Send>>;
900
901 fn call(self, ctx: RequestContext, state: S, args: Value) -> Self::Future {
902 Box::pin(async move {
903 let t1 =
904 Json::<T>::from_tool_request(&ctx, &state, &args).map_err(Into::<Error>::into)?;
905 self(t1).await
906 })
907 }
908}
909
910#[allow(deprecated)]
912impl<S, F, Fut, T1, T> TypedExtractorHandler<S, (T1, Json<T>), T> for F
913where
914 S: Clone + Send + Sync + 'static,
915 F: Fn(T1, Json<T>) -> Fut + Clone + Send + Sync + 'static,
916 Fut: Future<Output = Result<CallToolResult>> + Send,
917 T1: FromToolRequest<S> + Send,
918 T: DeserializeOwned + JsonSchema + Send,
919{
920 type Future = Pin<Box<dyn Future<Output = Result<CallToolResult>> + Send>>;
921
922 fn call(self, ctx: RequestContext, state: S, args: Value) -> Self::Future {
923 Box::pin(async move {
924 let t1 = T1::from_tool_request(&ctx, &state, &args).map_err(Into::<Error>::into)?;
925 let t2 =
926 Json::<T>::from_tool_request(&ctx, &state, &args).map_err(Into::<Error>::into)?;
927 self(t1, t2).await
928 })
929 }
930}
931
932#[allow(deprecated)]
934impl<S, F, Fut, T1, T2, T> TypedExtractorHandler<S, (T1, T2, Json<T>), T> for F
935where
936 S: Clone + Send + Sync + 'static,
937 F: Fn(T1, T2, Json<T>) -> Fut + Clone + Send + Sync + 'static,
938 Fut: Future<Output = Result<CallToolResult>> + Send,
939 T1: FromToolRequest<S> + Send,
940 T2: FromToolRequest<S> + Send,
941 T: DeserializeOwned + JsonSchema + Send,
942{
943 type Future = Pin<Box<dyn Future<Output = Result<CallToolResult>> + Send>>;
944
945 fn call(self, ctx: RequestContext, state: S, args: Value) -> Self::Future {
946 Box::pin(async move {
947 let t1 = T1::from_tool_request(&ctx, &state, &args).map_err(Into::<Error>::into)?;
948 let t2 = T2::from_tool_request(&ctx, &state, &args).map_err(Into::<Error>::into)?;
949 let t3 =
950 Json::<T>::from_tool_request(&ctx, &state, &args).map_err(Into::<Error>::into)?;
951 self(t1, t2, t3).await
952 })
953 }
954}
955
956#[allow(deprecated)]
958impl<S, F, Fut, T1, T2, T3, T> TypedExtractorHandler<S, (T1, T2, T3, Json<T>), T> for F
959where
960 S: Clone + Send + Sync + 'static,
961 F: Fn(T1, T2, T3, Json<T>) -> Fut + Clone + Send + Sync + 'static,
962 Fut: Future<Output = Result<CallToolResult>> + Send,
963 T1: FromToolRequest<S> + Send,
964 T2: FromToolRequest<S> + Send,
965 T3: FromToolRequest<S> + Send,
966 T: DeserializeOwned + JsonSchema + Send,
967{
968 type Future = Pin<Box<dyn Future<Output = Result<CallToolResult>> + Send>>;
969
970 fn call(self, ctx: RequestContext, state: S, args: Value) -> Self::Future {
971 Box::pin(async move {
972 let t1 = T1::from_tool_request(&ctx, &state, &args).map_err(Into::<Error>::into)?;
973 let t2 = T2::from_tool_request(&ctx, &state, &args).map_err(Into::<Error>::into)?;
974 let t3 = T3::from_tool_request(&ctx, &state, &args).map_err(Into::<Error>::into)?;
975 let t4 =
976 Json::<T>::from_tool_request(&ctx, &state, &args).map_err(Into::<Error>::into)?;
977 self(t1, t2, t3, t4).await
978 })
979 }
980}
981
982use crate::tool::{
987 BoxFuture, GuardLayer, Tool, ToolCatchError, ToolHandler, ToolHandlerService, ToolRequest,
988};
989use tower::util::BoxCloneService;
990use tower_service::Service;
991
992pub(crate) struct ExtractorToolHandler<S, F, T> {
994 state: S,
995 handler: F,
996 input_schema: Value,
997 _phantom: PhantomData<T>,
998}
999
1000impl<S, F, T> ToolHandler for ExtractorToolHandler<S, F, T>
1001where
1002 S: Clone + Send + Sync + 'static,
1003 F: ExtractorHandler<S, T> + Clone,
1004 T: Send + Sync + 'static,
1005{
1006 fn call(&self, args: Value) -> BoxFuture<'_, Result<CallToolResult>> {
1007 let ctx = RequestContext::new(crate::protocol::RequestId::Number(0));
1008 self.call_with_context(ctx, args)
1009 }
1010
1011 fn call_with_context(
1012 &self,
1013 ctx: RequestContext,
1014 args: Value,
1015 ) -> BoxFuture<'_, Result<CallToolResult>> {
1016 let state = self.state.clone();
1017 let handler = self.handler.clone();
1018 Box::pin(async move { handler.call(ctx, state, args).await })
1019 }
1020
1021 fn uses_context(&self) -> bool {
1022 true
1023 }
1024
1025 fn input_schema(&self) -> Value {
1026 self.input_schema.clone()
1027 }
1028}
1029
1030#[doc(hidden)]
1032pub struct ToolBuilderWithExtractor<S, F, T> {
1033 pub(crate) name: String,
1034 pub(crate) title: Option<String>,
1035 pub(crate) description: Option<String>,
1036 pub(crate) output_schema: Option<Value>,
1037 pub(crate) validation: SchemaValidation,
1038 pub(crate) icons: Option<Vec<crate::protocol::ToolIcon>>,
1039 pub(crate) annotations: Option<crate::protocol::ToolAnnotations>,
1040 pub(crate) task_support: crate::protocol::TaskSupportMode,
1041 pub(crate) state: S,
1042 pub(crate) handler: F,
1043 pub(crate) input_schema: Value,
1044 pub(crate) _phantom: PhantomData<T>,
1045}
1046
1047impl<S, F, T> ToolBuilderWithExtractor<S, F, T>
1048where
1049 S: Clone + Send + Sync + 'static,
1050 F: ExtractorHandler<S, T> + Clone,
1051 T: Send + Sync + 'static,
1052{
1053 pub fn build(self) -> Tool {
1055 let handler = ExtractorToolHandler {
1056 state: self.state,
1057 handler: self.handler,
1058 input_schema: self.input_schema.clone(),
1059 _phantom: PhantomData,
1060 };
1061
1062 let handler_service = ToolHandlerService::new(handler);
1063 let catch_error = ToolCatchError::new(handler_service);
1064 let service = BoxCloneService::new(catch_error);
1065
1066 Tool {
1067 live_handler: None,
1068 name: self.name,
1069 title: self.title,
1070 description: self.description,
1071 output_schema: self.output_schema,
1072 icons: self.icons,
1073 annotations: self.annotations,
1074 meta: None,
1075 task_support: self.task_support,
1076 required_client_capabilities: None,
1077 task_preparer: None,
1078 service: Some(service),
1079 #[cfg(feature = "stateless")]
1080 mrtr_handler: None,
1081 input_schema: self.input_schema,
1082 }
1083 .with_schema_validation(self.validation)
1084 }
1085
1086 pub fn layer<L>(self, layer: L) -> ToolBuilderWithExtractorLayer<S, F, T, L> {
1122 ToolBuilderWithExtractorLayer {
1123 name: self.name,
1124 title: self.title,
1125 description: self.description,
1126 output_schema: self.output_schema,
1127 validation: self.validation,
1128 icons: self.icons,
1129 annotations: self.annotations,
1130 task_support: self.task_support,
1131 state: self.state,
1132 handler: self.handler,
1133 input_schema: self.input_schema,
1134 layer,
1135 _phantom: PhantomData,
1136 }
1137 }
1138
1139 pub fn guard<G>(self, guard: G) -> ToolBuilderWithExtractorLayer<S, F, T, GuardLayer<G>>
1143 where
1144 G: Fn(&ToolRequest) -> std::result::Result<(), String> + Clone + Send + Sync + 'static,
1145 {
1146 self.layer(GuardLayer::new(guard))
1147 }
1148}
1149
1150#[doc(hidden)]
1154pub struct ToolBuilderWithExtractorLayer<S, F, T, L> {
1155 name: String,
1156 title: Option<String>,
1157 description: Option<String>,
1158 output_schema: Option<Value>,
1159 validation: SchemaValidation,
1160 icons: Option<Vec<crate::protocol::ToolIcon>>,
1161 annotations: Option<crate::protocol::ToolAnnotations>,
1162 task_support: crate::protocol::TaskSupportMode,
1163 state: S,
1164 handler: F,
1165 input_schema: Value,
1166 layer: L,
1167 _phantom: PhantomData<T>,
1168}
1169
1170#[allow(private_bounds)]
1171impl<S, F, T, L> ToolBuilderWithExtractorLayer<S, F, T, L>
1172where
1173 S: Clone + Send + Sync + 'static,
1174 F: ExtractorHandler<S, T> + Clone,
1175 T: Send + Sync + 'static,
1176 L: tower::Layer<ToolHandlerService<ExtractorToolHandler<S, F, T>>>
1177 + Clone
1178 + Send
1179 + Sync
1180 + 'static,
1181 L::Service: Service<ToolRequest, Response = CallToolResult> + Clone + Send + 'static,
1182 <L::Service as Service<ToolRequest>>::Error: std::fmt::Display + Send,
1183 <L::Service as Service<ToolRequest>>::Future: Send,
1184{
1185 pub fn build(self) -> Tool {
1187 let handler = ExtractorToolHandler {
1188 state: self.state,
1189 handler: self.handler,
1190 input_schema: self.input_schema.clone(),
1191 _phantom: PhantomData,
1192 };
1193
1194 let handler_service = ToolHandlerService::new(handler);
1195 let layered = self.layer.layer(handler_service);
1196 let catch_error = ToolCatchError::new(layered);
1197 let service = BoxCloneService::new(catch_error);
1198
1199 Tool {
1200 live_handler: None,
1201 name: self.name,
1202 title: self.title,
1203 description: self.description,
1204 output_schema: self.output_schema,
1205 icons: self.icons,
1206 annotations: self.annotations,
1207 meta: None,
1208 task_support: self.task_support,
1209 required_client_capabilities: None,
1210 task_preparer: None,
1211 service: Some(service),
1212 #[cfg(feature = "stateless")]
1213 mrtr_handler: None,
1214 input_schema: self.input_schema,
1215 }
1216 .with_schema_validation(self.validation)
1217 }
1218
1219 pub fn layer<L2>(
1224 self,
1225 layer: L2,
1226 ) -> ToolBuilderWithExtractorLayer<S, F, T, tower::layer::util::Stack<L2, L>> {
1227 ToolBuilderWithExtractorLayer {
1228 name: self.name,
1229 title: self.title,
1230 description: self.description,
1231 output_schema: self.output_schema,
1232 validation: self.validation,
1233 icons: self.icons,
1234 annotations: self.annotations,
1235 task_support: self.task_support,
1236 state: self.state,
1237 handler: self.handler,
1238 input_schema: self.input_schema,
1239 layer: tower::layer::util::Stack::new(layer, self.layer),
1240 _phantom: PhantomData,
1241 }
1242 }
1243
1244 pub fn guard<G>(
1248 self,
1249 guard: G,
1250 ) -> ToolBuilderWithExtractorLayer<S, F, T, tower::layer::util::Stack<GuardLayer<G>, L>>
1251 where
1252 G: Fn(&ToolRequest) -> std::result::Result<(), String> + Clone + Send + Sync + 'static,
1253 {
1254 self.layer(GuardLayer::new(guard))
1255 }
1256}
1257
1258#[doc(hidden)]
1260#[deprecated(
1261 since = "0.8.0",
1262 note = "Use `ToolBuilderWithExtractor` via `extractor_handler` instead"
1263)]
1264pub struct ToolBuilderWithTypedExtractor<S, F, T, I> {
1265 pub(crate) name: String,
1266 pub(crate) title: Option<String>,
1267 pub(crate) description: Option<String>,
1268 pub(crate) output_schema: Option<Value>,
1269 pub(crate) validation: SchemaValidation,
1270 pub(crate) input_schema_override: Option<Value>,
1271 pub(crate) icons: Option<Vec<crate::protocol::ToolIcon>>,
1272 pub(crate) annotations: Option<crate::protocol::ToolAnnotations>,
1273 pub(crate) task_support: crate::protocol::TaskSupportMode,
1274 pub(crate) state: S,
1275 pub(crate) handler: F,
1276 pub(crate) _phantom: PhantomData<(T, I)>,
1277}
1278
1279#[allow(deprecated)]
1280impl<S, F, T, I> ToolBuilderWithTypedExtractor<S, F, T, I>
1281where
1282 S: Clone + Send + Sync + 'static,
1283 F: TypedExtractorHandler<S, T, I> + Clone,
1284 T: Send + Sync + 'static,
1285 I: JsonSchema + Send + Sync + 'static,
1286{
1287 pub fn build(self) -> Tool {
1289 let input_schema = {
1290 let schema = self.input_schema_override.unwrap_or_else(|| {
1291 let schema = schemars::schema_for!(I);
1292 serde_json::to_value(schema).unwrap_or_else(|_| {
1293 serde_json::json!({
1294 "type": "object"
1295 })
1296 })
1297 });
1298 crate::tool::ensure_object_schema(schema)
1299 };
1300
1301 let handler = TypedExtractorToolHandler {
1302 state: self.state,
1303 handler: self.handler,
1304 input_schema: input_schema.clone(),
1305 _phantom: PhantomData,
1306 };
1307
1308 let handler_service = crate::tool::ToolHandlerService::new(handler);
1309 let catch_error = ToolCatchError::new(handler_service);
1310 let service = BoxCloneService::new(catch_error);
1311
1312 Tool {
1313 live_handler: None,
1314 name: self.name,
1315 title: self.title,
1316 description: self.description,
1317 output_schema: self.output_schema,
1318 icons: self.icons,
1319 annotations: self.annotations,
1320 meta: None,
1321 task_support: self.task_support,
1322 required_client_capabilities: None,
1323 task_preparer: None,
1324 service: Some(service),
1325 #[cfg(feature = "stateless")]
1326 mrtr_handler: None,
1327 input_schema,
1328 }
1329 .with_schema_validation(self.validation)
1330 }
1331}
1332
1333struct TypedExtractorToolHandler<S, F, T, I> {
1335 state: S,
1336 handler: F,
1337 input_schema: Value,
1338 _phantom: PhantomData<(T, I)>,
1339}
1340
1341#[allow(deprecated)]
1342impl<S, F, T, I> ToolHandler for TypedExtractorToolHandler<S, F, T, I>
1343where
1344 S: Clone + Send + Sync + 'static,
1345 F: TypedExtractorHandler<S, T, I> + Clone,
1346 T: Send + Sync + 'static,
1347 I: JsonSchema + Send + Sync + 'static,
1348{
1349 fn call(&self, args: Value) -> BoxFuture<'_, Result<CallToolResult>> {
1350 let ctx = RequestContext::new(crate::protocol::RequestId::Number(0));
1351 self.call_with_context(ctx, args)
1352 }
1353
1354 fn call_with_context(
1355 &self,
1356 ctx: RequestContext,
1357 args: Value,
1358 ) -> BoxFuture<'_, Result<CallToolResult>> {
1359 let state = self.state.clone();
1360 let handler = self.handler.clone();
1361 Box::pin(async move { handler.call(ctx, state, args).await })
1362 }
1363
1364 fn uses_context(&self) -> bool {
1365 true
1366 }
1367
1368 fn input_schema(&self) -> Value {
1369 self.input_schema.clone()
1370 }
1371}
1372
1373#[cfg(test)]
1374mod tests {
1375 use super::*;
1376 use crate::protocol::RequestId;
1377 use schemars::JsonSchema;
1378 use serde::Deserialize;
1379 use std::sync::Arc;
1380
1381 #[derive(Debug, Deserialize, JsonSchema)]
1382 struct TestInput {
1383 name: String,
1384 count: i32,
1385 }
1386
1387 #[derive(Debug, Deserialize, JsonSchema)]
1392 #[schemars(crate = "crate::schemars")]
1393 struct ReexportInput {
1394 field: String,
1395 }
1396
1397 #[test]
1398 fn reexported_schemars_derive_produces_schema() {
1399 let schema = <Json<ReexportInput> as HasSchema>::schema()
1400 .expect("re-exported schemars derive should yield a schema");
1401 assert_eq!(schema["type"], "object");
1402 assert!(schema["properties"].get("field").is_some());
1403
1404 let ctx = RequestContext::new(RequestId::Number(1));
1406 let args = serde_json::json!({"field": "value"});
1407 let Json(input) = Json::<ReexportInput>::from_tool_request(&ctx, &(), &args)
1408 .expect("deserialization should succeed");
1409 assert_eq!(input.field, "value");
1410 }
1411
1412 #[test]
1413 fn test_json_extraction() {
1414 let args = serde_json::json!({"name": "test", "count": 42});
1415 let ctx = RequestContext::new(RequestId::Number(1));
1416
1417 let result = Json::<TestInput>::from_tool_request(&ctx, &(), &args);
1418 assert!(result.is_ok());
1419 let Json(input) = result.unwrap();
1420 assert_eq!(input.name, "test");
1421 assert_eq!(input.count, 42);
1422 }
1423
1424 #[test]
1425 fn test_json_extraction_error() {
1426 let args = serde_json::json!({"name": "test"}); let ctx = RequestContext::new(RequestId::Number(1));
1428
1429 let result = Json::<TestInput>::from_tool_request(&ctx, &(), &args);
1430 assert!(result.is_err());
1431 let rejection = result.unwrap_err();
1432 assert!(rejection.message().contains("count"));
1434 }
1435
1436 #[test]
1437 fn test_state_extraction() {
1438 let args = serde_json::json!({});
1439 let ctx = RequestContext::new(RequestId::Number(1));
1440 let state = Arc::new("my-state".to_string());
1441
1442 let result = State::<Arc<String>>::from_tool_request(&ctx, &state, &args);
1443 assert!(result.is_ok());
1444 let State(extracted) = result.unwrap();
1445 assert_eq!(*extracted, "my-state");
1446 }
1447
1448 #[test]
1449 fn test_context_extraction() {
1450 let args = serde_json::json!({});
1451 let ctx = RequestContext::new(RequestId::Number(42));
1452
1453 let result = Context::from_tool_request(&ctx, &(), &args);
1454 assert!(result.is_ok());
1455 let extracted = result.unwrap();
1456 assert_eq!(*extracted.request_id(), RequestId::Number(42));
1457 }
1458
1459 #[test]
1460 fn test_raw_args_extraction() {
1461 let args = serde_json::json!({"foo": "bar", "baz": 123});
1462 let ctx = RequestContext::new(RequestId::Number(1));
1463
1464 let result = RawArgs::from_tool_request(&ctx, &(), &args);
1465 assert!(result.is_ok());
1466 let RawArgs(extracted) = result.unwrap();
1467 assert_eq!(extracted["foo"], "bar");
1468 assert_eq!(extracted["baz"], 123);
1469 }
1470
1471 #[test]
1472 fn test_extension_extraction() {
1473 use crate::context::Extensions;
1474
1475 #[derive(Clone, Debug, PartialEq)]
1476 struct DatabasePool {
1477 url: String,
1478 }
1479
1480 let args = serde_json::json!({});
1481
1482 let mut extensions = Extensions::new();
1484 extensions.insert(Arc::new(DatabasePool {
1485 url: "postgres://localhost".to_string(),
1486 }));
1487
1488 let ctx = RequestContext::new(RequestId::Number(1)).with_extensions(Arc::new(extensions));
1490
1491 let result = Extension::<Arc<DatabasePool>>::from_tool_request(&ctx, &(), &args);
1493 assert!(result.is_ok());
1494 let Extension(pool) = result.unwrap();
1495 assert_eq!(pool.url, "postgres://localhost");
1496 }
1497
1498 #[test]
1499 fn test_extension_extraction_missing() {
1500 #[derive(Clone, Debug)]
1501 struct NotPresent;
1502
1503 let args = serde_json::json!({});
1504 let ctx = RequestContext::new(RequestId::Number(1));
1505
1506 let result = Extension::<NotPresent>::from_tool_request(&ctx, &(), &args);
1508 assert!(result.is_err());
1509 let rejection = result.unwrap_err();
1510 assert!(rejection.type_name().contains("NotPresent"));
1512 }
1513
1514 #[tokio::test]
1515 async fn test_single_extractor_handler() {
1516 let handler = |Json(input): Json<TestInput>| async move {
1517 Ok(CallToolResult::text(format!(
1518 "{}: {}",
1519 input.name, input.count
1520 )))
1521 };
1522
1523 let ctx = RequestContext::new(RequestId::Number(1));
1524 let args = serde_json::json!({"name": "test", "count": 5});
1525
1526 let result: Result<CallToolResult> =
1528 ExtractorHandler::<(), (Json<TestInput>,)>::call(handler, ctx, (), args).await;
1529 assert!(result.is_ok());
1530 }
1531
1532 #[tokio::test]
1533 async fn test_two_extractor_handler() {
1534 let handler = |State(state): State<Arc<String>>, Json(input): Json<TestInput>| async move {
1535 Ok(CallToolResult::text(format!(
1536 "{}: {} - {}",
1537 state, input.name, input.count
1538 )))
1539 };
1540
1541 let ctx = RequestContext::new(RequestId::Number(1));
1542 let state = Arc::new("prefix".to_string());
1543 let args = serde_json::json!({"name": "test", "count": 5});
1544
1545 let result: Result<CallToolResult> = ExtractorHandler::<
1547 Arc<String>,
1548 (State<Arc<String>>, Json<TestInput>),
1549 >::call(handler, ctx, state, args)
1550 .await;
1551 assert!(result.is_ok());
1552 }
1553
1554 #[tokio::test]
1555 async fn test_three_extractor_handler() {
1556 let handler = |State(state): State<Arc<String>>,
1557 ctx: Context,
1558 Json(input): Json<TestInput>| async move {
1559 assert!(!ctx.is_cancelled());
1561 Ok(CallToolResult::text(format!(
1562 "{}: {} - {}",
1563 state, input.name, input.count
1564 )))
1565 };
1566
1567 let ctx = RequestContext::new(RequestId::Number(1));
1568 let state = Arc::new("prefix".to_string());
1569 let args = serde_json::json!({"name": "test", "count": 5});
1570
1571 let result: Result<CallToolResult> = ExtractorHandler::<
1573 Arc<String>,
1574 (State<Arc<String>>, Context, Json<TestInput>),
1575 >::call(handler, ctx, state, args)
1576 .await;
1577 assert!(result.is_ok());
1578 }
1579
1580 #[test]
1581 fn test_json_schema_generation() {
1582 let schema = Json::<TestInput>::schema();
1583 assert!(schema.is_some());
1584 let schema = schema.unwrap();
1585 assert!(schema.get("properties").is_some());
1586 }
1587
1588 #[test]
1589 fn test_rejection_into_error() {
1590 let rejection = Rejection::new("test error");
1591 let error: Error = rejection.into();
1592 assert!(error.to_string().contains("test error"));
1593 }
1594
1595 #[test]
1596 fn test_json_rejection() {
1597 let rejection = JsonRejection::new("missing field `name`");
1599 assert_eq!(rejection.message(), "missing field `name`");
1600 assert!(rejection.path().is_none());
1601 assert!(rejection.to_string().contains("Invalid input"));
1602
1603 let rejection = JsonRejection::with_path("expected string", "users[0].name");
1605 assert_eq!(rejection.message(), "expected string");
1606 assert_eq!(rejection.path(), Some("users[0].name"));
1607 assert!(rejection.to_string().contains("users[0].name"));
1608
1609 let error: Error = rejection.into();
1611 assert!(error.to_string().contains("users[0].name"));
1612 }
1613
1614 #[test]
1615 fn test_json_rejection_from_serde_error() {
1616 #[derive(Debug, serde::Deserialize)]
1618 struct TestStruct {
1619 #[allow(dead_code)]
1620 name: String,
1621 }
1622
1623 let result: std::result::Result<TestStruct, _> =
1624 serde_json::from_value(serde_json::json!({"count": 42}));
1625 assert!(result.is_err());
1626
1627 let rejection: JsonRejection = result.unwrap_err().into();
1628 assert!(rejection.message().contains("name"));
1629 }
1630
1631 #[test]
1632 fn test_extension_rejection() {
1633 let rejection = ExtensionRejection::not_found::<String>();
1635 assert!(rejection.type_name().contains("String"));
1636 assert!(rejection.to_string().contains("not found"));
1637
1638 let message = rejection.to_string();
1642 assert!(message.contains("with_state"));
1643 assert!(message.contains("with_extension"));
1644 assert!(message.contains(&format!(
1647 "bridge_extension::<{}>()",
1648 std::any::type_name::<String>()
1649 )));
1650
1651 let error: Error = rejection.into();
1653 assert!(error.to_string().contains("not found"));
1654 }
1655
1656 #[tokio::test]
1657 async fn test_tool_builder_extractor_handler() {
1658 use crate::ToolBuilder;
1659
1660 let state = Arc::new("shared-state".to_string());
1661
1662 let tool =
1663 ToolBuilder::new("test_extractor")
1664 .description("Test extractor handler")
1665 .extractor_handler(
1666 state,
1667 |State(state): State<Arc<String>>,
1668 ctx: Context,
1669 Json(input): Json<TestInput>| async move {
1670 assert!(!ctx.is_cancelled());
1671 Ok(CallToolResult::text(format!(
1672 "{}: {} - {}",
1673 state, input.name, input.count
1674 )))
1675 },
1676 )
1677 .build();
1678
1679 assert_eq!(tool.name, "test_extractor");
1680 assert_eq!(tool.description.as_deref(), Some("Test extractor handler"));
1681
1682 let result = tool
1684 .call(serde_json::json!({"name": "test", "count": 42}))
1685 .await;
1686 assert!(!result.is_error);
1687 }
1688
1689 #[tokio::test]
1690 #[allow(deprecated)]
1691 async fn test_tool_builder_extractor_handler_typed() {
1692 use crate::ToolBuilder;
1693
1694 let state = Arc::new("typed-state".to_string());
1695
1696 let tool = ToolBuilder::new("test_typed")
1697 .description("Test typed extractor handler")
1698 .extractor_handler_typed::<_, _, _, TestInput>(
1699 state,
1700 |State(state): State<Arc<String>>, Json(input): Json<TestInput>| async move {
1701 Ok(CallToolResult::text(format!(
1702 "{}: {} - {}",
1703 state, input.name, input.count
1704 )))
1705 },
1706 )
1707 .build();
1708
1709 assert_eq!(tool.name, "test_typed");
1710
1711 let def = tool.definition();
1713 let schema = def.input_schema;
1714 assert!(schema.get("properties").is_some());
1715
1716 let result = tool
1718 .call(serde_json::json!({"name": "world", "count": 99}))
1719 .await;
1720 assert!(!result.is_error);
1721 }
1722
1723 #[tokio::test]
1724 async fn test_extractor_handler_auto_schema() {
1725 use crate::ToolBuilder;
1726
1727 let state = Arc::new("auto-schema".to_string());
1728
1729 let tool = ToolBuilder::new("test_auto_schema")
1731 .description("Test auto schema detection")
1732 .extractor_handler(
1733 state,
1734 |State(state): State<Arc<String>>, Json(input): Json<TestInput>| async move {
1735 Ok(CallToolResult::text(format!(
1736 "{}: {} - {}",
1737 state, input.name, input.count
1738 )))
1739 },
1740 )
1741 .build();
1742
1743 let def = tool.definition();
1745 let schema = def.input_schema;
1746 assert!(
1747 schema.get("properties").is_some(),
1748 "Schema should have properties from TestInput, got: {}",
1749 schema
1750 );
1751 let props = schema.get("properties").unwrap();
1752 assert!(
1753 props.get("name").is_some(),
1754 "Schema should have 'name' property"
1755 );
1756 assert!(
1757 props.get("count").is_some(),
1758 "Schema should have 'count' property"
1759 );
1760
1761 let result = tool
1763 .call(serde_json::json!({"name": "world", "count": 99}))
1764 .await;
1765 assert!(!result.is_error);
1766 }
1767
1768 #[test]
1769 fn test_extractor_handler_no_json_fallback() {
1770 use crate::ToolBuilder;
1771
1772 let tool = ToolBuilder::new("test_no_json")
1774 .description("Test no json fallback")
1775 .extractor_handler((), |RawArgs(args): RawArgs| async move {
1776 Ok(CallToolResult::json(args))
1777 })
1778 .build();
1779
1780 let def = tool.definition();
1781 let schema = def.input_schema;
1782 assert_eq!(
1783 schema.get("type").and_then(|v| v.as_str()),
1784 Some("object"),
1785 "Schema should be generic object"
1786 );
1787 assert_eq!(
1788 schema.get("additionalProperties").and_then(|v| v.as_bool()),
1789 Some(true),
1790 "Schema should allow additional properties"
1791 );
1792 assert!(
1794 schema.get("properties").is_none(),
1795 "Generic schema should not have specific properties"
1796 );
1797 }
1798
1799 #[tokio::test]
1800 async fn test_extractor_handler_with_layer() {
1801 use crate::ToolBuilder;
1802 use std::time::Duration;
1803 use tower::timeout::TimeoutLayer;
1804
1805 let state = Arc::new("layered".to_string());
1806
1807 let tool = ToolBuilder::new("test_extractor_layer")
1808 .description("Test extractor handler with layer")
1809 .extractor_handler(
1810 state,
1811 |State(s): State<Arc<String>>, Json(input): Json<TestInput>| async move {
1812 Ok(CallToolResult::text(format!("{}: {}", s, input.name)))
1813 },
1814 )
1815 .layer(TimeoutLayer::new(Duration::from_secs(5)))
1816 .build();
1817
1818 let result = tool
1820 .call(serde_json::json!({"name": "test", "count": 1}))
1821 .await;
1822 assert!(!result.is_error);
1823 assert_eq!(result.first_text().unwrap(), "layered: test");
1824
1825 let def = tool.definition();
1827 let schema = def.input_schema;
1828 assert!(
1829 schema.get("properties").is_some(),
1830 "Schema should have properties even with layer"
1831 );
1832 }
1833
1834 #[tokio::test]
1835 async fn test_extractor_handler_with_timeout_layer() {
1836 use crate::ToolBuilder;
1837 use std::time::Duration;
1838 use tower::timeout::TimeoutLayer;
1839
1840 let tool = ToolBuilder::new("test_extractor_timeout")
1841 .description("Test extractor handler timeout")
1842 .extractor_handler((), |Json(input): Json<TestInput>| async move {
1843 tokio::time::sleep(Duration::from_millis(200)).await;
1844 Ok(CallToolResult::text(input.name.to_string()))
1845 })
1846 .layer(TimeoutLayer::new(Duration::from_millis(50)))
1847 .build();
1848
1849 let result = tool
1851 .call(serde_json::json!({"name": "slow", "count": 1}))
1852 .await;
1853 assert!(result.is_error);
1854 let msg = result.first_text().unwrap().to_lowercase();
1855 assert!(
1856 msg.contains("timed out") || msg.contains("timeout") || msg.contains("elapsed"),
1857 "Expected timeout error, got: {}",
1858 msg
1859 );
1860 }
1861
1862 #[tokio::test]
1863 async fn test_extractor_handler_with_multiple_layers() {
1864 use crate::ToolBuilder;
1865 use std::time::Duration;
1866 use tower::limit::ConcurrencyLimitLayer;
1867 use tower::timeout::TimeoutLayer;
1868
1869 let state = Arc::new("multi".to_string());
1870
1871 let tool = ToolBuilder::new("test_multi_layer")
1872 .description("Test multiple layers")
1873 .extractor_handler(
1874 state,
1875 |State(s): State<Arc<String>>, Json(input): Json<TestInput>| async move {
1876 Ok(CallToolResult::text(format!("{}: {}", s, input.name)))
1877 },
1878 )
1879 .layer(TimeoutLayer::new(Duration::from_secs(5)))
1880 .layer(ConcurrencyLimitLayer::new(10))
1881 .build();
1882
1883 let result = tool
1884 .call(serde_json::json!({"name": "test", "count": 1}))
1885 .await;
1886 assert!(!result.is_error);
1887 assert_eq!(result.first_text().unwrap(), "multi: test");
1888 }
1889}