> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vertz.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Dev Server Tools for AI Agents

> Use the built-in MCP server and HTTP bridge to give AI agents full observability into your running Vertz app

The Vertz dev server exposes a built-in [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server and an HTTP bridge that give AI coding agents real-time access to errors, rendered pages, API specs, audit logs, and more. Instead of guessing whether code works, agents can **verify** by inspecting the running app.

## Quick setup

The MCP server is always available when the dev server is running — no extra flags needed:

```bash theme={null}
vtz dev
```

### Connect your AI tool

<Tabs>
  <Tab title="Claude Code (MCP)">
    Add to your project's `.mcp.json`:

    ```json theme={null}
    {
      "mcpServers": {
        "vertz-dev": {
          "url": "http://localhost:3000/__vertz_mcp"
        }
      }
    }
    ```

    Claude Code auto-discovers the tools on connection.
  </Tab>

  <Tab title="HTTP Bridge">
    For AI tools that don't support MCP natively, start the HTTP bridge on a separate port:

    ```bash theme={null}
    vtz dev --bridge-port 3001
    ```

    The bridge exposes the same tools as plain HTTP endpoints — no WebSocket required.
  </Tab>
</Tabs>

## Available tools

The dev server provides **21 MCP tools** that agents can call — 9 observability tools and 12 browser interaction tools (11 connected-tab + 1 headless screenshot):

### `vertz_get_errors`

Returns current compilation and runtime errors with file paths, line numbers, code snippets, and fix suggestions.

```json theme={null}
// No parameters needed
// Returns: { errors: [...], count: 2, category: "build" }
```

**When to use:** After every code change to verify nothing is broken. This is the single most important tool for agent workflows.

### `vertz_render_page`

Server-side renders a URL and returns an HTML "text screenshot" with render timing and metadata.

```json theme={null}
{ "url": "/tasks" }
// Returns: rendered HTML + timing + SSR status
```

**When to use:** To verify what a page looks like after making UI changes, without needing a browser.

### `vertz_get_audit_log`

Returns a unified timeline of all server events — API requests, SSR renders, compilations, file changes, and errors — with nanosecond-precision timestamps.

```json theme={null}
{ "last": 50, "type": "error,api_request", "since": "2026-04-05T14:30:00Z" }
```

All parameters are optional. `type` filters by event kind (comma-separated): `api_request`, `ssr_render`, `compilation`, `file_change`, `error`. `since` filters events after an ISO 8601 timestamp.

**When to use:** To understand what happened in the server after a change — did the API return errors? Did SSR fail? Were there compilation issues?

### `vertz_get_diagnostics`

Returns a health snapshot: uptime, compilation cache stats, module graph size, connected HMR clients, SSR pool metrics, and current errors.

```json theme={null}
// No parameters needed
```

**When to use:** To diagnose server health issues or understand the current state of the dev environment.

### `vertz_get_api_spec`

Returns an OpenAPI 3.1 specification with all entity CRUD routes, service endpoints, request/response schemas, and access rules.

```json theme={null}
{ "filter": "tasks,users" }
```

`filter` is optional — comma-separated entity/service names to include. Without it, returns the full spec.

**When to use:** To understand the API surface when writing frontend code or integration tests.

### `vertz_render_component`

Renders a single component in isolation with optional props.

```json theme={null}
{ "file": "src/components/TaskCard.tsx", "props": { "title": "Buy milk" } }
```

**When to use:** To quickly check a component's rendered output during iterative editing, without navigating to a page.

### `vertz_navigate`

Triggers client-side navigation in the browser via the HMR WebSocket — no full page reload.

```json theme={null}
{ "to": "/tasks/123" }
```

**When to use:** To navigate the user's browser to a specific page for visual verification.

### `vertz_get_events_url`

Returns the WebSocket URL for real-time event push, so agents can subscribe to live updates instead of polling.

```json theme={null}
// No parameters needed
// Returns: { url: "ws://localhost:3000/__vertz_mcp/events" }
```

### `vertz_get_console` (deprecated)

Use `vertz_get_audit_log` instead — it provides the same information in a more structured, filterable format.

## Browser interaction tools

These tools let agents interact with the live browser page — clicking buttons, filling forms, reading page state — without needing a separate browser automation tool like Playwright.

### `vertz_browser_list_tabs`

Lists all connected browser tabs with their current URL, title, and control status.

```json theme={null}
// No parameters needed
// Returns: { tabs: [{ id, url, title, controlled }] }
```

### `vertz_browser_connect`

Connects to a browser tab for interactive control. Returns a session ID and an initial page snapshot showing all interactive elements.

```json theme={null}
{ "tabId": "tab-abc123" }
// tabId is optional — auto-connects if only one tab is open
// Returns: { sessionId, tab: { id, url, title }, snapshot: { ... } }
```

### `vertz_browser_disconnect`

Releases a browser control session.

```json theme={null}
{ "sessionId": "sess-abc123" }
```

### `vertz_browser_snapshot`

Returns a structured snapshot of the page: interactive elements with refs, form structure, focused element, and current values.

```json theme={null}
{ "sessionId": "sess-abc123", "maxElements": 50 }
// sessionId is optional if only one session is active
```

The snapshot includes:

* **Elements**: inputs, buttons, selects, links, checkboxes — each with a stable `ref` for targeting
* **Forms**: form elements with their field refs, action, and method
* **Focused element**: which element currently has focus
* **URL and title**: current page state

### `vertz_browser_click`

Clicks an element. Target can be an element ref from a snapshot, a CSS selector, or a text/name/label matcher.

```json theme={null}
{ "target": "submit-btn" }
{ "target": "#my-button" }
{ "target": { "text": "Save" } }
// Returns updated snapshot after the page settles
```

### `vertz_browser_type`

Types text into an input or textarea.

```json theme={null}
{ "target": "email", "text": "user@example.com" }
```

### `vertz_browser_select`

Selects an option in a `<select>` element.

```json theme={null}
{ "target": "priority", "value": "high" }
```

### `vertz_browser_fill_form`

Fills multiple form fields at once. Handles text inputs, textareas, selects, checkboxes, and radio buttons.

```json theme={null}
{
  "target": "f1",
  "data": {
    "title": "My Task",
    "priority": "high",
    "assignee": "alice"
  }
}
```

### `vertz_browser_submit`

Submits a form. Waits for navigation if it occurs (up to 2s).

```json theme={null}
{ "target": "f1" }
// Returns: snapshot + navigation info if URL changed
```

### `vertz_browser_press_key`

Presses a keyboard key on the currently focused element.

```json theme={null}
{ "key": "Enter" }
{ "key": "Escape" }
{ "key": "Tab" }
```

### `vertz_browser_wait`

Waits for a condition to be met in the browser, polling every 100ms.

```json theme={null}
{ "condition": { "text": "Task created" }, "timeoutMs": 5000 }
{ "condition": { "selector": ".success-message" } }
{ "condition": { "url": "/tasks" } }
{ "condition": { "absent": ".loading-spinner" } }
```

### `vertz_browser_screenshot`

Headless pixel-perfect PNG of any route served by the dev server. Returns the image inline (the
agent sees it) plus a local file path and URL that a human can click to open the PNG.

```json theme={null}
{ "url": "/" }
{ "url": "/tasks", "viewport": { "width": 375, "height": 667 } }
{ "url": "/tasks", "fullPage": true }
{ "url": "/", "crop": ".hero" }
{ "url": "/", "crop": { "text": "Get started" } }
{ "url": "/", "waitFor": "networkidle" }
```

**Parameters**

| Param      | Type                                            | Default         | Description                                                                                                            |
| ---------- | ----------------------------------------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `url`      | `string` (required)                             | —               | Path (`/`, `/tasks`) or a same-origin URL (`http://localhost:PORT/...`). External hosts are rejected.                  |
| `viewport` | `{ width: number, height: number }`             | `1280x720`      | Viewport size. Max 4096x4096.                                                                                          |
| `fullPage` | `boolean`                                       | `false`         | Capture the full scrollable page (uses `captureBeyondViewport`).                                                       |
| `crop`     | `string \| { text \| name \| label: string }`   | `null`          | Clip to a single element. CSS string or a single-key object using the same locator dialect as the other browser tools. |
| `waitFor`  | `"domcontentloaded" \| "networkidle" \| "load"` | `"networkidle"` | When to take the screenshot. `networkidle` catches `query()` resolution.                                               |

**Response**

Two MCP content blocks: an `image` block with the base64-encoded PNG and a `text` block with
stringified metadata:

```json theme={null}
{
  "path": "/absolute/path/.vertz/artifacts/screenshots/2026-04-21T12-00-00Z-tasks-1280x720.png",
  "url": "http://localhost:3000/__vertz_artifacts/screenshots/2026-04-21T12-00-00Z-tasks-1280x720.png",
  "dimensions": { "width": 1280, "height": 720 },
  "pageUrl": "http://localhost:3000/tasks",
  "capturedInMs": 142
}
```

The returned image renders inline in the agent's UI. The `url` field is a local dev-server link —
share it with humans in chat messages; they can click it to open the PNG directly.

**Error codes** (MCP `isError: true` with stringified JSON in the `text` block):

| Code                   | When it fires                                                                                                                                                                 |
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `URL_INVALID`          | External host, protocol-relative URL, or malformed input                                                                                                                      |
| `CHROME_LAUNCH_FAILED` | No Chrome / Chromium on the machine (set `VERTZ_CHROME_PATH` or install one)                                                                                                  |
| `NAVIGATION_FAILED`    | Browser failed to navigate to the route                                                                                                                                       |
| `SELECTOR_INVALID`     | `crop` was provided but is malformed (e.g., bad CSS)                                                                                                                          |
| `SELECTOR_NOT_FOUND`   | `crop` matched no element                                                                                                                                                     |
| `CAPTURE_FAILED`       | Screenshot request itself failed                                                                                                                                              |
| `AUTH_REQUIRED`        | Final URL looked like an auth page (`/login`, `/signin`, `/sign_in`, etc.) — the tool refuses to silently return a login screen. See `finalUrl` in the error body to confirm. |

**Scope limits (v1)**

* **Same-origin only.** Paths and `localhost` / `127.0.0.1` / `::1` URLs pass; everything else returns `URL_INVALID`.
* **Public routes only.** If the route redirects to an auth gate, the tool returns `AUTH_REQUIRED` rather than screenshotting the login screen. Authenticated-route capture ships in a later phase.
* **No session sharing.** Each call launches an isolated Chromium page — cookies and local storage don't carry over from the `vertz_browser_*` connected-tab tools.

**Artifacts**

Every successful call writes one PNG to `.vertz/artifacts/screenshots/` (gitignored). Filenames are
`<UTC-iso>-<slug>-<viewport>.png`, lexicographically sortable so the newest is last:

```
.vertz/artifacts/screenshots/
├── 2026-04-21T11-48-12Z-1280x720.png
├── 2026-04-21T11-49-03Z-tasks-375x667.png
└── 2026-04-21T11-50-44Z-tasks-full.png
```

**Tips**

* Before reporting a UI change as done, call `vertz_browser_screenshot({ url: '<the-affected-route>' })` and compare against what the task asked for.
* Check layout at mobile AND desktop with two calls that differ only in `viewport`.
* Narrow noisy full-page captures with `crop` — either a CSS selector or `{ text: "..." }`.
* `waitFor: "networkidle"` is the safest default; use `"domcontentloaded"` only when you explicitly want to see a loading state.

### Element targeting

All interaction tools accept flexible targeting:

| Format       | Example                | Description                                        |
| ------------ | ---------------------- | -------------------------------------------------- |
| Ref string   | `"submit-btn"`         | Element ref from the last snapshot (most reliable) |
| CSS selector | `"#my-button"`         | Standard CSS selector via `querySelector`          |
| Text match   | `{ "text": "Save" }`   | Find element by visible text content               |
| Name match   | `{ "name": "email" }`  | Find element by `name` attribute                   |
| Label match  | `{ "label": "Email" }` | Find input by its associated `<label>` text        |

### Browser interaction workflow

```
1. vertz_browser_connect      → get session + initial snapshot
2. vertz_browser_fill_form    → fill the form fields
3. vertz_browser_submit       → submit and check navigation
4. vertz_browser_wait         → wait for success message
5. vertz_browser_disconnect   → release the session
```

<Tip>
  If only one browser tab is open (the common case), you can omit both `tabId` in connect and
  `sessionId` in all subsequent calls — the tools auto-resolve to the single tab/session.
</Tip>

## Real-time events

Instead of polling, agents can subscribe to a WebSocket stream for live updates:

```
ws://localhost:3000/__vertz_mcp/events
```

Or via the HTTP bridge (SSE):

```
GET http://localhost:3001/events
```

### Event types

| Event              | Description                                         |
| ------------------ | --------------------------------------------------- |
| `error_update`     | Error state changed (new errors, errors cleared)    |
| `file_change`      | Source file was modified                            |
| `hmr_update`       | HMR sent update to browser                          |
| `ssr_refresh`      | SSR module re-imported after file change            |
| `typecheck_update` | Type checker diagnostics updated                    |
| `server_status`    | Server status change (sent on connect as handshake) |

### Filtering

Subscribe to specific events via query parameter:

```
ws://localhost:3000/__vertz_mcp/events?subscribe=error_update,typecheck_update
```

## HTTP bridge

The HTTP bridge (`--bridge-port`) exposes the same capabilities as plain REST endpoints for tools that don't support MCP or WebSocket:

| Method | Endpoint   | Description                                                  |
| ------ | ---------- | ------------------------------------------------------------ |
| `GET`  | `/health`  | Bridge health check, lists available event types             |
| `GET`  | `/tools`   | Tool discovery (list all available tools with descriptions)  |
| `POST` | `/command` | Execute a tool: `{ "tool": "vertz_get_errors", "args": {} }` |
| `GET`  | `/events`  | SSE event stream (same events as WebSocket)                  |

### Example: calling a tool via the bridge

```bash theme={null}
curl -X POST http://localhost:3001/command \
  -H 'Content-Type: application/json' \
  -d '{"tool": "vertz_get_errors", "args": {}}'
```

Response:

```json theme={null}
{ "ok": true, "result": { "errors": [], "count": 0 } }
```

## Recommended agent workflow

The dev server tools are most effective when agents follow this pattern:

1. **Make a code change** (edit a file)
2. **Check for errors** — call `vertz_get_errors` to catch compilation/type issues immediately
3. **Check the audit log** — call `vertz_get_audit_log` with `type: "error"` to see if runtime errors occurred
4. **Verify the UI** — call `vertz_render_page` to see the rendered output
5. **Interact with the page** — call `vertz_browser_connect`, then use click/type/fill\_form/submit to test interactive behavior
6. **Only then report success** — don't tell the user "it works" until you've verified via the tools

This prevents the common failure mode where an agent says "Done!" but the app has a build error or renders a blank page.

<Tip>
  For long-running development sessions, subscribe to the real-time event stream via WebSocket or
  SSE. This way the agent is notified immediately when errors appear, instead of discovering them
  later.
</Tip>

## Diagnostic endpoints

These HTTP endpoints are always available (no MCP or bridge needed):

| Endpoint                           | Description                                                          |
| ---------------------------------- | -------------------------------------------------------------------- |
| `GET /__vertz_diagnostics`         | JSON health snapshot (uptime, cache, module graph, errors, SSR pool) |
| `GET /__vertz_ai/errors`           | Current errors formatted for LLM consumption                         |
| `GET /__vertz_ai/render?url=/path` | SSR render for LLM consumption                                       |
| `GET /__vertz_ai/console?last=N`   | Recent console output                                                |
| `POST /__vertz_ai/navigate`        | Navigate the browser                                                 |

These are the same underlying handlers as the MCP tools, exposed as direct HTTP endpoints for simpler integrations.
