Summary
On a global npm install, claude-in-chrome can never connect. The code builds its native-messaging host path and its MCP server argv from dist/cli.js, but the published package ships only dist/cli.mjs. Both consumers therefore point at a file that does not exist.
The user-visible symptom is Failed to reconnect to claude-in-chrome in /mcp. This happens with Chrome uninvolved — the MCP server process dies on module resolution before any browser interaction is attempted, so the error is easy to misattribute to the extension or to the browser install.
Environment
|
|
| Package |
@gitlawb/openclaude 0.28.0 (current latest on npm) |
| Install |
global npm — $PREFIX/lib/node_modules/@gitlawb/openclaude |
| Node |
v22 |
| OS |
Debian 13, x86_64 |
Paths below are written as $PREFIX (npm global prefix, e.g. /usr or /usr/local) and $HOME.
Reproduce
$ ls $PREFIX/lib/node_modules/@gitlawb/openclaude/dist/
cli.mjs
sdk.mjs
There is no cli.js. But the generated wrapper references one:
$ cat $HOME/.openclaude/chrome/chrome-native-host
#!/bin/sh
# Chrome native host wrapper script
# Generated by Claude Code - do not edit manually
exec "/path/to/node" "$PREFIX/lib/node_modules/@gitlawb/openclaude/dist/cli.js" --chrome-native-host
Running what the MCP server config specifies fails immediately:
$ node $PREFIX/lib/node_modules/@gitlawb/openclaude/dist/cli.js --claude-in-chrome-mcp
node:internal/modules/cjs/loader:1433
throw err;
^
Error: Cannot find module '$PREFIX/lib/node_modules/@gitlawb/openclaude/dist/cli.js'
Root cause
dist/cli.mjs, in the non-bundled branch of setupClaudeInChrome():
let __filename3 = fileURLToPath9(import.meta.url),
__dirname2 = join146(__filename3, ".."),
cliPath = join146(__dirname2, "cli.js"); // <-- shipped file is cli.mjs
return createWrapperScript(`"${process.execPath}" "${cliPath}" --chrome-native-host`)
.then((manifestBinaryPath) => installChromeNativeHostManifest(manifestBinaryPath))
.catch((e) => logForDebugging(`[Claude in Chrome] Failed to install native host: ${e}`, {level:"error"})),
{
mcpConfig: {
[CLAUDE_IN_CHROME_MCP_SERVER_NAME]: {
type: "stdio",
command: process.execPath,
args: [`${cliPath}`, "--claude-in-chrome-mcp"], // <-- same bad path
scope: "dynamic",
...
}
},
...
}
The same cliPath feeds both the native-host wrapper and the MCP server argv, so a single hardcoded extension breaks both halves.
The bundled branch is unaffected — it uses command: process.execPath, args: ["--claude-in-chrome-mcp"] with no cliPath, which is presumably why this survives testing.
Two consequences worth noting:
- The path is recomputed from
import.meta.url on every startup and the wrapper is rewritten, so editing $HOME/.openclaude/chrome/chrome-native-host by hand does not stick.
createWrapperScript swallows its failure into logForDebugging(..., {level:"error"}), so nothing surfaces at the point of breakage.
Suggested fix
Derive the filename rather than hardcoding it:
let __filename3 = fileURLToPath9(import.meta.url),
cliPath = __filename3; // this file *is* the CLI entrypoint
Or resolve against the package's bin/main. A cli.js → cli.mjs symlink in dist/ would also satisfy both consumers and survive regeneration, but that treats the symptom.
Minor, adjacent: __dirname2 = join146(__filename3, "..") joins a file path with "..". It happens to normalize correctly, but dirname() is what's meant.
Scope of what I verified
I confirmed the module-resolution failure and the two call sites it breaks. I could not verify the full connection path downstream of the fix — this host has no Chrome installed, and pairing requires an interactive claude.ai login in the browser profile.
For transparency: invoking the correct file directly does get past module resolution and into application code, then fails differently in my environment —
TypeError: Cannot read properties of null (reading 'connect')
at runClaudeInChromeMcpServer (.../dist/cli.mjs:1858:11635)
That is very likely just an artifact of running the server standalone with stdin closed and no extension present, rather than a second defect. Flagging it only so the path fix isn't assumed to be a complete end-to-end validation.
Summary
On a global npm install,
claude-in-chromecan never connect. The code builds its native-messaging host path and its MCP server argv fromdist/cli.js, but the published package ships onlydist/cli.mjs. Both consumers therefore point at a file that does not exist.The user-visible symptom is
Failed to reconnect to claude-in-chromein/mcp. This happens with Chrome uninvolved — the MCP server process dies on module resolution before any browser interaction is attempted, so the error is easy to misattribute to the extension or to the browser install.Environment
@gitlawb/openclaude0.28.0 (currentlateston npm)$PREFIX/lib/node_modules/@gitlawb/openclaudePaths below are written as
$PREFIX(npm global prefix, e.g./usror/usr/local) and$HOME.Reproduce
There is no
cli.js. But the generated wrapper references one:Running what the MCP server config specifies fails immediately:
Root cause
dist/cli.mjs, in the non-bundled branch ofsetupClaudeInChrome():The same
cliPathfeeds both the native-host wrapper and the MCP server argv, so a single hardcoded extension breaks both halves.The bundled branch is unaffected — it uses
command: process.execPath, args: ["--claude-in-chrome-mcp"]with nocliPath, which is presumably why this survives testing.Two consequences worth noting:
import.meta.urlon every startup and the wrapper is rewritten, so editing$HOME/.openclaude/chrome/chrome-native-hostby hand does not stick.createWrapperScriptswallows its failure intologForDebugging(..., {level:"error"}), so nothing surfaces at the point of breakage.Suggested fix
Derive the filename rather than hardcoding it:
Or resolve against the package's
bin/main. Acli.js→cli.mjssymlink indist/would also satisfy both consumers and survive regeneration, but that treats the symptom.Minor, adjacent:
__dirname2 = join146(__filename3, "..")joins a file path with"..". It happens to normalize correctly, butdirname()is what's meant.Scope of what I verified
I confirmed the module-resolution failure and the two call sites it breaks. I could not verify the full connection path downstream of the fix — this host has no Chrome installed, and pairing requires an interactive claude.ai login in the browser profile.
For transparency: invoking the correct file directly does get past module resolution and into application code, then fails differently in my environment —
That is very likely just an artifact of running the server standalone with stdin closed and no extension present, rather than a second defect. Flagging it only so the path fix isn't assumed to be a complete end-to-end validation.