preview-bridge
Click on "Install 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., "@preview-bridgeWhat's the current form state on the page?"
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.
preview-bridge
A standalone MCP server that exposes live browser-preview state to Claude Code.
Lets Claude answer questions like:
"What's in the form on the page right now?"
"The preview just errored — what was the message?"
"What did the user click last?"
"Wait until the next route change, then tell me."
Works with any browser tab — frameworks-agnostic. Includes its own WebSocket relay and HTTP host page; no FastAPI, no Python, no studio-v2 dependency.
Architecture
3 layers, no backend dependency.
Claude Code ── MCP stdio ──> preview-bridge MCP server (Node)
│
├── http://127.0.0.1:5250 serves /__bridge.js + host.html + /state + /health
└── ws://127.0.0.1:5251 receives events + state-snapshot replies from the page
│
└── browser tab loaded with __bridge.js
├── window.onerror / unhandledrejection
├── console.{warn,error} (verbose mode: log too)
├── form input + change
├── click on [data-bridge="track"]
└── history.pushState wrap500-entry ring buffer; all 5 MCP tools are read-only in v1.
Related MCP server: gotham-browser
Install
Requires Node.js 20 or newer.
git clone https://github.com/verbalogicproject-creator/preview-bridge.git
cd preview-bridge
npm ci
npm run build
npm run smoke
npm run test:multiThe smoke test reports 17 passing assertions; the multi-session test reports
8 more. Then add the built server to your Claude Code MCP config (for example,
~/.claude.json), replacing /absolute/path/to/preview-bridge with this
checkout's absolute path:
{
"mcpServers": {
"preview-bridge": {
"command": "node",
"args": ["/absolute/path/to/preview-bridge/dist/index.js"],
"env": {
"PREVIEW_BRIDGE_HTTP_PORT": "5250",
"PREVIEW_BRIDGE_RELAY_PORT": "5251"
}
}
}
}Restart Claude Code. /mcp should now show preview-bridge with 5 tools.
Two usage modes
(a) Bring-your-own-page mode (recommended)
Add one line to your app's HTML:
<script src="http://127.0.0.1:5250/__bridge.js"></script>__bridge.js auto-detects it's running top-level, opens a WebSocket to the bridge, and starts emitting events. No iframe wrapper needed.
(b) Hosted-iframe mode
Open http://127.0.0.1:5250/?app=<URL> in a browser. The host page wraps <URL> in an iframe. Important: for cross-origin URLs the host cannot inject __bridge.js into the iframe automatically — the iframe's own HTML must include the script tag. Use mode (a) unless the app you're observing already includes __bridge.js.
MCP tools
Tool | Input | What it returns |
|
| Ring-buffered events, filtered |
|
| Recent runtime + unhandled-rejection errors with stack/file/line |
|
| Live snapshot from the connected page (800ms timeout) |
|
| Long-poll: blocks until matching event arrives or timeout |
|
| Bridge connection state, mode, event count |
Composition pattern for "what is the user looking at right now"
1. get_session_info() → confirm bridge is connected
2. query_preview_state({kind:'all'}) → route + focused el + form + selection
3. get_event_log({source:'iframe', limit:20}) → user's recent pathEvent taxonomy
Single envelope, ARIA-shaped:
interface BridgeEvent {
id: string; // monotonic
ts: number; // epoch ms
source: 'iframe' | 'host' | 'bridge';
level: 'debug' | 'info' | 'warn' | 'error' | 'system';
kind: BridgeKind;
component: string;
message: string;
data?: unknown;
}Kinds: session-connected, session-disconnected, runtime-error, unhandled-rejection, iframe-click, iframe-form-change, iframe-console, iframe-route-change, bridge-reinstalled.
Captured by default
Errors and unhandled rejections
console.warn+console.error(always)console.log— only when the page sets<html data-bridge="verbose">orwindow.__PREVIEW_BRIDGE_VERBOSE = truebefore__bridge.jsloadsForm input/change events (300ms debounce) on
<input>,<textarea>,<select>—type=passwordis filtered at sourceClicks on elements with
data-bridge="track"(opt-in by the app)history.pushStateandpopstatefor route changes
Configuration
Env var | Default | Purpose |
| 5250 | HTTP server port |
| 5251 | WebSocket relay port |
Privacy / hygiene
The bridge captures form values from the observed page. Password fields (type=password) are filtered at the __bridge.js source and never reach the wire. But text/email/textarea/select fields are captured as typed. Don't point the bridge at pages containing real credentials, PII, or secrets.
The ring buffer is in-memory only; nothing persists across MCP restarts. Events do not leave the local machine.
Tests
npm run smoke
npm run test:multiThe smoke suite spawns the MCP, opens a simulated WebSocket client, and checks all five tools plus state snapshots, long-polling, and ring-buffer filtering. The multi-session suite verifies leader/follower proxying and promotion.
Limits & known gaps
Cross-origin iframe injection: the hosted-iframe mode (option b) can't inject
__bridge.jsinto a cross-origin iframe. Use mode (a) for cross-origin scenarios.No reverse channel in v1: tools are read-only. No
inject_dom_commandyet. Future enhancement.No screenshot capture: visual state is out of scope (requires a headless-browser path not currently solved on Termux).
Single ring buffer: 500 events, shared across all sessions. Multi-tab correlation (per-session ids) is future work.
Roadmap
Reverse channel (
inject_dom_command)Session archive tool that drops a draft into gemini-expert's review queue once
propose_memoryshipsSession replay UI in
host.htmlPer-session correlation IDs
Built on
Ring-buffer + pub/sub primitive ported from ARIA's
RingBufferLoggerMCP server skeleton mirrors
gemini-expert/mcp-server/Event-taxonomy philosophy inspired by Background Studio's functional-journey logging
Multiple Claude Code sessions (leader / follower)
Claude Code spawns one MCP child process per session, but this server owns two fixed ports and there is only ever one browser page being observed. So instances elect a role at startup:
Role | Owns | Answers tools by |
| the WebSocket relay + HTTP host + the 500-entry event ring | reading its own ring |
| nothing | asking the leader over HTTP ( |
A follower's answers are the leader's answers — same ring, same page. get_session_info
reports the leader's state and stamps via: {role:"follower", pid} so the two are
never confused.
What this fixes. Before, the second session's child hit EADDRINUSE, called
process.exit(1), and Claude Code reported only "Connection closed" — a totally
deterministic failure that looked like a flaky server and never recovered while
the first session lived.
Two further guarantees:
A follower promotes itself. It polls the leader every 5s; when the leader's session ends, the follower binds the ports and takes over.
A leader dies with its client. The process now exits on stdin EOF, not just on SIGINT/SIGTERM. Previously a client that closed the pipe without signalling left a live process holding both ports with nothing attached to it, which poisoned every future session permanently. This was the actual outage.
A follower refuses to proxy to a service that does not identify itself as
preview-bridge on /health — forwarding your questions to some other project's
dev server that happens to hold port 5250 would be worse than failing.
Available Tools
5 toolsget_event_logA
Returns recent BridgeEvents from the live preview, ring-buffered (500 entries). Optional filters: source (iframe/host/bridge), level (debug/info/warn/error/system), kind. Pagination via 'since' (event id cursor). bridge-reinstalled events are filtered out by default; pass kind:'bridge-reinstalled' to see them.
| Name | Required | Description | Default |
|---|---|---|---|
| kind | No | Filter by event kind. | |
| level | No | Filter by severity. | |
| limit | No | Max events to return (1-200, default 50). | |
| since | No | Return events after this event id (cursor pagination). | |
| source | No | Filter by source. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden. It discloses meaningful traits: ring-buffered with 500 entries, 'bridge-reinstalled' events filtered out by default, and cursor-based pagination via 'since'. It does not mention output shape or access requirements, but it provides solid behavioral context beyond the schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three dense sentences contain all the essential facts with no wasted words. The core behavior is front-loaded, and parameters are summarized economically before the important default-filtering caveat.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with no required parameters and a fully documented schema, the description covers the important invocation details: source, level, kind, limit, since, and the default filter behavior. It does not describe the event return shape, and there is no output schema, but the tool is still sufficiently specified for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description adds value above the schema by explaining the ring-buffer behavior, the cursor semantics of 'since', and the non-obvious default exclusion of 'bridge-reinstalled' events, which clarifies how the 'kind' parameter behaves.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states a specific resource ('recent BridgeEvents from the live preview') and a verb ('Returns'), and it is easy to understand what the tool produces. However, it does not explicitly differentiate from the sibling 'tail_events', which is likely the closest alternative.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description conveys the context in which the tool is useful (reading recent live-preview events) and details optional filters and pagination. It gives no direct guidance about when to prefer another sibling like 'tail_events' or 'get_runtime_errors', so the selection knowledge is only implicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_runtime_errorsA
Returns recent runtime errors and unhandled-rejection events from the preview iframe. Each error includes message, stack, file, line, column when available. Use this when the user reports a broken preview or you want to see what just crashed.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max errors to return (1-50, default 10). | |
| sinceTs | No | Only return errors with ts >= sinceTs (epoch ms). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the burden of explaining behavior. It discloses the source (preview iframe), the event types, and the available error fields, plus the 'recent' and 'when available' caveats. It does not mention ordering, retention limits, or whether retrieval has side effects, but for a read-only error lookup the description is reasonably transparent.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences with no filler. The first sentence states the action and output fields; the second provides the key usage scenario. It is appropriately sized and front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers the tool's purpose, output fields, source context, and a clear trigger for use. The optional parameters are well documented in the schema. The only notable gap is not addressing how this tool relates to sibling tools such as get_event_log or tail_events, but this is not critical for basic invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema already documents both parameters with descriptions, ranges, and defaults, so schema coverage is 100%. The description adds no additional parameter-specific semantics, which is acceptable because the schema fully carries that weight.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool returns runtime errors and unhandled-rejection events from the preview iframe, with a specific verb and resource. It is easy to tell it is error-focused, though it does not explicitly name or differentiate itself from sibling tools like get_event_log or tail_events.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives a direct usage cue: 'Use this when the user reports a broken preview or you want to see what just crashed.' This provides clear context for when to invoke the tool, but it does not mention when not to use it or how it compares to alternative event/log tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_session_infoA
Returns bridge connection state: whether a host page is connected, in which mode (hosted-iframe vs top-level), the preview URL if known, total event count, and bridge version. Use to verify the bridge is actually receiving data before relying on other tools.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden. It discloses what the tool returns, including the caveat 'if known' for the preview URL, and implies a read-only diagnostic operation. It does not explicitly state side-effect-free behavior, but the zero-parameter getter framing makes this reasonably clear.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences: the first lists what is returned, and the second gives the use case. It is front-loaded, efficient, and contains no redundant filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter diagnostic tool with no output schema, the description is complete enough. It names all relevant return fields and explains why an agent would call it, making it clear and actionable.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema has no properties, so there are no parameters to document. The description correctly adds no unnecessary parameter details, meeting the baseline for zero-parameter tools.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool returns bridge connection state and enumerates specific fields: connection mode, preview URL, event count, and bridge version. It is specific and unambiguous, but it does not explicitly differentiate itself from sibling tools like get_event_log or query_preview_state.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides a clear usage directive: 'Use to verify the bridge is actually receiving data before relying on other tools.' This tells an agent when to use it, though it does not mention when not to use it or name alternative tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
query_preview_stateA
Asks the connected browser page for a live state snapshot. Kinds: 'form' (all input/textarea/select values, password fields filtered), 'route' (current URL/pathname), 'selection' (highlighted text), 'focus' (focused element selector + value), 'all' (combined). Times out in 800ms if no host page is connected.
| Name | Required | Description | Default |
|---|---|---|---|
| kind | Yes | Which slice of preview state to query. | |
| selector | No | Optional CSS selector to scope the query. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full behavioral burden, and it does so well. It discloses that the operation is a live snapshot request, that password fields are filtered out of form results, and that it times out in 800ms when no host page is connected. These are meaningful behavioral traits beyond the schema, giving the agent accurate expectations about safety and failure behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact and front-loaded, with the core action in the first sentence and the most important behavioral caveats (password filtering, timeout) included without filler. Every clause earns its place, and the list of kinds is structured for quick parsing.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-parameter tool with no output schema and no annotations, the description is remarkably complete. It explains what the tool returns for each kind, warns about password filtering, and specifies the timeout failure mode when no host page is connected. An agent has enough context to select, invoke, and interpret the result correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the schema already documents both parameters, but the description adds substantial meaning beyond the bare field names. It explains what each 'kind' value returns, notes password filtering, and clarifies that 'combined' is available. The optional 'selector' parameter is less elaborated, but the schema's 'Optional CSS selector to scope the query' is adequate there.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Asks the connected browser page for a live state snapshot.' It then enumerates the exact kinds ('form', 'route', 'selection', 'focus', 'all'), making the tool's purpose unambiguous. It also distinguishes itself from the session/log/event/error siblings by framing this as a page-state query rather than historical or session data.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description establishes clear usage context: use this when you need the current live state of the connected browser page, especially form values, route, selection, or focus. It does not explicitly name sibling alternatives or state when not to use them, but the sibling names (get_session_info, get_event_log, get_runtime_errors, tail_events) are clearly different domains, so the intended placement is reasonably obvious.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tail_eventsA
Long-poll: blocks until at least one event matching kinds arrives, OR maxWaitMs elapses. Returns the matched events. Use to wait for specific runtime signals (route change, build complete, etc.) without polling get_event_log. Default maxWaitMs=5000, max 30000.
| Name | Required | Description | Default |
|---|---|---|---|
| kinds | Yes | Event kinds to wait for (matches any in the list). | |
| limit | No | Max events to return (default 20). | |
| maxWaitMs | No | Max wait in ms (default 5000, max 30000). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does disclose the key blocking behavior, timeout semantics, and return of matched events. It does not explicitly state what happens when maxWaitMs elapses with no matching event (empty list vs. null vs. error), which is a meaningful ambiguity.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The definition is compact and well-structured: the core blocking behavior is front-loaded, followed by use-case guidance, examples, and defaults. Every sentence earns its place without unnecessary filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the absence of annotations and an output schema, the description covers the essential behavior, parameters, and alternative usage well. The only notable gap is the unspecified timeout result, which prevents it from being fully complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the schema already documents kinds, limit, and maxWaitMs. The description adds helpful context like runtime-signal examples and the default/max wait time, but it mostly restates what the schema already provides.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly identifies a specific behavior (long-poll blocking) and a specific resource (events matching `kinds`). It also distinguishes itself from the more obvious sibling, get_event_log, by explicitly saying this tool avoids polling.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It states exactly when to use the tool: to wait for runtime signals like route changes or build completion. It also names the polling alternative (get_event_log) and frames this tool as the non-polling option, giving an agent clear selection guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.
5 tool updates
v0.1.0- First observed
get_event_log - First observed
get_runtime_errors - First observed
get_session_info - First observed
query_preview_state - First observed
tail_events
TDQS
Scored across 5 tools
Most tools have clearly distinct purposes: session info, event log, runtime errors, page state, and long-polling. The only possible confusion is between get_event_log and get_runtime_errors, since runtime errors may also appear as error-level events in the log, and between get_event_log and tail_events, though the descriptions explicitly frame one as historical and the other as waiting for new events.
Three tools use the get_ prefix and the remaining two use query_ and tail_, which are understandable action verbs. The names are readable and mostly follow a verb_noun pattern, but the mix of get/query/tail prevents a perfect consistency score.
Five tools is well-scoped for a preview bridge debugging surface. Each tool covers a distinct aspect of inspecting live previews without redundancy or bloat.
The toolset covers the main observability workflow: check connection, read historical events, inspect errors, query live page state, and wait for future events. Minor gaps exist, such as no explicit tool for arbitrary DOM inspection or waiting for the bridge to connect, but the surface is strong for its apparent purpose.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Live browser debugging for AI assistants — DOM, console, network via MCP.
A paid remote MCP for AI agent browser DevTools MCP, built to return verdicts, receipts, usage logs,
AI Visibility and Content Intelligence tools for Claude and MCP-compatible agents.
Live SEO workflow tools for Claude Code, Codex, and AI agents.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceBridges browser content, developer tools data, and web page interactions with Claude through MCP. Enables page inspection, DOM analysis, JavaScript execution, console monitoring, network activity tracking, and screenshot capture across multiple browser tabs.MIT
- FlicenseNot gradedqualityBmaintenanceEnables Claude Code to control a real browser using AI for web scraping, competitive intelligence, and UX auditing through the MCP protocol.-
- AlicenseCqualityCmaintenanceEnables debugging of web applications by connecting Claude to Chrome's developer tools through MCP, allowing network monitoring, console inspection, and performance analysis via natural language.121MIT
- AlicenseAqualityDmaintenanceProvides AI coding agents real-time browser access to console logs, network requests, DOM elements, and screenshots via MCP, enabling tight edit-reload-verify feedback loops.15122MIT