Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions symposium/mcp-server/src/actor/dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,13 +278,13 @@ impl DispatchHandle {
pub fn new(
client_rx: mpsc::Receiver<IPCMessage>,
client_tx: mpsc::Sender<IPCMessage>,
shell_pid: u32,
shell_pid: Option<u32>,
reference_handle: crate::actor::ReferenceHandle,
) -> Self {
let (actor_tx, actor_rx) = mpsc::channel(32);

let sender = create_sender(shell_pid);
info!("MCP server sender with PID {shell_pid} sender info: {sender:?}");
info!("MCP server sender with PID {shell_pid:?} sender info: {sender:?}");

let actor = DispatchActor::new(
actor_rx,
Expand Down Expand Up @@ -375,15 +375,15 @@ impl DispatchHandle {

}

fn create_sender(shell_pid: u32) -> crate::types::MessageSender {
fn create_sender(shell_pid: Option<u32>) -> crate::types::MessageSender {
// Try to extract taskspace UUID from directory structure
let taskspace_uuid = crate::ipc::extract_project_info()
.map(|(_, uuid)| uuid)
.ok();
crate::types::MessageSender {
working_directory: working_directory(),
taskspace_uuid,
shell_pid: Some(shell_pid),
shell_pid,
}
}

Expand Down
35 changes: 19 additions & 16 deletions symposium/mcp-server/src/ipc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@ pub struct IPCCommunicator {

/// Terminal shell PID for this MCP server instance
/// Reported to extension during handshake for smart terminal selection
terminal_shell_pid: u32,
/// None when VSCode PID discovery fails (e.g., persistent agents)
terminal_shell_pid: Option<u32>,

/// When true, disables actual IPC communication and uses only local logging.
/// Used during unit testing to avoid requiring a running VSCode extension.
Expand All @@ -119,11 +120,11 @@ pub struct IPCCommunicator {

impl IPCCommunicator {
pub async fn new(
shell_pid: u32,
shell_pid: Option<u32>,
reference_handle: crate::actor::ReferenceHandle,
options: crate::Options,
) -> Result<Self> {
info!("Creating IPC communicator for shell PID {shell_pid}");
info!("Creating IPC communicator for shell PID {shell_pid:?}");

// Create actor system alongside existing connection management
let dispatch_handle = {
Expand Down Expand Up @@ -161,7 +162,7 @@ impl IPCCommunicator {

Self {
dispatch_handle: crate::actor::dispatch::DispatchHandle::spawn_with_mock(mock_fn),
terminal_shell_pid: 0, // Dummy PID for test mode
terminal_shell_pid: None,
test_mode: true,
}
}
Expand Down Expand Up @@ -307,39 +308,41 @@ impl IPCCommunicator {
}

/// Send Polo discovery message (MCP server announces presence with shell PID)
pub async fn send_polo(&self, terminal_shell_pid: u32) -> Result<()> {
pub async fn send_polo(&self) -> Result<()> {
if self.test_mode {
info!(
"Polo discovery message sent (test mode) with shell PID: {}",
terminal_shell_pid
"Polo discovery message sent (test mode) with shell PID: {:?}",
self.terminal_shell_pid
);
return Ok(());
}

// Use new actor-based dispatch system
let polo_message = crate::types::PoloMessage { terminal_shell_pid };
// Note: PoloMessage payload is empty; shell_pid is in MessageSender
let polo_message = crate::types::PoloMessage {};
self.dispatch_handle
.send(polo_message)
.await
.map_err(|e| IPCError::SendError(format!("Failed to send Polo via actors: {}", e)))?;
info!(
"Polo discovery message sent via actor system with shell PID: {}",
terminal_shell_pid
"Polo discovery message sent via actor system with shell PID: {:?}",
self.terminal_shell_pid
);
Ok(())
}

/// Send Goodbye discovery message (MCP server announces departure with shell PID)
pub async fn send_goodbye(&self, terminal_shell_pid: u32) -> Result<()> {
pub async fn send_goodbye(&self) -> Result<()> {
if self.test_mode {
info!(
"Goodbye discovery message sent (test mode) with shell PID: {}",
terminal_shell_pid
"Goodbye discovery message sent (test mode) with shell PID: {:?}",
self.terminal_shell_pid
);
return Ok(());
}

// Use new actor-based dispatch system
// Note: GoodbyePayload is empty; shell_pid is in MessageSender
let goodbye_payload = crate::types::GoodbyePayload {};
self.dispatch_handle
.send(goodbye_payload)
Expand All @@ -348,8 +351,8 @@ impl IPCCommunicator {
IPCError::SendError(format!("Failed to send Goodbye via actors: {}", e))
})?;
info!(
"Goodbye discovery message sent via actor system with shell PID: {}",
terminal_shell_pid
"Goodbye discovery message sent via actor system with shell PID: {:?}",
self.terminal_shell_pid
);
Ok(())
}
Expand Down Expand Up @@ -539,7 +542,7 @@ impl IPCCommunicator {
return Ok(());
}

self.send_goodbye(self.terminal_shell_pid).await?;
self.send_goodbye().await?;
info!("Sent Goodbye discovery message during shutdown");
Ok(())
}
Expand Down
19 changes: 11 additions & 8 deletions symposium/mcp-server/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,16 +143,19 @@ impl SymposiumServer {
}

pub async fn new(options: crate::Options) -> Result<Self> {
// First, discover VSCode PID by walking up the process tree
// Try to discover VSCode PID by walking up the process tree
let current_pid = std::process::id();
let Some((vscode_pid, shell_pid)) =
crate::pid_discovery::find_vscode_pid_from_mcp(current_pid).await?
else {
anyhow::bail!("Could not discover VSCode PID from process tree");
let shell_pid = match crate::pid_discovery::find_vscode_pid_from_mcp(current_pid).await? {
Some((vscode_pid, shell_pid)) => {
info!("Discovered VSCode PID: {vscode_pid} and shell PID: {shell_pid}");
Some(shell_pid)
}
None => {
info!("Could not discover VSCode PID from process tree - continuing without shell PID");
None
}
};

info!("Discovered VSCode PID: {vscode_pid} and shell PID: {shell_pid}");

// Connect to the global message bus daemon (started by VSCode extension or other clients)

// Create shared reference handle for both IPC and MCP tools
Expand All @@ -168,7 +171,7 @@ impl SymposiumServer {
Self::setup_log_forwarding(&ipc);

// Send unsolicited Polo message to announce our presence
ipc.send_polo(shell_pid).await?;
ipc.send_polo().await?;

// Initialize Dialect interpreter with IDE functions
let mut interpreter = DialectInterpreter::new(ipc.clone());
Expand Down
2 changes: 0 additions & 2 deletions symposium/mcp-server/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,6 @@ impl IpcPayload for PresentWalkthroughMessage {
/// Polo discovery message - announces presence with shell PID
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PoloMessage {
#[serde(rename = "terminalShellPid")]
pub terminal_shell_pid: u32,
}

impl IpcPayload for PoloMessage {
Expand Down
2 changes: 1 addition & 1 deletion symposium/mcp-server/tests/reference_integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ async fn test_reference_integration_via_dispatch_actor() {
let reference_handle = ReferenceHandle::new();

// Create dispatch handle with the reference handle
let _dispatch_handle = DispatchHandle::new(mock_rx, client_tx, 12345, reference_handle.clone());
let _dispatch_handle = DispatchHandle::new(mock_rx, client_tx, Some(12345), reference_handle.clone());

// Test data
let test_context = json!({
Expand Down