Skip to main content

mcpkit_server/
dispatch.rs

1//! Object-safe dispatch layer.
2//!
3//! The public handler traits ([`ToolHandler`] et al.) return `impl Future`
4//! (RPITIT) and so are not object-safe, which previously forced request routing
5//! into a combinatorial per-handler-combination typestate macro. The `Dyn*`
6//! traits here box the handler futures so a single router impl can dispatch any
7//! registered handler, and the `*Slot` traits expose "the registered handler,
8//! or `None`" uniformly across the typestate slots (`Registered<H>` /
9//! `NotRegistered`).
10//!
11//! Adding a dispatched capability is then one `Dyn`/`Slot` pair plus one match
12//! arm — no combinatorial growth.
13
14use crate::builder::{NotRegistered, Registered};
15use crate::context::Context;
16use crate::handler::{CompletionHandler, PromptHandler, ResourceHandler, TaskHandler, ToolHandler};
17use mcpkit_core::error::McpError;
18use mcpkit_core::types::{
19    CancelTaskResult, CompleteRequest, CompleteResult, GetPromptResult, GetTaskResult,
20    ListTasksResult, Object, Prompt, Resource, ResourceContents, ResourceTemplate, TaskId, Tool,
21    ToolOutput,
22};
23use serde_json::Value;
24use std::future::Future;
25use std::pin::Pin;
26
27/// A boxed, `Send` future borrowing for `'a`.
28type BoxFut<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
29
30// ============================================================================
31// Object-safe handler traits (boxed futures), blanket-impl'd over the public
32// RPITIT traits.
33// ============================================================================
34
35/// Object-safe form of [`ToolHandler`] (only the dispatched methods).
36pub trait DynToolHandler: Send + Sync {
37    /// See [`ToolHandler::list_tools`].
38    fn list_tools<'a>(&'a self, ctx: &'a Context<'_>) -> BoxFut<'a, Result<Vec<Tool>, McpError>>;
39    /// See [`ToolHandler::call_tool`].
40    fn call_tool<'a>(
41        &'a self,
42        name: &'a str,
43        args: Object,
44        ctx: &'a Context<'_>,
45    ) -> BoxFut<'a, Result<ToolOutput, McpError>>;
46}
47
48impl<T: ToolHandler> DynToolHandler for T {
49    fn list_tools<'a>(&'a self, ctx: &'a Context<'_>) -> BoxFut<'a, Result<Vec<Tool>, McpError>> {
50        Box::pin(ToolHandler::list_tools(self, ctx))
51    }
52    fn call_tool<'a>(
53        &'a self,
54        name: &'a str,
55        args: Object,
56        ctx: &'a Context<'_>,
57    ) -> BoxFut<'a, Result<ToolOutput, McpError>> {
58        Box::pin(ToolHandler::call_tool(self, name, args, ctx))
59    }
60}
61
62/// Object-safe form of [`ResourceHandler`] (only the dispatched methods).
63pub trait DynResourceHandler: Send + Sync {
64    /// See [`ResourceHandler::list_resources`].
65    fn list_resources<'a>(
66        &'a self,
67        ctx: &'a Context<'_>,
68    ) -> BoxFut<'a, Result<Vec<Resource>, McpError>>;
69    /// See [`ResourceHandler::list_resource_templates`].
70    fn list_resource_templates<'a>(
71        &'a self,
72        ctx: &'a Context<'_>,
73    ) -> BoxFut<'a, Result<Vec<ResourceTemplate>, McpError>>;
74    /// See [`ResourceHandler::read_resource`].
75    fn read_resource<'a>(
76        &'a self,
77        uri: &'a str,
78        ctx: &'a Context<'_>,
79    ) -> BoxFut<'a, Result<Vec<ResourceContents>, McpError>>;
80    /// See [`ResourceHandler::subscribe`].
81    fn subscribe<'a>(
82        &'a self,
83        uri: &'a str,
84        ctx: &'a Context<'_>,
85    ) -> BoxFut<'a, Result<bool, McpError>>;
86    /// See [`ResourceHandler::unsubscribe`].
87    fn unsubscribe<'a>(
88        &'a self,
89        uri: &'a str,
90        ctx: &'a Context<'_>,
91    ) -> BoxFut<'a, Result<bool, McpError>>;
92}
93
94impl<R: ResourceHandler> DynResourceHandler for R {
95    fn list_resources<'a>(
96        &'a self,
97        ctx: &'a Context<'_>,
98    ) -> BoxFut<'a, Result<Vec<Resource>, McpError>> {
99        Box::pin(ResourceHandler::list_resources(self, ctx))
100    }
101    fn list_resource_templates<'a>(
102        &'a self,
103        ctx: &'a Context<'_>,
104    ) -> BoxFut<'a, Result<Vec<ResourceTemplate>, McpError>> {
105        Box::pin(ResourceHandler::list_resource_templates(self, ctx))
106    }
107    fn read_resource<'a>(
108        &'a self,
109        uri: &'a str,
110        ctx: &'a Context<'_>,
111    ) -> BoxFut<'a, Result<Vec<ResourceContents>, McpError>> {
112        Box::pin(ResourceHandler::read_resource(self, uri, ctx))
113    }
114    fn subscribe<'a>(
115        &'a self,
116        uri: &'a str,
117        ctx: &'a Context<'_>,
118    ) -> BoxFut<'a, Result<bool, McpError>> {
119        Box::pin(ResourceHandler::subscribe(self, uri, ctx))
120    }
121    fn unsubscribe<'a>(
122        &'a self,
123        uri: &'a str,
124        ctx: &'a Context<'_>,
125    ) -> BoxFut<'a, Result<bool, McpError>> {
126        Box::pin(ResourceHandler::unsubscribe(self, uri, ctx))
127    }
128}
129
130/// Object-safe form of [`PromptHandler`] (only the dispatched methods).
131pub trait DynPromptHandler: Send + Sync {
132    /// See [`PromptHandler::list_prompts`].
133    fn list_prompts<'a>(
134        &'a self,
135        ctx: &'a Context<'_>,
136    ) -> BoxFut<'a, Result<Vec<Prompt>, McpError>>;
137    /// See [`PromptHandler::get_prompt`].
138    fn get_prompt<'a>(
139        &'a self,
140        name: &'a str,
141        args: Option<serde_json::Map<String, Value>>,
142        ctx: &'a Context<'_>,
143    ) -> BoxFut<'a, Result<GetPromptResult, McpError>>;
144}
145
146impl<P: PromptHandler> DynPromptHandler for P {
147    fn list_prompts<'a>(
148        &'a self,
149        ctx: &'a Context<'_>,
150    ) -> BoxFut<'a, Result<Vec<Prompt>, McpError>> {
151        Box::pin(PromptHandler::list_prompts(self, ctx))
152    }
153    fn get_prompt<'a>(
154        &'a self,
155        name: &'a str,
156        args: Option<serde_json::Map<String, Value>>,
157        ctx: &'a Context<'_>,
158    ) -> BoxFut<'a, Result<GetPromptResult, McpError>> {
159        Box::pin(PromptHandler::get_prompt(self, name, args, ctx))
160    }
161}
162
163/// Object-safe form of [`TaskHandler`].
164pub trait DynTaskHandler: Send + Sync {
165    /// See [`TaskHandler::list_tasks`].
166    fn list_tasks<'a>(
167        &'a self,
168        ctx: &'a Context<'_>,
169    ) -> BoxFut<'a, Result<ListTasksResult, McpError>>;
170    /// See [`TaskHandler::get_task`].
171    fn get_task<'a>(
172        &'a self,
173        id: &'a TaskId,
174        ctx: &'a Context<'_>,
175    ) -> BoxFut<'a, Result<Option<GetTaskResult>, McpError>>;
176    /// See [`TaskHandler::cancel_task`].
177    fn cancel_task<'a>(
178        &'a self,
179        id: &'a TaskId,
180        ctx: &'a Context<'_>,
181    ) -> BoxFut<'a, Result<Option<CancelTaskResult>, McpError>>;
182}
183
184impl<K: TaskHandler> DynTaskHandler for K {
185    fn list_tasks<'a>(
186        &'a self,
187        ctx: &'a Context<'_>,
188    ) -> BoxFut<'a, Result<ListTasksResult, McpError>> {
189        Box::pin(TaskHandler::list_tasks(self, ctx))
190    }
191    fn get_task<'a>(
192        &'a self,
193        id: &'a TaskId,
194        ctx: &'a Context<'_>,
195    ) -> BoxFut<'a, Result<Option<GetTaskResult>, McpError>> {
196        Box::pin(TaskHandler::get_task(self, id, ctx))
197    }
198    fn cancel_task<'a>(
199        &'a self,
200        id: &'a TaskId,
201        ctx: &'a Context<'_>,
202    ) -> BoxFut<'a, Result<Option<CancelTaskResult>, McpError>> {
203        Box::pin(TaskHandler::cancel_task(self, id, ctx))
204    }
205}
206
207/// Object-safe form of [`CompletionHandler`].
208pub trait DynCompletionHandler: Send + Sync {
209    /// See [`CompletionHandler::complete`].
210    fn complete<'a>(
211        &'a self,
212        request: &'a CompleteRequest,
213        ctx: &'a Context<'_>,
214    ) -> BoxFut<'a, Result<CompleteResult, McpError>>;
215}
216
217impl<C: CompletionHandler> DynCompletionHandler for C {
218    fn complete<'a>(
219        &'a self,
220        request: &'a CompleteRequest,
221        ctx: &'a Context<'_>,
222    ) -> BoxFut<'a, Result<CompleteResult, McpError>> {
223        Box::pin(CompletionHandler::complete(self, request, ctx))
224    }
225}
226
227// ============================================================================
228// Typestate slots: expose `Option<&dyn Dyn*Handler>` for both `Registered<H>`
229// and `NotRegistered`, so one router impl can be generic over the slots.
230// ============================================================================
231
232/// A tool-handler slot.
233pub trait ToolSlot: Send + Sync {
234    /// The registered tool handler, or `None`.
235    fn as_tool_handler(&self) -> Option<&dyn DynToolHandler>;
236}
237impl ToolSlot for NotRegistered {
238    fn as_tool_handler(&self) -> Option<&dyn DynToolHandler> {
239        None
240    }
241}
242impl<T: ToolHandler> ToolSlot for Registered<T> {
243    fn as_tool_handler(&self) -> Option<&dyn DynToolHandler> {
244        Some(&self.0)
245    }
246}
247
248/// A resource-handler slot.
249pub trait ResourceSlot: Send + Sync {
250    /// The registered resource handler, or `None`.
251    fn as_resource_handler(&self) -> Option<&dyn DynResourceHandler>;
252}
253impl ResourceSlot for NotRegistered {
254    fn as_resource_handler(&self) -> Option<&dyn DynResourceHandler> {
255        None
256    }
257}
258impl<R: ResourceHandler> ResourceSlot for Registered<R> {
259    fn as_resource_handler(&self) -> Option<&dyn DynResourceHandler> {
260        Some(&self.0)
261    }
262}
263
264/// A prompt-handler slot.
265pub trait PromptSlot: Send + Sync {
266    /// The registered prompt handler, or `None`.
267    fn as_prompt_handler(&self) -> Option<&dyn DynPromptHandler>;
268}
269impl PromptSlot for NotRegistered {
270    fn as_prompt_handler(&self) -> Option<&dyn DynPromptHandler> {
271        None
272    }
273}
274impl<P: PromptHandler> PromptSlot for Registered<P> {
275    fn as_prompt_handler(&self) -> Option<&dyn DynPromptHandler> {
276        Some(&self.0)
277    }
278}
279
280/// A task-handler slot.
281pub trait TaskSlot: Send + Sync {
282    /// The registered task handler, or `None`.
283    fn as_task_handler(&self) -> Option<&dyn DynTaskHandler>;
284}
285impl TaskSlot for NotRegistered {
286    fn as_task_handler(&self) -> Option<&dyn DynTaskHandler> {
287        None
288    }
289}
290impl<K: TaskHandler> TaskSlot for Registered<K> {
291    fn as_task_handler(&self) -> Option<&dyn DynTaskHandler> {
292        Some(&self.0)
293    }
294}