Skip to main content
Glama
joshghal

holoscrape-mcp

by joshghal
README.md
# holoscrape-mcp

Give an AI agent your own logged-in browser. This runs locally, does not upload anything, and never
sees your passwords — the [HoloScrape](https://github.com/joshghal/holoscrape) Chrome extension does
the actual reading, in your own signed-in Chrome, and this process is only the bridge that lets an
MCP-speaking agent (Claude Code, Claude Desktop, Cursor, Windsurf) ask it to.

## Use it

You do not install this — your agent's MCP config runs it on demand:

```json
{
  "mcpServers": {
    "holoscrape": { "command": "npx", "args": ["-y", "holoscrape-mcp@latest"] }
  }
}
```

Then open the HoloScrape side panel in Chrome once. The extension finds your agent's server on its own
— there is no code to copy or type. After the first connection it reconnects by itself whenever the
browser and your agent are both running, and Claude Code, Claude Desktop and Cursor can all be
connected at the same time.

**Cursor:** press ⌘⇧P and run **Open MCPs** — it opens the Customize editor on its MCPs tab (recent
versions have no MCP entry in Settings). Click **New MCP Server**, which opens `mcp.json`, paste the
block above and save. Or edit `~/.cursor/mcp.json` (every project) / `.cursor/mcp.json` (one project)
directly. `holoscrape` should then appear in that list with its tools; if not, run **Developer: Reload
Window**. Cursor starts the server itself, so nothing else needs to be running. `@latest` matters: without it npx keeps reusing
whatever version it cached first.

### Unlock the full HoloScrape MCP: the headless companion (one more command, once)

```
npx -y holoscrape-mcp@latest --install-browser
```

This downloads Chromium for Testing (about 150 MB) through Playwright. It is the only step that
downloads anything, and it is never done silently — you run it. The command checks first: if the
browser this version needs is already there it downloads nothing, and after a download it confirms
the server can find it. Run it again after updating holoscrape-mcp. `npx -y holoscrape-mcp@latest
--check-browser` only reports (exit 0 = ready), and the server prints the same check when it starts.

What it unlocks: a **companion** browser the server launches itself, headless, with the same
extension loaded and **no session in it**. Your own Chrome stays the default and the only place a
signed-in site is ever read. The companion steps in, on its own, when a lane in your Chrome cannot
paint a page: DevTools open on the tab, or a managed Chrome that blocks the debugger (a policy
Chrome 155 ships in October 2026). It also steps back out: a page that wants your login is bounced
in the companion and re-run in your Chrome. Every reply that switched says so (`switched`).

Without this step everything still works in your Chrome; a reply that would have switched carries
one sentence telling you to run the command above. To turn the companion off for a session, set
`HOLOSCRAPE_COMPANION=0` in the server's env.

## What it can do

Once connected, an agent can read whatever list is on a page in this browser — a search results grid,
a directory listing, a table — and walk its pages. Any http or https site can be read; a short list
of restricted hosts (video and music streaming) stays off limits no matter what. Tools that act —
navigate, press, type — are marked as such, so your agent asks you before each one.

On a page with more than one scrollable region — a chat app's sidebar list beside an open
conversation, a filter rail beside results — the automatic ranking favours the denser pane, which
may not be the one you mean. The fix is two calls: `page_study` lists every candidate with its
selector, and `list_extract` with that selector pinned reads exactly that pane:

```
page_study   { tabId: 12 }                                    → lists[1].selector: "aside.rail>div.chats…"
list_extract { tabId: 12, selector: "aside.rail>div.chats…" } → grows and reads the sidebar, not the feed
```

## Why local

The alternative is a hosted relay that sees every page it reads. This process listens only on
`127.0.0.1` — never on your network — and refuses any connection from a web page (by the `Origin`
header, which a page cannot fake), so a site you visit cannot pose as the extension. Cookies and
auth tokens are never handed to the agent: credential-shaped values come back masked.

## Tools

| Tool | What it does |
|---|---|
| `current_page` | The tab the person has open right now, and whether it can be read. The place to start for "I have a page open, scrape it". |
| `tabs_list` | Every http(s) tab currently open — for when `current_page` is not the one they meant. |
| `tab_here` | Point a tab you already have at a URL, and wait until it is loaded. The navigation primitive. `search:` turns plain words into a real search; `newTab:true` opens a new tab instead, which nothing will ever close. |
| `page_study` | Every repeating structure on a tab, ranked with evidence — not one verdict, several candidates so the caller chooses. Flags page furniture (footers, filter sidebars) that a naive reading mistakes for the real list. |
| `list_extract` | Read the rows already on this page into a table, following its pages. The fast path, and the one to try first. Takes an optional `selector` naming the container, for pages with more than one scrollable region; a selector that matches nothing fails by name instead of silently falling back. |
| `page_harvest` | A list page and the records behind its links — many pages, one call, no rows through the agent. Opens one page per record in parallel lanes, so it costs minutes: if the fields you need are already on the cards, `list_extract` answers in seconds. |
| `page_grow` | Make a list longer and report whether it actually grew — press a load-more, or scroll a named pane to its own bottom. `mode:"walk"` presses one thing and reports what changed, for apps that swap content without loading a document. `mode:"explore"` opens what is collapsed. |
| `page_state` | Read the layers under the rendered page: the app's own in-memory store, and the network responses it renders from. Discovery first (what exists, with the path to read each part), then a data path. For virtualized lists, where the DOM holds only the mounted window, and for fields the markup omits. `@dom(<css>)` reads the rendered page instead. Takes a path, never code; credential-shaped values come back masked, and counted, by design. |
| `results` | Saved tables and the runs that fill them, under one `action`: `status` and `stop` for a run, `list`, `get` and `export` for a finished table. |

## Try it without an agent

```
node try.mjs tabs
node try.mjs study <tabId>
```

Drives the same stdio protocol an agent would, useful for checking the whole chain — this script →
the server → the WebSocket → the extension → a real tab — works before pointing a real agent at it.

## Requirements

- Node.js 18+
- The HoloScrape Chrome extension, installed (open its side panel once)
- Optional, for the headless companion: Chromium for Testing via `npx -y holoscrape-mcp@latest --install-browser`

## License

MIT

Maintenance

ActivityActive
ResponsivenessNo issues