#2219

feat(mcp): add --lock-launch so the host owns launch settings

OpenNyarlathotepppppwants to merge intofrom

The MCP client can set namespace, idleTimeout, headed, restore* and caCert on every tool call. extraArgs also still accepts --namespace, --idle-timeout, --restore and --headed (#2181 blocked --profile and --executable-path, but not these). So when the host starts agent-browser mcp with its own AGENT_BROWSER_NAMESPACE, AGENT_BROWSER_IDLE_TIMEOUT_MS and a clean profile, it cannot keep them. An agent passes idleTimeout: "0" or another namespace, and the host's idle cleanup does not reach that browser anymore.

We run many LLM agents in parallel on one MCP server and hit this: about 50 headless Chromes left over, 40 GB RSS on a 16 GB Mac. --allowed-domains blocks some launch options, but it needs a domain list, so it does not fit open-web research.

Repro

Server: AGENT_BROWSER_NAMESPACE=agents AGENT_BROWSER_IDLE_TIMEOUT_MS=600000 agent-browser mcp

{"name": "agent_browser_open", "arguments": {"url": "https://example.com", "idleTimeout": "0"}}
{"name": "agent_browser_open", "arguments": {"url": "https://example.com", "namespace": "other"}}
{"name": "agent_browser_snapshot", "arguments": {"extraArgs": ["--idle-timeout", "0"]}}

All three are accepted. The daemon runs with idle shutdown off, or in a namespace the host does not clean.

What this changes

New opt-in agent-browser mcp --lock-launch (or AGENT_BROWSER_MCP_LOCK_LAUNCH=1):

  • tool schemas drop namespace, idleTimeout, restore*, caCert, clearCaCert, useSystemCa, headed, webgpu, webmcp. A call that still sends them gets invalid-params with a clear message.
  • extraArgs also rejects --namespace, --idle-timeout, --restore*, --headed, --webgpu, --no-webmcp, --engine. Same check style as #2181, only a longer list.
  • agent_browser_batch rejects inner commands (typed commands, and positional extraArgs, which the CLI runs instead of them) that run connect or carry those flags. Today batch inner commands ignore global flags, but connect works there, and #1268 may add inline flags later.
  • agent_browser_connect, agent_browser_plugin_add and agent_browser_chat are hidden. connect attaches another browser, plugin_add can write a launch.mutate or browser.provider plugin into the config, and chat runs its own commands (connect is in its allow list).
  • agent_browser_tools_profiles counts and restart hints follow the lock.
  • session still works, so each agent keeps its own browser inside the host's namespace and idle timeout.

Without the flag nothing changes. I did not do it like allowedDomains in #2181 (reject when env already sets it), because a per-call headed override of AGENT_BROWSER_HEADED is documented and tested, so that would break people. If you prefer another name or a different list, I can change it.

Not covered here: like the #2181 guard, a typed value that looks like a flag (#1997) can still pass one through, for example url: "--idle-timeout" with extraArgs: ["0"]. #2040 works on that. Also this is not a sandbox: the debug profile still has install, upgrade, doctor --fix and dashboard, so do not give that profile to agents you do not trust.

Docs: --help, README, docs site (commands, configuration), core skill and its reference. No changelog entry, since AGENTS.md says it is written at release.

Tests

  • new unit tests in cli/src/mcp.rs: flag and env parsing, locked schemas (connect, plugin_add, chat hidden), locked fields / extraArgs flags / hidden tools rejected, batch with connect or locked flags rejected, profile counts and hints, session and normal extraArgs / batch still pass
  • cargo fmt --check, cargo clippy -- -D warnings, cargo test --profile ci: pass (1486 passed, main was 1480). One run hit native::actions::tests::test_execute_unknown_command failing with "Invalid CDP target: invalid-cdp-target": it reads AGENT_BROWSER_CDP without the env guard while the next test sets it. Not related to this change: it also fails with only the native::actions::tests::test_execute* tests selected, and reruns are green.
  • pnpm --filter docs test, type-check, build, test:routes for production and preview: pass
  • pnpm run test:launcher, check-version-sync.js: pass
  • by hand, built binary over stdio with --lock-launch: tools/list has no locked fields, the repro calls above, connect, and batch [["connect","9222"]] get invalid-params, no daemon starts

macOS arm64, Rust 1.99.1, Node 26 (CI uses 24). I did not run the ignored e2e tests, this does not touch the daemon.

Activity

6
vercel[bot]5d ago

@Nyarlathoteppppp is attempting to deploy a commit to the Vercel Labs Team on Vercel.

A member of the Team first needs to authorize it.

vercel[bot]reviewed5d ago
Nyarlathotepppppreviewed4d ago
vercel[bot]reviewed4d ago