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 annotations: None,
73 meta: None,
74 ..Tool::default()
75 };
76
77 FunctionToolHandler::new(tool_def, handler)
78}
79
80pub fn tool_with_schema<F, Fut>(
92 name: impl Into<String>,
93 description: impl Into<String>,
94 schema: ToolInputSchema,
95 handler: F,
96) -> impl ToolHandler
97where
98 F: Fn(CallToolRequest, RequestContext) -> Fut + Send + Sync + 'static,
99 Fut: std::future::Future<Output = ServerResult<CallToolResult>> + Send + 'static,
100{
101 let name = name.into();
102 let description = description.into();
103
104 let tool_def = Tool {
105 name: name.clone(),
106 title: Some(name),
107 description: Some(description),
108 input_schema: schema,
109 output_schema: None,
110 annotations: None,
111 meta: None,
112 ..Tool::default()
113 };
114
115 FunctionToolHandler::new(tool_def, handler)
116}
117
118pub struct FunctionPromptHandler {
120 prompt: Prompt,
121 handler: Box<
122 dyn Fn(
123 GetPromptRequest,
124 RequestContext,
125 ) -> futures::future::BoxFuture<'static, ServerResult<GetPromptResult>>
126 + Send
127 + Sync,
128 >,
129}
130
131impl std::fmt::Debug for FunctionPromptHandler {
132 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
133 f.debug_struct("FunctionPromptHandler")
134 .field("prompt", &self.prompt)
135 .finish()
136 }
137}
138
139impl FunctionPromptHandler {
140 pub fn new<F, Fut>(prompt: Prompt, handler: F) -> Self
142 where
143 F: Fn(GetPromptRequest, RequestContext) -> Fut + Send + Sync + 'static,
144 Fut: std::future::Future<Output = ServerResult<GetPromptResult>> + Send + 'static,
145 {
146 Self {
147 prompt,
148 handler: Box::new(move |req, ctx| Box::pin(handler(req, ctx))),
149 }
150 }
151}
152
153#[async_trait::async_trait]
154impl PromptHandler for FunctionPromptHandler {
155 async fn handle(
156 &self,
157 request: GetPromptRequest,
158 ctx: RequestContext,
159 ) -> ServerResult<GetPromptResult> {
160 (self.handler)(request, ctx).await
161 }
162
163 fn prompt_definition(&self) -> Prompt {
164 self.prompt.clone()
165 }
166}
167
168pub fn prompt<F, Fut>(
176 name: impl Into<String>,
177 description: impl Into<String>,
178 handler: F,
179) -> impl PromptHandler
180where
181 F: Fn(GetPromptRequest, RequestContext) -> Fut + Send + Sync + 'static,
182 Fut: std::future::Future<Output = ServerResult<GetPromptResult>> + Send + 'static,
183{
184 let name = name.into();
185 let description = description.into();
186
187 let prompt_def = Prompt {
188 name: name.clone(),
189 title: Some(name),
190 description: Some(description),
191 arguments: None,
192 meta: None,
193 };
194
195 FunctionPromptHandler::new(prompt_def, handler)
196}
197
198pub struct FunctionResourceHandler {
200 resource: Resource,
201 handler: Box<
202 dyn Fn(
203 ReadResourceRequest,
204 RequestContext,
205 ) -> futures::future::BoxFuture<'static, ServerResult<ReadResourceResult>>
206 + Send
207 + Sync,
208 >,
209}
210
211impl std::fmt::Debug for FunctionResourceHandler {
212 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
213 f.debug_struct("FunctionResourceHandler")
214 .field("resource", &self.resource)
215 .finish()
216 }
217}
218
219impl FunctionResourceHandler {
220 pub fn new<F, Fut>(resource: Resource, handler: F) -> Self
222 where
223 F: Fn(ReadResourceRequest, RequestContext) -> Fut + Send + Sync + 'static,
224 Fut: std::future::Future<Output = ServerResult<ReadResourceResult>> + Send + 'static,
225 {
226 Self {
227 resource,
228 handler: Box::new(move |req, ctx| Box::pin(handler(req, ctx))),
229 }
230 }
231}
232
233#[async_trait::async_trait]
234impl ResourceHandler for FunctionResourceHandler {
235 async fn handle(
236 &self,
237 request: ReadResourceRequest,
238 ctx: RequestContext,
239 ) -> ServerResult<ReadResourceResult> {
240 (self.handler)(request, ctx).await
241 }
242
243 fn resource_definition(&self) -> Resource {
244 self.resource.clone()
245 }
246
247 async fn exists(&self, _uri: &str) -> bool {
248 true }
250}
251
252pub fn resource<F, Fut>(
260 uri: impl Into<String>,
261 name: impl Into<String>,
262 handler: F,
263) -> impl ResourceHandler
264where
265 F: Fn(ReadResourceRequest, RequestContext) -> Fut + Send + Sync + 'static,
266 Fut: std::future::Future<Output = ServerResult<ReadResourceResult>> + Send + 'static,
267{
268 let uri = uri.into();
269 let name = name.into();
270
271 let resource_def = Resource {
272 name: name.clone(),
273 title: Some(name),
274 uri,
275 description: None,
276 mime_type: Some("text/plain".to_string()),
277 annotations: None,
278 size: None,
279 meta: None,
280 };
281
282 FunctionResourceHandler::new(resource_def, handler)
283}