agentic_codebase/mcp/
tenant.rs1use std::collections::HashMap;
4use std::path::{Path, PathBuf};
5use std::sync::Arc;
6
7use super::server::McpServer;
8use crate::AcbResult;
9
10#[cfg(feature = "sse")]
13type MutexType<T> = tokio::sync::Mutex<T>;
14#[cfg(not(feature = "sse"))]
15type MutexType<T> = std::sync::Mutex<T>;
16
17pub struct TenantRegistry {
19 data_dir: PathBuf,
20 servers: HashMap<String, Arc<MutexType<McpServer>>>,
21}
22
23impl TenantRegistry {
24 pub fn new(data_dir: &Path) -> Self {
26 Self {
27 data_dir: data_dir.to_path_buf(),
28 servers: HashMap::new(),
29 }
30 }
31
32 pub fn get_or_create(&mut self, user_id: &str) -> AcbResult<Arc<MutexType<McpServer>>> {
37 if let Some(server) = self.servers.get(user_id) {
38 return Ok(server.clone());
39 }
40
41 std::fs::create_dir_all(&self.data_dir).map_err(|e| {
43 crate::AcbError::Io(std::io::Error::other(format!(
44 "Failed to create data dir {}: {e}",
45 self.data_dir.display()
46 )))
47 })?;
48
49 let graph_path = self.data_dir.join(format!("{user_id}.acb"));
50
51 tracing::info!(
52 "Opening graph for user '{user_id}': {}",
53 graph_path.display()
54 );
55
56 let mut server = McpServer::new();
57
58 if graph_path.is_file() {
60 match crate::AcbReader::read_from_file(&graph_path) {
61 Ok(graph) => {
62 server.load_graph(user_id.to_string(), graph);
63 }
64 Err(e) => {
65 tracing::warn!(
66 "Failed to load graph for user '{user_id}': {e} — starting empty"
67 );
68 }
69 }
70 }
71
72 let server = Arc::new(MutexType::new(server));
73 self.servers.insert(user_id.to_string(), server.clone());
74
75 Ok(server)
76 }
77
78 pub fn count(&self) -> usize {
80 self.servers.len()
81 }
82}