use crate::api_key::{ApiKeyProvider, SecureString};
use crate::error::{Error, Result};
use crate::gemini::GeminiClient;
use crate::tools::{
AnalyzeFilesTool, AskAboutCodeTool, SearchCodebaseTool, SummarizeDirectoryTool,
};
use mocopr::prelude::*;
use std::sync::Arc;
use tracing::info;
pub struct MCPServer {
gemini_client: Arc<GeminiClient>,
}
impl MCPServer {
pub fn new(gemini_client: Arc<GeminiClient>) -> Self {
Self { gemini_client }
}
pub async fn run(self) -> Result<()> {
info!("Starting MCP server for dynamic grounding");
let search_tool = SearchCodebaseTool::new(self.gemini_client.clone());
let analyze_tool = AnalyzeFilesTool::new(self.gemini_client.clone());
let ask_tool = AskAboutCodeTool::new(self.gemini_client.clone());
let summarize_tool = SummarizeDirectoryTool::new(self.gemini_client.clone());
let server = McpServer::builder()
.with_info(
"dynamic_grounding_for_github_copilot",
env!("CARGO_PKG_VERSION"),
)
.with_tools()
.with_tool(search_tool)
.with_tool(analyze_tool)
.with_tool(ask_tool)
.with_tool(summarize_tool)
.build()
.map_err(|e| Error::McpError(format!("Failed to build server: {}", e)))?;
info!("MCP server started with 4 tools. Awaiting stdio communication...");
server
.run_stdio()
.await
.map_err(|e| Error::McpError(format!("Server error: {}", e)))?;
Ok(())
}
}
pub struct MCPApiKeyProvider {
api_key: parking_lot::RwLock<Option<String>>,
}
impl MCPApiKeyProvider {
pub fn new() -> Self {
Self {
api_key: parking_lot::RwLock::new(None),
}
}
pub fn set_key(&self, key: String) {
*self.api_key.write() = Some(key);
}
}
#[async_trait::async_trait]
impl ApiKeyProvider for MCPApiKeyProvider {
async fn get_key(&self) -> Result<SecureString> {
self.api_key
.read()
.as_ref()
.map(|k| SecureString::new(k.clone()))
.ok_or(Error::ApiKeyError("No API key set".to_string()))
}
async fn set_key(&self, key: SecureString) -> Result<()> {
*self.api_key.write() = Some(key.as_str().to_string());
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mcp_api_key_provider() {
let provider = MCPApiKeyProvider::new();
provider.set_key("test_key".to_string());
assert!(provider.api_key.read().is_some());
}
}