//! Generated MCP proxy
//!
//! Server: {{server_name}} v{{server_version}}
//! Generated: {{generation_date}}
//! Frontend: {{frontend_type}}
//! Backend: {{backend_type}}
//!
//! Environment:
//! - `BACKEND_CMD`: the upstream server's command (required)
//! - `BACKEND_ARGS`: its arguments, comma-separated
//! - `BACKEND_WORKING_DIR`: its working directory
{{#if has_http}}
//! - `BIND_ADDR`: where to listen (default `127.0.0.1:3000`)
{{/if}}
{{#if has_websocket}}
//! - `BIND_ADDR`: where to listen (default `127.0.0.1:3000`)
{{/if}}
//! - `RUST_LOG`: log filter (default `info`); logs go to stderr
#[allow(dead_code)]
mod proxy;
#[allow(dead_code)]
mod types;
use turbomcp_client::Client;
use turbomcp_transport::{ChildProcessConfig, ChildProcessTransport, Transport};
use proxy::ProxyRouter;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// stderr, always: with a stdio frontend, stdout is the MCP channel.
tracing_subscriber::fmt()
.with_writer(std::io::stderr)
.with_env_filter(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info")),
)
.init();
tracing::info!("Starting proxy");
let router = ProxyRouter::connect(connect_backend().await?).await?;
tracing::info!("Backend initialized");
{{#if has_http}}
let bind = std::env::var("BIND_ADDR").unwrap_or_else(|_| "127.0.0.1:3000".to_string());
tracing::info!("Serving Streamable HTTP on http://{bind}/mcp");
turbomcp_server::transport::http::run(&router, &bind).await?;
{{else}}
{{#if has_websocket}}
let bind = std::env::var("BIND_ADDR").unwrap_or_else(|_| "127.0.0.1:3000".to_string());
tracing::info!("Serving WebSocket on ws://{bind}");
turbomcp_server::transport::websocket::run(&router, &bind).await?;
{{else}}
turbomcp_server::transport::stdio::run(&router).await?;
{{/if}}
{{/if}}
Ok(())
}
/// Start the upstream server as a subprocess.
async fn connect_backend() -> Result<Client<ChildProcessTransport>, Box<dyn std::error::Error>> {
let command = std::env::var("BACKEND_CMD")
.map_err(|_| "BACKEND_CMD must name the upstream server's command")?;
let args: Vec<String> = std::env::var("BACKEND_ARGS")
.unwrap_or_default()
.split(',')
.filter(|arg| !arg.is_empty())
.map(String::from)
.collect();
tracing::info!("Backend command: {command} {args:?}");
let transport = ChildProcessTransport::new(ChildProcessConfig {
command,
args,
working_directory: std::env::var("BACKEND_WORKING_DIR").ok(),
environment: None,
..Default::default()
});
transport.connect().await?;
Ok(Client::new(transport))
}