pub trait ClientPlugin:
Send
+ Sync
+ Debug {
// Required methods
fn name(&self) -> &str;
fn version(&self) -> &str;
fn initialize<'life0, 'life1, 'async_trait>(
&'life0 self,
context: &'life1 PluginContext,
) -> Pin<Box<dyn Future<Output = PluginResult<()>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait;
fn before_request<'life0, 'life1, 'async_trait>(
&'life0 self,
context: &'life1 mut RequestContext,
) -> Pin<Box<dyn Future<Output = PluginResult<()>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait;
fn after_response<'life0, 'life1, 'async_trait>(
&'life0 self,
context: &'life1 mut ResponseContext,
) -> Pin<Box<dyn Future<Output = PluginResult<()>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait;
fn handle_custom<'life0, 'life1, 'async_trait>(
&'life0 self,
method: &'life1 str,
params: Option<Value>,
) -> Pin<Box<dyn Future<Output = PluginResult<Option<Value>>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait;
// Provided methods
fn description(&self) -> Option<&str> { ... }
fn dependencies(&self) -> Vec<&str> { ... }
fn cleanup<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = PluginResult<()>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait { ... }
}
Expand description
Core trait for client plugins
Plugins can hook into the client lifecycle at various points:
- initialization: Called when the plugin is registered
- before_request: Called before sending requests to the server
- after_response: Called after receiving responses from the server
- handle_custom: Called for custom method handling
All methods are async and return PluginResult to allow for error handling and async operations like network calls, database access, etc.
§Examples
use turbomcp_client::plugins::{ClientPlugin, PluginContext, RequestContext, ResponseContext, PluginResult};
use async_trait::async_trait;
use serde_json::Value;
#[derive(Debug)]
struct LoggingPlugin;
#[async_trait]
impl ClientPlugin for LoggingPlugin {
fn name(&self) -> &str {
"logging"
}
fn version(&self) -> &str {
"1.0.0"
}
async fn initialize(&self, context: &PluginContext) -> PluginResult<()> {
println!("Logging plugin initialized for client: {}", context.client_name);
Ok(())
}
async fn before_request(&self, context: &mut RequestContext) -> PluginResult<()> {
println!("Request: {} {}", context.method(),
context.params().unwrap_or(&Value::Null));
Ok(())
}
async fn after_response(&self, context: &mut ResponseContext) -> PluginResult<()> {
println!("Response: {} took {:?}", context.method(), context.duration);
Ok(())
}
async fn handle_custom(&self, method: &str, params: Option<Value>) -> PluginResult<Option<Value>> {
if method == "logging.get_stats" {
Ok(Some(serde_json::json!({"logged_requests": 42})))
} else {
Ok(None) // Not handled by this plugin
}
}
}
Required Methods§
Sourcefn initialize<'life0, 'life1, 'async_trait>(
&'life0 self,
context: &'life1 PluginContext,
) -> Pin<Box<dyn Future<Output = PluginResult<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn initialize<'life0, 'life1, 'async_trait>(
&'life0 self,
context: &'life1 PluginContext,
) -> Pin<Box<dyn Future<Output = PluginResult<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Initialize the plugin
Called once when the plugin is registered with the client. Use this to set up resources, validate configuration, check dependencies, etc.
§Arguments
context
- Plugin context with client info, capabilities, and configuration
§Returns
Returns Ok(())
if initialization succeeds, or PluginError
if it fails.
Sourcefn before_request<'life0, 'life1, 'async_trait>(
&'life0 self,
context: &'life1 mut RequestContext,
) -> Pin<Box<dyn Future<Output = PluginResult<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn before_request<'life0, 'life1, 'async_trait>(
&'life0 self,
context: &'life1 mut RequestContext,
) -> Pin<Box<dyn Future<Output = PluginResult<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Hook called before sending a request to the server
This allows plugins to:
- Modify request parameters
- Add metadata for tracking
- Implement features like authentication, request logging, etc.
- Abort requests by returning an error
§Arguments
context
- Mutable request context that can be modified
§Returns
Returns Ok(())
to continue processing, or PluginError
to abort.
Sourcefn after_response<'life0, 'life1, 'async_trait>(
&'life0 self,
context: &'life1 mut ResponseContext,
) -> Pin<Box<dyn Future<Output = PluginResult<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn after_response<'life0, 'life1, 'async_trait>(
&'life0 self,
context: &'life1 mut ResponseContext,
) -> Pin<Box<dyn Future<Output = PluginResult<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Hook called after receiving a response from the server
This allows plugins to:
- Process response data
- Log metrics and performance data
- Implement features like caching, retry logic, etc.
- Modify response metadata
§Arguments
context
- Mutable response context that can be modified
§Returns
Returns Ok(())
if processing succeeds, or PluginError
if it fails.
Sourcefn handle_custom<'life0, 'life1, 'async_trait>(
&'life0 self,
method: &'life1 str,
params: Option<Value>,
) -> Pin<Box<dyn Future<Output = PluginResult<Option<Value>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn handle_custom<'life0, 'life1, 'async_trait>(
&'life0 self,
method: &'life1 str,
params: Option<Value>,
) -> Pin<Box<dyn Future<Output = PluginResult<Option<Value>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Handle custom methods not part of the standard MCP protocol
This allows plugins to implement custom functionality that can be invoked by clients. Each plugin can handle its own set of custom methods.
§Arguments
method
- The custom method name (e.g., “metrics.get_stats”)params
- Optional parameters for the method
§Returns
Returns Some(Value)
if the method was handled, None
if not handled by this plugin,
or PluginError
if handling failed.
Provided Methods§
Sourcefn description(&self) -> Option<&str>
fn description(&self) -> Option<&str>
Optional plugin description
Sourcefn dependencies(&self) -> Vec<&str>
fn dependencies(&self) -> Vec<&str>
Plugin dependencies (other plugins that must be registered first)
Sourcefn cleanup<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = PluginResult<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn cleanup<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = PluginResult<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Optional cleanup when plugin is unregistered
Default implementation does nothing. Override to perform cleanup like closing connections, flushing buffers, etc.