elicitation/plugin/mod.rs
1//! Type-erased plugin interface for the elicitation tool registry.
2//!
3//! Each shadow crate (e.g., `elicit_reqwest`) provides a `Plugin` struct that
4//! implements [`ElicitPlugin`]. The [`PluginRegistry`](crate::PluginRegistry)
5//! collects these and serves them as a single MCP server.
6//!
7//! # Implementing a plugin
8//!
9//! **Simple path** — implement [`DescriptorPlugin`] and expose a slice of
10//! [`ToolDescriptor`]s built with [`make_descriptor`]. The blanket impl
11//! provides [`ElicitPlugin`] for free.
12//!
13//! **Full control** — implement [`ElicitPlugin`] directly.
14
15pub mod context;
16pub mod descriptor;
17pub mod descriptor_plugin;
18
19pub use context::PluginContext;
20pub use descriptor::{
21 PluginToolRegistration, ToolDescriptor, make_descriptor, make_descriptor_ctx,
22};
23pub use descriptor_plugin::DescriptorPlugin;
24
25use std::borrow::Cow;
26use std::sync::Arc;
27
28use futures::future::BoxFuture;
29use rmcp::{
30 ErrorData,
31 model::{CallToolRequestParams, CallToolResult, Tool},
32 service::RequestContext,
33};
34
35use crate::rmcp::RoleServer;
36
37/// Type-erased interface for a shadow-crate tool plugin.
38///
39/// # Object Safety
40///
41/// This trait is object-safe: all async methods return `BoxFuture`.
42///
43/// Prefer implementing [`DescriptorPlugin`] over this trait directly unless
44/// you need custom dispatch logic.
45pub trait ElicitPlugin: Send + Sync + 'static {
46 /// Human-readable plugin name, used as the namespace prefix.
47 ///
48 /// E.g. `"http"` produces tools named `http__get`, `http__post`, etc.
49 fn name(&self) -> &'static str;
50
51 /// List all tools provided by this plugin (without namespace prefix).
52 fn list_tools(&self) -> Vec<Tool>;
53
54 /// Dispatch a tool call to this plugin.
55 ///
56 /// `params.name` will already have the namespace prefix stripped by
57 /// `PluginRegistry` before this is called.
58 fn call_tool<'a>(
59 &'a self,
60 params: CallToolRequestParams,
61 ctx: RequestContext<RoleServer>,
62 ) -> BoxFuture<'a, Result<CallToolResult, ErrorData>>;
63}
64
65/// A type-erased, cheaply-cloneable plugin reference.
66pub type ArcPlugin = Arc<dyn ElicitPlugin>;
67
68/// Apply the namespace prefix to a tool name.
69///
70/// `"http"` + `"get"` → `"http__get"`.
71pub(crate) fn prefixed_name(prefix: &str, name: &str) -> Cow<'static, str> {
72 Cow::Owned(format!("{prefix}__{name}"))
73}
74
75/// Strip the namespace prefix from a tool name, returning the bare name.
76///
77/// `"http__get"` with prefix `"http"` → `"get"`.
78/// Returns `None` if the name does not start with `{prefix}__`.
79pub(crate) fn strip_prefix<'a>(prefix: &str, name: &'a str) -> Option<&'a str> {
80 let sep = format!("{prefix}__");
81 name.strip_prefix(sep.as_str())
82}