blnq MCP Server Spec

Model Context Protocol server for live browser editor integration. Tool listings generated from the shared registry.

Contents

Overview

The blnq MCP server connects an external AI harness (Claude Code, Cursor, Windsurf, any MCP-speaking client) to the blnq.studio browser editors in real time. Tool calls from the AI flow through a per-tab bridge and are pushed into the live Monaco editors via Server-Sent Events.

The same bridge is shared with the in-browser AI chat panel, so anything described here applies to both call paths. Edits from either side push to the browser over the same SSE channel.

TL;DR: external AI → POST /mcp (Streamable HTTP MCP transport) → bridge state → SSE → browser editor.

Quick Start

Three steps. Most people get stuck on step 1 because they assume the agent can see the project before they click Start MCP. It can't — the bridge is lazy by design.

  1. Open an editor tab & click Start MCP. Visit blnq.studio/e. In the top-right of the header you'll see a Start MCP button — click it. The button lights up, the bridge connects, and your MCP ID (a random UUID bound to this tab) is copied to your clipboard. Leave the tab open.
  2. Add blnq to your AI client. Pick your client in the next section and add the snippet. blnq speaks the standard Streamable HTTP MCP transport at https://blnq.studio/mcp — no API key, no auth header, no install step on the server side.
  3. Just start prompting. Say "using blnq, build me X". The agent will hit a tool call that needs a tabId and prompt you for your MCP ID — paste it then. This is often the easiest flow because you don't need to plan ahead. If you prefer, you can hand the ID over upfront ("My blnq MCP ID is abc1234, use it on every tool call") so the agent never has to ask.
If the Start MCP button isn't lit, agents see nothing. No project, no files, no preview. Re-click it; if it doesn't connect, refresh the editor tab and try again.

Adding blnq to your AI client

Every config below points the client at the same endpoint: https://blnq.studio/mcp over HTTP (Streamable HTTP transport). Pick the one that matches your tool.

Claude Code (CLI)

One command — the CLI writes the config for you:

claude mcp add --transport http blnq https://blnq.studio/mcp

Or add it manually to ~/.claude.json (user scope) or .mcp.json (project scope):

{
  "mcpServers": {
    "blnq": {
      "type": "http",
      "url": "https://blnq.studio/mcp"
    }
  }
}

Verify with claude mcp list; the entry should show connected once you start a session.

Claude Desktop

Open Settings → Developer → Edit Config (this opens claude_desktop_config.json). Add:

{
  "mcpServers": {
    "blnq": {
      "type": "http",
      "url": "https://blnq.studio/mcp"
    }
  }
}

Restart Claude Desktop. If your build doesn't yet support remote HTTP MCP natively, use the mcp-remote stdio bridge instead:

{
  "mcpServers": {
    "blnq": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://blnq.studio/mcp"]
    }
  }
}

Cursor

Edit ~/.cursor/mcp.json (global) or .cursor/mcp.json in a workspace (per-project), or use Settings → MCP → Add Server:

{
  "mcpServers": {
    "blnq": {
      "url": "https://blnq.studio/mcp"
    }
  }
}

Windsurf

Edit ~/.codeium/windsurf/mcp_config.json, or use Settings → Cascade → MCP Servers → Add Server:

{
  "mcpServers": {
    "blnq": {
      "serverUrl": "https://blnq.studio/mcp"
    }
  }
}

Any other MCP-compatible client

blnq is a stock Streamable HTTP MCP server. If your client supports remote MCP over HTTP, point it at https://blnq.studio/mcp — nothing else is required. If it only supports stdio, wrap it with mcp-remote as shown in the Claude Desktop fallback above.

Passing the MCP ID

Every tool that touches editor content takes a tabId parameter — that's the MCP ID copied to your clipboard when you clicked Start MCP. There is no discovery tool and no auto-resolution: in a shared-tenant deployment, listing connected tabs would leak other users' projects, so the server requires an explicit ID on every call. You have two ways to get it to the agent.

On-demand (easiest). Just start prompting — "using blnq, build me a portfolio site". The agent will hit its first tool call without a tabId, see the "MCP ID required" error, and ask you for one. Paste it then. You don't have to plan ahead; the harness handles the prompt for you.

Upfront (when you want to skip the round-trip). Paste the ID into the chat at the start of the session and ask the agent to remember it. Examples:

If the ID stops working, you'll see "MCP ID not recognised" from the tool. That means the editor tab was closed (the ID is revoked ~5 minutes after the SSE connection drops). Open a fresh tab at blnq.studio/e, click Start MCP again, and pass the new ID.

Treat the ID as a credential. Anyone with the ID can read and write your project while the tab is open. If it leaks, close the tab to revoke it and start a new session.

Example prompts

Once the agent has your MCP ID, talk to it like any other tool. A few starting points:

Using blnq, build me a one-page portfolio: sticky nav, hero
with my name, three project cards, contact section. Dark
theme, no AI blue, no gratuitous gradients.
Using blnq, read the current files, then make the hero
stack on mobile and add a subtle fade-in for the cards.
Run blnq_check_preview when you're done and fix anything
it flags.
Using blnq, generate three design variants of the landing
page — minimalist, editorial, brutalist — and show them in
the variant picker.

Architecture & lifecycle

The bridge is session-less from the harness's point of view — the only identity that matters is the tabId, a random UUID minted by the editor tab.

┌──────────────────────┐      POST /mcp         ┌──────────────────────┐
│  External AI harness │ ─────────────────────▶ │  blnq MCP server     │
│  (Claude Code, etc.) │ ◀───────────────────── │  (Streamable HTTP)   │
└──────────────────────┘   tool results / SSE   └──────────┬───────────┘
                                                           │
                                                  bridge state
                                                  (per tabId)
                                                           │
                           ┌───────────────────────────────┤
                           │                               │
                  sendToTab(event, data)       POST /api/bridge/sync
                    via SSE channel            (content mirror)
                           │                               │
                           ▼                               │
                ┌──────────────────────┐                   │
                │  Browser editor tab  │ ◀─────────────────┘
                │  (Monaco + preview)  │
                │  /api/bridge/events  │
                └──────────────────────┘

Connection lifecycle (what happens under the hood)

  1. User opens an editor tab at blnq.studio/e. The page mints an MCP ID and opens GET /api/bridge/events?tabId=…. This SSE stream is how the server pushes live updates back to the browser.
  2. User clicks Start MCP. The browser starts mirroring file content via POST /api/bridge/sync, seeding the in-memory state so external tools can read it. Without this click, the bridge stays idle and tools see no tab.
  3. User pastes the MCP ID into the AI client's chat.
  4. Harness sends initialize to POST /mcp. The server responds with tool definitions. Session-aware clients get an mcp-session-id header to reuse; stateless clients get a one-shot server per request.
  5. Harness calls tools with the MCP ID as the tabId parameter. The server resolves to the matching tab and pushes edits over the SSE channel.

Reading data back from the browser

Some tools need live runtime data, not just editor text — e.g. blnq_run_js, blnq_get_element_info, blnq_check_preview, blnq_click. These use a request / response pattern over SSE:

  1. Server sends an SSE event (e.g. js:eval, element:query, dom:action, a11y:run) with the request payload.
  2. Browser executes the request in the preview iframe.
  3. Browser POSTs the result to a matching endpoint (e.g. /api/bridge/js/results).
  4. Server resolves the pending promise; the tool call returns.

Each pending request has a timeout (default 8–15 s depending on tool). Only one pending request per tool type per tab at a time — the new request replaces the old one.

Prerequisites the harness should assume

MCP Connection Details

The MCP endpoint is at POST /mcp using the Streamable HTTP transport from the official MCP SDK.

Tools

Inspection, preview-driving, and project-state tools. Each tool is tagged read (safe to call without prompting the user) or write (mutates project state). Listings below are generated from the shared registry, so this page can't drift from the live tools.

blnq_check_preview read
Check the preview for JS errors and a11y (WCAG) violations. Call after edits to catch issues.
ParamTypeDescription
tabIdstring requiredMCP ID — ask the user once at session start; include it on every subsequent tool call.
blnq_search_code read
Search text or regex across project files. Optionally filter to specific filenames.
ParamTypeDescription
tabIdstring requiredMCP ID — ask the user once at session start; include it on every subsequent tool call.
querystring requiredText or regex to search for
filenamesstring[] optionalOptional list of filenames to search (e.g. ["/index.html"]). Omit for all files.
regexboolean optionalTreat query as regex
blnq_set_project_name write
Set the project name. Use when creating something new or renaming.
ParamTypeDescription
tabIdstring requiredMCP ID — ask the user once at session start; include it on every subsequent tool call.
namestring requiredShort descriptive project name
blnq_screenshot read
Take a full-page screenshot of the preview. Returns a compressed JPEG. Use when the user asks to see or verify visual result — do NOT call automatically after edits.
ParamTypeDescription
tabIdstring requiredMCP ID — ask the user once at session start; include it on every subsequent tool call.
widthnumber optionalViewport width in px (default 1280)
blnq_undo write
Undo recent edits. Use steps to revert multiple operations in one call (e.g. steps: 5 undoes the last 5 operations).
ParamTypeDescription
tabIdstring requiredMCP ID — ask the user once at session start; include it on every subsequent tool call.
stepsnumber optionalNumber of steps (default 1)
blnq_read_sketch read
Read the current Excalidraw sketch elements — positions, types, styles, bindings.
ParamTypeDescription
tabIdstring requiredMCP ID — ask the user once at session start; include it on every subsequent tool call.
blnq_write_sketch write
Write Excalidraw elements (rectangle, ellipse, diamond, text, arrow, line, freedraw, image). Required: type, id, x, y, width, height. Call blnq_read_sketch first to see the current element shapes.
ParamTypeDescription
tabIdstring requiredMCP ID — ask the user once at session start; include it on every subsequent tool call.
elementsobject[] requiredArray of Excalidraw element objects
blnq_get_element_info read
Query a CSS selector in the live preview — returns bounding rect, computed styles, attributes.
ParamTypeDescription
tabIdstring requiredMCP ID — ask the user once at session start; include it on every subsequent tool call.
selectorstring requiredCSS selector (e.g. '.hero', '#nav', 'h1')
stylesstring[] optionalCSS property names. Defaults to a common layout set.
blnq_run_js read
Run JavaScript in the live preview iframe. Returns the final expression value AND any console.log/info/debug output produced during the eval. A bare expression is auto-returned (e.g. `typeof THREE`); for multi-statement code, use an explicit `return`. Add console.log() to inspect state — output is captured and returned to you.
ParamTypeDescription
tabIdstring requiredMCP ID — ask the user once at session start; include it on every subsequent tool call.
codestring requiredJavaScript to run in the preview. Return value + console output are captured.
blnq_get_console read
Read recent console output from the live preview (log/info/debug + warn/error). Use after blnq_run_js or DOM actions to inspect console.log output, or any time you want to see what the page printed. Returns the most recent entries — pass sinceTimestamp from a prior call to fetch only new ones.
ParamTypeDescription
tabIdstring requiredMCP ID — ask the user once at session start; include it on every subsequent tool call.
levelsstring[] optionalFilter to these levels. Default: all.
limitnumber optionalMax entries to return (1-100, default 50)
sinceTimestampnumber optionalOnly return entries with timestamp > this (ms). Use the `now` field from a previous response to poll incrementally.
clearboolean optionalClear the log buffer after reading. Default false.
blnq_click write
Click an element in the live preview (e.g. a button) using the native click() so form submits and label-for-input dispatch work naturally. Returns any console.log output produced during the click — pair with blnq_get_console for state after the action.
ParamTypeDescription
tabIdstring requiredMCP ID — ask the user once at session start; include it on every subsequent tool call.
selectorstring requiredCSS selector to click
blnq_type write
Type text into a form field (input/textarea/select) or contenteditable element. Dispatches input + change events so framework listeners fire. By default clears existing value first — set clear:false to append.
ParamTypeDescription
tabIdstring requiredMCP ID — ask the user once at session start; include it on every subsequent tool call.
selectorstring requiredCSS selector for the input/textarea/contenteditable
textstring requiredText to type
clearboolean optionalClear field before typing (default true)
blnq_press write
Dispatch a keyboard key (keydown/keypress/keyup) at the preview — useful for games and shortcuts. Use the KeyboardEvent.key value (e.g. 'Enter', 'ArrowUp', 'Escape', 'a'). Targets the matching selector if given, otherwise the active element.
ParamTypeDescription
tabIdstring requiredMCP ID — ask the user once at session start; include it on every subsequent tool call.
keystring requiredKeyboardEvent.key value (e.g. 'Enter', 'ArrowUp', 'a')
selectorstring optionalTarget element (default: document.activeElement)
shiftboolean optional
ctrlboolean optional
altboolean optional
metaboolean optional
blnq_wait_for read
Poll the preview until a selector matches and is visible (and optionally contains some text). Returns when found or times out. Useful after triggering async work (e.g. starting a game, kicking off a fetch).
ParamTypeDescription
tabIdstring requiredMCP ID — ask the user once at session start; include it on every subsequent tool call.
selectorstring requiredCSS selector to wait for
textstring optionalOptional text substring the element must contain
timeoutMsnumber optionalMax time to wait in ms (100-15000, default 5000)
blnq_get_project_info read
Get project name, ID, and file list with line counts. Call at the start of a new session to know what you're working with.
ParamTypeDescription
tabIdstring requiredMCP ID — ask the user once at session start; include it on every subsequent tool call.
blnq_refresh_preview write
Force a full preview refresh. Use after updating a child blnq (web component) the current tab imports — the preview needs to re-fetch the latest code.
ParamTypeDescription
tabIdstring requiredMCP ID — ask the user once at session start; include it on every subsequent tool call.
blnq_get_cursor read
Get the user's current cursor position — which editor it's in and the line/column. Useful for making edits near what the user is looking at.
ParamTypeDescription
tabIdstring requiredMCP ID — ask the user once at session start; include it on every subsequent tool call.
set_viewport write
Set the editor's preview viewport to one of the named presets (e.g. mobile, tablet, desktop). Use when the user mentions a screen size or you want to verify responsive layout.
ParamTypeDescription
tabIdstring requiredMCP ID — ask the user once at session start; include it on every subsequent tool call.
idstring requiredViewport preset id (mobile, tablet, desktop, etc.)
get_page_metrics read
Read live preview metrics — DOM element count, image/script counts, and page-load timings (DOMContentLoaded, load, FCP).
ParamTypeDescription
tabIdstring requiredMCP ID — ask the user once at session start; include it on every subsequent tool call.
list_undo_steps read
List available undo steps. Each step shows which files were touched so the agent can decide how far to rewind.
ParamTypeDescription
tabIdstring requiredMCP ID — ask the user once at session start; include it on every subsequent tool call.
blnq_fetch_url read
Fetch a URL — returns page text and optional screenshot. Cookie banners are auto-dismissed before capture. Only HTTP/HTTPS; private/localhost addresses are rejected.
ParamTypeDescription
tabIdstring requiredMCP ID — ask the user once at session start; include it on every subsequent tool call.
urlstring requiredURL to fetch (http/https). Protocol is auto-prepended if omitted.
blnq_screenshotboolean optionalCapture a screenshot too (default true)
contentOnlyboolean optionalSkip screenshot — return text only (default false)
consentSelectorsstring[] optionalExtra CSS selectors for site-specific consent buttons

Multi-File Tools

Projects are a list of files keyed by path (e.g. /index.html, /page2.html, /style.css, /components/nav.js). /index.html is the default entry page and cannot be deleted; additional HTML pages can be created freely and linked with relative <a href="page2.html"> links. Other files are referenced via standard <link> and <script> tags.

blnq_list_files read
List all project files and folders. Folders have type 'folder'; files show line and character counts.
ParamTypeDescription
tabIdstring requiredMCP ID — ask the user once at session start; include it on every subsequent tool call.
blnq_read_file read
Read a file by filename/path. Supports startLine/endLine for reading ranges.
ParamTypeDescription
tabIdstring requiredMCP ID — ask the user once at session start; include it on every subsequent tool call.
filenamestring requiredFile path (e.g. /index.html, /style.css)
startLinenumber optional1-based start line
endLinenumber optional1-based end line (inclusive)
blnq_write_file write
Replace full content of a file. Use for initial creation or >80% rewrites. Use blnq_edit_file for smaller changes. Pass force=true to bypass the shrinkage guard when intentionally writing a shorter replacement (e.g. a full redesign).
ParamTypeDescription
tabIdstring requiredMCP ID — ask the user once at session start; include it on every subsequent tool call.
filenamestring requiredFile path (e.g. /style.css)
contentstring requiredFull file content
forceboolean optionalBypass shrinkage guard for intentional rewrites that are shorter than the original
blnq_create_file write
Create a new file in the project.
ParamTypeDescription
tabIdstring requiredMCP ID — ask the user once at session start; include it on every subsequent tool call.
filenamestring requiredFile path (e.g. /utils/helpers.js)
contentstring optionalInitial content (default: empty)
blnq_delete_file write
Delete a file from the project. Cannot delete /index.html.
ParamTypeDescription
tabIdstring requiredMCP ID — ask the user once at session start; include it on every subsequent tool call.
filenamestring requiredFile path to delete
blnq_create_folder write
Create a new folder in the project. Must be called before creating any files inside it. For nested folders, create each parent first.
ParamTypeDescription
tabIdstring requiredMCP ID — ask the user once at session start; include it on every subsequent tool call.
pathstring requiredFolder path (e.g. /js or /css/components)
blnq_delete_folder write
Delete a folder and all its contents (files and subfolders) from the project.
ParamTypeDescription
tabIdstring requiredMCP ID — ask the user once at session start; include it on every subsequent tool call.
pathstring requiredFolder path (e.g. /js or /css/components)
blnq_edit_file write
Edit a file with {startLine, endLine, newText} (line-range, read first for line numbers) OR {oldText, newText} (find-replace). If neither fits, use blnq_write_file with full content.
ParamTypeDescription
tabIdstring requiredMCP ID — ask the user once at session start; include it on every subsequent tool call.
filenamestring requiredFile path (e.g. /style.css)
startLinenumber optionalFirst line to replace (1-based, line-range mode)
endLinenumber optionalLast line to replace (1-based inclusive, line-range mode)
oldTextstring optionalText to find (find-replace mode)
newTextstring optionalReplacement text
replaceAllboolean optionalReplace all occurrences

Design Variants

The variants flow lets the AI stream multiple design options into the editor's variant picker. Plan N variants, call blnq_set_variants with the first only (the picker appears immediately), then call blnq_append_variants once per remaining variant so the user sees them arrive as they're generated. The first variant loads in the editor; the user can switch or dismiss via the picker UI.

Each variant is a { name, files: [{ filename, content }] } bundle — a complete file set, not a diff. When the user activates a variant, the browser POSTs the selected files to /api/bridge/variant/activate; dismissing the picker POSTs to /api/bridge/variant/clear. The AI can also commit one directly with blnq_select_variant (by number or name) — equivalent to the "Select" button: the server makes that variant the active project, discards the rest, and pushes variants:select so the editor loads it and closes the picker (no confirm dialog).

blnq_append_variants write
Add ONE more design variant to the picker (never batch). Use after blnq_set_variants during streaming, or when the user asks for more variants on an existing picker.
ParamTypeDescription
tabIdstring requiredMCP ID — ask the user once at session start; include it on every subsequent tool call.
variantsobject[] requiredNew variants to append
blnq_set_variants write
Start a design variants picker with the FIRST variant only. Then call blnq_append_variants for each remaining variant (one call per variant). Use only when the user asks for multiple options; each variant must be meaningfully different.
ParamTypeDescription
tabIdstring requiredMCP ID — ask the user once at session start; include it on every subsequent tool call.
variantsobject[] required1–4 complete design variants (use 1 for streaming — call blnq_set_variants then blnq_append_variants per variant)
blnq_select_variant write
Lock in one of the design variants currently in the picker — loads it into the editor as the active project AND discards the other variants (closing the picker). This is the "Select" action, not just a preview. Use when the user commits to an option (e.g. "go with variant 2" or "use the Editorial one"). Requires variants to exist (blnq_set_variants first).
ParamTypeDescription
tabIdstring requiredMCP ID — ask the user once at session start; include it on every subsequent tool call.
variantstring required1-based variant number (e.g. "2") or its name (e.g. "Editorial").

Panel-only Tools

These tools are not exposed over MCP. They need the in-browser chat panel UI to render their effect (button cards, task lists, skill content), so external clients won't see them. They're listed here for completeness.

blnq_ask_user write panel-only
Ask the user a structured question with 2–4 button options. Pauses the turn — do not call other tools after this. Do not list the options in chat text; they appear as buttons.
ParamTypeDescription
tabIdstring requiredMCP ID — ask the user once at session start; include it on every subsequent tool call.
questionstring required
optionsobject[] required
blnq_set_todos write panel-only
Show a live task list. Call at the start with the full plan, then again to update statuses as you work. Status: 'pending' | 'in_progress' | 'done'.
ParamTypeDescription
tabIdstring requiredMCP ID — ask the user once at session start; include it on every subsequent tool call.
todosobject[] required
blnq_get_skill read panel-only
Load a skill's instructions. Omit id to list available skills; pass name or id to load one. If the user is ambiguous about which skill, list then ask via blnq_ask_user.
ParamTypeDescription
tabIdstring requiredMCP ID — ask the user once at session start; include it on every subsequent tool call.
idstring optionalSkill name or numeric id. Omit to list all available skills.

REST / Bridge API

These HTTP endpoints back the MCP tools and are also used directly by the browser client. All endpoints that operate on a tab require tabId (body or query depending on method).

Connection

GET/api/bridge/events?tabId=…

SSE stream for a browser tab. One connection per tab — reconnecting closes the old connection. Heartbeat every 30 s. Tab state is cleaned up 5 minutes after disconnect.

POST/api/bridge/sync

Browser mirrors its content to the server. Body: { tabId, html?, css?, js?, files?, sketch?, projectId?, projectName? }.

POST/api/bridge/page/ready

Browser signals that the preview iframe has finished loading. Body: { tabId }. Tools that wait for page-ready (like blnq_check_preview) resolve when this fires.

POST/api/bridge/approve-mcp-session

Browser approves or rejects an external MCP client's pairing request. Body: { tabId, sessionId, decision }. Resolves a held mcp:pending-approval and pushes mcp:approval-decision.

Content

POST/api/bridge/search

Search across project files. Body: { tabId, query, filenames?, regex? }.

Variants

POST/api/bridge/variant/activate

Browser posts the selected variant's file bundle after the user picks one. Body: { tabId, files: [{ filename, content }] }. Server replaces the project files with the chosen set.

POST/api/bridge/variant/clear

Browser dismisses the variant picker. Body: { tabId }. Server drops stored variants and pushes variants:clear.

Sketch

GET/api/bridge/sketch?tabId=…

Read current sketch elements. Returns { tabId, elements, elementCount, projectId, projectName }.

POST/api/bridge/sketch

Write sketch elements. Body: { tabId, elements: [...] }. Pushes sketch:update via SSE.

Accessibility

POST/api/bridge/a11y/run

Trigger an axe-core scan in the browser and wait for the result. Body: { tabId }. Query: ?timeout=15000. Returns { violations, incomplete } or 504 on timeout.

POST/api/bridge/a11y/results

Browser posts axe-core results. Body: { tabId, violations, incomplete }.

GET/api/bridge/a11y?tabId=…

Read the last cached a11y report for a tab.

Console

POST/api/bridge/console/errors

Browser reports JS console errors. Body: { tabId, errors: [{ level, message, line?, col?, sourceFile? }] }. Capped at 50 errors per tab.

POST/api/bridge/console/clear

Clear stored console errors. Body: { tabId }.

GET/api/bridge/console/errors?tabId=…

Read stored console errors for a tab.

POST/api/bridge/console/logs

Browser reports console.log/info/debug entries (separate ring buffer from errors). Body: { tabId, logs: [{ level, message, sourceFile?, timestamp }] }. Capped at 200 entries per tab.

POST/api/bridge/console/logs/clear

Clear the log buffer. Body: { tabId }.

Cursor

POST/api/bridge/cursor

Browser reports cursor position. Body: { tabId, editor, line, col }.

GET/api/bridge/cursor?tabId=…

Read the latest cursor position for a tab.

Browser-side result endpoints

These are where the browser POSTs results after responding to an SSE request (the "read data back" pattern described earlier). The harness shouldn't need to call these directly.

POST/api/bridge/element/results

Body: { tabId, ...elementInfo }. Reply to element:query.

POST/api/bridge/js/results

Body: { tabId, result, error, logs? }. Reply to js:eval. logs contains any console.log/info/debug emitted during the eval window.

POST/api/bridge/dom/results

Body: { tabId, ok?, action, ...details, logs? }. Reply to dom:action (the unified channel for click / type / press / wait_for).

POST/api/bridge/screenshot/results

Body: { tabId, dataUrl, error }. Reply to a browser canvas capture request (the default screenshot path uses server-side Playwright — this is the fallback).

POST/api/bridge/metrics/results

Body: { tabId, ... }. Reply to metrics:run.

SSE Events

The SSE channel at GET /api/bridge/events?tabId=… pushes the following events. The browser client is the only intended consumer — an external harness never opens this stream.

EventPayloadMeaning
files:refresh{ files: [{ filename, content }] }Full file list replaced — after blnq_undo or writeSketch.
file:create{ filename, content }A new file was added to the project.
file:update{ filename, content }An existing file's content changed.
file:stream{ filename, content }Live partial content while the AI streams a file in (fine-grained tool streaming). The editor renders it as a preview as it grows; the authoritative content lands via file:create/file:update when the write completes.
file:delete{ filename }A file was removed.
sketch:update{ elements: [...] }Excalidraw sketch was written.
design:update{ css, manifest }Compiled design tokens from /DESIGN.md changed — browser refreshes the injected design-token stylesheet.
variants:set{ variants: [...], originalFiles: [...] }Variant picker was opened or extended. Payload includes full file content for every variant plus a snapshot of the pre-variants project so the user can revert.
variants:select{ index }Server committed a variant (via blnq_select_variant) — browser loads it into the editor, discards the other variants, and closes the picker (the "Select" action, no confirm).
variants:clear{}Variant picker was cleared.
project:name{ name }Project name was changed via blnq_set_project_name.
preview:refresh{}Browser should do a full preview rebuild.
a11y:run{}Server is requesting an axe-core scan. Browser replies via POST /api/bridge/a11y/results.
element:query{ selector, styles }Server is requesting an element inspection. Browser replies via POST /api/bridge/element/results.
js:eval{ code }Server is requesting a JS eval in the preview iframe. Browser replies via POST /api/bridge/js/results.
dom:action{ action, ...params }Server is requesting a DOM action (click / type / press / wait_for). Browser replies via POST /api/bridge/dom/results.
metrics:run{}Server is requesting page metrics. Browser replies via POST /api/bridge/metrics/results.
viewport:set{ id }Change the preview viewport preset.
mcp:activity{ toolName, isError, label, source }A tool was invoked — used by the UI to flash an indicator (and drive the streaming earcon / "agenting" outline).
mcp:pending-approval{ sessionId, clientInfo, toolName }An external MCP client is requesting pairing approval for this tab — browser shows a one-time approval prompt.
mcp:approval-decision{ sessionId, status }A pairing request was approved or rejected — resolves the approval prompt and unblocks held tool calls.

Project File Model

Projects contain multiple files, stored as a JSON array. /index.html is the default entry page (and cannot be deleted); additional HTML pages such as /about.html or /blog/post.html are fully supported. CSS and JS files are referenced with standard <link> and <script> tags.

FileRoleNotes
/index.htmlDefault entry pageFull <!DOCTYPE html> page. Loaded for /v/:projectId. References other files with <link href="style.css"> and <script src="main.js">.
/*.htmlAdditional pagesMultiple HTML files supported. Linked via relative <a href="page2.html">; viewable at /v/:projectId/page2.html.
*.cssStylesheetsReferenced via <link rel="stylesheet">. Multiple files supported.
*.jsJavaScript modulesReferenced via <script type="module" src="…">. Full ES module import/export.
/*.excalidrawSketch dataJSON. Rendered in the sketch tab; read/write via blnq_read_sketch / blnq_write_sketch.

The preview uses a Service Worker that serves project files from the in-memory map — ES modules, CSS @import, and relative fetch() all resolve natively against a real origin, not srcdoc.

Validation & Undo

Bracket balance check: every edit operation runs a balance check on the affected file. If unclosed or unmatched {}, (), or [] are detected, a ⚠ SYNTAX warning is returned alongside the successful edit result. This catches broken structure immediately rather than at preview time.

Undo history: every write, edit, create, and delete pushes a snapshot onto the per-tab undo stack. The blnq_undo tool restores prior snapshots and reports which files were affected. History is capped at 30 steps per tab.