Basic Examples¶
Examples showing common usage patterns with the actual AIRS MCP implementation.
MCP Client Examples¶
Basic Client Operations¶
use airsprotocols_mcp::integration::{McpClientBuilder, McpResult};
use airsprotocols_mcp::transport::adapters::stdio::StdioTransportClientBuilder;
use serde_json::json;
use std::time::Duration;
#[tokio::main]
async fn main() -> McpResult<()> {
// Create STDIO transport
let transport = StdioTransportClientBuilder::new()
.command("your-mcp-server")
.timeout(Duration::from_secs(30))
.build()
.await?;
// Create and initialize client
let mut client = McpClientBuilder::new()
.client_info("example-client", "1.0.0")
.build(transport);
let capabilities = client.initialize().await?;
println!("Server capabilities: {:?}", capabilities);
// List available tools
let tools_response = client.list_tools().await?;
println!("Available tools: {:?}", tools_response.tools);
// Call a tool if available
if let Some(tool) = tools_response.first() {
let result = client.call_tool(&tool.name, Some(json!({"input": "test data"}))).await?;
println!("Tool result: {:?}", result);
}
// List and read resources
let resources_response = client.list_resources().await?;
if let Some(resource) = resources_response.first() {
let content = client.read_resource(&resource.uri).await?;
println!("Resource content: {:?}", content);
}
// List available prompts
let prompts_response = client.list_prompts().await?;
println!("Available prompts: {:?}", prompts_response);
client.close().await?;
Ok(())
}
HTTP Client Example¶
use airsprotocols_mcp::integration::{McpClientBuilder, McpResult};
use airsprotocols_mcp::transport::adapters::http::{HttpTransportClientBuilder, AuthMethod};
use std::time::Duration;
#[tokio::main]
async fn main() -> McpResult<()> {
// Create HTTP transport with authentication
let transport = HttpTransportClientBuilder::new()
.endpoint("http://localhost:3000/mcp")?
.auth(AuthMethod::Bearer {
token: "your-access-token".to_string(),
})
.timeout(Duration::from_secs(30))
.build()
.await?;
// Create client
let mut client = McpClientBuilder::new()
.client_info("http-client", "1.0.0")
.build(transport);
// Initialize and perform operations
let _capabilities = client.initialize().await?;
let tools = client.list_tools().await?;
let resources = client.list_resources().await?;
let prompts = client.list_prompts().await?;
println!("Connected via HTTP: {} tools, {} resources, {} prompts",
tools.len(),
resources.len(),
prompts.len());
Ok(())
}
Error Handling Patterns¶
use airsprotocols_mcp::integration::{McpError, McpClientBuilder, McpResult};
async fn robust_client_handling() -> McpResult<()> {
// ... create transport and client ...
match client.initialize().await {
Ok(capabilities) => {
println!("Successfully initialized with capabilities: {:?}", capabilities);
}
Err(McpError::Integration(integration_error)) => {
eprintln!("Integration error: {}", integration_error);
return Err(McpError::Integration(integration_error));
}
Err(McpError::Protocol(protocol_error)) => {
eprintln!("Protocol error: {}", protocol_error);
return Err(McpError::Protocol(protocol_error));
}
Err(McpError::NotConnected) => {
eprintln!("Not connected to server");
return Err(McpError::NotConnected);
}
Err(e) => {
eprintln!("Unexpected error: {:?}", e);
return Err(e);
}
}
// Continue with operations...
match client.list_tools().await {
Ok(response) => {
for tool in &response {
println!("Tool: {} - {}", tool.name,
tool.description.as_deref().unwrap_or("No description"));
}
}
Err(e) => {
eprintln!("Failed to list tools: {:?}", e);
}
}
Ok(())
}
Configuration Examples¶
Client Configuration¶
use airsprotocols_mcp::integration::McpClientBuilder;
use airsprotocols_mcp::protocol::types::{ClientCapabilities, ProtocolVersion};
use std::time::Duration;
let client = McpClientBuilder::new()
.client_info("my-application", "2.1.0")
.capabilities(ClientCapabilities {
experimental: None,
sampling: None,
})
.protocol_version(ProtocolVersion::V2024_11_05)
.timeout(Duration::from_secs(60))
.build(transport);
Transport Configuration Examples¶
// STDIO with multiple arguments
let stdio_transport = StdioTransportClientBuilder::new()
.command("mcp-server")
.arg("--config")
.arg("/path/to/config.json")
.arg("--verbose")
.timeout(Duration::from_secs(45))
.build()
.await?;
// HTTP with OAuth2
let http_transport = HttpTransportClientBuilder::new()
.endpoint("https://api.example.com/mcp")?
.auth(AuthMethod::OAuth2 {
access_token: "access_token_here".to_string(),
token_type: Some("Bearer".to_string()),
})
.timeout(Duration::from_secs(30))
.build()
.await?;
// HTTP with API key
let api_key_transport = HttpTransportClientBuilder::new()
.endpoint("https://api.example.com/mcp")?
.auth(AuthMethod::ApiKey {
key: "api-key-here".to_string(),
header: "X-API-Key".to_string(),
})
.build()
.await?;
Prompts and Resources¶
Working with Prompts¶
use std::collections::HashMap;
// List available prompts
let prompts_response = client.list_prompts().await?;
for prompt in &prompts_response {
println!("Prompt: {} - {}", prompt.name,
prompt.description.as_deref().unwrap_or("No description"));
}
// Get a specific prompt
if let Some(prompt) = prompts_response.first() {
let mut args = HashMap::new();
args.insert("param1".to_string(), "value1".to_string());
let prompt_messages = client.get_prompt(&prompt.name, args).await?;
println!("Prompt messages: {:?}", prompt_messages);
}
Working with Resources¶
// List resources
let resources_response = client.list_resources().await?;
for resource in &resources_response {
println!("Resource: {} ({})", resource.name, resource.uri);
if let Some(description) = &resource.description {
println!(" Description: {}", description);
}
}
// Read a specific resource
if let Some(resource) = resources_response.first() {
let content_response = client.read_resource(&resource.uri).await?;
println!("Resource content: {:?}", content_response);
}
Testing and Debugging¶
Simple Test Client¶
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_basic_client_operations() -> McpResult<()> {
let transport = StdioTransportClientBuilder::new()
.command("echo-server") // Your test server
.build()
.await?;
let mut client = McpClientBuilder::new()
.client_info("test-client", "1.0.0")
.build(transport);
let capabilities = client.initialize().await?;
assert!(capabilities.capabilities.tools.is_some() || capabilities.capabilities.resources.is_some());
let tools = client.list_tools().await?;
assert!(!tools.is_empty());
Ok(())
}
}
Debug Logging¶
use log::{info, debug, error};
// Enable logging in your client code
#[tokio::main]
async fn main() -> McpResult<()> {
env_logger::init();
info!("Starting MCP client");
let transport = StdioTransportClientBuilder::new()
.command("your-server")
.build()
.await?;
debug!("Transport created successfully");
let mut client = McpClientBuilder::new().build(transport);
match client.initialize().await {
Ok(caps) => info!("Client initialized: {:?}", caps),
Err(e) => {
error!("Initialization failed: {:?}", e);
return Err(e);
}
}
Ok(())
}
Error Handling Patterns¶
Comprehensive Error Handling¶
use airsprotocols_mcp::integration::{McpClientBuilder, McpError};
use airsprotocols_mcp::transport::adapters::stdio::StdioTransportClientBuilder;
use std::time::Duration;
use serde_json::json;
async fn robust_client_example() -> Result<(), Box<dyn std::error::Error>> {
let transport = StdioTransportClientBuilder::new()
.command("mcp-server")
.timeout(Duration::from_secs(30))
.build()
.await?;
let mut client = McpClientBuilder::new()
.client_info("robust-client", "1.0.0")
.build(transport);
// Initialize first
client.initialize().await?;
// Call a tool with error handling
match client.call_tool("risky_operation", Some(json!({"data": "test"}))).await {
Ok(result) => {
println!("Success: {:?}", result);
}
Err(McpError::Transport(transport_error)) => {
eprintln!("Transport error: {}", transport_error);
}
Err(McpError::Protocol(protocol_error)) => {
eprintln!("Protocol error: {}", protocol_error);
}
Err(McpError::Json(json_error)) => {
eprintln!("JSON parsing error: {}", json_error);
}
Err(McpError::Timeout { timeout_ms }) => {
eprintln!("Request timed out after {}ms", timeout_ms);
}
Err(McpError::UnexpectedResponse { details }) => {
eprintln!("Unexpected response format: {}", details);
}
Err(McpError::Shutdown) => {
eprintln!("Client has been shutdown");
}
Err(e) => {
eprintln!("Unexpected error: {}", e);
}
}
Ok(())
}
Transport Configuration¶
Custom Transport Setup¶
Creating your own transport adapter allows you to support different communication protocols while maintaining compatibility with the MCP client interface.
Implementing TransportClient Trait¶
use airsprotocols_mcp::protocol::{TransportClient, JsonRpcRequest, JsonRpcResponse, TransportError};
use async_trait::async_trait;
use std::time::Duration;
use tokio::time::timeout;
/// Example custom transport that communicates over TCP
pub struct TcpTransportClient {
address: String,
timeout: Duration,
// Add your connection fields here
}
impl TcpTransportClient {
pub fn new(address: String, timeout: Duration) -> Self {
Self {
address,
timeout,
}
}
}
#[async_trait]
impl TransportClient for TcpTransportClient {
type Error = TransportError;
async fn call(&mut self, request: JsonRpcRequest) -> Result<JsonRpcResponse, Self::Error> {
// 1. Serialize the request
let request_json = serde_json::to_string(&request)
.map_err(|e| TransportError::Serialization { source: e })?;
// 2. Send request over your transport (TCP, WebSocket, etc.)
// Example: send over TCP connection
let response_json = timeout(
self.timeout,
self.send_and_receive(&request_json)
).await
.map_err(|_| TransportError::request_timeout(self.timeout))??;
// 3. Deserialize the response
let response = serde_json::from_str(&response_json)
.map_err(|e| TransportError::Serialization { source: e })?;
Ok(response)
}
fn is_ready(&self) -> bool {
// Return true if your transport is ready to send requests
true // Implement your readiness check
}
fn transport_type(&self) -> &'static str {
"tcp"
}
async fn close(&mut self) -> Result<(), Self::Error> {
// Clean up your transport resources
Ok(())
}
}
impl TcpTransportClient {
async fn send_and_receive(&mut self, request: &str) -> Result<String, TransportError> {
// Implement your actual transport logic here
// This is where you'd:
// 1. Connect to your server
// 2. Send the JSON-RPC request
// 3. Receive the JSON-RPC response
// 4. Return the response string
// Example placeholder:
todo!("Implement your transport protocol here")
}
}
Transport Builder Pattern¶
use std::time::Duration;
/// Builder for creating TCP transport clients
pub struct TcpTransportClientBuilder {
address: Option<String>,
timeout: Duration,
// Add other configuration fields
}
impl TcpTransportClientBuilder {
pub fn new() -> Self {
Self {
address: None,
timeout: Duration::from_secs(30),
}
}
pub fn address(mut self, address: impl Into<String>) -> Self {
self.address = Some(address.into());
self
}
pub fn timeout(mut self, timeout: Duration) -> Self {
self.timeout = timeout;
self
}
pub async fn build(self) -> Result<TcpTransportClient, TransportError> {
let address = self.address.ok_or_else(|| {
TransportError::invalid_request("Address is required".to_string())
})?;
Ok(TcpTransportClient::new(address, self.timeout))
}
}
Using Your Custom Transport¶
use airsprotocols_mcp::integration::{McpClientBuilder, McpResult};
async fn use_custom_transport() -> McpResult<()> {
// Create your custom transport
let transport = TcpTransportClientBuilder::new()
.address("tcp://localhost:8080")
.timeout(Duration::from_secs(60))
.build()
.await?;
// Use it with the MCP client
let mut client = McpClientBuilder::new()
.client_info("tcp-client", "1.0.0")
.build(transport);
// Now you can use the client normally
client.initialize().await?;
let tools = client.list_tools().await?;
println!("Tools available: {:?}", tools);
Ok(())
}
WebSocket Transport Example¶
// Example of a WebSocket transport implementation
pub struct WebSocketTransportClient {
url: String,
// Add WebSocket connection fields
}
#[async_trait]
impl TransportClient for WebSocketTransportClient {
type Error = TransportError;
async fn call(&mut self, request: JsonRpcRequest) -> Result<JsonRpcResponse, Self::Error> {
// Implementation would use a WebSocket library like tokio-tungstenite
// to send JSON-RPC requests and receive responses
todo!("Implement WebSocket transport")
}
fn is_ready(&self) -> bool {
// Check if WebSocket connection is open
true
}
fn transport_type(&self) -> &'static str {
"websocket"
}
async fn close(&mut self) -> Result<(), Self::Error> {
// Close WebSocket connection
Ok(())
}
}
Connection Management¶
use tokio::time::{timeout, Duration};
use airsprotocols_mcp::integration::{McpClientBuilder, McpResult};
use airsprotocols_mcp::transport::adapters::stdio::StdioTransportClientBuilder;
// Graceful connection handling
async fn managed_connection() -> McpResult<()> {
let transport = StdioTransportClientBuilder::new()
.command("your-server")
.build()
.await?;
let mut client = McpClientBuilder::new()
.client_info("managed-client", "1.0.0")
.build(transport);
// Initialize the client
client.initialize().await?;
// Set reasonable timeout for operations
let result = timeout(
Duration::from_secs(10),
client.list_tools()
).await??;
println!("Available tools: {:?}", result);
// Always clean up
client.close().await?;
Ok(())
}