Run MCP add/remove/get commands in poller PTY instead of execAsync
- Fixes issue where execAsync spawns non-login shell without claude in PATH - All MCP operations now run in the session's MCP poller PTY - Adds executeInMcpPoller helper to run commands and wait for completion - Uses prompt detection to know when command finishes - 10 second timeout for safety 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
7c344ff0f3
commit
84e43f656c
60
main.ts
60
main.ts
|
|
@ -631,23 +631,55 @@ async function listMcpServers() {
|
|||
}
|
||||
}
|
||||
|
||||
async function addMcpServer(name: string, config: any) {
|
||||
// Helper to execute command in MCP poller PTY and wait for completion
|
||||
function executeInMcpPoller(sessionId: string, command: string): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const mcpPoller = mcpPollerPtyProcesses.get(sessionId);
|
||||
if (!mcpPoller) {
|
||||
reject(new Error("No active MCP poller for this session"));
|
||||
return;
|
||||
}
|
||||
|
||||
let output = "";
|
||||
const timeout = setTimeout(() => {
|
||||
dataHandler && mcpPoller.off("data", dataHandler);
|
||||
reject(new Error("Command timeout"));
|
||||
}, 10000);
|
||||
|
||||
const dataHandler = (data: string) => {
|
||||
output += data;
|
||||
// Check if command completed (prompt returned)
|
||||
if (data.includes("% ") || data.includes("$ ") || data.includes("➜ ")) {
|
||||
clearTimeout(timeout);
|
||||
mcpPoller.off("data", dataHandler);
|
||||
resolve(output);
|
||||
}
|
||||
};
|
||||
|
||||
mcpPoller.on("data", dataHandler);
|
||||
mcpPoller.write(command + "\r");
|
||||
});
|
||||
}
|
||||
|
||||
async function addMcpServer(sessionId: string, name: string, config: any) {
|
||||
// Use add-json to support full configuration including env vars, headers, etc.
|
||||
const jsonConfig = JSON.stringify(config);
|
||||
await execAsync(`claude mcp add-json --scope user "${name}" '${jsonConfig}'`);
|
||||
const jsonConfig = JSON.stringify(config).replace(/'/g, "'\\''"); // Escape single quotes for shell
|
||||
const command = `claude mcp add-json --scope user "${name}" '${jsonConfig}'`;
|
||||
await executeInMcpPoller(sessionId, command);
|
||||
}
|
||||
|
||||
async function removeMcpServer(name: string) {
|
||||
await execAsync(`claude mcp remove "${name}"`);
|
||||
async function removeMcpServer(sessionId: string, name: string) {
|
||||
const command = `claude mcp remove "${name}"`;
|
||||
await executeInMcpPoller(sessionId, command);
|
||||
}
|
||||
|
||||
async function getMcpServerDetails(name: string) {
|
||||
async function getMcpServerDetails(sessionId: string, name: string) {
|
||||
try {
|
||||
const { stdout } = await execAsync(`claude mcp get "${name}"`);
|
||||
const output = await executeInMcpPoller(sessionId, `claude mcp get "${name}"`);
|
||||
|
||||
// Parse the output to extract details
|
||||
const details: any = { name };
|
||||
const lines = stdout.split("\n");
|
||||
const lines = output.split("\n");
|
||||
|
||||
for (const line of lines) {
|
||||
const trimmed = line.trim();
|
||||
|
|
@ -688,27 +720,27 @@ ipcMain.handle("list-mcp-servers", async (_event, sessionId: string) => {
|
|||
}
|
||||
});
|
||||
|
||||
ipcMain.handle("add-mcp-server", async (_event, name: string, config: any) => {
|
||||
ipcMain.handle("add-mcp-server", async (_event, sessionId: string, name: string, config: any) => {
|
||||
try {
|
||||
await addMcpServer(name, config);
|
||||
await addMcpServer(sessionId, name, config);
|
||||
} catch (error) {
|
||||
console.error("Error adding MCP server:", error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle("remove-mcp-server", async (_event, name: string) => {
|
||||
ipcMain.handle("remove-mcp-server", async (_event, sessionId: string, name: string) => {
|
||||
try {
|
||||
await removeMcpServer(name);
|
||||
await removeMcpServer(sessionId, name);
|
||||
} catch (error) {
|
||||
console.error("Error removing MCP server:", error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle("get-mcp-server-details", async (_event, name: string) => {
|
||||
ipcMain.handle("get-mcp-server-details", async (_event, sessionId: string, name: string) => {
|
||||
try {
|
||||
return await getMcpServerDetails(name);
|
||||
return await getMcpServerDetails(sessionId, name);
|
||||
} catch (error) {
|
||||
console.error("Error getting MCP server details:", error);
|
||||
throw error;
|
||||
|
|
|
|||
|
|
@ -949,7 +949,7 @@ function renderMcpServers() {
|
|||
e.stopPropagation();
|
||||
if (confirm(`Remove MCP server "${server.name}"?`)) {
|
||||
try {
|
||||
await ipcRenderer.invoke("remove-mcp-server", server.name);
|
||||
await ipcRenderer.invoke("remove-mcp-server", activeSessionId, server.name);
|
||||
await loadMcpServers();
|
||||
} catch (error) {
|
||||
alert(`Failed to remove server: ${error}`);
|
||||
|
|
@ -978,7 +978,7 @@ async function showMcpServerDetails(name: string) {
|
|||
detailsModal?.classList.remove("hidden");
|
||||
|
||||
try {
|
||||
const details = await ipcRenderer.invoke("get-mcp-server-details", name);
|
||||
const details = await ipcRenderer.invoke("get-mcp-server-details", activeSessionId, name);
|
||||
|
||||
if (detailsContent) {
|
||||
let html = "";
|
||||
|
|
@ -1139,7 +1139,7 @@ addMcpBtn?.addEventListener("click", async () => {
|
|||
addMcpBtn.classList.add("opacity-50", "cursor-not-allowed");
|
||||
|
||||
try {
|
||||
await ipcRenderer.invoke("add-mcp-server", name, config);
|
||||
await ipcRenderer.invoke("add-mcp-server", activeSessionId, name, config);
|
||||
await loadMcpServers();
|
||||
mcpModal?.classList.add("hidden");
|
||||
} catch (error) {
|
||||
|
|
@ -1168,7 +1168,7 @@ removeMcpDetailsBtn?.addEventListener("click", async () => {
|
|||
|
||||
if (confirm(`Remove MCP server "${serverName}"?`)) {
|
||||
try {
|
||||
await ipcRenderer.invoke("remove-mcp-server", serverName);
|
||||
await ipcRenderer.invoke("remove-mcp-server", activeSessionId, serverName);
|
||||
mcpDetailsModal?.classList.add("hidden");
|
||||
await loadMcpServers();
|
||||
} catch (error) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue