e2e 0.21.1 → 0.21.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/.build.json +1 -1
- package/dist/agent/act-validation.js +1 -0
- package/dist/agent/act.js +7 -3
- package/dist/agent/action-dispatcher.js +30 -8
- package/dist/agent/actions.d.ts +2 -0
- package/dist/agent/actions.js +5 -2
- package/dist/agent/error.js +1 -0
- package/dist/agent/executor.d.ts +14 -6
- package/dist/agent/index.js +5 -1
- package/dist/agent/invocation.d.ts +3 -0
- package/dist/agent/model/sdk.d.ts +5 -0
- package/dist/agent/model/sdk.js +18 -1
- package/dist/agent/observation.js +1 -0
- package/dist/agent/primitives.js +14 -3
- package/dist/agent/replay.js +9 -3
- package/dist/agent/step-cache.d.ts +3 -2
- package/dist/agent/step-cache.js +7 -4
- package/dist/agent/tool-loop.js +4 -1
- package/dist/cache/context.js +1 -1
- package/dist/cache/recorder.d.ts +2 -1
- package/dist/cache/recorder.js +11 -2
- package/dist/cache/trace-fields.d.ts +10 -0
- package/dist/cache/trace-fields.js +17 -0
- package/dist/cache/trace.d.ts +9 -1
- package/dist/cache/trace.js +26 -7
- package/dist/cli/feedback.js +2 -1
- package/dist/cli/init/sibling-versions.json +1 -1
- package/dist/collect/select.js +12 -2
- package/dist/config/agent.d.ts +1 -1
- package/dist/config/diagnose.js +9 -3
- package/dist/config/esm-hooks.js +3 -2
- package/dist/config/resolve.js +3 -2
- package/dist/config/typescript.js +52 -2
- package/dist/engine/contract.d.ts +3 -2
- package/dist/engine/manifest.js +2 -2
- package/dist/internal/errors.js +5 -2
- package/dist/internal/source.js +2 -0
- package/dist/internal/unserved-model.d.ts +5 -0
- package/dist/internal/unserved-model.js +22 -0
- package/dist/locator/screen.js +1 -2
- package/dist/oauth/copilot.js +23 -5
- package/dist/oauth/device-code.js +35 -19
- package/dist/oauth/errors.d.ts +3 -0
- package/dist/oauth/errors.js +26 -11
- package/dist/oauth/providers/github-copilot.d.ts +4 -0
- package/dist/oauth/providers/github-copilot.js +64 -16
- package/dist/oauth/providers/openai.d.ts +2 -2
- package/dist/oauth/providers/openai.js +47 -19
- package/dist/oauth/providers/opencode-console.js +36 -15
- package/dist/oauth/providers/xai.js +18 -9
- package/dist/oauth/token-endpoint.d.ts +15 -6
- package/dist/oauth/token-endpoint.js +18 -6
- package/dist/report/code-frame.js +9 -3
- package/dist/report/failure-text.d.ts +10 -2
- package/dist/report/failure-text.js +24 -8
- package/dist/report/list-model.d.ts +13 -0
- package/dist/report/list-steps.d.ts +6 -1
- package/dist/report/list-steps.js +29 -0
- package/dist/report/list.d.ts +1 -0
- package/dist/report/list.js +16 -7
- package/dist/report/live-window.js +2 -1
- package/dist/report/markdown.js +1 -1
- package/dist/run/fixtures.js +2 -0
- package/dist/run/host-trace.d.ts +3 -2
- package/dist/run/host-trace.js +2 -1
- package/dist/run/last-run.d.ts +9 -0
- package/dist/run/last-run.js +28 -0
- package/dist/run/open-session.d.ts +3 -1
- package/dist/run/open-session.js +1 -1
- package/dist/run/runner.js +6 -2
- package/dist/run/sessions.js +3 -3
- package/dist/run/standalone-attempt.d.ts +2 -1
- package/dist/run/standalone-attempt.js +3 -1
- package/dist/run/steps.d.ts +3 -1
- package/dist/run/units.d.ts +8 -0
- package/dist/run/units.js +14 -0
- package/dist/run/worker-models.d.ts +2 -0
- package/dist/run/worker-models.js +7 -0
- package/dist/types.d.ts +2 -2
- package/docs/authentication.mdx +26 -23
- package/docs/browser.mdx +7 -4
- package/docs/bug-bash.mdx +2 -2
- package/docs/cache.mdx +6 -1
- package/docs/ci/bitrise.mdx +1 -1
- package/docs/ci/codemagic.mdx +2 -2
- package/docs/ci/eas-workflows.mdx +1 -1
- package/docs/coding-agents.mdx +7 -35
- package/docs/debugging.mdx +24 -36
- package/docs/executors.mdx +1 -1
- package/docs/github.mdx +2 -1
- package/docs/integrations/eas.mdx +1 -1
- package/docs/integrations/index.mdx +6 -0
- package/docs/integrations/lightpanda.mdx +192 -0
- package/docs/integrations/testmu.mdx +152 -0
- package/docs/mcp.mdx +123 -0
- package/docs/migrate/detox.mdx +1 -1
- package/docs/migrate/maestro.mdx +101 -105
- package/docs/migrate/playwright.mdx +65 -70
- package/docs/mobile.mdx +18 -3
- package/docs/models.mdx +4 -2
- package/docs/quickstart.mdx +7 -0
- package/docs/reference/agent.mdx +369 -277
- package/docs/reference/app.mdx +141 -32
- package/docs/reference/cli.mdx +674 -558
- package/docs/reference/config.mdx +1406 -752
- package/docs/reference/engine.mdx +1062 -256
- package/docs/reference/environment.mdx +156 -84
- package/docs/reference/errors.mdx +106 -72
- package/docs/reference/expect.mdx +752 -372
- package/docs/reference/mcp.mdx +366 -141
- package/docs/reference/mobile.mdx +844 -403
- package/docs/reference/reporters.mdx +312 -106
- package/docs/reference/runner.mdx +373 -174
- package/docs/reference/screen.mdx +931 -398
- package/docs/reference/security.mdx +106 -40
- package/docs/reference/test.mdx +626 -273
- package/docs/reference/web.mdx +956 -533
- package/docs/security.mdx +6 -5
- package/docs/snippets/model-provider.jsx +763 -0
- package/docs/snippets/setup-prompt-card.jsx +22 -0
- package/docs/snippets/show-more.jsx +22 -0
- package/docs/snippets/video-card.jsx +34 -0
- package/docs/snippets/video.jsx +25 -0
- package/docs/subscriptions.mdx +10 -3
- package/docs/telemetry.mdx +39 -39
- package/package.json +3 -2
- package/schema/report-v1.schema.json +6 -0
- package/skills/e2e/references/agent.md +8 -3
- package/skills/e2e/references/debugging.md +1 -1
- package/skills/e2e/references/mcp.md +1 -1
- package/skills/e2e/references/setup.md +12 -3
- package/skills/e2e/references/writing-tests.md +1 -1
package/docs/reference/mcp.mdx
CHANGED
|
@@ -6,6 +6,26 @@ icon: "plug"
|
|
|
6
6
|
"twitter:image": "https://e2e.tester.army/docs/images/og/reference/mcp.png?v=3d3880edad"
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
`e2e mcp` lets coding agents such as Claude Code and Cursor inspect your app,
|
|
10
|
+
try locators, and take masked screenshots before writing tests. It runs over
|
|
11
|
+
stdio and uses the project's configured engine. For setup and what to ask
|
|
12
|
+
the agent, see the [MCP server guide](/mcp).
|
|
13
|
+
|
|
14
|
+
```json title=".mcp.json"
|
|
15
|
+
{
|
|
16
|
+
"mcpServers": {
|
|
17
|
+
"e2e": { "command": "npx", "args": ["e2e", "mcp"] }
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Four MCP tools manage the session and call its available app tools. The app
|
|
23
|
+
tool catalog reloads from the config whenever a session opens.
|
|
24
|
+
|
|
25
|
+
## Reference
|
|
26
|
+
|
|
27
|
+
### Flags
|
|
28
|
+
|
|
9
29
|
<CodeGroup>
|
|
10
30
|
```bash npm
|
|
11
31
|
npx e2e mcp [--config <path>] [--target <name>] [--headed] [--max-sessions <n>]
|
|
@@ -20,12 +40,263 @@ bunx e2e mcp [--config <path>] [--target <name>] [--headed] [--max-sessions <n>]
|
|
|
20
40
|
```
|
|
21
41
|
</CodeGroup>
|
|
22
42
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
43
|
+
| Flag | Default |
|
|
44
|
+
| --- | --- |
|
|
45
|
+
| [`--config <path>`](#config) | discovered like `e2e run` |
|
|
46
|
+
| [`--target <name>`](#target) | the only target |
|
|
47
|
+
| [`--headed`](#headed) | headless |
|
|
48
|
+
| [`--max-sessions <n>`](#max-sessions) | `4` |
|
|
26
49
|
|
|
27
|
-
|
|
28
|
-
|
|
50
|
+
#### `--config`
|
|
51
|
+
|
|
52
|
+
The config every session loads unless `open_session` names one. Each
|
|
53
|
+
session loads it afresh ([Several sessions](#several-sessions)).
|
|
54
|
+
|
|
55
|
+
#### `--target`
|
|
56
|
+
|
|
57
|
+
The target a session opens on when `open_session` names none.
|
|
58
|
+
|
|
59
|
+
- Without it and without a name, the only target is used.
|
|
60
|
+
- Several targets and no name is `TARGET_REQUIRED`.
|
|
61
|
+
|
|
62
|
+
#### `--headed`
|
|
63
|
+
|
|
64
|
+
Shows the browser or simulator, when the engine supports it, in every
|
|
65
|
+
session whose `open_session` does not set `headed`.
|
|
66
|
+
|
|
67
|
+
- Sessions are headless by default, as in `e2e run`.
|
|
68
|
+
|
|
69
|
+
#### `--max-sessions`
|
|
70
|
+
|
|
71
|
+
How many sessions may be open at once, 1 through 16. See
|
|
72
|
+
[Several sessions](#several-sessions).
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
e2e mcp --max-sessions 8 # default: 4
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Tools
|
|
79
|
+
|
|
80
|
+
| Tool | Description |
|
|
81
|
+
| --- | --- |
|
|
82
|
+
| [`open_session`](#open_session) | Opens a session |
|
|
83
|
+
| [`tools`](#tools-2) | Lists the catalog or one tool |
|
|
84
|
+
| [`call`](#call) | Runs one catalog tool |
|
|
85
|
+
| [`close_session`](#close_session) | Ends the session and its engine |
|
|
86
|
+
|
|
87
|
+
`session` may be left out while exactly one session is open. See
|
|
88
|
+
[Several sessions](#several-sessions).
|
|
89
|
+
|
|
90
|
+
#### `open_session`
|
|
91
|
+
|
|
92
|
+
Loads the config, starts the target's app command, boots the engine, starts
|
|
93
|
+
one attempt, and opens the app URL when the engine can navigate. Returns the
|
|
94
|
+
session id, the catalog, and the first observation.
|
|
95
|
+
|
|
96
|
+
```txt
|
|
97
|
+
open_session {target: "web", headed: true}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
| Input | Type | Required | Does |
|
|
101
|
+
| --- | --- | --- | --- |
|
|
102
|
+
| `target` | string | no | The target to open. Otherwise `--target`, else the only target. |
|
|
103
|
+
| `config` | string (path) | no | The config for this session. Otherwise `--config`, else discovered like `e2e run`. |
|
|
104
|
+
| `headed` | boolean | no | `true` shows this session's browser or simulator, `false` hides it. Without it the session follows `--headed`. |
|
|
105
|
+
|
|
106
|
+
#### `tools`
|
|
107
|
+
|
|
108
|
+
Returns the catalog, one line per tool.
|
|
109
|
+
|
|
110
|
+
```txt
|
|
111
|
+
tools {tool: "locate"}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
| Input | Type | Required | Does |
|
|
115
|
+
| --- | --- | --- | --- |
|
|
116
|
+
| `tool` | string | no | A catalog tool. Returns its full description and argument schema. |
|
|
117
|
+
| `session` | string | while several are open | The session id. |
|
|
118
|
+
|
|
119
|
+
#### `call`
|
|
120
|
+
|
|
121
|
+
Runs one catalog tool.
|
|
122
|
+
|
|
123
|
+
```txt
|
|
124
|
+
call {tool: "tap", args: {target: "n42"}, session: "<id>"}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
| Input | Type | Required | Does |
|
|
128
|
+
| --- | --- | --- | --- |
|
|
129
|
+
| `tool` | string | yes | A catalog tool name. |
|
|
130
|
+
| `args` | object | no | The tool's arguments. |
|
|
131
|
+
| `session` | string | while several are open | The session id. |
|
|
132
|
+
|
|
133
|
+
- Arguments are validated against the tool's closed schema first. A wrong
|
|
134
|
+
type or an argument the tool does not declare is `INVALID_ARGUMENT`, and
|
|
135
|
+
nothing runs.
|
|
136
|
+
- An action that fails is an error result that still carries the screen it
|
|
137
|
+
re-observed. It leads with the tool, its target, and the code:
|
|
138
|
+
`navigate file:///etc/passwd failed: POLICY_DENIED: forbidden URL scheme: file:`.
|
|
139
|
+
- Project tools honor their AI SDK `toModelOutput` renderer, including when
|
|
140
|
+
`execute` returns a string. Rendered `error-text` and `error-json` outputs
|
|
141
|
+
are MCP error results (`isError: true`). The coding agent can tell a failed
|
|
142
|
+
tool call from a successful one.
|
|
143
|
+
|
|
144
|
+
#### `close_session`
|
|
145
|
+
|
|
146
|
+
Saves a recording still running, ends the attempt, and disposes the engine.
|
|
147
|
+
It stops the app processes the session started once no other session uses
|
|
148
|
+
them.
|
|
149
|
+
|
|
150
|
+
```txt
|
|
151
|
+
close_session {session: "<id>"}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
| Input | Type | Required | Does |
|
|
155
|
+
| --- | --- | --- | --- |
|
|
156
|
+
| `session` | string | while several are open | The session id. |
|
|
157
|
+
|
|
158
|
+
### The catalog
|
|
159
|
+
|
|
160
|
+
What `call` can run depends on the session's target.
|
|
161
|
+
|
|
162
|
+
| Catalog tool | Description |
|
|
163
|
+
| --- | --- |
|
|
164
|
+
| [`observe`](#observe) | Returns a fresh observation |
|
|
165
|
+
| [Actions](#actions) | Act on an observed node |
|
|
166
|
+
| [`dismiss_keyboard`](#dismiss_keyboard) | Hides the on-screen keyboard |
|
|
167
|
+
| [`locate`](#locate) | Tries a semantic locator |
|
|
168
|
+
| [`screenshot`](#screenshot) | Returns the masked viewport |
|
|
169
|
+
| [Point tools](#point-tools) | Act at a screenshot point |
|
|
170
|
+
| [`start_recording`, `stop_recording`](#recording) | Records a video of the app |
|
|
171
|
+
| [Project tools](#project-tools) | Your `defineTool` tools |
|
|
172
|
+
|
|
173
|
+
#### `observe`
|
|
174
|
+
|
|
175
|
+
A fresh observation, one node per line as `#id role "name" ...`, with the
|
|
176
|
+
current path and revision. Takes no arguments.
|
|
177
|
+
|
|
178
|
+
- A link's `href` is its path when it is on the app URL's origin, else
|
|
179
|
+
origin and path.
|
|
180
|
+
- `mailto:`, `tel:`, and `blob:` targets keep their scheme.
|
|
181
|
+
- `…` marks what is left out: `?…` a query, `#…` a fragment, `data:…` or
|
|
182
|
+
`javascript:…` an inline payload, a trailing `…` a target cut at 256
|
|
183
|
+
characters.
|
|
184
|
+
|
|
185
|
+
#### Actions
|
|
186
|
+
|
|
187
|
+
`tap`, `double_tap`, `long_press`, `right_click`, `hover`, `type`, `press`,
|
|
188
|
+
`select`, `check`, `scroll`, `scroll_to`, `drag`, `upload`, `navigate`,
|
|
189
|
+
`back`, `type_secret`. Each returns the action's summary and what changed on
|
|
190
|
+
screen.
|
|
191
|
+
|
|
192
|
+
| Input | Does |
|
|
193
|
+
| --- | --- |
|
|
194
|
+
| A node id | From the newest observation. |
|
|
195
|
+
| The value, key, `checked` state, direction, drop target, project-relative `files`, URL, or secret name | What the verb needs. |
|
|
196
|
+
| `modifiers` (optional) | On `tap`, `double_tap`, and `right_click`, when the engine declares `tapModifiers`: a list of `Shift`, `Control`, `Alt`, `Meta`, `ControlOrMeta`, held for the click. |
|
|
197
|
+
|
|
198
|
+
- A verb the engine does not declare is not in the catalog.
|
|
199
|
+
- `upload` refuses a path outside the project, a hidden file, or a file
|
|
200
|
+
under a hidden directory with `POLICY_DENIED`.
|
|
201
|
+
- `type_secret` is listed when the config declares `credentials` or
|
|
202
|
+
`secrets` and the engine declares the `fill` action. The plaintext never reaches the agent.
|
|
203
|
+
|
|
204
|
+
#### `dismiss_keyboard`
|
|
205
|
+
|
|
206
|
+
Hides the on-screen keyboard. Takes no arguments. Listed when the engine's
|
|
207
|
+
keyboard declares `dismiss`, as the device engines do.
|
|
208
|
+
|
|
209
|
+
#### `locate`
|
|
210
|
+
|
|
211
|
+
Returns how many nodes match a semantic locator, which ones, and the
|
|
212
|
+
`screen.*` call to write.
|
|
213
|
+
|
|
214
|
+
| Input | Does |
|
|
215
|
+
| --- | --- |
|
|
216
|
+
| `role` (with optional `name`), `text`, `label`, `placeholder`, `testId` | Exactly one of them. |
|
|
217
|
+
| `exact` | Exact matching; `false` for substring, case-insensitive. |
|
|
218
|
+
|
|
219
|
+
- Zero or several matches name the error a test would get.
|
|
220
|
+
- A `name` matches the accessible name only. `observe` shows a `status`,
|
|
221
|
+
`alert`, or `listitem` by its text, so one of those with a name that
|
|
222
|
+
matches nothing also points at `getByRole(role).filter({ hasText })` and
|
|
223
|
+
`getByText`.
|
|
224
|
+
- What it shows of a node passes the secret ledger, as `observe` does: a
|
|
225
|
+
filled secret reads `<secret:name>`.
|
|
226
|
+
|
|
227
|
+
#### `screenshot`
|
|
228
|
+
|
|
229
|
+
The masked viewport as an image. Takes no arguments.
|
|
230
|
+
|
|
231
|
+
- From then on every action result carries a fresh screenshot.
|
|
232
|
+
- After a secret fill it answers `PIXEL_TAINTED`. See [Sessions](#sessions).
|
|
233
|
+
|
|
234
|
+
#### Point tools
|
|
235
|
+
|
|
236
|
+
Each takes `x` and `y` in the latest screenshot's pixels.
|
|
237
|
+
|
|
238
|
+
| Tool | Inputs | Does | Listed when the engine declares |
|
|
239
|
+
| --- | --- | --- | --- |
|
|
240
|
+
| `tap_at` | `x`, `y` | Taps a listed control under the point by id, otherwise the bare point through the engine's `performAt`. Without `performAt` the point must land on a listed control. | `tap` or `performAt` |
|
|
241
|
+
| `hover_at` | `x`, `y` | Hovers the same way: a listed control under it by id, otherwise the bare point through `performAt`'s `hover` kind. | the `hover` action or pointer kind |
|
|
242
|
+
| `type_at` | `x`, `y`, `value`; `replace` on an engine with a keyboard | Fills a listed input under the point by id. With a keyboard, anything else is tapped to focus it and typed into, at the caret unless `replace` is set. Without one, a point on nothing listed fails. | `type` or a keyboard |
|
|
243
|
+
| `press_at` | `x`, `y`, `key` | Sends the key to a listed control under the point by id. With a keyboard, anything else is tapped to focus it and the key goes through the keyboard. | `press` or a keyboard |
|
|
244
|
+
| `select_at` | `x`, `y`, `value` (the option's visible label) | Picks the option in the listed select-like control under the point. Nothing listed there fails. | `select` |
|
|
245
|
+
|
|
246
|
+
<Note>
|
|
247
|
+
Before the session's first `screenshot`, each point tool answers with the
|
|
248
|
+
line that says to take one, at no cost. Like `screenshot`, they answer
|
|
249
|
+
`PIXEL_TAINTED` after a secret fill.
|
|
250
|
+
</Note>
|
|
251
|
+
|
|
252
|
+
#### Recording
|
|
253
|
+
|
|
254
|
+
`start_recording` and `stop_recording` record a video of the app. Both are
|
|
255
|
+
listed when the engine records video.
|
|
256
|
+
|
|
257
|
+
```txt
|
|
258
|
+
call {tool: "start_recording", args: {name: "checkout-flow"}}
|
|
259
|
+
call {tool: "stop_recording"}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
| Tool | Input | Does |
|
|
263
|
+
| --- | --- | --- |
|
|
264
|
+
| `start_recording` | `name` (optional): up to 64 letters, digits, `-`, and `_`, starting with a letter or digit | One recording at a time. A second start reports the one running. |
|
|
265
|
+
| `stop_recording` | none | Saves the video and returns each file's absolute path, or the URL where a provider keeps it. |
|
|
266
|
+
|
|
267
|
+
- A session records video only between `start_recording` and
|
|
268
|
+
`stop_recording`, as many times as needed. The configured
|
|
269
|
+
[`video`](/reference/config#video) mode applies to runs, not sessions, and
|
|
270
|
+
a session writes no [trace](/reference/config#trace).
|
|
271
|
+
- Each recording is saved to `<output>/videos/<session>/<n>[-<name>].webm`
|
|
272
|
+
(`.mp4` on mobile, and [`output`](/reference/config#output) is `.e2e` by
|
|
273
|
+
default), where `n` counts the session's recordings.
|
|
274
|
+
- A web recording that spans a restart or a state reset has one file per
|
|
275
|
+
page, `-part2`, `-part3`, and so on, in play order.
|
|
276
|
+
- A browser or device provider that records the lease itself records the
|
|
277
|
+
session instead. [Kernel](/integrations/kernel) downloads its replay, saved
|
|
278
|
+
here as `<n>[-<name>].mp4` like any other file. A provider that keeps its
|
|
279
|
+
recordings is listed by URL instead of a file.
|
|
280
|
+
- `close_session`, the idle timeout, and the session TTL save a recording
|
|
281
|
+
still running, and the close result lists its files.
|
|
282
|
+
|
|
283
|
+
<Warning>
|
|
284
|
+
Recordings are not masked and are not withheld after a secret was filled:
|
|
285
|
+
they are files for a person to watch, and no tool returns their pixels. A
|
|
286
|
+
secret typed into a plain input while recording is readable in the video,
|
|
287
|
+
and a recording stopped after a secret was filled says so.
|
|
288
|
+
</Warning>
|
|
289
|
+
|
|
290
|
+
#### Project tools
|
|
291
|
+
|
|
292
|
+
Project tools in `agents.default.tools` follow in the catalog under their
|
|
293
|
+
own names, scoped to the target's platform. A mobile project that passes
|
|
294
|
+
`mobileTools(...)` also gets `open_app`, `swipe`, and `alert`.
|
|
295
|
+
|
|
296
|
+
| Throws | When |
|
|
297
|
+
| --- | --- |
|
|
298
|
+
| `INVALID_CONFIG` | A project tool named like a built-in, at config load. |
|
|
299
|
+
| `UNKNOWN_TOOL` | A name not in the catalog. The message lists the names. |
|
|
29
300
|
|
|
30
301
|
## Setup
|
|
31
302
|
|
|
@@ -46,60 +317,80 @@ claude mcp add --scope project e2e -- bunx e2e mcp
|
|
|
46
317
|
```
|
|
47
318
|
</CodeGroup>
|
|
48
319
|
|
|
49
|
-
For clients that use a `mcpServers` config, add
|
|
50
|
-
|
|
51
|
-
```json
|
|
52
|
-
{
|
|
53
|
-
"mcpServers": {
|
|
54
|
-
"e2e": { "command": "npx", "args": ["e2e", "mcp"] }
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
```
|
|
58
|
-
|
|
320
|
+
For clients that use a `mcpServers` config, add the entry shown above.
|
|
59
321
|
Codex reads the project's `.codex/config.toml` once the project is trusted:
|
|
60
322
|
|
|
61
|
-
```toml
|
|
323
|
+
```toml title=".codex/config.toml"
|
|
62
324
|
[mcp_servers.e2e]
|
|
63
325
|
command = "npx"
|
|
64
326
|
args = ["e2e", "mcp"]
|
|
65
327
|
```
|
|
66
328
|
|
|
329
|
+
The server is also listed in the
|
|
330
|
+
[MCP Registry](https://registry.modelcontextprotocol.io) as
|
|
331
|
+
`io.github.tester-army/e2e`. A client that installs it from there runs the
|
|
332
|
+
listed release with `npx` and must start it in the project directory. The
|
|
333
|
+
project still needs e2e and its engine installed, since the config imports
|
|
334
|
+
them. A listed release on another version than the project's e2e can reject
|
|
335
|
+
config keys it does not know; the registration above always matches.
|
|
336
|
+
|
|
67
337
|
The server needs no model: a project without the optional `ai` package
|
|
68
338
|
opens sessions and drives the app with the deterministic tools.
|
|
69
339
|
|
|
70
340
|
Start the server in the project directory. Config discovery works like
|
|
71
|
-
`e2e run`.
|
|
72
|
-
`open_session`
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
##
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
341
|
+
`e2e run`. For another config, see [`--config`](#config) and
|
|
342
|
+
[`open_session`](#open_session).
|
|
343
|
+
|
|
344
|
+
## Output
|
|
345
|
+
|
|
346
|
+
- Operator output goes to stderr, prefixed `e2e mcp:`. Stdout carries only
|
|
347
|
+
the protocol.
|
|
348
|
+
- What your code writes through `console`, `process.stdout`, or
|
|
349
|
+
`process.stderr` (the config's top-level code, project tools) goes to
|
|
350
|
+
stderr with secret values redacted.
|
|
351
|
+
- Output printed while a config loads appears once it has loaded. If the
|
|
352
|
+
load fails, that output is withheld, since the config's secrets are
|
|
353
|
+
unknown.
|
|
354
|
+
- An error nobody caught is logged redacted and shuts the server down.
|
|
355
|
+
|
|
356
|
+
<Note>
|
|
357
|
+
A child process that inherits the server's stdio writes past this.
|
|
358
|
+
</Note>
|
|
359
|
+
|
|
360
|
+
## Sessions
|
|
361
|
+
|
|
362
|
+
A session is one attempt on one target. A `call` before any session is open
|
|
363
|
+
fails with `NO_SESSION`.
|
|
364
|
+
|
|
365
|
+
A session closes:
|
|
366
|
+
|
|
367
|
+
- on `close_session`,
|
|
368
|
+
- after 30 idle minutes,
|
|
369
|
+
- after 4 hours,
|
|
370
|
+
- when the client disconnects,
|
|
371
|
+
- on SIGINT or SIGTERM.
|
|
372
|
+
|
|
373
|
+
The client disconnects when it closes the server's stdin or dies. Then every
|
|
374
|
+
session closes, the app commands the sessions started stop, and the server
|
|
375
|
+
exits.
|
|
376
|
+
|
|
377
|
+
Sessions enforce what tests enforce:
|
|
378
|
+
|
|
379
|
+
- Node ids are valid only against the newest observation.
|
|
380
|
+
- A secret fills only through `type_secret`. Once one has been filled,
|
|
381
|
+
[`screenshot`](#screenshot) and the [point tools](#point-tools) stay in the
|
|
382
|
+
catalog but answer `PIXEL_TAINTED` for the rest of the session.
|
|
383
|
+
- Once the config has loaded, its secret values are redacted from every text
|
|
384
|
+
a tool returns, a node `locate` found, an action's result, an
|
|
385
|
+
`open_session` or `close_session` failure, a cleanup line, and the server's
|
|
386
|
+
own log lines.
|
|
387
|
+
- Nothing a session does is recorded as a test result or into the replay
|
|
388
|
+
cache. Its artifacts land under
|
|
389
|
+
`<output>/results/mcp/<target>-<attemptId>/`, its videos under
|
|
390
|
+
`<output>/videos/`. A later `e2e run` clears `<output>/results`; the videos
|
|
391
|
+
stay.
|
|
392
|
+
- A queued call cancelled by the client does not execute when its turn
|
|
393
|
+
arrives.
|
|
103
394
|
|
|
104
395
|
## Several sessions
|
|
105
396
|
|
|
@@ -107,27 +398,30 @@ Several sessions can be open at once, up to `--max-sessions`, each on its
|
|
|
107
398
|
own attempt and its own browser or device. Parallel agents, such as a
|
|
108
399
|
coding agent's subagents, share one server this way: each opens a session
|
|
109
400
|
and passes its id to every `tools`, `call`, and `close_session`.
|
|
401
|
+
[Bug bashes](/bug-bash) use this: every verifying subagent drives its own
|
|
402
|
+
session.
|
|
110
403
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
404
|
+
| Throws | When |
|
|
405
|
+
| --- | --- |
|
|
406
|
+
| `SESSION_REQUIRED` | A call without `session` while several are open. The message lists the open sessions. |
|
|
407
|
+
| `NO_SESSION` | A `session` id that is not open. For a session that is closing or ended, the message says why. |
|
|
408
|
+
| `SESSION_OPEN` | An `open_session` past the limit, or while the server shuts down. |
|
|
409
|
+
| `CONFIG_IN_USE` | An `open_session` on another config while sessions are open. |
|
|
410
|
+
| `ENGINE_IN_USE` | A second session on an engine created inside a package. |
|
|
411
|
+
| `CANCELLED` | An `open_session` the client cancelled or disconnected from, or the server shut down during, once what it started has stopped. |
|
|
412
|
+
|
|
413
|
+
- A session holds its slot from `open_session` until its attempt has closed,
|
|
414
|
+
so sessions still booting or still closing count toward the limit.
|
|
119
415
|
- Sessions open at once share one config: credentials and secrets resolve
|
|
120
|
-
through one registry per process.
|
|
121
|
-
fails with `CONFIG_IN_USE` until the others close.
|
|
416
|
+
through one registry per process.
|
|
122
417
|
- While the server shuts down it cancels sessions still opening and waits
|
|
123
|
-
for them, closes every session, and refuses new ones
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
still waiting on its `readyUrl`, say) has stopped.
|
|
418
|
+
for them, closes every session, and refuses new ones. A cancelled open
|
|
419
|
+
fails once what it started (an app command still waiting on its
|
|
420
|
+
`readyUrl`, say) has stopped.
|
|
127
421
|
- Each session loads the config afresh, down to the files it imports by
|
|
128
422
|
path, such as a base config it spreads, so each gets engine instances of
|
|
129
423
|
its own. An engine created inside a package is one instance every session
|
|
130
|
-
shares
|
|
424
|
+
shares.
|
|
131
425
|
- Sessions on one app command share its process: the first session starts
|
|
132
426
|
it, and it stops when the last of them closes. A URL declared with port
|
|
133
427
|
`0` gets its own port in each session, and so its own process. A shared
|
|
@@ -137,80 +431,6 @@ and passes its id to every `tools`, `call`, and `close_session`.
|
|
|
137
431
|
target per device, each naming its `device`, and open each session on its
|
|
138
432
|
own target.
|
|
139
433
|
|
|
140
|
-
[Bug bashes](/bug-bash) use this: every verifying subagent drives its own
|
|
141
|
-
session.
|
|
142
|
-
|
|
143
|
-
### The catalog
|
|
144
|
-
|
|
145
|
-
What `call` can run depends on the session's target:
|
|
146
|
-
|
|
147
|
-
| Catalog tool | Arguments | Result |
|
|
148
|
-
| --- | --- | --- |
|
|
149
|
-
| `observe` | none | A fresh observation, one node per line as `#id role "name" ...`, with the current path and revision. A link's `href` is its path when it is on the app URL's origin, else origin and path; `mailto:`, `tel:`, and `blob:` targets keep their scheme. `…` marks what is left out: `?…` a query, `#…` a fragment, `data:…` or `javascript:…` an inline payload, a trailing `…` a target cut at 256 characters. |
|
|
150
|
-
| `tap`, `double_tap`, `long_press`, `right_click`, `hover`, `type`, `press`, `select`, `check`, `scroll`, `scroll_to`, `drag`, `upload`, `navigate`, `back`, `type_secret` | A node id from the newest observation, plus the value, key, `checked` state, direction, drop target, project-relative `files`, URL, or secret name | The action's summary and what changed on screen. A verb the engine does not declare is not in the catalog. `upload` refuses a path outside the project, a hidden file, or a file under a hidden directory with `POLICY_DENIED`. `type_secret` is listed when the config declares `credentials` or `secrets`; the plaintext never reaches the agent. |
|
|
151
|
-
| `dismiss_keyboard` | none | Hides the on-screen keyboard. Listed when the engine's keyboard declares `dismiss`, as the device engines do. |
|
|
152
|
-
| `locate` | Exactly one of `role` (with optional `name`), `text`, `label`, `placeholder`, `testId`; `exact` | How many nodes match, which, and the `screen.*` call to write. Zero or several matches name the error a test would get. A `name` matches the accessible name only; `observe` shows a `status`, `alert`, or `listitem` by its text, so one of those with a name that matches nothing also points at `getByRole(role).filter({ hasText })` and `getByText`. What it shows of a node passes the secret ledger, as `observe` does: a filled secret reads `<secret:name>`. |
|
|
153
|
-
| `screenshot` | none | The masked viewport as an image. From then on every action result carries a fresh screenshot. Once a secret has been filled it answers `PIXEL_TAINTED` instead, for the rest of the session. |
|
|
154
|
-
| `tap_at` | `x`, `y` in the latest screenshot's pixels | Taps that point: a listed control under it by id, otherwise the bare point through the engine's `performAt`; without `performAt` the point must land on a listed control. Listed when the engine declares `tap` or `performAt`. |
|
|
155
|
-
| `hover_at` | `x`, `y` in the latest screenshot's pixels | Hovers that point the same way: a listed control under it by id, otherwise the bare point through `performAt`'s `hover` kind. Listed when the engine declares the `hover` action or pointer kind. |
|
|
156
|
-
| `type_at` | `x`, `y`, `value`; `replace` on an engine with a keyboard | Fills a listed input under the point by id. With a keyboard, anything else is tapped to focus it and typed into, at the caret unless `replace` is set; without one, a point on nothing listed fails. Listed when the engine declares `type` or a keyboard. |
|
|
157
|
-
| `press_at` | `x`, `y`, `key` | Sends the key to a listed control under the point by id; with a keyboard, anything else is tapped to focus it and the key goes through the keyboard. Listed when the engine declares `press` or a keyboard. |
|
|
158
|
-
| `select_at` | `x`, `y`, `value`, the option's visible label | Picks the option in the listed select-like control under the point; nothing listed there fails. Listed when the engine declares `select`. |
|
|
159
|
-
| `start_recording` | `name` (optional; up to 64 letters, digits, `-`, and `_`, starting with a letter or digit) | Starts a video of the app. One recording at a time; a second start reports the one running. Listed when the engine records video. |
|
|
160
|
-
| `stop_recording` | none | Stops the recording and returns the absolute path of each video file. Listed when the engine records video. |
|
|
161
|
-
|
|
162
|
-
The five point tools answer `PIXEL_TAINTED` once a secret has been filled.
|
|
163
|
-
Before the session's first `screenshot` each answers with the line that says
|
|
164
|
-
to take one, at no cost.
|
|
165
|
-
|
|
166
|
-
Project tools in `agents.default.tools` follow in the catalog under
|
|
167
|
-
their own names, scoped to the target's platform. A mobile project that
|
|
168
|
-
passes `mobileTools(...)` also gets `open_app`, `swipe`,
|
|
169
|
-
and `alert`. A project tool named like a built-in fails the config load with `INVALID_CONFIG`. An unknown
|
|
170
|
-
name fails with `UNKNOWN_TOOL` and the list of names.
|
|
171
|
-
|
|
172
|
-
### Recording
|
|
173
|
-
|
|
174
|
-
A session records video only between `start_recording` and
|
|
175
|
-
`stop_recording`, as many times as needed; the configured
|
|
176
|
-
[`video`](/reference/config#video) mode applies to runs, not sessions, and a
|
|
177
|
-
session writes no [trace](/reference/config#trace). Each recording is saved to
|
|
178
|
-
`<output>/videos/<session>/<n>[-<name>].webm` (`.mp4` on mobile, and
|
|
179
|
-
[`output`](/reference/config#output) is `.e2e` by default), where `n`
|
|
180
|
-
counts the session's recordings. A web recording that spans a restart or a
|
|
181
|
-
state reset has one file per page, `-part2`, `-part3`, and so on, in play
|
|
182
|
-
order. A browser or device provider that records the lease itself records
|
|
183
|
-
the session instead: [Kernel](/integrations/kernel) downloads its replay,
|
|
184
|
-
saved here as `<n>[-<name>].mp4` like any other file, and a provider that
|
|
185
|
-
keeps its recordings is listed by URL instead of a file. `close_session`, the idle timeout, and the session
|
|
186
|
-
TTL save a recording still running, and the close result lists its files.
|
|
187
|
-
|
|
188
|
-
Recordings are not masked and are not withheld after a secret was filled:
|
|
189
|
-
they are files for a person to watch, and no tool returns their pixels. A
|
|
190
|
-
secret typed into a plain input while recording is readable in the video,
|
|
191
|
-
and a recording stopped after a secret was filled says so.
|
|
192
|
-
|
|
193
|
-
A session is one attempt on one target. A `call` before any session is
|
|
194
|
-
open fails with `NO_SESSION`. A session closes on `close_session`, after 30
|
|
195
|
-
idle minutes, after 4 hours, when the client disconnects, and on SIGINT or
|
|
196
|
-
SIGTERM. The client disconnects when it closes the server's stdin or dies,
|
|
197
|
-
so the pipes break under the server: every session closes, the app commands
|
|
198
|
-
the sessions started stop, and the server exits.
|
|
199
|
-
|
|
200
|
-
Sessions enforce what tests enforce. Node ids are valid only against the
|
|
201
|
-
newest observation. A secret fills only through `type_secret`; once one has
|
|
202
|
-
been filled, `screenshot` and the point tools (`tap_at`, `hover_at`,
|
|
203
|
-
`type_at`, `press_at`, `select_at`) stay in the catalog but answer
|
|
204
|
-
`PIXEL_TAINTED` for the rest of the session. Every text a tool returns, a node `locate` found,
|
|
205
|
-
an action's result, an `open_session` or `close_session` failure, or a cleanup line, has the
|
|
206
|
-
config's secret values redacted once the config has loaded, as do the server's own log lines. Nothing a
|
|
207
|
-
session does is recorded as a test result or into the replay cache; its
|
|
208
|
-
artifacts land under `<output>/results/mcp/<target>-<attemptId>/`, its videos under
|
|
209
|
-
`<output>/videos/`. A later `e2e run` clears `<output>/results`; the
|
|
210
|
-
videos stay.
|
|
211
|
-
|
|
212
|
-
A queued call cancelled by the client does not execute when its turn arrives.
|
|
213
|
-
|
|
214
434
|
## Resources
|
|
215
435
|
|
|
216
436
|
| URI | Content |
|
|
@@ -220,7 +440,12 @@ A queued call cancelled by the client does not execute when its turn arrives.
|
|
|
220
440
|
|
|
221
441
|
## Exit codes
|
|
222
442
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
443
|
+
| Code | When |
|
|
444
|
+
| --- | --- |
|
|
445
|
+
| `0` | The client disconnects, with or without a session open, or a signal ends it. |
|
|
446
|
+
| `1` | An uncaught error shuts the server down. |
|
|
447
|
+
| `2` | The command line is invalid. |
|
|
448
|
+
| The error's [category code](/reference/cli#exit-codes) | A startup failure. |
|
|
449
|
+
|
|
450
|
+
Config and engine failures come back as tool errors with the same codes the
|
|
451
|
+
CLI prints.
|