mod cli;
use std::sync::Arc;
#[cfg(feature = "profiling")]
#[global_allocator]
static ALLOC: dhat::Alloc = dhat::Alloc;
use clap::{Parser, Subcommand};
use tokio::sync::Mutex;
use github_mcp::auth::auth_manager::AuthManager;
use github_mcp::core::component_registry::ComponentRegistry;
use github_mcp::core::config_manager::load_config;
use github_mcp::core::config_schema::Transport;
use github_mcp::core::health_check_manager::HealthCheckManager;
use github_mcp::core::logger::init_logging;
use github_mcp::core::mcp_server::{McpifyServer, connect_stdio};
use github_mcp::core::otel;
use github_mcp::core::shutdown_handler::{install_shutdown_handlers, on_shutdown};
use github_mcp::data::store::{open_store, resolve_store_path};
use github_mcp::http::server::start_http_server;
use github_mcp::http::types::HttpServerConfig;
#[derive(Parser)]
#[command(name = "github-mcp", about = "GitHub v3 REST API MCP server", version)]
struct Cli {
#[command(subcommand)]
command: Command,
}
#[derive(Subcommand)]
enum Command {
Setup,
Search {
query: String,
#[arg(short = 'l', long, default_value_t = 5)]
limit: usize,
},
Get { operation_id: String },
Call {
operation_id: String,
#[arg(short = 'a', long, default_value = "{}")]
args: String,
},
Start,
Http {
#[arg(long)]
host: Option<String>,
#[arg(long)]
port: Option<u16>,
#[arg(long)]
cors_allow: Option<String>,
},
TestConnection,
Config,
Version,
Versions,
}
async fn run_harness_server() -> anyhow::Result<()> {
let config = load_config(serde_json::Map::new())?;
let (otel_layer, otel_provider) = match otel::build_layer("github-mcp") {
Ok((layer, provider)) => (Some(layer), Some(provider)),
Err(_) => (None, None),
};
init_logging(otel_layer);
install_shutdown_handlers();
if let Some(provider) = otel_provider {
on_shutdown(Box::new(move || {
let provider = provider.clone();
Box::pin(async move { otel::shutdown_tracing(provider) })
}));
}
let registry = Arc::new(Mutex::new(ComponentRegistry::new()));
let mut health_checks = HealthCheckManager::new(registry.clone());
let db_path = resolve_store_path(&config.api_version)?;
{
let db_path = db_path.clone();
health_checks
.register(
"store",
true,
Box::new(move || {
let db_path = db_path.clone();
Box::pin(async move {
open_store(&db_path)?;
Ok(())
})
}),
)
.await;
}
let health_checks = Arc::new(health_checks);
let health_check_handle = health_checks.start();
on_shutdown(Box::new(move || {
health_check_handle.abort();
Box::pin(async move {})
}));
let auth_manager = Arc::new(Mutex::new(AuthManager::new(config.auth_method)));
if config.transport == Transport::Http {
let http_config = HttpServerConfig {
host: config.host.clone(),
port: config.port,
cors_allow: config.cors_allow.clone(),
};
let (header_location, header_name) =
github_mcp::auth::auth_manager::header_location_for(config.auth_method);
let factory_config = config.clone();
start_http_server(
move || {
Ok(McpifyServer::new(
factory_config.api_version.clone(),
factory_config.clone(),
auth_manager.clone(),
))
},
&http_config,
registry,
header_location,
header_name.to_string(),
)
.await?;
} else {
let api_version = config.api_version.clone();
connect_stdio(McpifyServer::new(api_version, config, auth_manager)).await?;
}
Ok(())
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
rustls::crypto::aws_lc_rs::default_provider()
.install_default()
.expect("failed to install rustls crypto provider");
#[cfg(feature = "profiling")]
let _dhat_profiler = dhat::Profiler::new_heap();
let cli = Cli::parse();
let result = match cli.command {
Command::Setup => cli::setup::run().await,
Command::Search { query, limit } => cli::search::run(&query, limit).await,
Command::Get { operation_id } => cli::get::run(&operation_id).await,
Command::Call { operation_id, args } => cli::call::run(&operation_id, &args).await,
Command::Start => {
unsafe {
std::env::set_var("GITHUB_MCP_TRANSPORT", "stdio");
}
run_harness_server().await
}
Command::Http {
host,
port,
cors_allow,
} => {
unsafe {
std::env::set_var("GITHUB_MCP_TRANSPORT", "http");
if let Some(host) = &host {
std::env::set_var("GITHUB_MCP_HOST", host);
}
if let Some(port) = port {
std::env::set_var("GITHUB_MCP_PORT", port.to_string());
}
if let Some(cors_allow) = &cors_allow {
std::env::set_var("GITHUB_MCP_CORS_ALLOW", cors_allow);
}
}
run_harness_server().await
}
Command::TestConnection => cli::test_connection::run().await,
Command::Config => cli::config::run(),
Command::Version => cli::version::run(),
Command::Versions => cli::versions::run(),
};
if let Err(err) = result {
eprintln!("{err}");
std::process::exit(1);
}
Ok(())
}