OmniSnap MCP
Connects to the user's real Google Chrome session through the OmniSnap AI Studio extension, enabling AI clients to inspect pages, collect and verify ranked selectors, capture screenshots, read diagnostics and accessibility/design information, and propose actions that require explicit user approval.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@OmniSnap MCP get stability-ranked selectors for the login button"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
omnisnap-mcp
Selectors that don't rot, and a page your agent can actually see.
Agents guess selectors from a DOM dump, and the test breaks next sprint. This server returns selectors ranked for how likely they are to survive a redeploy, each one counted against the live page so you know whether it matches one element or three. It reads the page as you see it: logged in, in your own session.
The reading is done by the OmniSnap AI Studio Chrome extension. This server is the bridge between it and your AI client.
Requires OmniSnap AI Studio 1.9 or later. The bridge lives in the extension. Earlier versions have no Connect to AI switch, so this server will start and every tool will report that it is not paired. If that happens, check the version on the extension's store listing before filing a bug.
How this is different
Several projects connect an agent to your real Chrome through an extension, so the category is not empty. Tools such as Playwright MCP and Chrome DevTools MCP drive a browser: they click, type and navigate.
This one reads your browser, and asks before it touches it. What comes back is not a driven session but the artefacts of a handoff.
Your real session. An automated browser needs a scripted login to reach anything behind SSO. This is already in the tab.
Scored selectors. A class like
css-1x2y3zor an id like:r7:is a build artefact that changes when the bundler runs. Both are scored down, with the reason attached.Uniqueness checked. A selector that looks perfect and quietly matches three elements is the most common cause of a flaky suite. Every candidate is counted against the live DOM before you see it.
A page it can see.
annotate_interactivereturns a numbered screenshot and a selector for each number, so the model can reason about the picture and still act precisely.
Related MCP server: visual-browser-agent
Requirements
Node.js 18 or newer
Google Chrome, with OmniSnap AI Studio 1.9 or later
An MCP client: Claude Code, Claude Desktop, Cursor, VS Code, Windsurf, Codex, Cline, Goose, or anything else that speaks the protocol
Install
{
"mcpServers": {
"omnisnap": {
"command": "npx",
"args": ["-y", "omnisnap-mcp"]
}
}
}Client | |
Claude Code |
|
Claude Desktop |
|
Cursor |
|
VS Code |
|
Windsurf |
|
Cline |
|
Codex |
|
Goose | Settings, Extensions, Add, as a stdio command |
Anything else that speaks MCP over stdio takes the same command and args.
Options go in the same args array, which is usually easier than setting
environment variables:
{
"mcpServers": {
"omnisnap": {
"command": "npx",
"args": ["-y", "omnisnap-mcp", "--port", "8787", "--log-level", "debug"]
}
}
}Then install the extension, version 1.9 or later. Click its icon and
turn on Connect to AI. Ask your agent to call omnisnap_status: it reports
a six-digit code. Type that code into the extension. You are paired for good.
If the switch is not there, the installed extension predates the bridge.
Tools
Twenty-two of them, in three tiers. The tier depends on who has to agree to it.
It reads
Nothing in this group changes your page.
Tool | What it gives you |
| Whether the bridge is paired, and how to fix it if not |
| Numbered screenshot, a selector per number, and how much of the page you are seeing |
| Every selector for an element, ranked and verified |
| A page object for Playwright, Selenium, Cypress or WebdriverIO |
| Which of your existing selectors have rotted |
| The page as text, with a heading outline |
| Screenshots, including three widths side by side |
| Vitals, failed requests, console errors, storage |
| Waits, then reports the errors, failed requests and layout shifts that happened |
| Palette with contrast audit, type scale, spacing |
| WCAG failures, with selectors |
| Box model and computed styles for one element |
| Which tabs can be read |
It moves the camera
These change what is on screen, not what the page contains. They are declared as changes rather than as reads.
Tool | |
| See the rest of the page. One capture covers one viewport, which on a long page is a few per cent of it |
| Hold the page at 375, 768 or 1440, so every tool afterwards answers about that width |
| Light or dark. Dark mode is where contrast regressions hide |
| Photograph a dropdown, a tooltip or a focus ring. No ordinary capture reaches these |
It asks before it acts
Tool | |
| Proposes a click or a keystroke. The extension outlines the element and asks the user in the page. Nothing happens unless they approve |
| Hands the question back. The user clicks the element they mean, and you get it with ranked selectors |
Once an element has a number from annotate_interactive, every tool above also
takes element: 5 instead of a selector you would otherwise copy out of a
table and retype exactly.
Prompts and resources
Four slash commands: /write-test, /file-bug, /audit-accessibility,
/extract-design-system.
Resource | |
| The ranking and the reasoning behind it: what makes a selector fragile, and how the right choice differs per framework |
| What the vitals mean, and the two mistakes that produce confidently wrong bug reports |
| The most recent screenshot and its metadata |
| Install and pairing steps |
The first two work before you have paired anything. Any server can hand back a list of selectors. Knowing which one to pick is what decides whether the test still passes next sprint, so the reasoning travels with the data.
Troubleshooting
npx omnisnap-mcp --doctorThis checks the port, checks whether a token is stored, then waits for the extension and walks you through pairing. It prints the code to type in and waits for that to land. Almost every first-run problem is one of three things, and this says which.
Security
You are pairing a tool that can read any page you are signed in to. That deserves a straight answer about what stops it being abused.
Loopback only. The bridge binds
127.0.0.1. Nothing off the machine can reach it.No web page can connect. The server requires a
chrome-extension://origin. A browser sets that header itself, and page JavaScript cannot forge it.Acting needs a human, every time. One tool can click or type, and it never does so on its own. The extension outlines the element in your page and asks you to approve or deny. The Approve control accepts only a real click from you: a page can dispatch a click on that button from script, but it cannot forge
event.isTrusted. Nothing navigates, and nothing submits a form.Pairing needs a human. The bridge sits on a fixed port, so a local process could race to claim it. Pairing therefore requires you to read a six-digit code from your AI client and type it into the extension. A rogue server can issue a code of its own, but it cannot make your AI client show you that code. If your client is not showing one, type nothing. Something else is listening on the port.
The code never crosses the socket. If it did, a rogue client could read it back and pair itself. A test asserts this.
Rate limited and short lived. Six digits, five attempts, two minutes, single use.
Constant-time comparison for codes and tokens, against a fixed-width buffer, so neither the value nor its length leaks.
No telemetry. No account, no remote config, nothing phones home. The only state on disk is the pairing token at
~/.omnisnap/bridge.json, mode0600.
Unpair at any time by deleting that file, or from the extension.
This is not a security boundary. It is a set of deliberate limits with a human in the loop, which is a different claim. Anyone who can run processes as you can read that token, and an AI client you pair is a program you have chosen to trust with your browser. Treat it the way you would treat handing that client your terminal. The MCP security best practices are worth reading before you pair anything, including this.
PROTOCOL.md is the full wire format.
Configuration
Every option is both a flag and an environment variable. A flag wins, because it sits in the client config in front of you, where the environment may have come from a shell profile written months ago.
Flag | Environment | Default | |
|
|
| Must match the extension |
|
|
|
|
|
|
| Where the pairing token lives |
|
|
| Per call |
|
|
| Captures get longer |
|
|
| In flight at once |
|
|
| Past this a capture is refused rather than sent |
|
|
| How long a pairing code lasts |
|
| Any single message from the extension |
omnisnap-mcp --help prints the same list with the defaults resolved on your
machine.
Limits worth knowing
There is one browser at the other end, and much of what it does is serialised. Chrome allows two screenshots a second, and a prompt waiting on a human cannot be had twice. A fan-out of twenty calls is therefore refused at six, rather than becoming a queue whose last entries time out having never been reached.
Screenshots come back as JPEG with the long edge at 1536px, roughly 100KB for a
viewport. Vision models downscale to about that anyway, so anything larger is
paid for and then discarded. read_page returns up to 12,000 characters by
default and 60,000 on request, which costs a fortieth of a picture when the
question is what the page says. annotate_interactive numbers at most 60
elements, since a picture with two hundred badges on it is no longer a map.
verify_selectors takes 200 selectors at a time. watch_page runs for up to
120 seconds, records 60 events of each kind, and only one watch may run on a
page at a time.
Logs go to stderr, always. Stdout carries the MCP protocol, and a single stray write to it corrupts the session, so a test spawns the real binary and asserts that every line it emits parses as JSON-RPC.
Development
npm install
npm test38 tests cover pairing and its failure modes, origin rejection, the bridge end to end against a fake extension, the stdio contract, the concurrency limit, and that every option works as a flag, as an environment variable, and in the right order of precedence.
tests/extension.test.js goes further. It runs the Chrome extension's own
bridge module, unmodified, against a real server, so the two halves cannot
drift apart unnoticed. It skips unless the extension is checked out beside this
repository.
Contributing
Bug reports and pull requests are welcome. CONTRIBUTING.md covers the setup, the house style, and the one constraint worth knowing before you write anything: tools that let the agent act without a person in the loop will not be merged.
Security problems should go through private reporting rather than a public issue. See SECURITY.md.
Licence
MIT. Playwright, Chrome and any other product named here are trademarks of their respective owners, referred to only for comparison.
Available Tools
22 toolsannotate_interactiveNumbered map of interactive elementsARead-only
Returns a screenshot with every interactive element outlined and numbered, plus a table mapping each number to its best selector and a stability score. This is the tool to use before acting on a page: look at the image, pick a number, then use that number's selector. Far more reliable than guessing a selector from the DOM.
Inputs: include, viewport.
await_user_selectionAsk the user to point at itARead-only
Arms the element picker in the browser and waits for the user to click the elements they mean, then returns them with ranked selectors. Use this instead of guessing when a page has several things that match the description ("the save button", "that broken card"), or when you cannot find what the user is describing. Asking costs one turn; picking the wrong element costs the whole task.
Inputs: timeoutMs.
capture_elementCapture one elementARead-only
Screenshots a single element, given a CSS selector. Cheaper and far easier to reason about than a full page when the question is about one component.
Inputs: element, selector.
capture_matrixThe page at three widthsARead-only
Captures the full page at phone, tablet and desktop widths and returns them side by side in one image. Use it to check a responsive change in a single look, rather than three captures you have to hold in mind at once.
Inputs: widths.
capture_pageCapture the pageARead-only
Takes a screenshot of the active tab and returns it as an image the model can see. Captures the full scrollable page by default, including pages that scroll inside a panel. Use it to check layout, verify a fix visually, or see what the user is describing.
Inputs: mode, viewport.
collect_selectorsSelectors for an elementARead-only
Given a description or a CSS hint, returns every selector that could reach the element, ranked by how likely it is to survive a redeploy and checked against the live page so you know whether it matches one element or several. Prefer the top result. A selector built on a generated class such as css-1x2y3z is scored down because it changes whenever the bundler runs.
Inputs: hint, element.
focus_elementFocus and photographBIdempotent
Focuses an element and returns a screenshot. Use it to check focus rings, which are the most commonly missing accessibility feature and are invisible in an ordinary capture.
Inputs: element, selector.
get_design_tokensColours, type and spacingARead-only
Extracts the design system the page actually uses: palette with contrast audit, type scale, radii, shadows and spacing. Use it to match a new component to an existing product, or to find accessibility problems in the live UI.
Inputs: format.
get_diagnosticsPage health and failuresARead-only
Returns Web Vitals, failed and slow network requests, console errors, storage use and the exact browser environment for the active tab. This is measured in the user's real session, so it reflects their network, their extensions and their data, which a clean automated browser cannot show you.
Inputs: no named parameters.
get_page_objectPage object for a frameworkBRead-only
Exports the selectors collected in the browser as a ready page object. The best selector differs per framework: Playwright can match a role and an accessible name, Selenium cannot and falls back to CSS or XPath for the same element, so always pass the framework you are actually writing for.
Inputs: framework (required).
hoverHover and photographAIdempotent
Puts the pointer on an element and returns a screenshot of what that reveals: dropdowns, tooltips, hover styling. These states cannot be reached any other way: a synthetic event fires the page handlers but leaves the CSS untouched, so this uses a real pointer move. Nothing is clicked.
Inputs: element, selector.
inspect_elementMeasurements for one elementARead-only
Returns the box model, computed typography, colours and spacing for a single element. Use it when a design question needs numbers rather than a picture.
Inputs: element, selector.
list_tabsList open tabsARead-only
Lists the open browser tabs OmniSnap can act on, with their titles and URLs. Use it to find the right tab before capturing or collecting selectors.
Inputs: no named parameters.
measure_accessibilityAccessibility checkARead-only
Audits the live page for contrast failures, missing accessible names, skipped heading levels and touch targets below the minimum size. Returns the offending elements with selectors.
Inputs: no named parameters.
omnisnap_statusBridge statusARead-only
Reports whether the OmniSnap browser extension is paired with this server, and which tab is active. Call this first when any other OmniSnap tool fails, and to get pairing instructions.
Inputs: no named parameters.
propose_actionAsk the user to approve an actionADestructive
Proposes clicking an element or typing into a field. The extension outlines the element and asks the user to approve; nothing happens unless they do. Use it to reach states that only exist after an interaction: an open menu, a validation error, the next step of a form. That is where most defects are. The user may decline. If they do, do not retry: ask them what they want instead.
Inputs: action (required), value, element, selector, +1 more.
read_pageThe page as textARead-only
Returns the page's text and heading outline. Use this first when the question is what the page says rather than what it looks like: it costs a fortieth of a screenshot and a model reads a paragraph far more reliably than a picture of one. Reach for a capture when the question is visual.
Inputs: limit, scope.
scroll_toMove the viewportAIdempotent
Scrolls the page. Use it to see the rest of a page: annotate_interactive and the screenshots only cover what is on screen, and its "coverage" field says how much of the page that is. Scroll, then capture again. This moves the camera, not the hands: it cannot click, type or navigate.
Inputs: y, to, element, selector.
set_color_schemeLight or darkAIdempotent
Shows the page as if the user preferred light or dark, without them changing anything. Dark mode is where contrast regressions hide, because it is the theme nobody screenshots. Use "auto" to hand it back.
Inputs: scheme (required).
set_viewportShow the page at another widthAIdempotent
Holds the page at a phone, tablet or desktop width, and leaves it there. Every tool afterwards, including the numbered map, the accessibility audit and the measurements, then answers about that width, which is how you find what is wrong at 375px rather than just noticing that something is. Call it with reset to give the page back. Chrome shows a debugging banner while this is held; that is Chrome, not the page.
Inputs: reset, width, height, preset.
verify_selectorsCheck selectors still matchARead-only
Takes a list of selectors and reports, for each, how many elements it matches on the page now. Use it to find selectors that have rotted after a UI change, before a test run does.
Inputs: selectors (required).
watch_pageWatch for a whileARead-only
Watches the page for a number of seconds and reports the console errors, failed requests and layout shifts that happen during it. Every other tool here asks a question and gets an answer as of that instant; most of what goes wrong does not wait to be asked. Use it while the user reproduces something, or just after a change, when asking afterwards would find a page that looks fine.
Inputs: seconds.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 22 tools
Tools mostly carve out distinct jobs—visual capture vs. text reading vs. inspection vs. selector discovery—and the descriptions clarify when to use which. There is residual overlap among the screenshot variants (capture_page, capture_element, capture_matrix), selector tools (collect_selectors, await_user_selection, annotate_interactive), and accessibility surfaces (get_design_tokens, measure_accessibility), so an agent could still pause.
All tool names use snake_case and most follow a clear verb_noun or verb_object pattern such as capture_page, verify_selectors, and set_viewport. Minor exceptions like omnisnap_status and the bare hover keep it from being perfectly uniform, but the convention is predictable.
At 22 tools, the surface is heavy for a browser-extension inspection server, though each tool maps to a real capability. Several could be consolidated or grouped, especially the capture variants and selector helpers, making this borderline in the 16–25 tool range.
The set covers status/tabs, visual and text capture, selector discovery and verification, design-token extraction, accessibility auditing, element inspection, and interactive state probing. Minor gaps remain around page navigation, raw DOM/HTML export, and richer interaction such as keypresses or select controls, but most browser-QA workflows are reachable.
Maintenance
Related MCP Connectors
Let AI agents use your real, signed-in Chrome: tabs, clicks, forms, screenshots and video.
Connect AI agents to your own Chrome with scoped access, approvals, and verified task results.
Give AI agents a real cloud Chrome: open sessions, navigate, read pages, click, type and screenshot.
Let AI agents use any website you're logged in to. Sessions stay encrypted on your machine.
Related MCP Servers
AlicenseNot gradedqualityBmaintenanceEnables AI assistants and terminals to control a user's already logged-in Chrome session, allowing them to navigate pages, read content, click and type, run JavaScript, and inspect console or network activity.31MIT- AlicenseNot gradedqualityBmaintenanceEnables AI coding agents to see and interact with real Chrome pages through structured DOM inspection, targeted screenshots, recordings, and human-in-the-loop approvals across major coding-agent hosts.MIT
- AlicenseNot gradedqualityCmaintenanceConnects AI agents to your existing daily-driver Chrome so they can inspect, extract from, record, and replay workflows across background tabs without stealing cursor focus, breaking 2FA/SSO sessions, or leaking secrets. A deterministic R0–R4 risk firewall gates state-changing and destructive actions behind human approval.2Apache 2.0
- AlicenseNot gradedqualityBmaintenanceEnables any coding agent to use the user's existing Chrome profiles and logins to open tabs, read pages, fill forms, click through flows, and take screenshots.63 npmMIT