Fix MCP server removal and execAsync shell invocation
- Fix execInLoginShell to properly wrap commands instead of malformed shell option - Fix serverMap accumulating deleted servers by clearing on each poll - Add optimistic UI removal for instant feedback when removing servers - Add refresh after add operation with loading spinner - Remove recently removed servers tracking (unnecessary with proper serverMap clear) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
45f2aaefa3
commit
ead8191b22
9
main.ts
9
main.ts
|
|
@ -150,7 +150,8 @@ function spawnMcpPoller(sessionId: string, projectDir: string) {
|
||||||
try {
|
try {
|
||||||
const servers = parseMcpOutput(outputBuffer);
|
const servers = parseMcpOutput(outputBuffer);
|
||||||
|
|
||||||
// Merge servers into the map (upsert by name)
|
// Clear and replace the server map with current results
|
||||||
|
serverMap.clear();
|
||||||
servers.forEach(server => {
|
servers.forEach(server => {
|
||||||
serverMap.set(server.name, server);
|
serverMap.set(server.name, server);
|
||||||
});
|
});
|
||||||
|
|
@ -639,9 +640,9 @@ function getUserShell(): string {
|
||||||
// Execute command in user's login shell
|
// Execute command in user's login shell
|
||||||
async function execInLoginShell(command: string): Promise<string> {
|
async function execInLoginShell(command: string): Promise<string> {
|
||||||
const userShell = getUserShell();
|
const userShell = getUserShell();
|
||||||
const { stdout } = await execAsync(command, {
|
// Wrap command to execute in login shell
|
||||||
shell: `${userShell} -l -c`
|
const wrappedCommand = `${userShell} -l -c '${command.replace(/'/g, "'\\''")}'`;
|
||||||
});
|
const { stdout } = await execAsync(wrappedCommand);
|
||||||
return stdout;
|
return stdout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
22
renderer.ts
22
renderer.ts
|
|
@ -973,11 +973,16 @@ function renderMcpServers() {
|
||||||
removeBtn?.addEventListener("click", async (e) => {
|
removeBtn?.addEventListener("click", async (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
if (confirm(`Remove MCP server "${server.name}"?`)) {
|
if (confirm(`Remove MCP server "${server.name}"?`)) {
|
||||||
|
// Optimistically remove from UI
|
||||||
|
mcpServers = mcpServers.filter(s => s.name !== server.name);
|
||||||
|
renderMcpServers();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await ipcRenderer.invoke("remove-mcp-server", server.name);
|
await ipcRenderer.invoke("remove-mcp-server", server.name);
|
||||||
await refreshMcpServers();
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
alert(`Failed to remove server: ${error}`);
|
alert(`Failed to remove server: ${error}`);
|
||||||
|
// Refresh to restore correct state on error
|
||||||
|
await refreshMcpServers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -1193,23 +1198,28 @@ removeMcpDetailsBtn?.addEventListener("click", async () => {
|
||||||
if (!serverName) return;
|
if (!serverName) return;
|
||||||
|
|
||||||
if (confirm(`Remove MCP server "${serverName}"?`)) {
|
if (confirm(`Remove MCP server "${serverName}"?`)) {
|
||||||
|
// Close modal immediately
|
||||||
|
mcpDetailsModal?.classList.add("hidden");
|
||||||
|
|
||||||
|
// Optimistically remove from UI
|
||||||
|
mcpServers = mcpServers.filter(s => s.name !== serverName);
|
||||||
|
renderMcpServers();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await ipcRenderer.invoke("remove-mcp-server", serverName);
|
await ipcRenderer.invoke("remove-mcp-server", serverName);
|
||||||
mcpDetailsModal?.classList.add("hidden");
|
|
||||||
await refreshMcpServers();
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
alert(`Failed to remove server: ${error}`);
|
alert(`Failed to remove server: ${error}`);
|
||||||
|
// Refresh to restore correct state on error
|
||||||
|
await refreshMcpServers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Listen for MCP polling started event
|
// Listen for MCP polling started event
|
||||||
ipcRenderer.on("mcp-polling-started", (_event, sessionId: string) => {
|
ipcRenderer.on("mcp-polling-started", (_event, sessionId: string) => {
|
||||||
console.log(`[MCP UI] Polling started for session ${sessionId}, active: ${activeSessionId}`);
|
|
||||||
if (sessionId === activeSessionId) {
|
if (sessionId === activeSessionId) {
|
||||||
const addMcpServerBtn = document.getElementById("add-mcp-server");
|
const addMcpServerBtn = document.getElementById("add-mcp-server");
|
||||||
if (addMcpServerBtn) {
|
if (addMcpServerBtn) {
|
||||||
console.log("[MCP UI] Setting add button to loading state");
|
|
||||||
addMcpServerBtn.innerHTML = '<span class="loading-spinner"></span>';
|
addMcpServerBtn.innerHTML = '<span class="loading-spinner"></span>';
|
||||||
addMcpServerBtn.classList.add("pointer-events-none");
|
addMcpServerBtn.classList.add("pointer-events-none");
|
||||||
}
|
}
|
||||||
|
|
@ -1218,14 +1228,12 @@ ipcRenderer.on("mcp-polling-started", (_event, sessionId: string) => {
|
||||||
|
|
||||||
// Listen for MCP server updates from main process
|
// Listen for MCP server updates from main process
|
||||||
ipcRenderer.on("mcp-servers-updated", (_event, sessionId: string, servers: McpServer[]) => {
|
ipcRenderer.on("mcp-servers-updated", (_event, sessionId: string, servers: McpServer[]) => {
|
||||||
console.log(`[MCP UI] Received update for session ${sessionId}, active: ${activeSessionId}`);
|
|
||||||
// Only update if this is for the active session
|
// Only update if this is for the active session
|
||||||
if (sessionId === activeSessionId) {
|
if (sessionId === activeSessionId) {
|
||||||
mcpServers = servers;
|
mcpServers = servers;
|
||||||
renderMcpServers();
|
renderMcpServers();
|
||||||
|
|
||||||
// Restore add button
|
// Restore add button
|
||||||
console.log("[MCP UI] Restoring add button from loading state");
|
|
||||||
const addMcpServerBtn = document.getElementById("add-mcp-server");
|
const addMcpServerBtn = document.getElementById("add-mcp-server");
|
||||||
if (addMcpServerBtn) {
|
if (addMcpServerBtn) {
|
||||||
addMcpServerBtn.innerHTML = '+';
|
addMcpServerBtn.innerHTML = '+';
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue