turbomcp_server/handlers/
utils.rs1use std::collections::HashMap;
8use turbomcp_protocol::RequestContext;
9use turbomcp_protocol::types::{
10 CallToolRequest, CallToolResult, GetPromptRequest, GetPromptResult, Prompt,
11 ReadResourceRequest, ReadResourceResult, Resource, Tool, ToolInputSchema,
12};
13
14use crate::ServerResult;
15use crate::handlers::implementations::FunctionToolHandler;
16use crate::handlers::traits::{PromptHandler, ResourceHandler, ToolHandler};
17
18pub fn tool<F, Fut>(
50 name: impl Into<String>,
51 description: impl Into<String>,
52 handler: F,
53) -> impl ToolHandler
54where
55 F: Fn(CallToolRequest, RequestContext) -> Fut + Send + Sync + 'static,
56 Fut: std::future::Future<Output = ServerResult<CallToolResult>> + Send + 'static,
57{
58 let name = name.into();
59 let description = description.into();
60
61 let tool_def = Tool {
62 name: name.clone(),
63 title: Some(name),
64 description: Some(description),
65 input_schema: ToolInputSchema {
66 schema_type: "object".to_string(),
67 properties: Some(HashMap::new()),
68 required: None,
69 additional_properties: None,
70 },
71 output_schema: None,
72 execution: None,
73 annotations: None,
74 meta: None,
75 ..Tool::default()
76 };
77
78 FunctionToolHandler::new(tool_def, handler)
79}
80
81pub fn tool_with_schema<F, Fut>(
93 name: impl Into<String>,
94 description: impl Into<String>,
95 schema: ToolInputSchema,
96 handler: F,
97) -> impl ToolHandler
98where
99 F: Fn(CallToolRequest, RequestContext) -> Fut + Send + Sync + 'static,
100 Fut: std::future::Future<Output = ServerResult<CallToolResult>> + Send + 'static,
101{
102 let name = name.into();
103 let description = description.into();
104
105 let tool_def = Tool {
106 name: name.clone(),
107 title: Some(name),
108 description: Some(description),
109 input_schema: schema,
110 output_schema: None,
111 execution: None,
112 annotations: None,
113 meta: None,
114 ..Tool::default()
115 };
116
117 FunctionToolHandler::new(tool_def, handler)
118}
119
120pub struct FunctionPromptHandler {
122 prompt: Prompt,
123 handler: Box<
124 dyn Fn(
125 GetPromptRequest,
126 RequestContext,
127 ) -> futures::future::BoxFuture<'static, ServerResult<GetPromptResult>>
128 + Send
129 + Sync,
130 >,
131}
132
133impl std::fmt::Debug for FunctionPromptHandler {
134 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
135 f.debug_struct("FunctionPromptHandler")
136 .field("prompt", &self.prompt)
137 .finish()
138 }
139}
140
141impl FunctionPromptHandler {
142 pub fn new<F, Fut>(prompt: Prompt, handler: F) -> Self
144 where
145 F: Fn(GetPromptRequest, RequestContext) -> Fut + Send + Sync + 'static,
146 Fut: std::future::Future<Output = ServerResult<GetPromptResult>> + Send + 'static,
147 {
148 Self {
149 prompt,
150 handler: Box::new(move |req, ctx| Box::pin(handler(req, ctx))),
151 }
152 }
153}
154
155#[async_trait::async_trait]
156impl PromptHandler for FunctionPromptHandler {
157 async fn handle(
158 &self,
159 request: GetPromptRequest,
160 ctx: RequestContext,
161 ) -> ServerResult<GetPromptResult> {
162 (self.handler)(request, ctx).await
163 }
164
165 fn prompt_definition(&self) -> Prompt {
166 self.prompt.clone()
167 }
168}
169
170pub fn prompt<F, Fut>(
178 name: impl Into<String>,
179 description: impl Into<String>,
180 handler: F,
181) -> impl PromptHandler
182where
183 F: Fn(GetPromptRequest, RequestContext) -> Fut + Send + Sync + 'static,
184 Fut: std::future::Future<Output = ServerResult<GetPromptResult>> + Send + 'static,
185{
186 let name = name.into();
187 let description = description.into();
188
189 let prompt_def = Prompt {
190 name: name.clone(),
191 title: Some(name),
192 description: Some(description),
193 arguments: None,
194 meta: None,
195 };
196
197 FunctionPromptHandler::new(prompt_def, handler)
198}
199
200pub struct FunctionResourceHandler {
202 resource: Resource,
203 handler: Box<
204 dyn Fn(
205 ReadResourceRequest,
206 RequestContext,
207 ) -> futures::future::BoxFuture<'static, ServerResult<ReadResourceResult>>
208 + Send
209 + Sync,
210 >,
211}
212
213impl std::fmt::Debug for FunctionResourceHandler {
214 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
215 f.debug_struct("FunctionResourceHandler")
216 .field("resource", &self.resource)
217 .finish()
218 }
219}
220
221impl FunctionResourceHandler {
222 pub fn new<F, Fut>(resource: Resource, handler: F) -> Self
224 where
225 F: Fn(ReadResourceRequest, RequestContext) -> Fut + Send + Sync + 'static,
226 Fut: std::future::Future<Output = ServerResult<ReadResourceResult>> + Send + 'static,
227 {
228 Self {
229 resource,
230 handler: Box::new(move |req, ctx| Box::pin(handler(req, ctx))),
231 }
232 }
233}
234
235#[async_trait::async_trait]
236impl ResourceHandler for FunctionResourceHandler {
237 async fn handle(
238 &self,
239 request: ReadResourceRequest,
240 ctx: RequestContext,
241 ) -> ServerResult<ReadResourceResult> {
242 (self.handler)(request, ctx).await
243 }
244
245 fn resource_definition(&self) -> Resource {
246 self.resource.clone()
247 }
248
249 async fn exists(&self, _uri: &str) -> bool {
250 true }
252}
253
254pub fn resource<F, Fut>(
262 uri: impl Into<String>,
263 name: impl Into<String>,
264 handler: F,
265) -> impl ResourceHandler
266where
267 F: Fn(ReadResourceRequest, RequestContext) -> Fut + Send + Sync + 'static,
268 Fut: std::future::Future<Output = ServerResult<ReadResourceResult>> + Send + 'static,
269{
270 let uri = uri.into();
271 let name = name.into();
272
273 let resource_def = Resource {
274 name: name.clone(),
275 title: Some(name),
276 uri,
277 description: None,
278 mime_type: Some("text/plain".to_string()),
279 annotations: None,
280 size: None,
281 meta: None,
282 };
283
284 FunctionResourceHandler::new(resource_def, handler)
285}