pub struct SharedClient<T: Transport> { /* private fields */ }
Expand description
Thread-safe wrapper for sharing Client across async tasks
This wrapper encapsulates the Arc/Mutex complexity and provides a clean API
for concurrent access to MCP client functionality. It addresses the limitations
identified in PR feedback where Client requires &mut self
for all operations
but needs to be shared across multiple async tasks.
§Design Rationale
All Client methods require &mut self
because:
- MCP connections maintain state (initialized flag, connection status)
- Request correlation tracking for JSON-RPC requires mutation
- Handler and plugin registries need mutable access
While Client implements Send + Sync, this only means it’s safe to move/share between threads, not that multiple tasks can mutate it concurrently.
§Examples
use turbomcp_client::{Client, SharedClient};
use turbomcp_transport::stdio::StdioTransport;
let transport = StdioTransport::new();
let client = Client::new(transport);
let shared = SharedClient::new(client);
// Initialize once
shared.initialize().await?;
// Clone for sharing across tasks
let shared1 = shared.clone();
let shared2 = shared.clone();
// Both tasks can use the client concurrently
let handle1 = tokio::spawn(async move {
shared1.list_tools().await
});
let handle2 = tokio::spawn(async move {
shared2.list_prompts().await
});
let (tools, prompts) = tokio::try_join!(handle1, handle2).unwrap();
Implementations§
Sourcepub fn new(client: Client<T>) -> Self
pub fn new(client: Client<T>) -> Self
Create a new shared client wrapper
Takes ownership of a Client and wraps it for thread-safe sharing. The original client can no longer be accessed directly after this call.
Sourcepub async fn initialize(&self) -> Result<InitializeResult>
pub async fn initialize(&self) -> Result<InitializeResult>
Initialize the MCP connection
This method should be called once before using any other client operations. It negotiates capabilities with the server and establishes the communication protocol.
Sourcepub async fn list_tools(&self) -> Result<Vec<Tool>>
pub async fn list_tools(&self) -> Result<Vec<Tool>>
List all available tools from the MCP server
Returns a list of complete tool definitions with schemas that can be used for form generation, validation, and documentation. Tools represent executable functions provided by the server.
Sourcepub async fn list_tool_names(&self) -> Result<Vec<String>>
pub async fn list_tool_names(&self) -> Result<Vec<String>>
List available tool names from the MCP server
Returns only the tool names for cases where full schemas are not needed.
For most use cases, prefer list_tools()
which provides complete tool definitions.
Sourcepub async fn call_tool(
&self,
name: &str,
arguments: Option<HashMap<String, Value>>,
) -> Result<Value>
pub async fn call_tool( &self, name: &str, arguments: Option<HashMap<String, Value>>, ) -> Result<Value>
Execute a tool with the given name and arguments
Calls a specific tool on the MCP server with the provided arguments. The arguments should match the tool’s expected parameter schema.
Sourcepub async fn list_prompts(&self) -> Result<Vec<Prompt>>
pub async fn list_prompts(&self) -> Result<Vec<Prompt>>
List all available prompts from the MCP server
Returns full Prompt objects with metadata including name, title, description, and argument schemas. This information can be used to generate UI forms for prompt parameter collection.
Sourcepub async fn get_prompt(
&self,
name: &str,
arguments: Option<PromptInput>,
) -> Result<GetPromptResult>
pub async fn get_prompt( &self, name: &str, arguments: Option<PromptInput>, ) -> Result<GetPromptResult>
Get a prompt with optional argument substitution
Retrieves a prompt from the server. If arguments are provided, template
parameters (e.g., {parameter}
) will be substituted with the given values.
Pass None
for arguments to get the raw template form.
Sourcepub async fn list_resources(&self) -> Result<Vec<String>>
pub async fn list_resources(&self) -> Result<Vec<String>>
List available resources from the MCP server
Resources represent data or content that can be read by the client. Returns a list of resource identifiers and metadata.
Sourcepub async fn read_resource(&self, uri: &str) -> Result<ReadResourceResult>
pub async fn read_resource(&self, uri: &str) -> Result<ReadResourceResult>
Read a specific resource from the MCP server
Retrieves the content of a resource identified by its URI. The content format depends on the specific resource type.
Sourcepub async fn list_resource_templates(&self) -> Result<Vec<String>>
pub async fn list_resource_templates(&self) -> Result<Vec<String>>
List resource templates from the MCP server
Resource templates define patterns for generating resource URIs. They allow servers to describe families of related resources.
Sourcepub async fn set_log_level(&self, level: LogLevel) -> Result<SetLevelResult>
pub async fn set_log_level(&self, level: LogLevel) -> Result<SetLevelResult>
Set the logging level for the MCP server
Controls the verbosity of logs sent from the server to the client. Higher log levels provide more detailed information.
Sourcepub async fn subscribe(&self, uri: &str) -> Result<EmptyResult>
pub async fn subscribe(&self, uri: &str) -> Result<EmptyResult>
Subscribe to notifications from a specific URI
Registers interest in receiving notifications when the specified resource or endpoint changes. Used for real-time updates.
Sourcepub async fn unsubscribe(&self, uri: &str) -> Result<EmptyResult>
pub async fn unsubscribe(&self, uri: &str) -> Result<EmptyResult>
Unsubscribe from notifications for a specific URI
Removes a previously registered subscription to stop receiving notifications for the specified resource or endpoint.
Sourcepub async fn ping(&self) -> Result<PingResult>
pub async fn ping(&self) -> Result<PingResult>
Send a ping to test connection health
Verifies that the MCP connection is still active and responsive. Used for health checking and keepalive functionality.
Sourcepub async fn capabilities(&self) -> ClientCapabilities
pub async fn capabilities(&self) -> ClientCapabilities
Get the client’s configured capabilities
Returns the capabilities that this client supports. These are negotiated during initialization.
Sourcepub async fn complete(
&self,
handler_name: &str,
argument_value: &str,
) -> Result<CompletionResponse>
pub async fn complete( &self, handler_name: &str, argument_value: &str, ) -> Result<CompletionResponse>
Request argument completion from the MCP server
Provides autocompletion suggestions for prompt arguments and resource URIs. This enables rich, IDE-like experiences with contextual suggestions.
§Arguments
handler_name
- The completion handler nameargument_value
- The partial value to complete
§Examples
let shared = SharedClient::new(Client::new(StdioTransport::new()));
shared.initialize().await?;
let result = shared.complete("complete_path", "/usr/b").await?;
println!("Completions: {:?}", result.values);
Sourcepub async fn complete_prompt(
&self,
prompt_name: &str,
argument_name: &str,
argument_value: &str,
context: Option<CompletionContext>,
) -> Result<CompletionResponse>
pub async fn complete_prompt( &self, prompt_name: &str, argument_name: &str, argument_value: &str, context: Option<CompletionContext>, ) -> Result<CompletionResponse>
Complete a prompt argument with full MCP protocol support
This method provides access to the complete MCP completion protocol, allowing specification of argument names, prompt references, and context.
§Arguments
prompt_name
- Name of the prompt to complete forargument_name
- Name of the argument being completedargument_value
- Current value for completion matchingcontext
- Optional context with previously resolved arguments
§Examples
let shared = SharedClient::new(Client::new(StdioTransport::new()));
shared.initialize().await?;
// Complete with context
let mut context_args = HashMap::new();
context_args.insert("language".to_string(), "rust".to_string());
let context = CompletionContext { arguments: Some(context_args) };
let completions = shared.complete_prompt(
"code_review",
"framework",
"tok",
Some(context)
).await?;
for completion in completions.values {
println!("Suggestion: {}", completion);
}
Sourcepub async fn complete_resource(
&self,
resource_uri: &str,
argument_name: &str,
argument_value: &str,
context: Option<CompletionContext>,
) -> Result<CompletionResponse>
pub async fn complete_resource( &self, resource_uri: &str, argument_name: &str, argument_value: &str, context: Option<CompletionContext>, ) -> Result<CompletionResponse>
Complete a resource template URI with full MCP protocol support
This method provides completion for resource template URIs, allowing servers to suggest values for URI template variables.
§Arguments
resource_uri
- Resource template URI (e.g., “/files/{path}”)argument_name
- Name of the argument being completedargument_value
- Current value for completion matchingcontext
- Optional context with previously resolved arguments
§Examples
let shared = SharedClient::new(Client::new(StdioTransport::new()));
shared.initialize().await?;
let completions = shared.complete_resource(
"/files/{path}",
"path",
"/home/user/doc",
None
).await?;
for completion in completions.values {
println!("Path suggestion: {}", completion);
}
Sourcepub async fn list_roots(&self) -> Result<Vec<String>>
pub async fn list_roots(&self) -> Result<Vec<String>>
List filesystem roots available to the server
Returns filesystem root directories that the server has access to. This helps servers understand their operating boundaries and available resources within the filesystem.
§Examples
let shared = SharedClient::new(Client::new(StdioTransport::new()));
shared.initialize().await?;
let roots = shared.list_roots().await?;
for root_uri in roots {
println!("Available root: {}", root_uri);
}
Sourcepub async fn on_elicitation(&self, handler: Arc<dyn ElicitationHandler>)
pub async fn on_elicitation(&self, handler: Arc<dyn ElicitationHandler>)
Register an elicitation handler for processing server requests for user information
Elicitation handlers respond to server requests for additional information from users during interactions. This enables interactive workflows where servers can gather necessary information dynamically.
§Arguments
handler
- The elicitation handler implementation
§Examples
use turbomcp_client::{Client, SharedClient};
use turbomcp_client::handlers::{ElicitationHandler, ElicitationRequest, ElicitationResponse, ElicitationAction, HandlerResult};
use turbomcp_transport::stdio::StdioTransport;
use async_trait::async_trait;
use std::sync::Arc;
#[derive(Debug)]
struct MyElicitationHandler;
#[async_trait]
impl ElicitationHandler for MyElicitationHandler {
async fn handle_elicitation(&self, request: ElicitationRequest) -> HandlerResult<ElicitationResponse> {
// Process user input request and return response
Ok(ElicitationResponse {
action: ElicitationAction::Accept,
content: Some(serde_json::json!({"name": "example"})),
})
}
}
let shared = SharedClient::new(Client::new(StdioTransport::new()));
shared.on_elicitation(Arc::new(MyElicitationHandler)).await;
Sourcepub async fn on_progress(&self, handler: Arc<dyn ProgressHandler>)
pub async fn on_progress(&self, handler: Arc<dyn ProgressHandler>)
Register a progress handler for processing server progress notifications
Progress handlers receive updates about long-running operations on the server. This enables progress bars, status updates, and better user experience during extended operations.
§Arguments
handler
- The progress handler implementation
Sourcepub async fn on_log(&self, handler: Arc<dyn LogHandler>)
pub async fn on_log(&self, handler: Arc<dyn LogHandler>)
Register a log handler for processing server log messages
Log handlers receive log messages from the server and can route them to the client’s logging system. This is useful for debugging and maintaining a unified log across client and server.
§Arguments
handler
- The log handler implementation
Sourcepub async fn on_resource_update(&self, handler: Arc<dyn ResourceUpdateHandler>)
pub async fn on_resource_update(&self, handler: Arc<dyn ResourceUpdateHandler>)
Register a resource update handler for processing resource change notifications
Resource update handlers receive notifications when subscribed resources change on the server. This enables reactive updates to cached data or UI refreshes when server-side resources change.
§Arguments
handler
- The resource update handler implementation
Sourcepub async fn has_elicitation_handler(&self) -> bool
pub async fn has_elicitation_handler(&self) -> bool
Check if an elicitation handler is registered
Sourcepub async fn has_progress_handler(&self) -> bool
pub async fn has_progress_handler(&self) -> bool
Check if a progress handler is registered
Sourcepub async fn has_log_handler(&self) -> bool
pub async fn has_log_handler(&self) -> bool
Check if a log handler is registered
Sourcepub async fn has_resource_update_handler(&self) -> bool
pub async fn has_resource_update_handler(&self) -> bool
Check if a resource update handler is registered