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.
The Vertz dev server exposes a built-in Model Context Protocol (MCP) 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:
Claude Code (MCP)
HTTP Bridge
Add to your project’s .mcp.json:{
"mcpServers": {
"vertz-dev": {
"url": "http://localhost:3000/__vertz_mcp"
}
}
}
Claude Code auto-discovers the tools on connection. For AI tools that don’t support MCP natively, start the HTTP bridge on a separate port:vtz dev --bridge-port 3001
The bridge exposes the same tools as plain HTTP endpoints — no WebSocket required.
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.
// 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.
{ "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.
{ "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.
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.
{ "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.
{ "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.
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.
// 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.
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.
// 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.
{ "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.
{ "sessionId": "sess-abc123" }
vertz_browser_snapshot
Returns a structured snapshot of the page: interactive elements with refs, form structure, focused element, and current values.
{ "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.
{ "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.
{ "target": "email", "text": "user@example.com" }
vertz_browser_select
Selects an option in a <select> element.
{ "target": "priority", "value": "high" }
Fills multiple form fields at once. Handles text inputs, textareas, selects, checkboxes, and radio buttons.
{
"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).
{ "target": "f1" }
// Returns: snapshot + navigation info if URL changed
vertz_browser_press_key
Presses a keyboard key on the currently focused element.
{ "key": "Enter" }
{ "key": "Escape" }
{ "key": "Tab" }
vertz_browser_wait
Waits for a condition to be met in the browser, polling every 100ms.
{ "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.
{ "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:
{
"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
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.
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) |
curl -X POST http://localhost:3001/command \
-H 'Content-Type: application/json' \
-d '{"tool": "vertz_get_errors", "args": {}}'
Response:
{ "ok": true, "result": { "errors": [], "count": 0 } }
Recommended agent workflow
The dev server tools are most effective when agents follow this pattern:
- Make a code change (edit a file)
- Check for errors — call
vertz_get_errors to catch compilation/type issues immediately
- Check the audit log — call
vertz_get_audit_log with type: "error" to see if runtime errors occurred
- Verify the UI — call
vertz_render_page to see the rendered output
- Interact with the page — call
vertz_browser_connect, then use click/type/fill_form/submit to test interactive behavior
- 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.
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.
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.