turbomcp_server/handlers/
utils.rs

1//! Utility functions for creating handlers from closures
2//!
3//! This module provides convenience functions for creating handler implementations
4//! from simple closures. These are primarily used by the `#[server]` macro but
5//! can also be used directly for quick handler creation.
6
7use 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
18/// Create a tool handler from a closure
19///
20/// This is a convenience function for creating simple tool handlers without
21/// manually constructing Tool definitions.
22///
23/// # Arguments
24///
25/// * `name` - Tool name
26/// * `description` - Tool description
27/// * `handler` - Async closure that handles tool calls
28///
29/// # Examples
30///
31/// ```rust,no_run
32/// use turbomcp_server::handlers::utils::tool;
33/// use turbomcp_protocol::RequestContext;
34/// use turbomcp_protocol::types::{CallToolRequest, CallToolResult, Content, TextContent};
35///
36/// let my_tool = tool("echo", "Echoes back the input", |req: CallToolRequest, _ctx: RequestContext| async move {
37///     Ok(CallToolResult {
38///         content: vec![Content::Text(TextContent {
39///             text: format!("Echo: {:?}", req.arguments),
40///             annotations: None,
41///             meta: None,
42///         })],
43///         is_error: None,
44///         structured_content: None,
45///         _meta: None,
46///     })
47/// });
48/// ```
49pub 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
81/// Create a tool handler with a custom schema
82///
83/// This allows specifying the input schema for the tool, which is used by
84/// the `#[server]` macro to provide type-safe tool definitions.
85///
86/// # Arguments
87///
88/// * `name` - Tool name
89/// * `description` - Tool description
90/// * `schema` - Input schema for the tool
91/// * `handler` - Async closure that handles tool calls
92pub 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
120/// Function-based prompt handler
121pub 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    /// Create new prompt handler
143    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
170/// Create a prompt handler from a closure
171///
172/// # Arguments
173///
174/// * `name` - Prompt name
175/// * `description` - Prompt description
176/// * `handler` - Async closure that handles prompt requests
177pub 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
200/// Function-based resource handler
201pub 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    /// Create new resource handler
223    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 // Default implementation
251    }
252}
253
254/// Create a resource handler from a closure
255///
256/// # Arguments
257///
258/// * `uri` - Resource URI
259/// * `name` - Resource name
260/// * `handler` - Async closure that handles resource read requests
261pub 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}