diff --git a/Cargo.toml b/Cargo.toml index 6262e3f..576f014 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,3 +45,4 @@ rmcp = { git = "https://github.com/modelcontextprotocol/rust-sdk", branch = "mai "reqwest", ] } schemars = "1.0" +http = "1" diff --git a/src/mcp/client.rs b/src/mcp/client.rs index 7d60f48..72a79e3 100644 --- a/src/mcp/client.rs +++ b/src/mcp/client.rs @@ -9,7 +9,9 @@ use rmcp::{ RoleClient, ServiceExt, service::RunningService, transport::TokioChildProcess, + transport::streamable_http_client::{StreamableHttpClientTransport, StreamableHttpClientTransportConfig}, }; +use http::{HeaderName, HeaderValue}; use tokio::process::Command; use crate::mcp::config::{McpServerConfig, McpTransportConfig}; @@ -81,14 +83,8 @@ impl McpClientManager { McpTransportConfig::Stdio { command, args, env } => { self.connect_stdio(command, args, env).await? } - McpTransportConfig::Http { url, headers: _ } => { - // HTTP transport requires additional setup - // For now, we'll return an error for HTTP transport - return Err(anyhow::anyhow!( - "HTTP transport for MCP server '{}' is not yet implemented. URL: {}", - config.name, - url - )); + McpTransportConfig::Http { url, headers } => { + self.connect_http(url, headers).await? } }; @@ -117,7 +113,7 @@ impl McpClientManager { Ok(server_info) } - /// Connect via stdio transport + /// Connect via stdio transport (spawn child process) async fn connect_stdio( &self, command: &str, @@ -140,6 +136,43 @@ impl McpClientManager { Ok(client) } + /// Connect via HTTP transport (Streamable HTTP) + async fn connect_http( + &self, + url: &str, + headers: &HashMap, + ) -> anyhow::Result { + // Build custom headers + let custom_headers: HashMap = headers + .iter() + .filter_map(|(key, value)| { + // Try to parse header name and value + HeaderName::try_from(key.clone()) + .ok() + .and_then(|name| { + HeaderValue::try_from(value.clone()) + .ok() + .map(|val| (name, val)) + }) + }) + .collect(); + + // Create transport config with custom headers + let config = StreamableHttpClientTransportConfig::with_uri(url) + .custom_headers(custom_headers); + + // Create transport using reqwest client (default) + let transport = StreamableHttpClientTransport::with_client( + reqwest::Client::default(), + config, + ); + + // Connect + let client = ().serve(transport).await?; + + Ok(client) + } + /// Get a client by server name pub async fn get_client(&self, name: &str) -> Option> { let clients = self.clients.read().await;