Docs should be viewed from a larger screen.

Documentation

Easy Sanity Agent Documentation

Full reference for the Easy Sanity agent: what it is, how it works, how to install it, how to configure it, how to use it, what it stores, and every MCP tool it exposes.

Overview

What Easy Sanity is

Easy Sanity is a local browser automation and sanity-testing MCP server. It sits between your IDE agent and a real Chromium browser driven by Playwright.

It is designed to let an IDE agent such as Codex use natural language to:

  • Open and inspect web applications
  • Interact with forms, buttons, dropdowns, dialogs, tables, and cards
  • Verify app behavior with explicit assertions
  • Capture screenshots, reports, and action history
  • Save reusable workflows as slash-command style tasks
  • Build persistent application understanding from a target code repository

Core idea

  1. You ask the IDE agent to perform or validate a browser workflow.
  2. The IDE agent calls Easy Sanity MCP tools.
  3. Easy Sanity executes those tool calls in a local Playwright-controlled browser.
  4. The IDE agent reads page state, semantic summaries, assertions, screenshots, action history, and reports to decide what to do next.

This turns the IDE agent from a text-only assistant into a browser-based sanity-testing agent.

Browser session lifecycle management

Semantic browser actions

Explicit assertions

Synchronization and wait tools

Extraction tools for tables, lists, links, and JSON-like page data

Evidence capture through screenshots and markdown reports

Reusable tasks and slash-command workflows

Variable rendering and environment profiles

Structured task definitions

Persistent repo understanding and testing memory

Architecture

Codebase structure

The codebase is organized into focused modules:

main.py

Thin MCP entrypoint. Registers task tools, browser tools, and memory tools.

browser/

Browser session state, browser tools, semantic domain understanding, and report generation.

tasks/

Saved task system, structured tasks, sample tasks, task rendering, task linting, and profile management.

memory/

Persistent repo understanding, app memory, testing briefs, and sync-driven learned notes.

config/

Runtime configuration and environment-backed paths/defaults.

prompts/

LLM-facing prompt text and reusable prompt templates.

data/

Saved tasks, profiles, sample tasks, and app memory artifacts.

artifacts/

Generated screenshots, downloads, and markdown reports.

Installation

Install and run Easy Sanity

Easy Sanity supports three recommended setups: `uvx` for the fastest no-clone install, `pipx` for a permanent global install, and source checkout for development.

Recommended: `uvx`

This is the simplest way to run Easy Sanity without cloning the repository.

1. Install the Playwright browser
uvx easy-sanity install-browser
2. Launch command
uvx easy-sanity

Permanent install: `pipx`

Use this if you want a globally available `easy-sanity` command.

Install package and browser
pipx install easy-sanity
easy-sanity install-browser

Development install from source

Use this when you are working on the repo itself.

Quick setup
./scripts/setup.sh
Manual setup and run
curl -LsSf https://astral.sh/uv/install.sh | sh
uv sync
uv run easy-sanity install-browser
uv run easy-sanity

What the setup script does

  • `uv sync`
  • `uv run easy-sanity install-browser`

Configuration

MCP client configuration

The recommended MCP startup pattern is to pass runtime values through the server `env` block. `uvx` is the default launch command for Codex, while `pipx` and installed builds use `easy-sanity` directly.

Codex (`uvx`)
{
  "mcpServers": {
    "easy-sanity": {
      "command": "uvx",
      "args": ["easy-sanity"],
      "env": {
        "BROWSER_HEADLESS_DEFAULT": "true",
        "BROWSER_DEFAULT_TIMEOUT_MS": "45000",
        "BROWSER_REPORTS_DIR": "artifacts/reports",
        "BROWSER_SCREENSHOTS_DIR": "artifacts/screenshots",
        "BROWSER_DOWNLOADS_DIR": "artifacts/downloads",
        "APP_MEMORY_DIR": "data/app_memory"
      }
    }
  }
}
Installed build (`easy-sanity`)
{
  "mcpServers": {
    "easy-sanity": {
      "command": "easy-sanity",
      "args": [],
      "env": {
        "BROWSER_HEADLESS_DEFAULT": "true",
        "BROWSER_DEFAULT_TIMEOUT_MS": "45000"
      }
    }
  }
}
Claude Code / Cursor / Cline
{
  "mcpServers": {
    "browser-automation": {
      "command": "easy-sanity",
      "args": [],
      "env": {
        "BROWSER_HEADLESS_DEFAULT": "true",
        "BROWSER_DEFAULT_TIMEOUT_MS": "45000"
      }
    }
  }
}

After setup

  1. Restart the MCP client or IDE fully.
  2. Confirm the server appears in MCP tools.
  3. Run a smoke prompt such as `Use easy-sanity to open https://example.com and tell me the page title.`
VariablePurposeDefault
BROWSER_HEADLESS_DEFAULTWhether `browser_start` runs headless by defaultfalse
BROWSER_DEFAULT_TIMEOUT_MSDefault Playwright timeout in milliseconds30000
BROWSER_REPORTS_DIRDirectory for markdown test reportsartifacts/reports
BROWSER_SCREENSHOTS_DIRDirectory for per-step screenshotsartifacts/screenshots
BROWSER_DOWNLOADS_DIRDirectory for downloaded filesartifacts/downloads
APP_MEMORY_DIRDirectory for persistent app-understanding memorydata/app_memory
EASY_SANITY_HOMEBase directory used by installed builds for user-owned runtime dataplatform-specific app data dir

Runtime paths

  • Installed builds default to a user-owned app data directory.
  • Source checkout runs keep using repo-local paths unless you override them with environment variables.
  • Use `EASY_SANITY_HOME` if you want to relocate the installed runtime home.
Inspect active runtime paths
easy-sanity paths

uv run easy-sanity paths

Data Model

Saved data and runtime artifacts

Saved data

  • `data/tasks.json` for saved reusable tasks
  • `data/profiles.json` for reusable variable and environment profiles
  • `data/sample_tasks.json` for built-in onboarding and smoke-test samples
  • `data/app_memory/` for persistent repo-understanding memory for target applications

Runtime artifacts

  • `artifacts/reports/` for markdown reports written at the end of browser sessions
  • `artifacts/screenshots/` for per-step screenshots grouped by session
  • `artifacts/downloads/` for downloaded files grouped by session

Runtime Model

Reporting and app memory

Reporting model

  • Step-by-step action history
  • Timestamps
  • Semantic route and page-change context
  • Screenshots
  • Final markdown report

Reports are useful for QA evidence, debugging regressions, demo artifacts, and sharing what the agent saw and did.

App memory model

  • Scan a local code repository
  • Infer tech stack
  • Detect likely workflows
  • Detect route and entrypoint hints
  • Detect environment and test asset hints
  • Generate testing guidance
  • Persist manual notes and learned notes
  • Update over time across multiple sync runs

This is stored per repo under `APP_MEMORY_DIR`.

Workflow Layer

Tasks and profiles

The task system is designed to make long browser workflows reusable.

Free-form prompt tasks

Structured tasks

Placeholders such as `{{base_url}}` or `{{password}}`

Secret variable detection

Reusable profiles such as `dev`, `staging`, or `prod`

Sample task import

Linting and task-authoring helpers

Important current behavior

  • Saved slash-command tasks store a prompt template.
  • Profiles and task rendering are supported.
  • Profile-backed direct slash-command execution may still require the IDE agent to render or bind profile values intentionally.

Tool Reference

Browser session tools

browser_start(task, headless=None)

Starts a new browser session.

  • Begin a browser workflow
  • Optionally override headless mode
  • Initialize report, screenshot, and download directories for the session

browser_stop(final_result=None)

Ends the browser session and writes the final report.

  • Close the browser cleanly
  • Finalize action history
  • Generate the markdown run report
  • Return report and screenshot paths

browser_get_history()

Returns session action history.

  • Action list
  • Semantic change summaries
  • Route context
  • Report path
  • Screenshot directory

Tool Reference

Browser inspection tools

browser_get_state()

Returns the current page state.

  • URL
  • Title
  • Visible text
  • Interactive elements
  • Screenshot as base64
  • Semantic page summary
  • Route context
  • Recent semantic change summary

browser_get_dom_summary()

Returns a semantic page understanding summary.

  • Understanding forms
  • Understanding cards and tables
  • Identifying dialogs, alerts, headings, and workflow step context

browser_get_accessibility_tree()

Returns a simplified accessibility-oriented tree of interactive elements.

  • Robust role/name-based interaction
  • Accessibility-friendly selectors

browser_list_forms()

Returns visible forms and their fields.

  • Labels
  • Names
  • Placeholders
  • Required flags
  • Disabled state
  • Submit buttons

browser_list_links()

Returns visible links on the page.

  • Navigation validation
  • Crawl-like sanity workflows

browser_list_network_errors()

Returns failed network requests and error-level console messages seen during the session.

browser_get_console_logs(level="")

Returns session console logs.

  • Optional filter: error
  • Optional filter: warning
  • Optional filter: log

browser_get_requests(limit=100)

Returns recent request and response activity captured for the page.

browser_get_storage()

Returns browser storage details.

  • Cookies
  • localStorage
  • sessionStorage

browser_describe_changes()

Compares the current page to the last semantic snapshot and describes what changed.

  • Confirming route changes
  • Workflow-step changes
  • Dialog openings
  • Alert changes

Tool Reference

Browser navigation and interaction tools

browser_navigate(url)

Navigates to a URL and waits for page load/network idle behavior.

browser_find_element(description, limit=5)

Finds likely interactive elements using a natural-language description. Useful prompts include `email field`, `login button`, and `search input`.

browser_click(selector=None, text=None, element_index=None)

Low-level click tool supporting CSS selector click, visible text click, and `browser_get_state()` element index click.

browser_click_by_role(role, name, exact=False)

Clicks an element by accessible role and name. Examples include a button named `Login` or a link named `Dashboard`.

browser_click_by_label(name)

Clicks an element by human-readable label or visible name.

browser_fill(field, text, press_enter=False)

Fills a field semantically using label, placeholder, name, id, `data-testid`, and fallback aria/attribute matching.

browser_select_option(label, value)

Selects a value in a select/dropdown control using a semantic field label.

browser_type(selector, text, press_enter=False)

Low-level typed input by CSS selector.

browser_press_key(key)

Presses a keyboard key such as `Enter`, `Escape`, or `Tab`.

browser_hover(selector)

Hovers over an element by selector.

browser_drag_and_drop(source, target)

Performs drag-and-drop between two selectors.

browser_upload_file(selector, path)

Uploads a local file into a file input.

browser_download_file(link_or_selector)

Triggers a download and stores the file under the session download directory.

browser_refresh()

Refreshes the current page.

browser_go_back()

Moves backward in browser history.

browser_go_forward()

Moves forward in browser history.

browser_open_tab(url="")

Opens a new tab and optionally navigates it.

browser_switch_tab(index=-1, title="")

Switches the active tab by index or title substring.

browser_close_tab()

Closes the current tab and switches to another open tab.

browser_scroll(direction="down", amount=500)

Scrolls the page up or down.

browser_wait(seconds=2)

Simple fixed wait helper. Useful for animation and unstable UI, though smarter wait tools are preferred when possible.

Tool Reference

Synchronization and wait tools

browser_wait_for_text(text, timeout_ms=10000)

Waits until visible text appears.

browser_wait_for_element(selector, timeout_ms=10000, state="visible")

Waits for an element to reach a given state such as `visible`, `attached`, `hidden`, or `detached`.

browser_wait_for_url(pattern, timeout_ms=10000)

Waits until the current URL matches a substring pattern.

browser_wait_for_navigation(timeout_ms=10000)

Waits until page navigation/load completes.

browser_wait_for_network_idle(timeout_ms=10000)

Waits until Playwright reports network-idle state.

browser_wait_for_disappearance(selector, timeout_ms=10000)

Waits until an element disappears or becomes hidden.

Tool Reference

Extraction tools

browser_extract(selector)

Extracts text from a specific element.

browser_extract_table(selector)

Extracts table headers and row data.

browser_extract_list(selector)

Extracts items from a list-like container.

browser_extract_json_from_page()

Extracts JSON-like page data from JSON script tags and common framework globals.

browser_extract_links()

Extracts link text and URLs from the page.

browser_capture_section(selector)

Captures the section text and a base64 screenshot of that section.

browser_compare_text(selector, expected)

Compares an element’s text to an expected value and returns pass/fail data.

Tool Reference

Assertion tools

These tools shift Easy Sanity from browser automation into explicit testing.

assert_url_contains(expected_text)

Asserts the current URL contains a substring.

assert_url_equals(expected_url)

Asserts the current URL exactly matches a value.

assert_page_title(expected_text)

Asserts the page title contains the expected text.

assert_text_visible(text)

Asserts specific text is visible on the page.

assert_text_not_visible(text)

Asserts specific text is not visible.

assert_text_contains(text)

Asserts the body text contains a string.

assert_text_matches(pattern)

Asserts the visible page text matches a regex pattern.

assert_element_exists(selector)

Asserts an element exists.

assert_element_visible(selector)

Asserts an element exists and is visible.

assert_element_hidden(selector)

Asserts an element is hidden or absent.

assert_element_enabled(selector)

Asserts an element exists and is enabled.

assert_input_value(selector, expected)

Asserts an input/select/textarea has the expected current value.

assert_count(selector, expected)

Asserts a selector matches an exact number of elements.

assert_no_console_errors()

Asserts no error-level console messages were captured in the session.

assert_no_failed_requests()

Asserts no failed requests were captured in the session.

assert_screenshot_stable(selector="body")

Takes two short-interval screenshots and asserts they are identical. Useful for checking visual stability and detecting still-changing UI.

Tool Reference

Task tools

task_create(name, prompt, description="")

Creates a reusable saved task and registers it as a slash-command-style prompt.

task_create_structured(...)

Creates a structured task definition with fields such as purpose, steps, assertions, inputs, retry policy, and expected result.

task_preview_structured(...)

Previews a structured task without saving it.

task_list()

Lists saved tasks.

task_get(name)

Returns the full definition of a saved task.

task_delete(name)

Deletes a saved task.

task_render(name, profile="", variables_json="{}", mask_secrets=False)

Renders a templated task with merged variables. Precedence is explicit variables, then profile values, then environment variables.

task_lint(name="", prompt="")

Lints a task prompt for vagueness, missing URL, missing verification, hardcoded secret-like content, and missing structure.

task_wizard_template(goal, start_url="", include_placeholders=True, include_assertions=True)

Generates a starter task draft from a goal.

sample_tasks_list()

Lists bundled sample tasks.

sample_tasks_import(names_json="[]", overwrite=False)

Imports bundled sample tasks into saved tasks.

Tool Reference

Profile tools

profile_save(name, variables_json, description="")

Creates or updates a reusable variable profile.

profile_list()

Lists all profiles.

profile_get(name, mask_secrets=True)

Returns a profile, optionally masking secrets.

profile_delete(name)

Deletes a profile.

Tool Reference

App memory tools

app_memory_sync(repo_path, app_name="", focus="", max_files=250)

Scans a target repository and builds or updates a persistent understanding map.

app_memory_get(repo_path)

Returns stored memory for a repo.

app_memory_add_note(repo_path, note, category="workflow")

Adds a manual persistent note to the memory map.

app_memory_testing_brief(repo_path, goal="")

Builds a test-oriented brief from stored repo understanding.

app_memory_list()

Lists all persisted repo memory entries.

Examples

Usage patterns

Example 1: Simple browser inspection

Prompt
Use easy-sanity to open https://example.com and tell me the page title.
  • `browser_start`
  • `browser_navigate`
  • `browser_get_state`
  • `browser_stop`

Example 2: Login sanity check

Prompt
Use easy-sanity to log into the app, verify the dashboard opens, and stop with a pass/fail summary.
  • `browser_start`
  • `browser_navigate`
  • `browser_fill`
  • `browser_click_by_role`
  • `browser_wait_for_network_idle`
  • `assert_url_contains`
  • `assert_text_visible`
  • `browser_stop`

Example 3: Reusable profile-backed task

Prompt
Create a saved browser workflow with placeholders for URL, email, and password.
  1. Save the task with `task_create`
  2. Save credentials with `profile_save`
  3. Render or execute with `task_render`

Example 4: Repo-aware sanity testing

Prompt
Use easy-sanity to sync the target app repo into memory and generate a testing brief for the login workflow.
  • `app_memory_sync`
  • `app_memory_testing_brief`

Guidance

Recommended usage guidance

  • Prefer semantic tools before raw selector tools.
  • Prefer wait tools over fixed sleeps.
  • Use assertions for explicit pass/fail checks.
  • Use `browser_get_dom_summary` when a page layout is complex.
  • Use tasks and profiles for repeated flows.
  • Use app memory when the agent also has access to the application repository.
  • Try the launch command directly in a terminal first if an MCP client is not showing tools.

Operations

Security and secret handling

  • Store passwords and secrets in profiles or external environment variables.
  • Avoid saving credentials directly inside reusable tasks.
  • Use placeholders such as {{password}} in tasks.
  • Use `mask_secrets=true` when retrieving or rendering sensitive profiles.

Important nuance

  • The secret exists in the runtime values if the agent needs to log in.
  • Masking helps reduce accidental display and persistence in tool output.
  • It does not replace real secret-management practices.

Operational Fit

Strengths and limitations

Current strengths

  • Internal smoke tests
  • Browser-based sanity checks
  • Navigating real product flows
  • Collecting step evidence
  • Reusable LLM-driven workflows
  • Repo-informed exploratory sanity testing

Current limitations

  • Profile-aware direct slash-command execution still requires intentional agent behavior.
  • Some highly dynamic or custom component libraries may still require fallback selectors.
  • Browser automation quality still depends partly on the reasoning quality of the IDE agent.
  • Local runtime environment must have Playwright Chromium installed.
  • Some advanced reporting-tool APIs proposed in the roadmap are not yet implemented.

Summary

What Easy Sanity brings together

Easy Sanity is a local MCP browser-testing agent that combines:

  • Browser control
  • Semantic page understanding
  • Explicit assertions
  • Evidence capture
  • Reusable tasks
  • Environment profiles
  • Persistent app memory

Together, those pieces make it much more than a raw browser driver. It is designed to be an autonomous, reusable, test-oriented browser agent for real application sanity testing.