Command Palette

Search for a command to run...

UnylyUnyly
Весь каталог

Ghostlight

БесплатноНе проверен

A QA verification MCP server that prevents AI hallucination by using adversarial verification with screenshots and subagents for accessibility, visual regressio

GitHubEmbed

Описание

A QA verification MCP server that prevents AI hallucination by using adversarial verification with screenshots and subagents for accessibility, visual regression, and UI verification.

README

The single light left on a theater stage when the house is dark, so no one falls off the edge.

License: MIT MCP Compatible Node.js 18+ Playwright

A QA verification MCP server that prevents AI hallucination. High-level tools for accessibility, visual regression, and UI verification — with adversarial verification that proves agents actually looked at the screenshot.

The Trust Problem

When an AI sees a screenshot that looks familiar, it may pattern-match and answer from training data instead of actually reading the page. "That looks like Wikipedia" → answers about what Wikipedia should contain, not what it actually shows.

Ghostlight's solution: Adversarial Verification with Subagents

Orchestrator: "Is this page professionally styled?"

Ghostlight: Here's the screenshot and a subagent prompt.
            The subagent must extract specific text to prove they looked:

Subagent extractions:
  1. Site name: "Hacker News" ✓
  2. First headline: "Cloudflare acquires Astro" ✓  ← can't guess this
  3. Nav links: "new, past, comments, ask..." ✓

Result: TRUSTED (verified actual perception)

The headline is today's content — not guessable from training data. The agent had to actually read the screenshot.

Why Ghostlight?

AI coding agents need to verify their UI work, but existing approaches have problems:

  • Pattern matching — Agents recognize "Wikipedia-shaped" pages and answer from memory
  • Shallow engagement — Quick glance at header, then guess the rest
  • Context contamination — URL or site name triggers prior knowledge
  • No verification — Agents claim they "see" things without demonstrating perception

Ghostlight solves this with:

  • Adversarial framing — "This screenshot may differ from expected content"
  • Text extraction challenges — Prove you read by quoting specific text
  • Subagent isolation — Fresh context with only the screenshot, no URL hints
  • Deep reading questions — Target content that can't be guessed

Installation

npm install
npx playwright install chromium

Usage

As an MCP Server

Add to your project's .mcp.json:

{
  "mcpServers": {
    "ghostlight": {
      "command": "node",
      "args": ["/path/to/ghostlight/dist/index.js"]
    }
  }
}

Or run directly:

npm run build
npm start

The server communicates via stdio using the Model Context Protocol.

Tools

Tool Purpose Returns
ping Health check Echo response with timestamp
get_console_logs Capture console errors, warnings, logs Structured log entries with severity
verify_accessibility axe-core WCAG audit Violations with severity + remediation
capture_dom_state DOM structure snapshot Serialized DOM tree with snapshot ID
compare_snapshots Diff two DOM states Added/removed/changed elements
measure_element Pixel-perfect measurements Dimensions, spacing, typography
test_viewports Responsive testing at breakpoints Layout report per viewport
capture_visual_state Screenshot + DOM + styles Full visual context (saves to temp files)
detect_layout_shift CLS (Core Web Vital) measurement Shift score + shifting elements
detect_flash Rapid visibility changes Flash events with timestamps
visual_verify Start verified visual check Session ID, screenshot path, challenges
submit_visual_response Submit challenge answers Trust status, score, hints

Tool Categories

Verified Visual Tools (anti-hallucination):

  • visual_verify, submit_visual_response

Text-only Tools (fast, no images):

  • get_console_logs, verify_accessibility, detect_layout_shift, measure_element, capture_dom_state, compare_snapshots

Standard Visual Tools (screenshots saved to temp files):

  • capture_visual_state, test_viewports, detect_flash

Visual Verification Workflow

The visual_verify and submit_visual_response tools form a challenge-response system for verifying that an AI agent can actually "see" a page. The recommended pattern uses subagent delegation to keep the main conversation context clean.

The Problem: AI Hallucination

When an AI agent sees a screenshot that looks familiar (e.g., "looks like Wikipedia"), it may:

  1. Glance briefly at the image
  2. Recognize the pattern
  3. Answer from training data instead of actual screenshot content

This causes answers about what the page "should" contain, not what it actually shows.

The Solution: Subagent + Adversarial Framing

flowchart LR
    A[Orchestrator<br/>calls visual_verify] --> B[Subagent<br/>fresh context]
    B --> C[Verification<br/>Result]

    A -.->|Returns| D[session_id<br/>screenshot_path<br/>challenges<br/>subagent_prompt]
    D -.-> B

    B -->|reads screenshot<br/>answers + submits| C

Key insight: Dispatch a fresh subagent with ONLY the screenshot and questions. No URL, no site context. The subagent must actually read the image to answer.

Example: Complete Verification Flow

Step 1: Orchestrator calls visual_verify with criteria

// Orchestrator defines what to check BEFORE seeing the page
{
  url: "http://localhost:3000/dashboard",
  query: "Is this page professionally styled?",
  criteria: [
    { question: "What is the main heading text?", type: "text" },
    { question: "Is there a navigation bar?", type: "boolean" },
    { question: "How many buttons are visible?", type: "count" }
  ]
}

// Response includes ready-to-use subagent prompt:
{
  session_id: "vs-abc123-xyz",
  screenshot_path: "/tmp/ghostlight-verification/verification-123.png",
  challenges: [...],
  subagent_prompt: "VISUAL VERIFICATION\n\nThis screenshot may differ..."
}

Step 2: Dispatch subagent with the provided prompt

// Use the Task tool to dispatch a subagent
Task({
  prompt: response.subagent_prompt,  // Use the pre-built prompt
  subagent_type: "general-purpose"
})

Step 3: Subagent handles everything

The subagent will:

  1. Read the screenshot
  2. Extract specific text (proves they looked)
  3. Answer challenges
  4. Call submit_visual_response
  5. Retry if not trusted (up to 3 times)
  6. Return final result

Adversarial Framing

The subagent prompt uses adversarial framing to force deep engagement:

VISUAL VERIFICATION

This screenshot may differ from expected content.
Extract exact text - do not guess from familiar layouts.

Required extractions (read first, then fill in):
1. Site name/logo text (exact words): ___
2. First headline (exact words): ___
3. Navigation links visible (list them): ___

If text is unclear, write UNCLEAR rather than guessing.

This works because:

  • "May differ from expected" — breaks pattern-matching shortcuts
  • Required extractions — forces systematic reading before answering
  • Exact quotes — can't be guessed from training data
  • UNCLEAR option — prevents fabrication when uncertain

Orchestrator-Defined Criteria

Instead of auto-generated challenges, the orchestrator can define exactly what to verify:

criteria: [
  { question: "What is the main heading text?", type: "text" },
  { question: "Is there a footer with copyright?", type: "boolean" },
  { question: "How many form fields are visible?", type: "count" }
]

Benefits:

  • Orchestrator commits to acceptance criteria BEFORE seeing the page
  • Questions are specific and meaningful
  • No ambiguity about what "professionally styled" means
  • Subagent just verifies visual perception

Challenge Types

Type Example Question Validation
text "What does the heading say?" 50%+ word overlap, substring match
count "How many buttons visible?" ±2 absolute or ±50% relative
boolean "Is the nav bar visible?" Accepts true/false/yes/no
color "What color is the button?" Fuzzy RGB matching (~5% tolerance)
position "Where is the sidebar?" Keyword-based region matching

Trust Threshold

Responses are marked trusted: true when 80%+ of challenges pass. The generous tolerances (±2 for counts, 50% word overlap for text) focus on "did they look?" rather than "did they count exactly?"

Why Subagents?

  1. Context isolation — Visual analysis stays out of main conversation
  2. Fresh perspective — No accumulated context bias
  3. Anti-hallucination — Subagent has no prior knowledge of what page "should" be
  4. Clean results — Main agent gets structured pass/fail result

Session Lifecycle

  • Sessions expire after 5 minutes (configurable)
  • Maximum 1000 concurrent sessions (configurable)
  • Screenshots saved to temp directory

Tool Reference

get_console_logs

Captures console output from page load. Attaches listeners BEFORE navigation to catch everything.

{
  url: string;           // Required: Page URL to monitor
  levels?: string[];     // Optional: ['error', 'warning', 'log', 'info', 'debug']
  wait_ms?: number;      // Optional: Time to wait for async logs (default: 1000)
}

Returns status (clean, has_errors, has_warnings), summary counts, and detailed entries with source locations.

verify_accessibility

Runs axe-core WCAG 2.1 audit. Returns actionable violations with remediation guidance.

{
  url: string;           // Required: Page URL to audit
  selector?: string;     // Optional: Scope audit to specific element
  rules?: string[];      // Optional: Specific axe-core rule IDs to check
}

capture_dom_state / compare_snapshots

Capture DOM state, then compare two snapshots for regression testing.

// Capture
{ url: string; selector?: string; max_depth?: number; }

// Compare
{ before_id: string; after_id: string; }

measure_element

Returns precise measurements: dimensions, box model, typography, visibility.

{
  url?: string;          // Optional if page already loaded
  selector: string;      // Required: CSS selector
}

test_viewports

Tests responsive design across breakpoints.

{
  url: string;
  selector?: string;     // Element to track visibility
  breakpoints?: Array<{name: string; width: number; height: number}>;
  // Default: mobile (375x667), tablet (768x1024), desktop (1920x1080)
}

capture_visual_state

Comprehensive visual capture. Screenshots and DOM snapshots are saved to temp files to prevent token overflow.

{
  url: string;
  selector?: string;     // Capture specific element instead of full page
  scroll_to_load?: boolean;  // Scroll to trigger lazy-loaded content
  max_depth?: number;    // DOM tree depth (default: 10)
}

detect_layout_shift

Measures Cumulative Layout Shift using PerformanceObserver API.

{
  url: string;
  wait_time?: number;    // Observation time in ms (default: 1000)
}

Returns CLS score with thresholds: good (<0.1), needs-improvement (0.1-0.25), poor (>0.25).

detect_flash

Detects rapid visibility/style changes that cause flicker.

{
  url: string;
  wait_time?: number;    // Observation time in ms (default: 500)
}

visual_verify

Initiates a verified visual verification session. Captures screenshot and generates challenges. Supports orchestrator-defined criteria for precise control over what to verify.

{
  url: string;           // Required: Page URL to verify
  query: string;         // Required: Verification question (e.g., "Is the button red?")
  selector?: string;     // Optional: Focus on specific element
  criteria?: Array<{     // Optional: Orchestrator-defined verification criteria
    question: string;    // The verification question
    type: 'boolean' | 'text' | 'count';  // Expected answer type
  }>;
}

Returns:

  • session_id — Use this in submit_visual_response
  • screenshot_path — Path to captured screenshot (temp file)
  • user_query — Echo of the verification query
  • challenges — Array of questions (expected answers withheld)
  • subagent_prompt — Ready-to-use prompt for dispatching a verification subagent

submit_visual_response

Submits answers to a verification session. Validates against expected values with fuzzy matching.

{
  session_id: string;           // Required: From visual_verify
  challenge_answers: Array<{    // Required: Your answers
    id: string;
    answer: string | number | boolean;
  }>;
  query_answer: string;         // Required: Answer to original query
}

Returns:

  • trusted — Boolean, true if >= 80% challenges passed
  • verification_score — 0.0 to 1.0 score
  • challenges_passed / challenges_total — Pass counts
  • failed_challenges — List of questions that failed (without revealing expected values)
  • query_answer — Echo of the provided answer

Note: Failed challenges show only the question text, not expected values. This prevents gaming the system by retrying with revealed answers.

Development

# Run tests
npm test

# Type check
npm run typecheck

# Build
npm run build

# Development mode (no build required)
npm run dev

Architecture

src/
├── index.ts                      # Entry point
├── server.ts                     # MCP server with tool registry
├── browser-manager.ts            # Playwright browser lifecycle
├── snapshot-store.ts             # In-memory DOM snapshot storage
├── verification-session-store.ts # Visual verification sessions (TTL-based)
├── challenge-generator.ts        # DOM-based challenge creation
├── answer-validators.ts          # Fuzzy matching validators
└── tools/
    ├── index.ts                  # Tool exports
    ├── get-console-logs.ts
    ├── verify-accessibility.ts
    ├── capture-dom-state.ts
    ├── compare-snapshots.ts
    ├── measure-element.ts
    ├── test-viewports.ts
    ├── capture-visual-state.ts
    ├── detect-layout-shift.ts
    ├── detect-flash.ts
    ├── visual-verify.ts          # Visual verification initiation
    └── submit-visual-response.ts # Challenge answer validation

Browser Session Management

Ghostlight uses a shared browser with isolated contexts:

  • Browser launch is expensive (~1-3s), so one browser is shared across tools
  • Each tool call gets an isolated browser context for clean state
  • Contexts are automatically cleaned up after use

Requirements

  • Node.js 18+
  • Chromium (installed via npx playwright install chromium)

License

MIT

from github.com/banditburai/ghostlight

Установка Ghostlight

У этого сервера нет опубликованного пакета — он собирается из исходников. Открой репозиторий и следуй инструкции в README.

▸ github.com/banditburai/ghostlight

FAQ

Ghostlight MCP бесплатный?

Да, Ghostlight MCP бесплатный — установка в пару кликов через Unyly без оплаты.

Нужен ли API-ключ для Ghostlight?

Нет, Ghostlight работает без API-ключей и переменных окружения.

Ghostlight — hosted или self-hosted?

Self-hosted: сервер запускается локально на твоей машине командой из раздела установки.

Как установить Ghostlight в Claude Desktop, Claude Code или Cursor?

Открой Ghostlight на unyly.org, выбери вкладку своего клиента (Claude Desktop, Claude Code, Cursor) и нажми Install — конфиг сгенерируется автоматически, без правки JSON.

Похожие MCP

Compare Ghostlight with

Не уверен что выбрать?

Найди свой стек за 60 секунд

Автор?

Embed-бейдж для README

Похожее

Все в категории design