chrome-page-mcp
by ryan-grey
README.md
# Chrome Page MCP
A local MCP server and Chrome extension by Ryan Grey for reading visible webpage
text and operating DOM controls without screenshots or computer control.
## Features
- Find an extension by exact name and request its removal using its verified ID
and name; requires `management` permission. Chrome always displays its native
removal confirmation. Pending requests return immediately; retry the same UUID
for completion and verify absence with `find_extension`. Self-removal and
policy-managed extensions are blocked. No browser settings-page control is added.
- Discover ordinary HTTP(S) tabs, open tabs, and navigate existing tabs.
- Find or save an exact-URL bookmark on the bookmarks bar, including an empty
title for icon-only display; requires Chrome's `bookmarks` permission.
- Read visible text and controls in the top frame, same-origin iframes and open shadow roots.
- Plain, short leaf labels are returned separately as `text_targets` (up to 80)
for SPAs that delegate clicks without button roles or pointer styling. These
are not confirmed interactive controls; use them only for requested actions
and verify the result. Their references retain visibility, disabled-state,
signature, frame and snapshot checks. `unmapped_text` provides bounded
presentation diagnostics without HTML, field values or action references.
- Fill supported fields, select options, and click controls using fresh element references.
- Recognize ARIA tabs, menu items, toggles and compact pointer-styled custom SPA
controls, including those in same-origin frames; custom detection is heuristic
and does not expose every canvas, icon or framework-specific interaction.
- Unlabeled icon controls include bounded visible ancestor context and SVG
class/fragment hints, without input values or image URLs; actions reject
changes to that context (for example, a drive card changing identity).
- Pause all access or exclude individual tabs.
- Keep health checks responsive when a page stalls; page injection reads time out after 10 seconds.
## Install on macOS
Requires Google Chrome, Python 3.11 or newer, and Node.js 20 or newer for DOM tests.
Clone this repository into a local development directory, then run:
```sh
python3 -m venv ~/.local/chrome-page-mcp/.venv
~/.local/chrome-page-mcp/.venv/bin/python -m pip install -r requirements.txt
~/.local/chrome-page-mcp/.venv/bin/python install.py
```
In Chrome, open `chrome://extensions`, enable Developer mode, choose **Load
unpacked**, and select this repository's `extension` directory. Chrome controls
permission approval. The extension ID is `ifbphlbegdjlbokcdbihikoadjianhgl`;
the manifest key is a public extension identity key, not a credential.
Configure your MCP client to launch the virtual environment's Python executable
with the absolute path to `server.py` as its sole argument. For clients accepting
an `mcpServers` configuration, replace both example paths with your actual paths:
```json
{
"mcpServers": {
"chrome-page": {
"command": "/absolute/path/to/.local/chrome-page-mcp/.venv/bin/python",
"args": ["/absolute/path/to/chrome-page-mcp/server.py"]
}
}
}
```
Restart the MCP client after configuration changes. Run
`~/.local/chrome-page-mcp/.venv/bin/python verify_connection.py` to verify the
tool handshake and live extension connection.
After changing extension code, reload it in Chrome Extensions. If the native
connection disconnects, open the extension popup to reconnect.
Page reads include bounded `table_rows` with cell text and visible icon labels
and classes. Screen-reader-only labels are omitted, so an inactive status label
is not mistaken for a rendered status icon. Icon URLs and field values are not
returned. Page-owned `javascript:` links can be activated with a fresh element
reference through a normal DOM click; the tool never accepts or evaluates
caller-supplied JavaScript. Other non-web link schemes remain blocked.
## Tools
| Tool | Purpose |
| --- | --- |
| `connection_status` | Check connection, enabled state and accessible tab count |
| `list_tabs` | List up to 500 accessible HTTP(S) tabs |
| `read_page` | Read bounded visible text and up to 500 controls |
| `act_on_element` | Click, fill or select using a fresh snapshot and element reference |
| `open_tab` | Open an HTTP(S) tab |
| `navigate_tab` | Navigate an accessible tab |
| `exclude_tab` | Exclude a tab for the extension session |
| `find_bar_bookmark` | Find exact-URL bookmarks directly on the bookmarks bar |
| `save_bar_bookmark` | Create or update an exact-URL bookmark; empty title means icon-only |
| `find_extension` | Find installed extensions by exact name and inspect removal eligibility |
| `remove_extension` | Request removal of a verified ID/name through Chrome confirmation |
Custom combobox/option actions use mouse-down and focus where appropriate;
options removed during selection are not clicked again. Formatting-only leaf
labels can appear as bounded text targets. Filled inputs receive focus before
their value setter and input/change events. These are synthetic DOM operations,
so read back the result rather than inferring server acceptance.
Bookmark writes require a user request, honor the access pause, reject non-HTTP(S)
URLs and embedded credentials, and verify Chrome's saved record. Existing exact-URL
bookmarks are updated in place; multiple matches require duplicate resolution.
No bookmark deletion or unrestricted bookmark-tree tool is exposed. After adding
this permission, reload Chrome Page MCP in Chrome Extensions and approve any
Chrome permission prompt. Reload the MCP client to discover the new tools.
Read before an action and again afterward. Use a canonical UUID request ID for
actions/navigation and reuse it when checking a retry. Successful dispatch is
not proof of server acceptance. Outcomes are deduplicated within the current
service-worker lifetime, with a 1,000-action limit; after reloading, inspect the
page before repeating an uncertain action.
Status, tab discovery and reads run independently of the mutation queue.
Page injection reads return `page_read_timeout` after 10 seconds; the timeout
does not cancel Chrome's underlying read. Queued mutations expire before dispatch
after 15 seconds. In-flight mutations are never automatically retried.
DOM traversal prunes hidden subtrees and stops at a 2.5-second traversal deadline,
returning `truncated` when a bound is reached. Unlabeled custom icons may receive
synthetic DOM hover events on nearby ancestors before a click; labelled dropdowns
do not. These heuristics do not guarantee access to every custom interface.
Intermittent `bridge_request_uncertain` and `page_read_timeout` results remain
possible even when an action has taken effect. Inspect the resulting state before
another action; do not assume failure or repeat an uncertain mutation. A tab's
foreground/background state may affect responsiveness, but that cause has not
been established. Navigating to the same URL is not a reliable refresh.
## Access and privacy
Access covers ordinary HTTP(S) tabs after the user approves the extension.
The popup can pause access. This is not authorization to send messages, make
purchases or perform other actions outside the user's request; webpage content
is untrusted data.
No screenshot, mouse/keyboard control, debugger, cookie, history, password
retrieval or arbitrary JavaScript tool is exposed. Input values are omitted,
and password/file/hidden/OTP/payment-card fields are excluded from supported
controls. Cross-origin iframes, closed shadow roots, canvas interfaces, browser
internal pages, Chrome Web Store and native dialogs are unsupported.
The native host uses an owner-only Unix socket at
`~/.local/share/chrome-page-mcp/bridge.sock` (directory mode 700, socket mode 600).
There is no TCP listener or cloud service. One Chrome profile owns the bridge
at a time; multiple local MCP clients can connect. The bridge does not persist
page text or form values, but client conversation logs may retain tool results.
## Verification
Page reads wait for 250 ms without DOM changes, bounded at 1.5 seconds and
64 accessible documents. `dom_settled` reports whether that quiet period was
reached; it does not confirm that a server accepted a preceding action.
Read the resulting controls and verify server state before retrying a submission.
```sh
~/.local/chrome-page-mcp/.venv/bin/python -m pytest -q
npm ci --ignore-scripts
npm test
```
For an end-to-end check, serve `fixture/` on loopback using
`python3 -m http.server 8765 --bind 127.0.0.1 --directory fixture`, then run
`~/.local/chrome-page-mcp/.venv/bin/python verify_fixture.py http://127.0.0.1:8765/`
in another terminal. This opens a harmless fixture tab and checks reads,
fill/select/click, navigation, deduplication and exclusion; stop the HTTP server
when finished.
The automated suite includes stalled-injection isolation, native framing,
socket round trips, hidden-field filtering, stale snapshots and iframe controls.
## License
MIT. See [LICENSE](LICENSE).
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues