--- name: "rapp-copilot-in-chrome" description: | Drive the user's real, logged-in Chrome browser from GitHub Copilot CLI — navigate, click, type, screenshot, read the accessibility tree or page text, run JavaScript, and inspect console and network traffic. Use when the user asks to open or browse a site, click through a web UI, fill a form, scrape or read a page, debug a web app in the browser, take a screenshot of a page, or automate anything in Chrome. Also use for "rapp copilot in chrome", "browser automation", "drive chrome", "open a tab", "check the console", or to install, verify, or diagnose the bridge itself. --- # rapp-copilot-in-chrome Browser automation against the browser you **actually use** — real profile, live cookies, authenticated sessions. Not a throwaway headless instance. ## Architecture ``` Copilot CLI (MCP client) -> ~/.copilot/bin/rapp-copilot-in-chrome (launcher shim) -> claude --claude-in-chrome-mcp (self-contained stdio MCP server) -> native host com.anthropic.claude_code_browser_extension -> Chrome extension fcoeoabgfenejglbffodgkkbkcdhcgfn -> live tabs ``` The MCP server is self-contained: it does **not** require a Claude Code session to be running. It needs only the Claude binary (which hosts the bridge) plus the Chrome extension installed and connected. That is the whole trick — the bridge was always a plain stdio MCP server, so any MCP client can drive it. ## Run this — do not improvise Setup and health checks have a **deterministic implementation** shipped next to this file as `rapp_copilot_in_chrome_agent.py`. It is stdlib-only Python with no install step. When asked to install, verify, or diagnose the bridge, execute it and use its output verbatim — do not guess at the state of the system: ```bash python3 rapp_copilot_in_chrome_agent.py '{"action": ""}' # doctor | status | install | uninstall python3 rapp_copilot_in_chrome_agent.py '{"action": "doctor"}' # full check + live Chrome round trip python3 rapp_copilot_in_chrome_agent.py '{"action": "status"}' # fast check, no browser traffic python3 rapp_copilot_in_chrome_agent.py '{"action": "install"}' # launcher + MCP registration + skill python3 rapp_copilot_in_chrome_agent.py --tool # emit the JSON tool contract ``` `doctor` verifies all seven links in the chain — Claude binary, native-host manifest, Chrome extension, launcher, MCP registration, MCP handshake, and a live round trip that reaches a real tab. ## Loading the tools (do this first) The 22 browser tools arrive **deferred** in Copilot CLI. Load them with a single tool-search call, batching every tool you expect to need — each extra search costs a round trip: ``` tool_search_tool pattern: "rapp-copilot-in-chrome" ``` Or target a subset: `"tabs_context_mcp|browser_batch|navigate|read_page|computer"`. ## The two rules that matter 1. **Always call `tabs_context_mcp` with `createIfEmpty: true` first.** Nearly every other tool requires a `tabId`, and tabs only exist inside this session's tab group. Skip it and you get `No tab available`. 2. **Prefer `browser_batch` over single calls.** It runs a whole sequence in ONE round trip, executing sequentially and stopping on the first error. Batch whenever you can predict two or more steps ahead. ```jsonc // 1. get a tabId tabs_context_mcp { "createIfEmpty": true } // -> {"availableTabs":[{"tabId":1363872857,...}],"tabGroupId":249531617} // 2. batch the rest browser_batch { "actions": [ { "name": "navigate", "input": { "url": "https://example.com", "tabId": 1363872857 } }, { "name": "get_page_text", "input": { "tabId": 1363872857 } } ] } ``` ## Tools (22) **Tabs** — `tabs_context_mcp` (list/create group), `tabs_create_mcp`, `tabs_close_mcp` **Navigation** — `navigate` (url, or back/forward), `resize_window` **Reading** — `get_page_text` (article text; best for reading), `read_page` (accessibility tree with `ref_id`s; supports `filter: interactive`, `depth`, `max_chars`), `find` (natural-language element lookup returning refs) **Interaction** — `computer` (screenshot, click, type, key, scroll, drag by coordinate), `form_input` (set a value by `ref`), `file_upload` (by `ref` — never click a file input, it opens a native dialog you cannot see), `upload_image` **Scripting** — `javascript_tool` (REPL semantics in page context: top-level `await` works and the last expression is returned — do not write `return`) **Debugging** — `read_console_messages` (`pattern` filter), `read_network_requests` (`urlPattern` filter) **Orchestration** — `browser_batch` **Recording** — `gif_creator` (start/stop/export an animated GIF of the session) **Shortcuts** — `shortcuts_list`, `shortcuts_execute` **Browser selection** — `list_connected_browsers`, `select_browser` (by deviceId), `switch_browser` (broadcast a pairing request, wait for the user to click Connect) > **Network tracking is lazy.** `read_network_requests` only begins recording the first time it is > called on a tab, so a page that already loaded shows nothing. Call it once *before* navigating or > triggering the requests you want, then call it again to read them. Requests also clear when the > tab navigates to a different domain. ## Choosing a reading tool - Reading an article, docs page, or any prose -> `get_page_text` - Need to click or fill something -> `find` (natural language) or `read_page` with `filter: "interactive"` to get `ref`s - Need pixel coordinates, or the page is canvas/visual -> `computer` screenshot first - `read_page` truncates at 50k chars; narrow with `ref_id` or `depth` rather than raising `max_chars` blindly ## Safety This drives the user's **actual** browser with their **real** authenticated sessions. Anything it clicks, submits, purchases, sends, or deletes happens as the user. Confirm before any destructive or irreversible action — sending messages, submitting payments, deleting data, changing account settings. Prefer opening a fresh tab per task rather than reusing tabs the user is actively working in. ## Troubleshooting | Symptom | Fix | | --- | --- | | `No tab available` | Call `tabs_context_mcp { createIfEmpty: true }` first, then pass `tabId`. | | `Cannot access a chrome:// URL` | New tabs start on `chrome://newtab`; navigate somewhere real first. | | `read_network_requests` empty | Tracking starts on first call — call it, then navigate/act, then read. | | Tools not listed | They are deferred — run the tool search above. | | Server missing | `copilot mcp get rapp-copilot-in-chrome` should report `Status: Enabled`. | | Cannot locate `claude` | Set `RAPP_CHROME_CLAUDE_BIN` to its absolute path. | | Browser not responding | Extension may be disconnected — `list_connected_browsers`, then `select_browser` or `switch_browser`. | Run `python3 rapp_copilot_in_chrome_agent.py '{"action": "doctor"}'` before debugging by hand — it names the exact broken link in the chain. ## Parameters The typed contract this capability answers to (JSON Schema — the deterministic layer): ```json { "type": "object", "properties": { "action": { "type": "string", "description": "Derived from `` used in the documented command at line 30." } }, "required": [] } ``` ## Deterministic steps Lifted verbatim from the procedure above by `toaster.py toast`. Run them in order, substituting the typed parameters; do not paraphrase: ```bash python3 rapp_copilot_in_chrome_agent.py '{"action": "doctor"}' python3 rapp_copilot_in_chrome_agent.py '{"action": ""}' # doctor | status | install | uninstall python3 rapp_copilot_in_chrome_agent.py '{"action": "doctor"}' # full check + live Chrome round trip python3 rapp_copilot_in_chrome_agent.py '{"action": "status"}' # fast check, no browser traffic python3 rapp_copilot_in_chrome_agent.py '{"action": "install"}' # launcher + MCP registration + skill python3 rapp_copilot_in_chrome_agent.py --tool # emit the JSON tool contract ```