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#[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 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 pub async fn run_stdio(self) -> crate::error::Result<()> {
53 let (stdin, stdout) = rmcp::transport::io::stdio();
54 let transport = (stdin, stdout);
55 let running = rmcp::service::serve_server(self, transport)
56 .await
57 .map_err(|e| crate::error::MnemeError::Mcp(e.to_string()))?;
58 running.waiting()
60 .await
61 .map_err(|e| crate::error::MnemeError::Mcp(e.to_string()))?;
62 Ok(())
63 }
64
65 async fn current_project(&self) -> String {
66 self.current_project.read().await.clone()
67 }
68}
69
70impl ServerHandler for MnemeServer {
71 async fn call_tool(
72 &self,
73 request: CallToolRequestParam,
74 _context: RequestContext<RoleServer>,
75 ) -> Result<CallToolResult, rmcp::Error> {
76 let project = self.current_project().await;
77 Ok(crate::mcp::tools::execute_tool(
78 &self.db,
79 &request.name,
80 request.arguments,
81 &project,
82 self.embeddings.as_ref(),
83 Some(&self.plugins),
84 )
85 .await)
86 }
87
88 async fn list_tools(
89 &self,
90 _request: PaginatedRequestParam,
91 _context: RequestContext<RoleServer>,
92 ) -> Result<ListToolsResult, rmcp::Error> {
93 Ok(ListToolsResult {
94 next_cursor: None,
95 tools: crate::mcp::tools::list_tools(Some(&self.plugins)),
96 })
97 }
98
99 fn get_info(&self) -> ServerInfo {
100 ServerInfo {
101 protocol_version: rmcp::model::ProtocolVersion::default(),
102 capabilities: ServerCapabilities::builder().enable_tools().build(),
103 server_info: Implementation {
104 name: "mneme".to_string(),
105 version: env!("CARGO_PKG_VERSION").to_string(),
106 },
107 instructions: Some("Mneme MCP server — persistent memory for AI agents".to_string()),
108 }
109 }
110}