# MCP tools & hub API

> Reference for project-scoped MCP tools, browser routes, leases, and configuration.

Run `npx -y @wcgw/vibe-check-mcp@0.3.0 hub` once. MCP clients spawn
`npx -y @wcgw/vibe-check-mcp@0.3.0 connect` and expose the tools below.

## Project selection

Every project-scoped tool accepts optional `project_id: string`. It may be
omitted only when exactly one active project exists or the session already owns
a lease. Multiple active projects without a selection return
`project-ambiguous`; an unknown value returns `project-not-found`.

## Tools

| Tool | Parameters | Returns |
|---|---|---|
| `list_projects` | — | Active project summaries with URL, last seen, issue count, queue depth, and agent state. |
| `get_performance_snapshot` | `project_id?` | Latest `VibeSnapshot`, or `no-snapshot`. |
| `get_detected_issues` | `project_id?`, `severity?`, `detector?` | `{ count, issues }` for one project. |
| `get_fix_suggestions` | `project_id?`, `issue_id` | Markdown suggestion, or `issue-not-found`. |
| `watch_performance` | `project_id?`, `timeout_seconds?` (1–300, default 30) | Acquires the lease and returns the next project snapshot, or `watch-timeout`. |
| `watch_for_issue` | `project_id?`, `timeout_seconds?` (1–300, default 30) | Acquires the lease and returns one queued widget dispatch plus its suggestion, or `watch-timeout`. |
| `acknowledge_issue` | `project_id?`, `issue_id` | `{ acknowledged: true, projectId, issue_id }`. |
| `resolve_issue` | `project_id?`, `issue_id` | `{ resolved: true, projectId, issue_id }`. |
| `release_project` | — | Releases the current session's lease. |

One session cannot select a different project while it owns a lease; the result
is `session-already-watching`. A healthy owner blocks another session with
`lease-conflict`.

## Browser API

| Route | Method | Response |
|---|---|---|
| `/api/health` | GET | `{ status: "ok", service: "vibe-check-hub", version }`. |
| `/api/snapshot` | POST | Accepts `{ projectId, instanceId, pageUrl, snapshot }`; returns `{ received: true, projectId }`. |
| `/api/projects/:projectId/status` | GET | `{ projectId, state, queueDepth, leaseExpiresAt, conflictAt }`. |
| `/api/projects/:projectId/dispatch` | POST | Accepts `{ projectId, instanceId, issue }`; returns a `DispatchIssueResponse`. |

Browser routes allow CORS. `/internal/*` is reserved for the local bridge and
rejects requests carrying a browser `Origin` header.

Dispatch result codes are `dispatched`, `unconfigured`, `hub-offline`,
`agent-not-watching`, `queue-full`, `invalid-issue`, and `failed`. The per-project
queue holds at most 10 issues.

## Lease timing

| Event | Timing |
|---|---|
| Bridge heartbeat | every 5 seconds |
| Widget reports watcher stale | after 10 seconds |
| Lease expires and may be reclaimed | after 15 seconds |
| Conflict warning retained | 30 seconds |

## Environment

| Role | Variable | Default |
|---|---|---|
| `hub` | `VIBE_CHECK_HOST` | `127.0.0.1` |
| `hub` | `VIBE_CHECK_PORT` | `4200` |
| `connect` | `VIBE_CHECK_HUB_URL` | `http://127.0.0.1:4200` |

**Keep the hub local**

  It is an unauthenticated development service. The default loopback bind is
  intentional; do not expose it to a public interface.
