1use crate::config::Config as GitIrisConfig;
7use crate::git::GitRepo;
8use crate::log_debug;
9use crate::mcp::config::{MCPServerConfig, MCPTransportType};
10use crate::mcp::tools::GitIrisHandler;
11
12use anyhow::{Context, Result};
13use rmcp::ServiceExt;
14use rmcp::transport::sse_server::SseServer;
15use std::net::SocketAddr;
16use std::sync::Arc;
17use tokio::io::{stdin, stdout};
18
19pub async fn serve(config: MCPServerConfig) -> Result<()> {
21 if config.dev_mode {
23 let log_path = format!("git-iris-mcp-{}.log", std::process::id());
25 if let Err(e) = crate::logger::set_log_file(&log_path) {
26 if config.transport != MCPTransportType::StdIO {
28 eprintln!("Failed to set up log file: {e}");
29 }
30 }
32
33 if config.transport == MCPTransportType::StdIO {
35 crate::logger::set_log_to_stdout(false);
36 } else {
37 crate::logger::set_log_to_stdout(true);
38 }
39
40 crate::logger::enable_logging();
41 }
42
43 log_debug!("Starting MCP server with config: {:?}", config);
44
45 if config.transport != MCPTransportType::StdIO {
47 use crate::ui;
48 ui::print_info(&format!(
49 "Starting Git-Iris MCP server with {:?} transport",
50 config.transport
51 ));
52 if let Some(port) = config.port {
53 ui::print_info(&format!("Port: {port}"));
54 }
55 if let Some(addr) = &config.listen_address {
56 ui::print_info(&format!("Listening on: {addr}"));
57 }
58 ui::print_info(&format!(
59 "Development mode: {}",
60 if config.dev_mode {
61 "Enabled"
62 } else {
63 "Disabled"
64 }
65 ));
66 }
67
68 let git_repo = Arc::new(GitRepo::new_from_url(None)?);
70 log_debug!(
71 "Initialized Git repository at: {}",
72 git_repo.repo_path().display()
73 );
74
75 let git_iris_config = GitIrisConfig::load()?;
77 log_debug!("Loaded Git-Iris configuration");
78
79 let handler = GitIrisHandler::new(git_repo, git_iris_config);
81
82 match config.transport {
84 MCPTransportType::StdIO => serve_stdio(handler, config.dev_mode).await,
85 MCPTransportType::SSE => {
86 let socket_addr = get_socket_addr(&config)?;
88 serve_sse(handler, socket_addr).await
89 }
90 }
91}
92
93async fn serve_stdio(handler: GitIrisHandler, _dev_mode: bool) -> Result<()> {
95 log_debug!("Starting MCP server with StdIO transport");
96
97 let transport = (stdin(), stdout());
98
99 let server = handler.serve(transport).await?;
100
101 log_debug!("MCP server initialized, waiting for completion");
103 let quit_reason = server.waiting().await?;
104 log_debug!("MCP server finished: {:?}", quit_reason);
105
106 Ok(())
107}
108
109async fn serve_sse(handler: GitIrisHandler, socket_addr: SocketAddr) -> Result<()> {
111 log_debug!("Starting MCP server with SSE transport on {}", socket_addr);
112
113 let server = SseServer::serve(socket_addr).await?;
115
116 let control = server.with_service(move || {
118 handler.clone()
120 });
121
122 log_debug!("SSE server initialized, waiting for interrupt signal");
124 tokio::signal::ctrl_c()
125 .await
126 .context("Failed to listen for ctrl+c signal")?;
127
128 log_debug!("Interrupt signal received, shutting down SSE server");
130 control.cancel();
131
132 Ok(())
133}
134
135fn get_socket_addr(config: &MCPServerConfig) -> Result<SocketAddr> {
137 let listen_address = config.listen_address.as_deref().unwrap_or("127.0.0.1");
139 let port = config.port.context("Port is required for SSE transport")?;
140
141 let socket_addr: SocketAddr = format!("{listen_address}:{port}")
143 .parse()
144 .context("Failed to parse socket address")?;
145
146 Ok(socket_addr)
147}