e2e mcp is an MCP server over stdio for a coding agent such as Claude Code
or Cursor. It opens a live attempt on one target and hands the agent the
vocabulary the testing agent gets on that engine: observe, tap, type, press,
select, scroll, navigate, type_secret, and the project’s own defineTool
values, plus locate to try a semantic locator before writing it and a
masked screenshot. The agent explores the real app, checks the exact
accessible names and locators, then writes deterministic tests and runs them
with e2e run from its shell. The agent skill is served as resources.
The server registers four MCP tools and never changes them. The session’s
vocabulary is a catalog behind call, in the style of
executor.sh: one call tool
runs any catalog tool by name, and tools describes them. The catalog
therefore follows whatever config and target the agent opens, a project tool
added to the config is available on the next open_session, and one server
serves every e2e project the agent can reach without a restart or a change
to the client’s tool list.
Setup
The server ships with@e2edev/e2e; nothing else is installed. e2e init
offers to register it in .mcp.json (Claude Code) and .cursor/mcp.json
(Cursor); a later init refreshes the files that exist. By hand:
.mcp.json, Cursor the workspace root). open_session
then finds the config the way e2e run does: the nearest e2e.config.ts or
e2e.config.mts, searching upward to the repository root. A config
elsewhere, in a monorepo subfolder or a second project, is named per session
with open_session’s config argument, or fixed for every session with
--config.
Everything the server prints for the operator goes to stderr, prefixed
e2e mcp:; stdout carries only the protocol. A config module or a library
that writes to stdout is redirected to stderr for the life of the process.
Tools
Results carry the session id, and every session tool accepts it as
session; today one session is open at a time, and a call naming another id
fails with NO_SESSION.
The catalog
Whatcall can run depends on the session’s target, so a project serves
what its engine can do:
Project tools passed to
createAgent({ tools }) follow in the catalog under
their own names, scoped to the session target’s platform, and run through
the same accounting as in a test step (mutates is honored; a tool that
declares mutates: false is marked read-only). A mobile project that passes
agentDeviceTools(...) therefore also catalogs open_app, swipe,
type_text, and alert. A project tool named like a catalog built-in is not
served; the server logs which. A name the catalog does not have fails with
UNKNOWN_TOOL and the list of names it does.
A session is one attempt on one target, and one at a time: a second
open_session fails with SESSION_OPEN, a call or tools without a
session with NO_SESSION (naming why the previous one ended). A session
closes on close_session, after 30 idle minutes, after 4 hours, when the
client disconnects, and on SIGINT or SIGTERM, so no browser or simulator is
left behind. An open_session that fails partway tears down what it started.
Sessions enforce what tests enforce. Navigation stays inside the engine’s
allowed origins (POLICY_DENIED otherwise). Node ids are valid only against
the newest observation; a stale id fails with LOCATOR_NOT_FOUND instead of
acting on the wrong node. Secrets fill only through type_secret, and once a
secret has been filled screenshot and tap_at leave the catalog: pixels are
withheld for the rest of the session (PIXEL_TAINTED).
Nothing a session does is recorded as a test result or into the trace
cache; its artifacts land under .e2e/artifacts/<target>/sessions/.
Resources
Exit codes
e2e mcp exits 0 when the client disconnects or a signal ends it, and 2
when the command line is invalid. Config and engine failures never end the
server: they come back as tool errors the agent can act on, with the same
codes the CLI prints.
Coding agents
What e2e gives an agent, and how it should use it.
CLI
Every command, flag by flag.
Errors
Every error code and its fix.
agent
The agent fixture and createAgent.
