Skip to main content

VFA — Visual & Functional Web Auditor (MCP Server)

Autonomous black-box web QA agent exposed as an MCP server over stdio. Pure hexagonal architecture (Ports & Adapters), one unified inference engine powered by LiteLLM, and a local-first Playwright Chromium sandbox — no Docker, no sidecars, no infrastructure to maintain.

Why VFA

Pillar What it means
Native Zero-Docker Playwright downloads and manages Chromium on the host itself. A built-in ephemeral BrowserContext sandbox plus an Anti-SSRF firewall ship by default, and local development is first-class (localhost, 127.0.0.1, *.local).
Universal LiteLLM Engine A single adapter drives DeepSeek, OpenAI, Anthropic, Gemini, xAI/Grok, OpenRouter and Ollama. Switching providers is a two-variable change.
Active Visual Grounding Multimodal vision inspects real UIs, reads raster image CAPTCHAs and clicks by visual coordinates whenever the accessibility tree is not enough.
1-Click Execution Plug-and-play: uvx vfa-mcp starts the server with no permanent install, pip install vfa-mcp if you prefer an isolated virtualenv.
uvx vfa-mcp

Quickstart

1. Install

One command, permanent install in your own virtualenv:

pip install vfa-mcp && playwright install chromium

Zero-install alternative (recommended for MCP clients):

uvx vfa-mcp
playwright install chromium   # first run only

2. Pick a provider

VFA resolves the API key from VFA_LLM_API_KEY first, then from the standard variable of the selected provider (OPENAI_API_KEY, ANTHROPIC_API_KEY, GEMINI_API_KEY, DEEPSEEK_API_KEY, XAI_API_KEY, OPENROUTER_API_KEY).

Provider VFA_LLM_PROVIDER Example VFA_LLM_MODEL API key variable Base URL / Endpoint variable
OpenAI openai gpt-4o OPENAI_API_KEY — (Default)
Anthropic anthropic claude-3-5-sonnet-20241022 ANTHROPIC_API_KEY — (Default)
Google Gemini gemini gemini/gemini-2.0-flash GEMINI_API_KEY — (Default)
DeepSeek (Direct) deepseek deepseek/deepseek-chat DEEPSEEK_API_KEY — (Default)
DeepSeek Vision deepseek deepseek/deepseek-flash DEEPSEEK_API_KEY — (Default)
DeepSeek (OpenAI-compat) openai openai/deepseek-chat (o deepseek-flash) DEEPSEEK_API_KEY / OPENAI_API_KEY OPENAI_API_BASE="https://api.deepseek.com"
DeepSeek (Anthropic-compat) anthropic claude-3-5-sonnet (alias) DEEPSEEK_API_KEY / ANTHROPIC_API_KEY ANTHROPIC_BASE_URL="https://api.deepseek.com/anthropic"
xAI / Grok xai xai/grok-2-vision-1212 XAI_API_KEY — (Default)
OpenRouter openrouter openrouter/anthropic/claude-3.5-sonnet OPENROUTER_API_KEY — (Default)
Ollama (local) ollama ollama/llama3.2-vision — (Placeholder) OLLAMA_API_BASE="http://localhost:11434"

Aliases accepted by the provider resolver: google (Gemini) and grok (xAI). Vision falls back to the reasoning model when VFA_VISION_PROVIDER / VFA_VISION_MODEL are left empty.

3. Wire it into your client

Zoo Code / Roo Code (mcpSettings.json)

Click the MCP Servers plug icon ⚙️ → Edit MCP Settings, then add:

{
  "mcpServers": {
    "vfa": {
      "command": "uvx",
      "args": ["vfa-mcp"],
      "env": {
        "OPENAI_API_KEY": "sk-...",
        "HEADLESS": "true",
        "VFA_ALLOW_LOCAL_HOST": "true",
        "LITELLM_LOG": "ERROR"
      }
    }
  }
}

Claude Desktop (claude_desktop_config.json)

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json
{
  "mcpServers": {
    "vfa": {
      "command": "uvx",
      "args": ["vfa-mcp"],
      "env": {
        "OPENAI_API_KEY": "sk-...",
        "HEADLESS": "true",
        "VFA_ALLOW_LOCAL_HOST": "true",
        "LITELLM_LOG": "ERROR"
      }
    }
  }
}

Cursor (.cursor/mcp.json or Settings → Features → MCP)

{
  "mcpServers": {
    "vfa": {
      "command": "uvx",
      "args": ["vfa-mcp"],
      "env": {
        "OPENAI_API_KEY": "sk-...",
        "HEADLESS": "true",
        "VFA_ALLOW_LOCAL_HOST": "true",
        "LITELLM_LOG": "ERROR"
      }
    }
  }
}

Provider Drop-in Presets (Replace the "env" block)

Simply swap the "env" object in any configuration above with your preferred model:

⚡ DeepSeek (Reasoning + Fast Vision)

"env": {
  "VFA_LLM_PROVIDER": "deepseek",
  "VFA_LLM_MODEL": "deepseek-chat",
  "VFA_VISION_PROVIDER": "deepseek",
  "VFA_VISION_MODEL": "deepseek-flash",
  "DEEPSEEK_API_KEY": "sk-...",
  "HEADLESS": "true",
  "VFA_ALLOW_LOCAL_HOST": "true",
  "LITELLM_LOG": "ERROR"
}

🧠 Anthropic Claude 3.5 Sonnet

"env": {
  "VFA_LLM_PROVIDER": "anthropic",
  "VFA_LLM_MODEL": "claude-3-5-sonnet-20241022",
  "ANTHROPIC_API_KEY": "sk-ant-...",
  "HEADLESS": "true",
  "VFA_ALLOW_LOCAL_HOST": "true",
  "LITELLM_LOG": "ERROR"
}

🚀 Google Gemini 2.0 Flash (Multimodal)

"env": {
  "VFA_LLM_PROVIDER": "gemini",
  "VFA_LLM_MODEL": "gemini/gemini-2.0-flash",
  "GEMINI_API_KEY": "AIzaSy...",
  "HEADLESS": "true",
  "VFA_ALLOW_LOCAL_HOST": "true",
  "LITELLM_LOG": "ERROR"
}

🌐 OpenRouter (Universal Gateway)

"env": {
  "VFA_LLM_PROVIDER": "openrouter",
  "VFA_LLM_MODEL": "openrouter/anthropic/claude-3.5-sonnet",
  "VFA_VISION_MODEL": "openrouter/google/gemini-2.0-flash-001",
  "OPENROUTER_API_KEY": "sk-or-v1-...",
  "HEADLESS": "true",
  "VFA_ALLOW_LOCAL_HOST": "true",
  "LITELLM_LOG": "ERROR"
}

🏠 Ollama (100% Local / Offline)

"env": {
  "VFA_LLM_PROVIDER": "ollama",
  "VFA_LLM_MODEL": "ollama/llama3.2-vision",
  "OLLAMA_API_BASE": "http://localhost:11434",
  "HEADLESS": "true",
  "VFA_ALLOW_LOCAL_HOST": "true",
  "LITELLM_LOG": "ERROR"
}

⚙️ OpenAI-compatible:

"env": {
  "VFA_LLM_PROVIDER": "openai",
  "VFA_LLM_MODEL": "openai/deepseek-v4-pro",
  "VFA_VISION_PROVIDER": "openai",
  "VFA_VISION_MODEL": "openai/deepseek-flash",
  "OPENAI_API_BASE": "https://api.deepseek.com",
  "OPENAI_API_KEY": "sk-...",
  "HEADLESS": "true",
  "VFA_ALLOW_LOCAL_HOST": "true",
  "LITELLM_LOG": "ERROR"
}

⚙️ Anthropic-compatible:

"env": {
  "VFA_LLM_PROVIDER": "anthropic",
  "VFA_LLM_MODEL": "claude-3-5-sonnet",
  "ANTHROPIC_BASE_URL": "https://api.deepseek.com/anthropic",
  "ANTHROPIC_API_KEY": "sk-...",
  "HEADLESS": "true",
  "VFA_ALLOW_LOCAL_HOST": "true",
  "LITELLM_LOG": "ERROR"
}

Agent-facing usage guide (when to invoke each tool, report template, security rules): SKILL.md. Marketplace manifest: skill.json.

Tool reference

qa_audit_url(url)

Full-page screenshot, vision analysis and runtime error collection in a single call.

Parameter Type Required Description
url string yes Reachable URL to audit. Private/loopback targets are rejected unless VFA_ALLOW_LOCAL_HOST is enabled.

Returns url, ok (true only when the audit passed and no runtime event was captured), summary, screenshot_base64 (PNG), issues[] (visual, accessibility and layout findings) and errors[].

qa_execute_user_flow(url, steps)

Replays a user journey described in plain language (English or Spanish).

Parameter Type Required Description
url string yes Starting URL of the flow.
steps string[] yes Ordered natural-language steps, e.g. ["Click 'Sign in'", "Verify ..."].

Returns url, success, ok, steps[{step, ok, detail}], screenshots[] (base64 PNG milestones) and errors[].

qa_get_runtime_errors()

Drains the browser error buffer: console.error, uncaught exceptions (pageerror) and HTTP responses with status >= 400.

Parameter Type Required Description
— — — No arguments.

Returns count and errors[{kind, message, url, status}], where kind is one of console, pageerror or http. The call is read-destructive: it empties the buffer, so invoke it once at the end of a sequence.

Configuration reference

VFA is configured exclusively through environment variables — there are no config files, no database and no flags to pass. Variables are grouped in three independent layers:

Layer Variables Consumed by
Reasoning engine VFA_LLM_PROVIDER, VFA_LLM_MODEL, VFA_LLM_API_KEY, VFA_LLM_BASE_URL The Deep Agent (LiteLLM tool-calling loop)
Vision engine VFA_VISION_PROVIDER, VFA_VISION_MODEL, VFA_VISION_API_KEY Visual grounding / CAPTCHA fallback
Browser & runtime HEADLESS, VFA_ALLOW_LOCAL_HOST, BROWSER_CDP_*, VFA_SESSION_*, … Playwright adapter, session pool, Anti-SSRF

Is anything mandatory?

Only one thing is truly mandatory in production: a valid API key for the reasoning model. Every other variable has a safe default and can be omitted.

# Minimal production setup — works because the defaults are openai/gpt-4o
export OPENAI_API_KEY="sk-..."

Does VFA_LLM_MODEL drive the Deep Agent?

Yes. VFA_LLM_MODEL is the model used by the Deep Agent — the component that executes qa_execute_user_flow and performs the reasoning step of qa_audit_url. Both tools go through LiteLLM with VFA_LLM_PROVIDER + VFA_LLM_MODEL, so:

  • You may omit them when you use OpenAI gpt-4o and export OPENAI_API_KEY (defaults: openai / gpt-4o).
  • You must set them when you use any other provider, any other model, or an OpenAI-compatible gateway (DeepSeek, OpenRouter, Ollama, vLLM, Azure proxy…).
  • The model must support tool calling (native function calling), because the ReAct loop sends a JSON action schema on every iteration.

For OpenAI-compatible endpoints, pair the model with a base URL:

export VFA_LLM_PROVIDER="openai"          # protocol, not the vendor
export VFA_LLM_MODEL="openai/deepseek-chat"
export OPENAI_API_KEY="sk-..."            # or VFA_LLM_API_KEY
export OPENAI_BASE_URL="https://api.deepseek.com"

Credential and endpoint precedence

What Resolution order
Reasoning API key VFA_LLM_API_KEY → provider standard variable (OPENAI_API_KEY, ANTHROPIC_API_KEY, GEMINI_API_KEY, DEEPSEEK_API_KEY, XAI_API_KEY, OPENROUTER_API_KEY) → OPENAI_API_KEY
Reasoning base URL VFA_LLM_BASE_URL → OPENAI_BASE_URL → OLLAMA_API_BASE (only when the provider is ollama)
Vision provider VFA_VISION_PROVIDER → VFA_LLM_PROVIDER → openai
Vision model VFA_VISION_MODEL → VFA_LLM_MODEL → gpt-4o
Vision API key VFA_VISION_API_KEY → VFA_LLM_API_KEY → provider standard variable → OPENAI_API_KEY

VFA_VISION_* is therefore optional by design: if the reasoning model is multimodal (gpt-4o, claude-3-5-sonnet, gemini/gemini-2.0-flash, xai/grok-2-vision-1212), leave it empty and one model does everything. Set it only when you want a cheaper/faster model for screenshots or when your reasoning model is text-only.

Full variable list

Variable Default Required when Purpose
VFA_LLM_PROVIDER openai Using a non-OpenAI provider SDK protocol/routing for the Deep Agent (openai, anthropic, gemini, deepseek, xai, openrouter, ollama; aliases google, grok)
VFA_LLM_MODEL gpt-4o Using a model other than gpt-4o Reasoning model (must support tool calling)
VFA_LLM_API_KEY (empty) Sharing one credential across providers Generic key that overrides the provider standard variable
VFA_LLM_BASE_URL (empty) OpenAI-compatible gateways, self-hosted or proxied endpoints Base URL override (OPENAI_BASE_URL also honoured)
VFA_VISION_PROVIDER inherits Vision served by a different vendor Routing for multimodal calls
VFA_VISION_MODEL inherits Reasoning model lacks image input, or cost split desired Model used for screenshots and visual grounding
VFA_VISION_API_KEY inherits Vision served by a different vendor Credential for multimodal calls
OLLAMA_API_BASE (empty) VFA_LLM_PROVIDER=ollama Local endpoint, e.g. http://localhost:11434 (no key needed, a placeholder is used)
VFA_AGENT_MAX_ITERATIONS 12 Long flows that exceed the ReAct budget Iteration ceiling of the Deep Agent loop
VFA_LLM_TEMPERATURE 0.0 Never (deterministic QA by default) Sampling temperature
VFA_LLM_TIMEOUT 60 Slow/self-hosted models Per-call timeout in seconds
VFA_LLM_REQUESTS_PER_SECOND 0 Free tiers or strict quotas Token-bucket rate limiter; 0 disables it
VFA_LLM_CHECKS_PER_SECOND 10.0 Never Rate limiter polling frequency
HEADLESS true Local debugging of the flow step by step Run Chromium without a window
VFA_ALLOW_LOCAL_HOST true Must be false for internet-facing production deployments Allow localhost, 127.0.0.1, 0.0.0.0, *.local; false restores strict Anti-SSRF blocking of loopback, private ranges and cloud metadata
BROWSER_CDP_URL (empty) Attaching to an already running Chromium Optional custom CDP endpoint; empty uses the managed local browser
BROWSER_CDP_TIMEOUT_MS 2000 Remote CDP endpoint slower than 2s Timeout of the CDP connection attempt
VFA_ACTION_TIMEOUT_MS 10000 Heavy SPAs with slow first paint Timeout of every individual Playwright action
VFA_SCREENSHOT_FULL_PAGE true Never (viewport-only reports needed) Full-page screenshots instead of viewport only
VFA_SESSION_POOL_ENABLED true Stateless/multi-tenant deployments may disable it Reuse browser sessions between tool calls
VFA_SESSION_POOL_TTL 300 Never Idle seconds before a pooled session is closed
VFA_SESSION_POOL_MAX_SIZE 5 High-concurrency hosts Maximum concurrent pooled sessions (LRU eviction)
VFA_SESSION_STORAGE_STATE (empty) Replaying an authenticated journey Imported Playwright storageState; empty keeps cookies in memory only
SSL_VERIFY true Self-signed certificates in local/staging TLS verification switch (leave empty to skip verification; any non-empty value keeps it on)

A copy-ready template with the same defaults lives in .env.example.

Production checklist

  1. One credential per deployment, injected by your secret manager — never committed and never placed in the MCP client JSON for shared machines.
  2. Keep VFA_ALLOW_LOCAL_HOST=false on any host that can reach an internal network; it is the only guard against SSRF from an LLM-driven navigation.
  3. Pin VFA_LLM_MODEL explicitly instead of relying on gpt-4o; upstream providers rotate model names and a silent change alters QA verdicts.
  4. Set VFA_LLM_REQUESTS_PER_SECOND when you run parallel audits against a rate-limited provider, and raise VFA_LLM_TIMEOUT for slow self-hosted models.
  5. Keep HEADLESS=true on servers (no X server is available) and keep SSL_VERIFY enabled outside staging.
  6. Do not mount VFA_SESSION_STORAGE_STATE unless the flow needs a reused login: it writes real session cookies to the host filesystem.

Architecture

app/
  domain/                  # Pure domain (no I/O, no frameworks)
    models.py              # AuditReport, StepResult, RuntimeErrorEvent, VFAContext
    parser.py              # Deterministic NLP parser (ES/EN)
    semantic.py            # Cascade: regex -> a11y snapshot -> LLM fallback
    vision.py              # Visual primitives and grounding contracts
  ports/                   # Abstract contracts
    browser.py             # BrowserSessionPort / BrowserGatewayPort
    llm.py                 # LLMPort / UsageLimiterPort
  adapters/
    browser/
      playwright_adapter.py  # Local sandboxed Chromium + ephemeral context
      network_guard.py       # Anti-SSRF firewall (private ranges / loopback)
      session_pool.py        # TTL + LRU pool, cookies in memory only
      capture.py             # console.error, pageerror, HTTP >= 400
      visual_primitives.py   # Screenshot and coordinate helpers
    llm/
      litellm_adapter.py     # Multi-provider acompletion + base64 vision
      deep_agent.py          # ReAct loop with native tool calling
      agent_tools.py         # Agent tool JSON schemas
      vision_grounding.py    # Visual grounding adapter
      rate_limiter.py        # Async token bucket
      visual_fallback.py     # Heuristic fallback chain
  tools/qa_tools.py        # The three QA tools
  config.py                # Single source of truth for configuration
server_mcp.py              # FastMCP entrypoint (stdio)

Security model

  • Chromium starts with strict isolation flags (--disable-background-networking, --disable-default-apps, --disable-sync, --no-first-run, --hide-scrollbars).
  • Every session uses an ephemeral in-memory BrowserContext, destroyed on close: no cache, cookies or storage are persisted to the host disk.
  • A context-level Anti-SSRF firewall aborts (accessdenied) requests to loopback, private ranges (10/8, 172.16/12, 192.168/16) and cloud metadata (169.254.169.254). Local development targets are allowed only while VFA_ALLOW_LOCAL_HOST is true; set it to false for strict production mode.
  • The LLM credential never reaches the audited page: only the server process talks to the inference provider.

Development

pip install -e ".[dev]"
playwright install chromium
pytest

Tests run without a real browser or network: every adapter is replaced by an in-memory double (tests/conftest.py, tests/visual_fakes.py).

License

Commercial, single-seat license. Distributed on PyPI as vfa-mcp and listed on Agensi.io (USD 49).

Metadata

Release files for vfa-mcp 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for vfa-mcp 1.0.0
File Size Uploaded Provenance
vfa_mcp-1.0.0.tar.gz 77.1 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for vfa-mcp 1.0.0
File Interpreter ABI Platform
vfa_mcp-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 142.5 kB

Release files / vfa_mcp-1.0.0.tar.gz

Download URL vfa_mcp-1.0.0.tar.gz
Size 77.1 kB
Tags Source
SHA-256 checksum
How to use checksums
61594ae07c1b9a914596d97c3bbfae83c4f6cbef10b5fc95dc692f9d713741d0
BLAKE2b-256 checksum
How to use checksums
01400566be2c6c2c0e1f1606d0bf96324ecd4af5d36781b5c4c03724e4c6049f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.10

Release files / vfa_mcp-1.0.0-py3-none-any.whl

Download URL vfa_mcp-1.0.0-py3-none-any.whl
Size 65.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7acd7794923520810bf571bd1e464a44a10d483eae2b2a1b4b2ce7cd5a5e2e81
BLAKE2b-256 checksum
How to use checksums
f4a4362246285d36256c760634861a69c96a66f216e601da18eb5c3887dcb372
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.10

Security for vfa-mcp 1.0.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page