#[cfg(feature = "mcp-server")]
use a2a_mcp::bridge::agent_to_mcp::AgentToMcpBridge;
#[cfg(feature = "mcp-server")]
use a2a_rs::{domain::AgentCard, port::AsyncMessageHandler};
#[cfg(feature = "mcp-server")]
use rmcp::{RoleServer, transport::stdio};
#[cfg(feature = "mcp-server")]
use tracing::info;
#[cfg(feature = "mcp-server")]
use crate::core::config::McpServerConfig;
#[cfg(feature = "mcp-server")]
pub async fn run_mcp_server<H>(
config: &McpServerConfig,
agent_card: AgentCard,
handler: H,
) -> Result<(), Box<dyn std::error::Error>>
where
H: AsyncMessageHandler + Clone + Send + Sync + 'static,
{
if !config.enabled {
return Ok(());
}
info!("Starting MCP server for agent: {}", agent_card.name);
if config.http.enabled {
return run_streamable_http(config, agent_card, handler).await;
}
if !config.stdio {
info!(
"No MCP transport enabled (set features.mcp_server.stdio or features.mcp_server.http.enabled)"
);
return Ok(());
}
info!("Starting stdio transport for MCP server");
let bridge = AgentToMcpBridge::with_handler(handler, agent_card)
.with_mcp_metadata(config.name.clone(), config.version.clone());
let (read, write) = stdio();
let _running =
rmcp::service::serve_directly::<RoleServer, _, _, _, _>(bridge, (read, write), None);
tokio::select! {
_ = tokio::signal::ctrl_c() => {
info!("Received Ctrl+C, shutting down");
}
_ = tokio::time::sleep(tokio::time::Duration::MAX) => {
}
}
info!("MCP server shutdown gracefully");
Ok(())
}
#[cfg(feature = "mcp-server")]
async fn run_streamable_http<H>(
config: &McpServerConfig,
agent_card: AgentCard,
handler: H,
) -> Result<(), Box<dyn std::error::Error>>
where
H: AsyncMessageHandler + Clone + Send + Sync + 'static,
{
use std::sync::Arc;
use rmcp::transport::streamable_http_server::{
StreamableHttpServerConfig, StreamableHttpService, session::local::LocalSessionManager,
};
let http = &config.http;
let addr = format!("{}:{}", http.host, http.port);
let mut server_config = StreamableHttpServerConfig::default();
if let Some(hosts) = &http.allowed_hosts {
server_config = server_config.with_allowed_hosts(hosts.clone());
}
if let Some(origins) = &http.allowed_origins {
server_config = server_config.with_allowed_origins(origins.clone());
}
let name = config.name.clone();
let version = config.version.clone();
let service = StreamableHttpService::new(
move || {
Ok(
AgentToMcpBridge::with_handler(handler.clone(), agent_card.clone())
.with_mcp_metadata(name.clone(), version.clone()),
)
},
Arc::new(LocalSessionManager::default()),
server_config,
);
let router = axum::Router::new().nest_service(&http.path, service);
let listener = tokio::net::TcpListener::bind(&addr).await?;
info!(
"MCP Streamable HTTP server listening on http://{}{}",
addr, http.path
);
axum::serve(listener, router).await?;
Ok(())
}
#[cfg(feature = "mcp-server")]
pub fn is_mcp_server_enabled(config: &McpServerConfig) -> bool {
config.enabled
}
#[cfg(not(feature = "mcp-server"))]
pub async fn run_mcp_server<H>(
_config: &crate::core::config::McpServerConfig,
_agent_card: a2a_rs::domain::AgentCard,
_handler: H,
) -> Result<(), Box<dyn std::error::Error>>
where
H: a2a_rs::port::AsyncMessageHandler + Clone + Send + Sync + 'static,
{
tracing::warn!("MCP server feature not enabled. Compile with --features mcp-server");
Ok(())
}
#[cfg(not(feature = "mcp-server"))]
pub fn is_mcp_server_enabled(_config: &crate::core::config::McpServerConfig) -> bool {
false
}