@wcgw/vibe-check-mcp
v0.3.0
Published
Project-isolated local hub and MCP bridge for VibeCheck browser issues
Maintainers
Readme
@wcgw/vibe-check-mcp
Local hub and MCP bridge for VibeCheck. Browser widgets send project-tagged snapshots to one long-running hub. Each AI-agent client launches a small stdio bridge that connects to that hub.
This split matters: starting another agent session no longer tries to bind a
second HTTP server to port 4200, and several dev servers remain isolated by
projectId.
From nothing to a widget-to-agent dispatch
You need Node.js 20+ and an MCP-capable coding agent.
Fast path: scaffold the project and agent
Run this from an existing React project:
npx -y @wcgw/vibe-check-mcp@latest setup --agent codex --project my-storefrontSupported agents are codex, claude-code, and cursor. The command detects
pnpm, npm, Yarn, or Bun; installs the widget at the same version as the MCP CLI;
creates a named VibeCheckDevtools component; and configures the selected MCP
client. Cursor configuration is merged into .cursor/mcp.json without removing
other servers. Setup also registers the project root, writes the commit-safe
.vibecheck/config.json, and adds only runtime state files to .gitignore.
| Setup option | Meaning |
|---|---|
| --agent <id> | Required: codex, claude-code, or cursor. |
| --project <id> | Stable routing ID. Defaults to package.json name. |
| --dry-run | Print install, file, agent, hub, and watch steps without changing anything. |
| --force | Replace an existing generated VibeCheckDevtools.tsx. Other app files are never rewritten. |
Mount <VibeCheckDevtools /> once near your React app root, start the hub, and
paste the project-specific watch instruction printed by setup. The generated
component returns null in production, so the monitor and local hub connection
cannot ship in the production UI. The detailed manual path below explains every
generated piece.
1. Install the React widget
npm install -D @wcgw/vibe-checkMount it only in development and choose a stable project ID:
import { PerfToggle } from '@wcgw/vibe-check'
export const App = () => (
<>
<YourApp />
{import.meta.env.DEV && (
<PerfToggle vibeCheckProps={{
beaconUrl: 'http://127.0.0.1:4200',
projectId: 'my-storefront',
}} />
)}
</>
)Without beaconUrl, the widget is deliberately local-only: collectors, panels,
annotations, and clipboard prompts work, but it cannot communicate with an agent.
2. Start one local hub
Run this in its own terminal and leave it running:
npx -y @wcgw/[email protected] hubExpected output:
[vibe-check] Hub listening on http://127.0.0.1:4200The widget first reports Waiting for an agent. That means browser-to-hub communication works; no agent session owns the project yet.
3. Add the MCP bridge to your agent
Choose the same client in the widget setup card and use its exact value.
Codex
codex mcp add vibe-check -- npx -y @wcgw/[email protected] connectVerify with codex mcp get vibe-check --json.
Claude Code
claude mcp add --scope local vibe-check -- npx -y @wcgw/[email protected] connectVerify with claude mcp get vibe-check.
Cursor
Merge the vibe-check entry into mcpServers in .cursor/mcp.json. If the file
already contains other MCP servers, keep them alongside this entry:
{
"vibe-check": {
"command": "npx",
"args": [
"-y",
"@wcgw/[email protected]",
"connect"
]
}
}Create the top-level mcpServers object first when starting from an empty file.
Approve the project MCP when Cursor asks. Verify it with
cursor-agent mcp list-tools vibe-check.
Restart the agent client after editing its MCP configuration. The bridge expects
the hub at http://127.0.0.1:4200; set VIBE_CHECK_HUB_URL when the hub uses a
different local port.
4. Claim the project and wait
Ask the agent:
Use the vibe-check MCP tools. Call list_projects, then call watch_for_issue with project_id "my-storefront" and keep waiting for the next issue I send from the widget.watch_for_issue acquires the project's exclusive watcher lease and waits. The
widget changes to Agent connected. While processing a delivered issue it may
show Agent working.
5. Send a real detected issue
Open the widget's Agent tab, expand an issue, and click Send to agent.
The pending watch_for_issue tool call returns:
- the exact structured issue selected in the browser;
- its project ID and dispatch timestamp; and
- a detector-specific fix suggestion.
The widget moves the issue into in progress only after the hub confirms the dispatch.
Copy prompt is a separate clipboard-only action and never claims delivery.
When the agent calls resolve_issue, VibeCheck enters Verifying and waits
for two newer snapshots from the same page without the issue. A returning fixed
issue is reopened as Regressed instead of being forgotten.
Workflow history is stored per project in .vibecheck/state.json. The file is
updated atomically, survives hub restarts, and is ignored by git. If setup was
not used, register the project manually before starting the hub:
npx -y @wcgw/[email protected] register --project my-storefront --root .Persisted project impact
The same project state keeps an impact ledger for shareable, evidence-backed results. Lifecycle totals are exact: issues detected and sent, unique issues fixed, verified fix cycles, failed verifications, regressions caught, and median time to fix. Performance savings are recorded only when comparable before/after browser snapshots support them. Every measurement includes its scope; for example, “4 duplicate requests removed per observed page load.”
Read it in the widget, ask the agent to call get_project_impact, or use:
npx -y @wcgw/[email protected] stats --project my-storefront
npx -y @wcgw/[email protected] stats --project my-storefront --markdown
npx -y @wcgw/[email protected] stats --project my-storefront --jsonStats remain readable from registered state after a hub restart even when the browser has not reconnected. Resetting impact sets a new reporting baseline; it does not delete issue timelines or the regression baseline.
Multiple projects and agent sessions
Use one hub for all local dev servers. Give each widget a different stable ID:
// localhost:3000
<VibeCheck beaconUrl="http://127.0.0.1:4200" projectId="storefront" />
// localhost:5173
<VibeCheck beaconUrl="http://127.0.0.1:4200" projectId="admin-console" />The hub keeps snapshots, issue histories, dispatch queues, and watcher leases per
project. Agent session A can watch storefront while session B watches
admin-console; neither can read or consume the other's dispatches.
The ownership rules are intentional:
- one agent session may watch one project at a time;
- one project may have one active agent watcher;
- a second watcher for the same project receives
lease-conflict; - the widget keeps showing the original watcher as connected and adds a warning that the second agent was rejected;
- leases are heartbeated every 5 seconds, become stale after 10 seconds, and are released after 15 seconds without a heartbeat;
release_projectreleases ownership immediately.
When several projects are active, pass project_id to project-scoped tools. If
exactly one is active, it may be omitted. This fail-closed behavior prevents an
agent from silently selecting the wrong dev server.
MCP tools
| Tool | Main arguments | Purpose |
|---|---|---|
| list_projects | — | List active project IDs, page URLs, last-seen times, issue counts, queue depth, and watcher state. |
| get_performance_snapshot | project_id? | Read the latest snapshot for one project. |
| get_detected_issues | project_id?, severity?, detector? | Read active issues for one project. |
| get_project_impact | project_id? | Read persisted exact outcomes and measured improvements. |
| get_fix_suggestions | project_id?, issue_id | Get the detector-specific fix guide for one issue. |
| watch_performance | project_id?, timeout_seconds? | Claim a project and wait for its next snapshot. |
| watch_for_issue | project_id?, timeout_seconds? | Claim a project and wait for a widget button dispatch. |
| acknowledge_issue | project_id?, issue_id | Acknowledge an issue in one project. |
| resolve_issue | project_id?, issue_id | Ask browser evidence to verify an agent fix. |
| release_project | — | Release this bridge session's current lease. |
Browser HTTP API
The public browser routes allow CORS and accept only browser-facing operations:
| Endpoint | Method | Purpose |
|---|---|---|
| /api/health | GET | Hub readiness and version. |
| /api/snapshot | POST | Receive a ProjectSnapshotEnvelope. |
| /api/projects/:projectId/status | GET | Widget-visible watcher, queue, and conflict state. |
| /api/projects/:projectId/workflow | GET | Browser-safe persisted issue phases and timelines. |
| /api/projects/:projectId/impact | GET | Browser-safe persisted impact summary. |
| /api/projects/:projectId/impact/reset | POST | Start a new impact reporting period without deleting workflow history. |
| /api/projects/:projectId/dispatch | POST | Queue a selected issue for the owning watcher. |
| /api/projects/:projectId/issues/:issueId/verify | POST | Request evidence-based fix verification. |
Bridge-only routes live under /internal. Requests with a browser Origin
header are rejected there, so a page cannot acquire leases or read another
project through the private API.
Configuration
| Process | Environment variable | Default | Purpose |
|---|---|---|---|
| hub | VIBE_CHECK_HOST | 127.0.0.1 | Hub bind address. |
| hub | VIBE_CHECK_PORT | 4200 | Hub port. |
| hub, setup, register | VIBE_CHECK_REGISTRY_PATH | ~/.vibecheck/projects.json | Project ID to local-root registry. |
| connect, doctor, stats | VIBE_CHECK_HUB_URL | http://127.0.0.1:4200 | Hub used by agent and diagnostic commands. |
For a port override, update all three places together:
VIBE_CHECK_PORT=4210 npx -y @wcgw/[email protected] hub
VIBE_CHECK_HUB_URL=http://127.0.0.1:4210 npx -y @wcgw/[email protected] connect<VibeCheck beaconUrl="http://127.0.0.1:4210" projectId="my-storefront" />Programmatic composition
import {
createHubClient,
createHubServer,
createLeaseManager,
createMcpServer,
} from '@wcgw/vibe-check-mcp'
const hub = createHubServer({ version: '0.3.0' })
hub.server.listen(4200, '127.0.0.1')
const client = createHubClient('http://127.0.0.1:4200')
const leases = createLeaseManager(client, crypto.randomUUID())
const mcp = createMcpServer(client, leases, '0.3.0')The CLI's connect mode also attaches StdioServerTransport and releases its
lease on shutdown.
Doctor command
doctor is a read-only check across the full local path. It never acquires or
releases a watcher lease.
npx -y @wcgw/[email protected] doctor
npx -y @wcgw/[email protected] doctor --project my-storefront
npx -y @wcgw/[email protected] doctor --project my-storefront --jsonOptions and environment:
| Input | Meaning |
|---|---|
| --project <id> | Select one project explicitly. Required when several browser projects are active. |
| --json | Emit the stable, versioned DoctorReport JSON shape. |
| VIBE_CHECK_HUB_URL | Override the hub URL used by doctor and connect. |
The result states are:
- ready — exits
0when the hub is reachable, the selected browser snapshot is fresh, and its owner state iswatchingorbusy; - offline — exits
1; startnpx -y @wcgw/[email protected] hub; - ambiguous — exits
1; rerundoctor --project <id>with a listed project; - missing — exits
1; open or reload the requested browser project; - stale — exits
1; reload a stale browser snapshot or reconnect a stale agent watcher; - waiting — exits
1; configure Codex, Claude Code, or Cursor, restart it, and paste the project-specific watch instruction.
JSON output contains schemaVersion, ok, hubUrl, generatedAt,
selectedProjectId, checks, projects, and nextSteps.
Repository maintainers can run pnpm test:clients. It uses temporary Codex,
Claude Code, and Cursor configuration, launches the built bridge through a
temporary hub, and makes no model request.
Troubleshooting
- MCP not configured — no
beaconUrlreached the widget. Add bothbeaconUrland a stableprojectId. - MCP server offline — the widget cannot reach the hub. Start
... hub, check/api/health, and make sure the hostname and port match exactly. - Waiting for an agent — browser-to-hub works. In an agent session, call
watch_for_issuefor the displayed project. - Agent disconnected — the lease heartbeat stopped. Restart/reconnect the MCP
client and call
watch_for_issueagain after the 15-second expiry. - Second agent was rejected — another healthy session owns that project.
Continue in the owning session, or call
release_projectthere before moving. project-ambiguous— more than one dev server is active. Calllist_projectsand pass the intendedproject_id.EADDRINUSEon 4200 — a hub may already be running; reuse it. Otherwise choose one alternate port and update the hub, bridge, and widget together.- Bridge fails during startup —
connectintentionally fails if the hub is unavailable. Start the hub first, then restart the agent client.
License
MIT
