genxevo-selenium
# GenXEvo AI Automation Agent — Python Selenium
**An MCP server that gives an AI coding agent reliable eyes and hands for Python + Selenium UI
automation engineering — deterministic capabilities, structured evidence, enforced safety
boundaries and verifiable results.**
[](https://github.com/genxevo/genxevo-ai-automation-agent-python-selenium/actions/workflows/ci.yml)
[](LICENSE)


---
## The problem
Ask any language model to fix a failing Selenium test and it will produce a confident, plausible,
wrong XPath.
It has to. It cannot see the page, it cannot see the test output, and it usually cannot even see
the project's real shape — which interpreter the suite runs on, which runner collects it, where
the page objects actually live. It fills the gap with fluency.
GenXEvo exists to remove the gap, so the model has something true to reason about.
## The principle
> **Evidence before modification. Evidence before success.**
The agent never invents a locator; it observes one. It never declares a fix; it proves one with a
run correlated by identifier to the failure it claims to have repaired. Every capability returns
evidence with an explicit trust level, every conclusion carries the signals that produced it, and
every result says in a machine-readable field whether it succeeded — because an agent that cannot
tell success from failure will confidently report a repair it never verified, and that outcome is
worse than not helping at all.
## What this is, and what it is not
| | |
|---|---|
| **Is** | An MCP capability layer around the UI automation engineering workflow you already run |
| **Is not** | A test framework, a Selenium wrapper, a replacement for pytest, or an AI of its own |
**There is no model inside this server.** The AI model reasons. GenXEvo is deterministic: it reads
what is actually on disk, and later drives a real browser and executes real tests, and returns
structured facts. When it does not know something, it says so, with a confidence level attached.
---
## Status — honestly
This is **phase 1A**: the foundation and exactly **two genuinely working capabilities**.
| | |
|---|---|
| **Built and tested** | Result contract, error vocabulary, evidence model, untrusted-content framing, configuration, path containment, secret redaction, test-selection validation, run model, capability catalogue, capability invoker, MCP adapter |
| **Working MCP tools** | `genxevo_agent_status`, `genxevo_discover_project` |
| **Designed, catalogued, NOT callable** | 15 further capabilities, each published with its delivery phase |
| **Not built** | Browser control, test execution, repair, verification |
**There are no stubs in this repository.** A planned capability is visible in
`genxevo_agent_status` so an agent can plan around it, and is not registered as a tool, so an agent
can never call one. A fake implementation is worse than an honest absence, because it teaches the
agent something false.
See [`docs/roadmap.md`](docs/roadmap.md) for what each phase delivers and its exit criteria.
---
## Quick start
### Requirements
- Python **3.11, 3.12 or 3.13**
- A Python automation project you want the agent to work on
> The 3.11 floor is an engineering decision, not a fashion one: `tomllib` entered the standard
> library in 3.11, and it is what lets project discovery parse `pyproject.toml` without a
> third-party parser in the core. On 3.10 that would require `tomli`. See
> [ADR-001](docs/decisions.md).
### Install
```bash
python -m venv .venv
# Windows
.venv\Scripts\activate
# macOS / Linux
source .venv/bin/activate
pip install -e .
```
Verify it starts — note that the banner goes to **stderr**, because stdout belongs to the MCP
transport:
```bash
genxevo-selenium-agent --version
```
### Connect it to an MCP client
Copy `.mcp.json.example` and point `--workspace` at your automation project:
```jsonc
{
"mcpServers": {
"genxevo-selenium": {
"command": "C:\\path\\to\\your\\.venv\\Scripts\\python.exe",
"args": [
"-m", "genxevo_selenium_agent",
"--workspace", "C:\\path\\to\\your\\automation-project"
]
}
}
}
```
Naming the interpreter explicitly is the reliable form on every platform: a console script lives
inside one virtual environment, and an MCP client does not inherit your activated shell.
Full instructions for Claude Code, VS Code and PyCharm: [`docs/installation.md`](docs/installation.md).
### Configure it (optional)
A missing configuration file is not an error — the defaults *are* the safe configuration. When you
want to change something, drop `genxevo.config.toml` in the workspace root:
```toml
version = 1
[execution]
enabled = false # test execution is off until you turn it on
require_selection = true # never run the whole suite by accident
[security]
redact_secrets = true
```
Every setting, its default and its rationale: [`docs/configuration.md`](docs/configuration.md).
---
## Architecture
```
AI MODEL (all reasoning lives here)
│ MCP · JSON-RPC over stdio
▼
┌──────────────────────────────────────────────────────────┐
│ genxevo_selenium_agent.mcp_server THIN ADAPTER │
│ tool names · descriptions · annotations · stderr logging │
│ every tool function holds no logic │
└──────────────────────────────────────────────────────────┘
│
┌──────────────────────────────────────────────────────────┐
│ genxevo_selenium_agent.core THE PRODUCT │
│ standard library + one typing-only shim, and nothing else │
│ │
│ capabilities runtime · invoker · catalog · 2 built │
│ discovery manifests · runners · venvs · page objects │
│ security paths · redaction · selection · globs │
│ contracts ToolResult · AgentError · Evidence │
│ runs RunId · RunOutcome · FileRunRegistry │
└──────────────────────────────────────────────────────────┘
│ │ │
▼ ▼ ▼
real project real browser (1C) real test runs (1D)
```
**Layer rule:** behaviour never lives in the adapter. A tool function cannot be unit tested through
an MCP client, so nothing that could be wrong is allowed in one.
---
## The result contract
Every capability returns the same envelope, and an agent **branches on `status`, never on prose**:
```jsonc
{
"contractVersion": "1.0",
"status": "partialSuccess", // one of nine values — see below
"operation": "project.discover",
"summary": "…one sentence for a human…",
"data": { }, // shape documented per capability
"warnings": [ { "code": "…", "message": "…", "detail": "…" } ],
"error": null, // present whenever status is not succeeding
"evidence": [ { "id": "…", "kind": "…", "trust": "trusted|untrusted", … } ],
"nextActions": [ { "tool": "…", "reason": "…" } ],
"durationMs": 41,
"startedAt": "2026-08-22T09:15:00Z",
"safeToRetry": true
}
```
**The nine statuses:** `success` · `partialSuccess` · `failure` · `validationError` ·
`configurationError` · `blocked` · `timeout` · `cancelled` · `skipped`
Each is a distinct decision an agent has to make. Nothing else is in the list.
Because the tools are annotated with a `TypedDict`, **this whole contract — including the `status`
enum — is published in `tools/list` as each tool's `outputSchema`.** An agent learns how to read a
result before it calls anything.
Invariants are enforced in code, not by convention: a succeeding status never carries an error, a
failing one always does, `status` is *derived* from the error's category so the two cannot
disagree, and a `partialSuccess` **cannot be constructed** without a warning explaining it.
---
## Security posture
GenXEvo reads untrusted content, hands it to a language model, and will later give that model
file-write and code-execution capabilities. The design assumption is that **the model will
eventually be persuaded to ask for something it should not have**, and that **the server, not the
model, refuses**.
| Control | What it does |
|---|---|
| Explicit workspace roots | Never inferred. Unconfigured means refuse, with the remedy |
| Path containment | Reject structurally → canonicalise → **then** contain → deny list → intent. Capabilities take a `ResolvedPath`, not a `str`, so unvalidated I/O does not type-check |
| Symlink resolution | `Path.resolve()` follows symlinks before containment is tested, so a link out of the workspace is refused |
| Deny list | Python-aware: `.pypirc`, `pip.conf`, `local_settings.py`, `secrets.py` alongside `.env`, `*.pem`, `~/.ssh` |
| Secret redaction | Key-name **and** value-shape detection, including Python source assignments like `PASSWORD = "…"` |
| **No project code is ever executed** | `setup.py` is recorded and never run; `conftest.py` is read as text and never imported; installed packages are read from `dist-info` directory names |
| Untrusted framing | Escape-proof — a payload cannot forge either delimiter |
| Selection validation | A selection starting with `-` is refused outright: `pytest -p some.module` is arbitrary code execution |
| Safe defaults | Execution off, redaction on, selection required |
| Bounded everything | Timeouts, cooperative cancellation, scan limits, repair-cycle ceiling |
| Run correlation | Stale artefacts cannot be read as proof of a fix |
| Error hygiene | No traceback ever reaches the agent; refusals never echo the absolute workspace path |
**Residual risks are documented, not hidden** — see [`SECURITY.md`](SECURITY.md) and
[`docs/security.md`](docs/security.md). Framing does not *prevent* influence, test execution is
arbitrary code by design, stdio MCP has no authentication, and redaction is heuristic.
---
## The GenXEvo family
This is the **second** product in a family of independent agents. Each is separately cloneable and
installable; what they share is a **contract**, not a build.
| | Selenium | Playwright |
|---|---|---|
| **C#** | [shipped](https://github.com/GenXEvo/genxevo-ai-automation-agent-csharp-selenium) | planned |
| **Python** | **this repository** | planned |
| Java · JavaScript · TypeScript | planned | planned |
What ports across languages is the JSON shape, the nine-status vocabulary, the error codes, the run
identifier format, the evidence model and the safety classes. An agent that has learned one GenXEvo
server should recognise the next one on first contact.
What is **not** shared is implementation. This product is Python-native by design: `TypedDict`
output schemas, `tomllib` configuration, `dataclasses` instead of a serialisation framework,
cooperative cancellation across `asyncio.to_thread`, and a discovery model built around
`pyproject.toml`, `pyvenv.cfg` and pytest's own collection rules.
---
## Documentation
| Document | Contents |
|---|---|
| [`docs/architecture.md`](docs/architecture.md) | Packages, layers, domain model, contract, evidence, runs, concurrency |
| [`docs/installation.md`](docs/installation.md) | Claude Code, VS Code, PyCharm; the interpreter trap |
| [`docs/configuration.md`](docs/configuration.md) | Every setting, default and rationale; precedence; validation |
| [`docs/mcp-tools.md`](docs/mcp-tools.md) | Full contract — 2 implemented in detail, 15 planned with their guarantees |
| [`docs/agent-workflows.md`](docs/agent-workflows.md) | The engineering loop, rules for agents, a worked example, anti-patterns |
| [`docs/security.md`](docs/security.md) | Threat model, controls with rationale, residual risks |
| [`docs/decisions.md`](docs/decisions.md) | Architecture decision records, each tied to the defect that motivated it |
| [`docs/roadmap.md`](docs/roadmap.md) | Phases 1A–3 with exit criteria and what is out of scope |
| [`docs/troubleshooting.md`](docs/troubleshooting.md) | Concrete failure modes and their fixes |
| [`prompts/`](prompts/) | How to talk to the agent, with complete worked prompts |
| [`examples/`](examples/) | Working configuration files |
---
## Development
```bash
pip install -e ".[dev]"
ruff check . # lint
ruff format --check . # format
mypy # strict type checking
pytest # the full suite
```
These checks also run in CI (**Build**) on every push and pull request — **ruff · ruff format · mypy · pytest across Python 3.11–3.13 on Linux & Windows**, plus stdout-purity and package-build checks.
The standard, written into [`CONTRIBUTING.md`](CONTRIBUTING.md): **every security control ships
with tests that assert the attack, not only the happy path**, and `genxevo_selenium_agent.core`
imports the standard library and exactly one typing-only shim — enforced by a test that parses every
module with `ast`, not by convention. The one exception is `typing_extensions`, and
[ADR-002](docs/decisions.md) explains why the alternative is a server that will not start on Python
3.11.
---
## Author
**Rajeshkumar Muthu** — Senior QA Automation Agentic AI Engineer.
Licensed under the [MIT License](LICENSE).
TDQS
Scored across 2 tools
genxevo_agent_status reports on the agent/server itself, while genxevo_discover_project inspects the workspace project. Their purposes and targets are completely distinct, so an agent should never confuse them.
Both tools share the genxevo_ prefix and use snake_case, which is good, but genxevo_agent_status is a noun-phrase while genxevo_discover_project is verb+noun. This is a minor stylistic inconsistency rather than a serious problem.
Two tools is on the thin side and feels borderline, but both are substantial read-only capabilities that form a coherent project-reconnaissance pair. The count is not excessive, but it is minimal.
The descriptions repeatedly mention tests, locators, and runs, but no tool actually executes tests, inspects locators, or interacts with the Selenium project. After discovery, an agent has no way to act on the project, which is a significant gap for a Selenium-oriented server.