dynamic_grounding_for_github_copilot 0.1.0

MCP server providing Google Gemini AI integration for enhanced codebase search and analysis
Documentation
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");

        // Create tool instances
        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());
    }
}