Skip to content

Commit d687523

Browse files
built-by-asclaude
andcommitted
Use execAsync with login shell for MCP add/remove/get commands
- Replaces PTY-based execution to avoid race conditions with MCP poller - Uses process.env.SHELL to detect user's preferred shell - Falls back to /bin/zsh on macOS, /bin/bash on Linux - Executes in login shell (-l) to ensure PATH is set correctly - Each command runs in independent shell process (no interference) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 84e43f6 commit d687523

File tree

1 file changed

+22
-38
lines changed

1 file changed

+22
-38
lines changed

main.ts

Lines changed: 22 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -631,51 +631,35 @@ async function listMcpServers() {
631631
}
632632
}
633633

634-
// Helper to execute command in MCP poller PTY and wait for completion
635-
function executeInMcpPoller(sessionId: string, command: string): Promise<string> {
636-
return new Promise((resolve, reject) => {
637-
const mcpPoller = mcpPollerPtyProcesses.get(sessionId);
638-
if (!mcpPoller) {
639-
reject(new Error("No active MCP poller for this session"));
640-
return;
641-
}
642-
643-
let output = "";
644-
const timeout = setTimeout(() => {
645-
dataHandler && mcpPoller.off("data", dataHandler);
646-
reject(new Error("Command timeout"));
647-
}, 10000);
648-
649-
const dataHandler = (data: string) => {
650-
output += data;
651-
// Check if command completed (prompt returned)
652-
if (data.includes("% ") || data.includes("$ ") || data.includes("➜ ")) {
653-
clearTimeout(timeout);
654-
mcpPoller.off("data", dataHandler);
655-
resolve(output);
656-
}
657-
};
634+
// Get user's shell, with fallback
635+
function getUserShell(): string {
636+
return process.env.SHELL || (os.platform() === "darwin" ? "/bin/zsh" : "/bin/bash");
637+
}
658638

659-
mcpPoller.on("data", dataHandler);
660-
mcpPoller.write(command + "\r");
639+
// Execute command in user's login shell
640+
async function execInLoginShell(command: string): Promise<string> {
641+
const userShell = getUserShell();
642+
const { stdout } = await execAsync(command, {
643+
shell: `${userShell} -l -c`
661644
});
645+
return stdout;
662646
}
663647

664-
async function addMcpServer(sessionId: string, name: string, config: any) {
648+
async function addMcpServer(name: string, config: any) {
665649
// Use add-json to support full configuration including env vars, headers, etc.
666650
const jsonConfig = JSON.stringify(config).replace(/'/g, "'\\''"); // Escape single quotes for shell
667651
const command = `claude mcp add-json --scope user "${name}" '${jsonConfig}'`;
668-
await executeInMcpPoller(sessionId, command);
652+
await execInLoginShell(command);
669653
}
670654

671-
async function removeMcpServer(sessionId: string, name: string) {
655+
async function removeMcpServer(name: string) {
672656
const command = `claude mcp remove "${name}"`;
673-
await executeInMcpPoller(sessionId, command);
657+
await execInLoginShell(command);
674658
}
675659

676-
async function getMcpServerDetails(sessionId: string, name: string) {
660+
async function getMcpServerDetails(name: string) {
677661
try {
678-
const output = await executeInMcpPoller(sessionId, `claude mcp get "${name}"`);
662+
const output = await execInLoginShell(`claude mcp get "${name}"`);
679663

680664
// Parse the output to extract details
681665
const details: any = { name };
@@ -720,27 +704,27 @@ ipcMain.handle("list-mcp-servers", async (_event, sessionId: string) => {
720704
}
721705
});
722706

723-
ipcMain.handle("add-mcp-server", async (_event, sessionId: string, name: string, config: any) => {
707+
ipcMain.handle("add-mcp-server", async (_event, _sessionId: string, name: string, config: any) => {
724708
try {
725-
await addMcpServer(sessionId, name, config);
709+
await addMcpServer(name, config);
726710
} catch (error) {
727711
console.error("Error adding MCP server:", error);
728712
throw error;
729713
}
730714
});
731715

732-
ipcMain.handle("remove-mcp-server", async (_event, sessionId: string, name: string) => {
716+
ipcMain.handle("remove-mcp-server", async (_event, _sessionId: string, name: string) => {
733717
try {
734-
await removeMcpServer(sessionId, name);
718+
await removeMcpServer(name);
735719
} catch (error) {
736720
console.error("Error removing MCP server:", error);
737721
throw error;
738722
}
739723
});
740724

741-
ipcMain.handle("get-mcp-server-details", async (_event, sessionId: string, name: string) => {
725+
ipcMain.handle("get-mcp-server-details", async (_event, _sessionId: string, name: string) => {
742726
try {
743-
return await getMcpServerDetails(sessionId, name);
727+
return await getMcpServerDetails(name);
744728
} catch (error) {
745729
console.error("Error getting MCP server details:", error);
746730
throw error;

0 commit comments

Comments
 (0)