pub mod context;
pub mod descriptor;
pub mod descriptor_plugin;
pub use context::{NoContext, PluginContext};
pub use descriptor::{
PluginToolRegistration, ToolDescriptor, make_descriptor, make_descriptor_ctx,
};
pub use descriptor_plugin::DescriptorPlugin;
use std::borrow::Cow;
use std::sync::Arc;
use futures::future::BoxFuture;
use rmcp::{
ErrorData,
model::{CallToolRequestParams, CallToolResult, Tool},
service::RequestContext,
};
use crate::rmcp::RoleServer;
pub trait ElicitPlugin: Send + Sync + 'static {
fn name(&self) -> &'static str;
fn list_tools(&self) -> Vec<Tool>;
fn call_tool<'a>(
&'a self,
params: CallToolRequestParams,
ctx: RequestContext<RoleServer>,
) -> BoxFuture<'a, Result<CallToolResult, ErrorData>>;
}
pub trait StatefulPlugin: Send + Sync + 'static {
type Context: PluginContext;
fn name(&self) -> &'static str;
fn list_tools(&self) -> Vec<Tool>;
fn tool_descriptors(&self) -> Vec<ToolDescriptor>;
fn context(&self) -> Arc<Self::Context>;
}
impl<P: StatefulPlugin> ElicitPlugin for P {
fn name(&self) -> &'static str {
StatefulPlugin::name(self)
}
fn list_tools(&self) -> Vec<Tool> {
StatefulPlugin::list_tools(self)
}
fn call_tool<'a>(
&'a self,
params: CallToolRequestParams,
_ctx: RequestContext<RoleServer>,
) -> BoxFuture<'a, Result<CallToolResult, ErrorData>> {
let bare = params
.name
.strip_prefix(&format!("{name}__", name = self.name()))
.map(|s| s.to_owned())
.unwrap_or_else(|| params.name.to_string());
let ctx: Arc<dyn std::any::Any + Send + Sync> = self.context();
let descriptors = self.tool_descriptors();
Box::pin(async move {
match descriptors.iter().find(|d| d.name == bare.as_str()) {
Some(descriptor) => descriptor.dispatch(ctx, params).await,
None => Err(ErrorData::invalid_params(
format!("unknown tool: {bare}"),
None,
)),
}
})
}
}
pub type ArcPlugin = Arc<dyn ElicitPlugin>;
pub(crate) fn prefixed_name(prefix: &str, name: &str) -> Cow<'static, str> {
Cow::Owned(format!("{prefix}__{name}"))
}
pub(crate) fn strip_prefix<'a>(prefix: &str, name: &'a str) -> Option<&'a str> {
let sep = format!("{prefix}__");
name.strip_prefix(sep.as_str())
}