Skip to main content

lspkit_mcp/
adapter.rs

1//! Adapter that mounts an [`lspkit::EngineApi`] implementation as an MCP server.
2//!
3//! The adapter does not leak `rmcp::*` types into its public surface; every
4//! request/response shape is wrapped behind newtypes defined in this crate.
5
6use std::sync::Arc;
7
8use async_trait::async_trait;
9use lspkit::EngineApi;
10use serde_json::Value;
11
12use crate::tools::{ToolDescription, ToolRegistry};
13
14/// Result of invoking a tool.
15#[non_exhaustive]
16#[derive(Debug, Clone)]
17pub struct ToolInvocation {
18    /// Tool name.
19    pub name: String,
20    /// Tool-defined output, JSON-encoded.
21    pub output: Value,
22}
23
24/// Errors from the adapter.
25#[non_exhaustive]
26#[derive(Debug, thiserror::Error)]
27pub enum AdapterError {
28    /// No tool registered with the given name.
29    #[error("tool not found: {0}")]
30    UnknownTool(String),
31    /// The tool's input did not match its schema.
32    #[error("invalid input for {tool}: {message}")]
33    InvalidInput {
34        /// Tool name.
35        tool: String,
36        /// Detail.
37        message: String,
38    },
39    /// The engine returned an error while servicing the tool.
40    #[error("engine error: {0}")]
41    Engine(String),
42}
43
44/// A tool handler: receives JSON input and produces JSON output via the engine.
45#[async_trait]
46pub trait ToolHandler<E: EngineApi>: Send + Sync + 'static {
47    /// Invoke the tool.
48    async fn invoke(&self, engine: &E, input: Value) -> Result<Value, AdapterError>;
49}
50
51/// Adapter binding an engine and its tool handlers.
52#[derive(Clone)]
53pub struct Adapter<E: EngineApi> {
54    engine: Arc<E>,
55    registry: ToolRegistry,
56    handlers: Vec<(String, Arc<dyn ToolHandler<E>>)>,
57}
58
59impl<E: EngineApi> Adapter<E> {
60    /// New adapter wrapping `engine`.
61    #[must_use]
62    pub fn new(engine: Arc<E>) -> Self {
63        Self {
64            engine,
65            registry: ToolRegistry::new(),
66            handlers: Vec::new(),
67        }
68    }
69
70    /// Register a tool with its description and handler.
71    pub fn register(&mut self, description: ToolDescription, handler: Arc<dyn ToolHandler<E>>) {
72        let name = description.name.clone();
73        self.registry.register(description);
74        if let Some(slot) = self.handlers.iter_mut().find(|(n, _)| n == &name) {
75            slot.1 = handler;
76        } else {
77            self.handlers.push((name, handler));
78        }
79    }
80
81    /// Snapshot registered tool descriptions.
82    #[must_use]
83    pub fn tools(&self) -> &[ToolDescription] {
84        self.registry.list()
85    }
86
87    /// Invoke a tool by name.
88    ///
89    /// # Errors
90    /// Returns [`AdapterError::UnknownTool`] when no handler is registered or
91    /// [`AdapterError::Engine`] when the handler propagates an engine error.
92    pub async fn invoke(&self, name: &str, input: Value) -> Result<ToolInvocation, AdapterError> {
93        let handler = self
94            .handlers
95            .iter()
96            .find(|(n, _)| n == name)
97            .map(|(_, h)| h.clone())
98            .ok_or_else(|| AdapterError::UnknownTool(name.to_owned()))?;
99        let output = handler.invoke(self.engine.as_ref(), input).await?;
100        Ok(ToolInvocation {
101            name: name.to_owned(),
102            output,
103        })
104    }
105}
106
107impl<E: EngineApi> std::fmt::Debug for Adapter<E> {
108    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
109        f.debug_struct("Adapter")
110            .field("tools", &self.registry.list().len())
111            .finish_non_exhaustive()
112    }
113}