MCP Tools¶
VFA exposes three tools over stdio. All of them return a single JSON object; failures return a structured error object instead of raising, so an agent can reason about them.
| Tool | Purpose |
|---|---|
qa_audit_url |
Autonomous black-box audit of a single page. |
qa_execute_user_flow |
Drives a multi-step natural-language user journey. |
qa_get_runtime_errors |
Drains the buffered runtime error channel. |
Every invocation runs inside an isolated, ephemeral BrowserContext that is destroyed on
completion. See the security overview for the guarantees this
provides.
qa_audit_url¶
Opens a URL, waits for network idle, inspects the rendered page and reports functional, visual and accessibility defects together with any console or network errors collected during the visit.
Parameters¶
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
url |
string |
Yes | — | Absolute http/https URL to audit. Private and loopback targets are rejected unless VFA_ALLOW_LOCAL_HOST=true. |
viewport |
object |
No | {"width": 1440, "height": 900} |
Viewport used for rendering and screenshots. |
wait_until |
string |
No | "networkidle" |
Load strategy: load, domcontentloaded or networkidle. |
max_depth |
integer |
No | 1 |
Follow same-origin links up to this depth. 0 audits only the entry page. |
include_screenshots |
boolean |
No | true |
Attach base64 screenshots (viewport and full page) to the report. |
focus |
string |
No | — | Free-text hint narrowing the audit, e.g. "checkout funnel and form validation". |
Behavior¶
- Validates the target against the anti-SSRF firewall and the license gate.
- Creates a fresh context, navigates and waits for the requested load state.
- Captures a full-page screenshot and an annotated DOM snapshot for visual grounding.
- Runs the reasoning pass: broken layouts, unreachable controls, missing labels, console
errors and HTTP failures
>= 400are all correlated into findings. - Returns the report and tears the context down.
Output schema¶
{
"status": "completed",
"url": "https://example.com/pricing",
"duration_ms": 41230,
"findings": [
{
"id": "FND-001",
"severity": "high",
"category": "functional",
"title": "Primary CTA does not respond to click",
"description": "Clicking 'Start free trial' produces no navigation and no state change.",
"selector_hint": "button[data-testid='trial-cta']",
"evidence": {
"screenshot_index": 0,
"console_errors": 1
}
}
],
"metrics": {
"pages_audited": 3,
"console_errors": 2,
"http_errors": 1,
"duration_ms": 41230
},
"screenshots": [
{ "name": "viewport", "mime_type": "image/png", "data": "<base64>" }
]
}
severity is one of critical, high, medium, low, info. category is one of
functional, visual, accessibility, performance, network.
qa_execute_user_flow¶
Executes a defined user journey — written in natural language — against a starting URL. This
is the functional counterpart to qa_audit_url: instead of inspecting a single page, VFA
attempts to complete a task the way a real user would, and reports where it got stuck.
Parameters¶
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
url |
string |
Yes | — | Entry point of the flow. Must be an absolute http/https URL. |
steps |
array |
Yes | — | Ordered list of natural-language steps. Minimum length is 1. |
expected_outcome |
string |
No | — | Natural-language success condition, e.g. "the order confirmation page is shown". |
timeout_ms |
integer |
No | 120000 |
Total budget for the whole flow. |
stop_on_failure |
boolean |
No | true |
Abort on the first failed step instead of continuing optimistically. |
Step format¶
Each entry in steps is a plain string describing intent, not a selector:
{
"url": "https://shop.example.com",
"steps": [
"Open the product page for the first item in the catalog",
"Select size M and add the item to the cart",
"Go to the cart and apply the coupon code SAVE10",
"Proceed to checkout without creating an account",
"Fill the shipping form with a test address and submit"
],
"expected_outcome": "The order confirmation page displays an order number",
"timeout_ms": 180000
}
VFA resolves each step through active visual grounding: the screenshot and annotated DOM are analyzed to locate the element a human would interact with, then the action is replayed in the browser. Failed steps include the reason and the last observed page state.
Output schema¶
{
"status": "failed",
"url": "https://shop.example.com",
"expected_outcome": "The order confirmation page displays an order number",
"steps": [
{
"index": 0,
"instruction": "Open the product page for the first item in the catalog",
"status": "success",
"action": "click",
"target": "a.product-card__link",
"duration_ms": 3120
},
{
"index": 2,
"instruction": "Go to the cart and apply the coupon code SAVE10",
"status": "failed",
"action": "fill",
"target": "input[name='coupon']",
"error": "Coupon field present but the apply button stayed disabled after input.",
"duration_ms": 18400
}
],
"outcome_matched": false,
"findings": [
{
"id": "FND-014",
"severity": "critical",
"category": "functional",
"title": "Coupon apply button never enables",
"description": "The apply button is disabled even with a syntactically valid coupon code."
}
],
"metrics": {
"steps_total": 5,
"steps_succeeded": 2,
"steps_failed": 1,
"steps_skipped": 2,
"duration_ms": 60510
},
"screenshots": [
{ "name": "step_2_failure", "mime_type": "image/png", "data": "<base64>" }
]
}
status is completed, failed or timeout. A steps[].status of skipped appears when
stop_on_failure is true and an earlier step already failed.
qa_get_runtime_errors¶
Drains VFA's runtime error buffer. The buffer collects everything the browser reported while auditing, independent of what the reasoning model decided to report as findings — useful when an agent wants raw evidence or a post-run regression signal.
Parameters¶
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
clear |
boolean |
No | true |
Empties the buffer after reading. Set to false to peek without draining. |
source |
string |
No | "all" |
Filter by origin: console, pageerror, http or all. |
min_severity |
string |
No | "info" |
Drop entries below this level (info, warning, error). |
Behavior¶
The buffer is populated continuously during every audit and flow run. It captures three channels:
console—console.warn,console.errorand uncaught assertions emitted by the page.pageerror— uncaught JavaScript exceptions, including stack traces.http— every response with status>= 400, with method, URL and response time.
Calling the tool with clear: true returns the entries and empties the buffer, which keeps
successive agent turns from re-reading the same events.
Output schema¶
{
"status": "completed",
"count": 3,
"entries": [
{
"id": "ERR-0001",
"source": "console",
"severity": "error",
"level": "error",
"message": "Uncaught TypeError: Cannot read properties of null (reading 'value')",
"url": "https://shop.example.com/cart",
"timestamp": "2025-01-14T10:22:41.512Z"
},
{
"id": "ERR-0002",
"source": "pageerror",
"severity": "error",
"message": "TypeError: cart.total is undefined",
"stack": "at renderCart (cart.js:84:17)\n at HTMLDocument.dispatch (app.js:31:9)",
"url": "https://shop.example.com/cart",
"timestamp": "2025-01-14T10:22:41.517Z"
},
{
"id": "ERR-0003",
"source": "http",
"severity": "warning",
"message": "POST https://shop.example.com/api/coupon responded with 422",
"status_code": 422,
"duration_ms": 312,
"url": "https://shop.example.com/cart",
"timestamp": "2025-01-14T10:22:44.980Z"
}
]
}
count reflects the number of entries returned in this call, not the historical total.
Error responses¶
All three tools share a common failure shape:
{
"status": "error",
"error": {
"code": "SSRF_BLOCKED",
"message": "Target resolves to a private address; navigation refused.",
"hint": "Set VFA_ALLOW_LOCAL_HOST=true to audit local applications."
}
}
| Code | Meaning |
|---|---|
LICENSE_INVALID |
VFA_LICENSE_KEY missing, expired or activated on too many seats. |
SSRF_BLOCKED |
Target resolved to loopback, link-local or private space. |
NAVIGATION_FAILED |
DNS failure, TLS error or the page never reached the requested load state. |
LLM_PROVIDER_ERROR |
Upstream provider rejected the request (bad key, unknown model, rate limit). |
BROWSER_UNAVAILABLE |
Chromium binary missing — run playwright install chromium. |
TIMEOUT |
The audit exceeded timeout_ms. |
See Environment Variables for the settings referenced above.