From 4002a26e0bc894bfe6c8f84c01adfa987ee7a57d Mon Sep 17 00:00:00 2001 From: PavelA85 Date: Tue, 21 Apr 2026 19:26:18 +0300 Subject: [PATCH 1/2] fix(mcp-server): resolve windows native host collision and CLI detection - Ensure each browser has a unique manifest directory on Windows - Use shell: true in spawnSync to correctly find .cmd CLIs on Windows --- packages/mcp-server/src/doctor/checks/claude-mcp.ts | 12 ++++++++++-- packages/mcp-server/src/doctor/checks/codex-mcp.ts | 12 ++++++++++-- packages/mcp-server/src/setup/configure-claude.ts | 8 +++++++- packages/mcp-server/src/setup/configure-codex.ts | 8 +++++++- packages/mcp-server/src/store/path.ts | 4 ++-- 5 files changed, 36 insertions(+), 8 deletions(-) diff --git a/packages/mcp-server/src/doctor/checks/claude-mcp.ts b/packages/mcp-server/src/doctor/checks/claude-mcp.ts index 7a94e7e..59b1f19 100644 --- a/packages/mcp-server/src/doctor/checks/claude-mcp.ts +++ b/packages/mcp-server/src/doctor/checks/claude-mcp.ts @@ -2,7 +2,11 @@ import { spawnSync } from 'node:child_process'; import type { CheckResult } from '../types.js'; export async function checkClaudeMcpRegistration(): Promise { - const exists = spawnSync('claude', ['--version'], { stdio: 'pipe', encoding: 'utf8' }); + const exists = spawnSync('claude', ['--version'], { + stdio: 'pipe', + encoding: 'utf8', + shell: process.platform === 'win32', + }); if (exists.status !== 0) { return { name: 'mcp.claude', @@ -12,7 +16,11 @@ export async function checkClaudeMcpRegistration(): Promise { }; } - const list = spawnSync('claude', ['mcp', 'list'], { stdio: 'pipe', encoding: 'utf8' }); + const list = spawnSync('claude', ['mcp', 'list'], { + stdio: 'pipe', + encoding: 'utf8', + shell: process.platform === 'win32', + }); if (list.status === 0 && `${list.stdout}${list.stderr}`.includes('onui-local')) { return { name: 'mcp.claude', diff --git a/packages/mcp-server/src/doctor/checks/codex-mcp.ts b/packages/mcp-server/src/doctor/checks/codex-mcp.ts index 78d1981..505fdd3 100644 --- a/packages/mcp-server/src/doctor/checks/codex-mcp.ts +++ b/packages/mcp-server/src/doctor/checks/codex-mcp.ts @@ -2,7 +2,11 @@ import { spawnSync } from 'node:child_process'; import type { CheckResult } from '../types.js'; export async function checkCodexMcpRegistration(): Promise { - const exists = spawnSync('codex', ['--version'], { stdio: 'pipe', encoding: 'utf8' }); + const exists = spawnSync('codex', ['--version'], { + stdio: 'pipe', + encoding: 'utf8', + shell: process.platform === 'win32', + }); if (exists.status !== 0) { return { name: 'mcp.codex', @@ -12,7 +16,11 @@ export async function checkCodexMcpRegistration(): Promise { }; } - const list = spawnSync('codex', ['mcp', 'list'], { stdio: 'pipe', encoding: 'utf8' }); + const list = spawnSync('codex', ['mcp', 'list'], { + stdio: 'pipe', + encoding: 'utf8', + shell: process.platform === 'win32', + }); if (list.status === 0 && `${list.stdout}${list.stderr}`.includes('onui-local')) { return { name: 'mcp.codex', diff --git a/packages/mcp-server/src/setup/configure-claude.ts b/packages/mcp-server/src/setup/configure-claude.ts index b1b99e9..c122566 100644 --- a/packages/mcp-server/src/setup/configure-claude.ts +++ b/packages/mcp-server/src/setup/configure-claude.ts @@ -7,7 +7,11 @@ export interface ClaudeConfigResult { } function commandExists(command: string): boolean { - const result = spawnSync(command, ['--version'], { stdio: 'pipe', encoding: 'utf8' }); + const result = spawnSync(command, ['--version'], { + stdio: 'pipe', + encoding: 'utf8', + shell: process.platform === 'win32', + }); return result.status === 0; } @@ -23,6 +27,7 @@ export function configureClaudeMcp(cliPath: string): ClaudeConfigResult { spawnSync('claude', ['mcp', 'remove', 'onui-local', '--scope', 'user'], { stdio: 'pipe', encoding: 'utf8', + shell: process.platform === 'win32', }); const add = spawnSync( @@ -31,6 +36,7 @@ export function configureClaudeMcp(cliPath: string): ClaudeConfigResult { { stdio: 'pipe', encoding: 'utf8', + shell: process.platform === 'win32', } ); diff --git a/packages/mcp-server/src/setup/configure-codex.ts b/packages/mcp-server/src/setup/configure-codex.ts index 59cf254..d1bae27 100644 --- a/packages/mcp-server/src/setup/configure-codex.ts +++ b/packages/mcp-server/src/setup/configure-codex.ts @@ -7,7 +7,11 @@ export interface CodexConfigResult { } function commandExists(command: string): boolean { - const result = spawnSync(command, ['--version'], { stdio: 'pipe', encoding: 'utf8' }); + const result = spawnSync(command, ['--version'], { + stdio: 'pipe', + encoding: 'utf8', + shell: process.platform === 'win32', + }); return result.status === 0; } @@ -23,11 +27,13 @@ export function configureCodexMcp(cliPath: string): CodexConfigResult { spawnSync('codex', ['mcp', 'remove', 'onui-local'], { stdio: 'pipe', encoding: 'utf8', + shell: process.platform === 'win32', }); const add = spawnSync('codex', ['mcp', 'add', 'onui-local', '--', 'node', cliPath, 'mcp'], { stdio: 'pipe', encoding: 'utf8', + shell: process.platform === 'win32', }); if (add.status !== 0) { diff --git a/packages/mcp-server/src/store/path.ts b/packages/mcp-server/src/store/path.ts index 54ac9d0..4391df7 100644 --- a/packages/mcp-server/src/store/path.ts +++ b/packages/mcp-server/src/store/path.ts @@ -68,9 +68,9 @@ export function getNativeHostDir( return join(configHome, browserDir, 'NativeMessagingHosts'); } case 'win32': - return join(getDataDir(platform), 'native-host'); + return join(getDataDir(platform), 'native-host', browser); default: - return join(getDataDir(platform), 'native-host'); + return join(getDataDir(platform), 'native-host', browser); } } From 5eae696e6a93e725856041b7baaa3a39e6d63333 Mon Sep 17 00:00:00 2001 From: Tushar Shukla Date: Tue, 28 Apr 2026 16:51:54 +0200 Subject: [PATCH 2/2] test(mcp-server): cover win32 native host manifest paths Asserts Chrome, Edge, and Firefox resolve to distinct manifest paths on Windows, locking in the collision fix from this PR. Co-Authored-By: Claude Opus 4.6 --- packages/mcp-server/src/store/path.test.ts | 25 ++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/packages/mcp-server/src/store/path.test.ts b/packages/mcp-server/src/store/path.test.ts index f915c22..ded4bbf 100644 --- a/packages/mcp-server/src/store/path.test.ts +++ b/packages/mcp-server/src/store/path.test.ts @@ -41,4 +41,29 @@ describe('path resolver', () => { 'HKCU\\Software\\Mozilla\\NativeMessagingHosts\\com.onui.native' ); }); + + it('resolves browser-specific win32 native host manifest paths', () => { + const originalAppData = process.env.APPDATA; + process.env.APPDATA = 'C:\\Users\\onui\\AppData\\Roaming'; + + try { + const chromePath = getNativeHostManifestPath('win32', 'chrome'); + const edgePath = getNativeHostManifestPath('win32', 'edge'); + const firefoxPath = getNativeHostManifestPath('win32', 'firefox'); + + expect(new Set([chromePath, edgePath, firefoxPath]).size).toBe(3); + expect(chromePath.split(/[\\/]+/)).toContain('chrome'); + expect(edgePath.split(/[\\/]+/)).toContain('edge'); + expect(firefoxPath.split(/[\\/]+/)).toContain('firefox'); + expect(chromePath).toContain('com.onui.native.json'); + expect(edgePath).toContain('com.onui.native.json'); + expect(firefoxPath).toContain('com.onui.native.json'); + } finally { + if (originalAppData === undefined) { + delete process.env.APPDATA; + } else { + process.env.APPDATA = originalAppData; + } + } + }); });