Skip to content

feat(mcp): connect the user's signed-in Chrome through opencli-mcp - #5638

Draft
Astro-Han wants to merge 4 commits into
apache:mainfrom
Astro-Han:feat/mcp-opencli-chrome
Draft

Astro-Han wants to merge 4 commits into
apache:mainfrom
Astro-Han:feat/mcp-opencli-chrome

Conversation

@Astro-Han

Copy link
Copy Markdown
Contributor

Summary

Maka's built-in browser tools drive the conversation's embedded browser, which has none of the user's logins. This PR adds the other half: an MCP connection to the user's own signed-in Chrome through opencli-mcp, bundled with Maka and set up from the MCP page.

Flow. 扩展 → MCP → 推荐 → Chrome +. The click stores a stdio connection and opens the Chrome Web Store page for the extension in Chrome. The row shows 等待 Chrome (attention) and the detail keeps a 连接 Chrome action until the extension reaches the host. The page polls the host every 2 s while that is pending, so nothing needs refreshing.

Why one store click remains. Chrome installs consumer extensions only from the Web Store; silent install exists only as enterprise policy. Everything else opencli-mcp setup does now runs inside Maka:

  • No Node install. Maka runs opencli-mcp on its own Electron binary with ELECTRON_RUN_AS_NODE. Two launchers under userData/opencli-mcp/ are rewritten on every start, and they are the only paths stored in mcp.json and in Chrome's host manifest. The executable path itself moves on every AppImage launch.
  • Maka writes the Native Messaging manifest itself instead of calling opencli-mcp's registerHost. That function's launcher runs process.execPath without ELECTRON_RUN_AS_NODE, which under Electron would start a Maka window. The directory list, profile detection and identifiers are imported from the package, not copied. A manifest that points at another live install (for example a global opencli-mcp setup) is left alone; the stdio server reaches whichever host Chrome starts. Registration writes into Chrome's directories only when the user clicks.
  • Chrome status comes from the host's /health, not from the MCP connection. Without the extension, the server still starts in its embedded runtime and lists all 16 tools. Showing that as "connected" would hide the missing step until a tab_* call fails.

The built-in browser_* tools are unchanged. The two cover different scenarios: the embedded browser is visible inside Maka and has no logins; this connection drives the user's own Chrome.

Packaging. opencli-mcp 0.0.12 is pinned exactly and has no type declarations, so the few imported modules are declared in opencli-mcp.d.ts. It ships its adapter SDK inside itself and links it with file:, which npm locks without a version. The notice generator now reads such bundled packages from their installed manifest. The SDK declares MIT but has no LICENSE file, so it gets an exact-version copyright override.

Verification

  • opencli-chrome.test.ts: the POSIX launchers actually run an entry in Node mode, including a path that contains a quote (stdio and host --native, with Chrome's appended origin ignored); Windows .cmd content; manifest targeting (Chrome always, other browsers only when their profile exists, a stale manifest overwritten, another live install kept).
  • module-hub-mcp-controller.test.ts: a configured Chrome server is polled until the extension connects, and polling then stops. mcp-preload-scope.test.ts covers the two new channels.
  • Stories: ExtensionsMcpChrome (the click adds the server, starts connecting once, leaves 推荐) and ExtensionsMcpChromeWaiting (the row shows 等待 Chrome and counts as needing attention, not "16 个工具"). Storybook smoke passes: 409 stories, 443 theme renders.
  • Packaged app: electron-builder --mac dir, then ELECTRON_RUN_AS_NODE=1 Maka.app/Contents/MacOS/Maka app.asar/node_modules/opencli-mcp/dist/src/main.js stdio initializes and runs sites_search (adapters load from the asar).
  • Dev app with an isolated profile, driven over CDP: the launchers are written at startup; adding the server through window.maka.mcp.add connects with 16 tools and chromeStatus().connected === false.
  • typecheck, typecheck:stories, format, lint, check:third-party-notices, audit-shipped-dependencies, windows:inventory.

Not run: Chrome → opencli-mcp-host → extension with the real Web Store extension installed; that needs a user to click Add to Chrome. Windows and Linux were not run locally. E2E is left to CI.

MCP recommendations, light
MCP recommendations, dark
Chrome detail after adding, light
Chrome detail after adding, dark

AI use

  • No generative tool made a substantive contribution
  • Generative tooling made a substantive contribution

Tool(s) and scope: Claude Code: investigation, implementation, tests, and this description.

Checklist

  • Tests cover the change and fail without it
  • Lint, format, typecheck and the affected suites pass locally

Does this PR entail a change in behavior?

  • Yes — described under Summary above
  • No

Add Chrome as an MCP recommendation backed by the bundled opencli-mcp.
One click stores a stdio connection and sends the user to the Chrome Web
Store for the extension; the detail keeps a Connect Chrome action and
follows the extension until it reaches the host.

Chrome only installs extensions from the store, so that click cannot be
removed. Everything else that `opencli-mcp setup` does runs inside Maka:

- Maka runs opencli-mcp on its own Electron binary in Node mode, so users
  need no Node install. Two launchers under userData are rewritten on every
  start and are the only paths mcp.json and Chrome's host manifest hold;
  the executable itself moves on every AppImage launch.
- Maka writes the Native Messaging manifest itself instead of calling
  opencli-mcp's registerHost, whose launcher runs process.execPath without
  ELECTRON_RUN_AS_NODE and would open a Maka window. Directory lists and
  identifiers are reused from the package. A manifest owned by another live
  install is left alone.
- Chrome status comes from the host's health endpoint, not the MCP status:
  without the extension the server still starts in its embedded runtime and
  lists every tool.

The built-in browser tools stay as they are: they drive the conversation's
embedded browser without the user's logins; this connection drives the
user's own Chrome.

The notice generator now reads packages that a registry dependency ships
inside itself and links by `file:`, which npm records without a version.

Generated-by: Claude Code
@github-actions github-actions Bot added the effort/L Under 1000 readable lines label Sep 23, 2026
A new desktop shared file grew the legacy AppShell renderer closure, which the
architecture ratchet forbids. The status is part of the MCP bridge surface, so
it lives with the other MCP bridge types.

Generated-by: Claude Code
The Chrome native host registration calls reg.exe on Windows.

Generated-by: Claude Code
… package

opencli-mcp parses JavaScript through web-tree-sitter and loads only the
grammar's wasm. The rebuilt native binding carried MSBuild intermediates
under app.asar.unpacked whose paths exceed MAX_PATH once installed, which
broke the Windows sandbox ACL walk and the uninstaller cleanup in the
installer lifecycle check.

Generated-by: Claude Code

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/L Under 1000 readable lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant