fix: MCP sees live sessions — wrap DashMap in Arc for shared state

DashMap::clone() deep-copies all entries into a new map. The MCP
server's cloned SshService/SftpService/RdpService/ScrollbackRegistry
were snapshots from startup that never saw new sessions.

Fix: wrap all DashMap fields in Arc<DashMap<...>> so clones share
the same underlying map. Sessions added after MCP startup are now
visible to MCP tools.

Affected: SshService, SftpService, RdpService, ScrollbackRegistry.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Vantz Stockwell 2026-03-26 14:28:13 -04:00
parent 9c3afa39bd
commit 58df4ac5c8
4 changed files with 8 additions and 8 deletions

View File

@ -19,12 +19,12 @@ use crate::mcp::scrollback::ScrollbackBuffer;
/// Shared between SSH/PTY output loops (writers) and MCP tools (readers).
#[derive(Clone)]
pub struct ScrollbackRegistry {
buffers: DashMap<String, Arc<ScrollbackBuffer>>,
buffers: Arc<DashMap<String, Arc<ScrollbackBuffer>>>,
}
impl ScrollbackRegistry {
pub fn new() -> Self {
Self { buffers: DashMap::new() }
Self { buffers: Arc::new(DashMap::new()) }
}
/// Create and register a new scrollback buffer for a session.

View File

@ -76,13 +76,13 @@ struct RdpSessionHandle {
}
pub struct RdpService {
sessions: DashMap<String, Arc<RdpSessionHandle>>,
sessions: Arc<DashMap<String, Arc<RdpSessionHandle>>>,
}
impl RdpService {
pub fn new() -> Self {
Self {
sessions: DashMap::new(),
sessions: Arc::new(DashMap::new()),
}
}

View File

@ -99,13 +99,13 @@ pub struct SftpService {
/// One `SftpSession` per SSH session, behind a mutex so async commands can
/// take a shared reference to the `SftpService` and still mutably borrow
/// individual sessions.
clients: DashMap<String, Arc<TokioMutex<SftpSession>>>,
clients: Arc<DashMap<String, Arc<TokioMutex<SftpSession>>>>,
}
impl SftpService {
pub fn new() -> Self {
Self {
clients: DashMap::new(),
clients: Arc::new(DashMap::new()),
}
}

View File

@ -76,13 +76,13 @@ impl client::Handler for SshClient {
#[derive(Clone)]
pub struct SshService {
sessions: DashMap<String, Arc<SshSession>>,
sessions: Arc<DashMap<String, Arc<SshSession>>>,
db: Database,
}
impl SshService {
pub fn new(db: Database) -> Self {
Self { sessions: DashMap::new(), db }
Self { sessions: Arc::new(DashMap::new()), db }
}
pub async fn connect(&self, app_handle: AppHandle, hostname: &str, port: u16, username: &str, auth: AuthMethod, cols: u32, rows: u32, sftp_service: &SftpService, scrollback: &ScrollbackRegistry, error_watcher: &ErrorWatcher) -> Result<String, String> {