Skip to main content

mneme/mcp/
server.rs

1use std::sync::Arc;
2
3use rmcp::handler::server::ServerHandler;
4use rmcp::model::{
5    CallToolRequestParam, CallToolResult, Implementation, ListToolsResult, PaginatedRequestParam,
6    ServerCapabilities, ServerInfo,
7};
8use rmcp::service::{RequestContext, RoleServer};
9use tokio::sync::RwLock;
10
11use crate::config::settings::Settings;
12use crate::store::db::Database;
13use crate::store::memory::Session;
14
15/// Servidor MCP para Mneme.
16#[derive(Debug, Clone)]
17pub struct MnemeServer {
18    db: Arc<Database>,
19    #[allow(dead_code)]
20    config: Arc<Settings>,
21    current_project: Arc<RwLock<String>>,
22    #[allow(dead_code)]
23    current_session: Arc<RwLock<Option<Session>>>,
24    embeddings: Option<Arc<crate::embeddings::engine::EmbeddingEngine>>,
25    plugins: Arc<crate::plugins::PluginManager>,
26}
27
28impl MnemeServer {
29    /// Crea un nuevo MnemeServer.
30    pub fn new(
31        db: Arc<Database>,
32        config: Arc<Settings>,
33        embeddings: Option<Arc<crate::embeddings::engine::EmbeddingEngine>>,
34    ) -> Self {
35        let project = config.mcp.default_project.clone();
36        Self {
37            db,
38            config,
39            current_project: Arc::new(RwLock::new(project)),
40            current_session: Arc::new(RwLock::new(None)),
41            embeddings,
42            plugins: Arc::new(
43                crate::plugins::PluginManager::load_from_default_dir().unwrap_or_else(|e| {
44                    tracing::warn!(error = %e, "plugin loading failed, continuing without plugins");
45                    crate::plugins::PluginManager::empty()
46                }),
47            ),
48        }
49    }
50
51    /// Ejecuta el servidor MCP sobre stdio.
52    pub async fn run_stdio(self) -> crate::error::Result<()> {
53        let (stdin, stdout) = rmcp::transport::io::stdio();
54        let transport = (stdin, stdout);
55        rmcp::service::serve_server(self, transport)
56            .await
57            .map_err(|e| crate::error::MnemeError::Mcp(e.to_string()))?;
58        Ok(())
59    }
60
61    async fn current_project(&self) -> String {
62        self.current_project.read().await.clone()
63    }
64}
65
66impl ServerHandler for MnemeServer {
67    async fn call_tool(
68        &self,
69        request: CallToolRequestParam,
70        _context: RequestContext<RoleServer>,
71    ) -> Result<CallToolResult, rmcp::Error> {
72        let project = self.current_project().await;
73        Ok(crate::mcp::tools::execute_tool(
74            &self.db,
75            &request.name,
76            request.arguments,
77            &project,
78            self.embeddings.as_ref(),
79            Some(&self.plugins),
80        )
81        .await)
82    }
83
84    async fn list_tools(
85        &self,
86        _request: PaginatedRequestParam,
87        _context: RequestContext<RoleServer>,
88    ) -> Result<ListToolsResult, rmcp::Error> {
89        Ok(ListToolsResult {
90            next_cursor: None,
91            tools: crate::mcp::tools::list_tools(Some(&self.plugins)),
92        })
93    }
94
95    fn get_info(&self) -> ServerInfo {
96        ServerInfo {
97            protocol_version: rmcp::model::ProtocolVersion::default(),
98            capabilities: ServerCapabilities::builder().enable_tools().build(),
99            server_info: Implementation {
100                name: "mneme".to_string(),
101                version: env!("CARGO_PKG_VERSION").to_string(),
102            },
103            instructions: Some("Mneme MCP server — persistent memory for AI agents".to_string()),
104        }
105    }
106}