Skip to main content

tools_kit/
handler.rs

1use std::{future::Future, pin::Pin};
2
3use crate::{
4    ToolError,
5    execution::{ToolHandlerInput, ToolHandlerOutput},
6    sources::{local, mcp},
7};
8
9/// Public implementation contract for one unique resumable tool handler.
10/// One handler owns one invocation. Start with `Invoke`, resume only the current
11/// interaction with its exact id, and use `Cancel` for cancellation. A completed
12/// result is terminal; a model-visible failure is a completed result with `is_error`.
13/// Import [`ToolHandlerBackend`] to call behavior on the supplied handle.
14///
15/// See [`crate::local::ToolRegistration`] for a complete example.
16///
17pub trait ToolHandlerBackend: Send + 'static {
18    /// Advances the unique handler with an invocation, interaction response,
19    /// or cancellation request.
20    ///
21    /// # Errors
22    ///
23    /// Returns [`ToolError`] for invalid transitions, arguments or interaction replies, unsupported behavior, cancellation, closure, transport or protocol failure. An accepted tool-authored failure is returned as a completed error result.
24    ///
25    /// See [`crate::local::ToolRegistration`] for a complete example.
26    ///
27    fn step(
28        &mut self,
29        input: ToolHandlerInput,
30    ) -> impl Future<Output = Result<ToolHandlerOutput, ToolError>> + Send + '_;
31}
32
33/// Opaque resumable handler for one selected executable tool.
34/// One handler owns one invocation. Start with `Invoke`, resume only the current
35/// interaction with its exact id, and use `Cancel` for cancellation. A completed
36/// result is terminal; a model-visible failure is a completed result with `is_error`.
37/// Import [`ToolHandlerBackend`] to call behavior on the supplied handle.
38///
39/// See [`crate::local::ToolRegistration`] for a complete example.
40///
41pub struct ToolHandler {
42    inner: InnerToolHandler,
43}
44
45enum InnerToolHandler {
46    Local(local::handler::LocalToolHandler),
47    Mcp(mcp::handler::McpToolHandler),
48    Extension(Box<dyn ErasedToolHandlerBackend>),
49}
50
51impl ToolHandler {
52    pub(crate) fn from_local(handler: local::handler::LocalToolHandler) -> Self {
53        Self {
54            inner: InnerToolHandler::Local(handler),
55        }
56    }
57
58    pub(crate) fn from_mcp(handler: mcp::handler::McpToolHandler) -> Self {
59        Self {
60            inner: InnerToolHandler::Mcp(handler),
61        }
62    }
63
64    pub(crate) fn from_extension<B: ToolHandlerBackend>(backend: B) -> Self {
65        Self {
66            inner: InnerToolHandler::Extension(Box::new(backend)),
67        }
68    }
69}
70
71impl std::fmt::Debug for ToolHandler {
72    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73        formatter
74            .debug_struct("ToolHandler")
75            .finish_non_exhaustive()
76    }
77}
78
79trait ErasedToolHandlerBackend: Send {
80    fn step_erased(
81        &mut self,
82        input: ToolHandlerInput,
83    ) -> Pin<Box<dyn Future<Output = Result<ToolHandlerOutput, ToolError>> + Send + '_>>;
84}
85
86impl<B: ToolHandlerBackend> ErasedToolHandlerBackend for B {
87    fn step_erased(
88        &mut self,
89        input: ToolHandlerInput,
90    ) -> Pin<Box<dyn Future<Output = Result<ToolHandlerOutput, ToolError>> + Send + '_>> {
91        Box::pin(self.step(input))
92    }
93}
94
95impl ToolHandlerBackend for InnerToolHandler {
96    async fn step(&mut self, input: ToolHandlerInput) -> Result<ToolHandlerOutput, ToolError> {
97        match self {
98            Self::Local(handler) => handler.step(input).await,
99            Self::Mcp(handler) => handler.step(input).await,
100            Self::Extension(handler) => handler.step_erased(input).await,
101        }
102    }
103}
104
105impl ToolHandlerBackend for ToolHandler {
106    fn step(
107        &mut self,
108        input: ToolHandlerInput,
109    ) -> impl Future<Output = Result<ToolHandlerOutput, ToolError>> + Send + '_ {
110        self.inner.step(input)
111    }
112}