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 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}