Skip to content

claude-in-chrome never connects on npm installs (dist/cli.js vs dist/cli.mjs) #2128

Description

@possiblyneal

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.jscli.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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions