@testdriverai/mcp 7.9.103-test โ 7.9.104-test
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/agent/interface.js +7 -1
- package/agent/lib/commands.js +8 -6
- package/agent/lib/system.js +60 -6
- package/docs/docs.json +16 -1
- package/docs/v7/ai/agent.mdx +72 -0
- package/docs/v7/ai/mcp.mdx +228 -0
- package/docs/v7/ai/skills.mdx +73 -0
- package/docs/v7/find.mdx +2 -0
- package/interfaces/cli/commands/init.js +81 -2
- package/lib/init-project.js +57 -28
- package/lib/install-clients.js +470 -0
- package/mcp-server/dist/server.mjs +245 -66
- package/mcp-server/src/server.ts +250 -32
- package/package.json +1 -1
- package/sdk.js +14 -12
package/agent/interface.js
CHANGED
|
@@ -10,7 +10,13 @@ function createCommandDefinitions(agent) {
|
|
|
10
10
|
init: {
|
|
11
11
|
description: "Initialize a new TestDriver project with Vitest SDK examples",
|
|
12
12
|
args: {},
|
|
13
|
-
flags: {
|
|
13
|
+
flags: {
|
|
14
|
+
client: Flags.string({
|
|
15
|
+
description:
|
|
16
|
+
"AI client(s) to install into (comma-separated, or 'all'). e.g. --client claude-code,cursor. Omit for an interactive picker.",
|
|
17
|
+
multiple: false,
|
|
18
|
+
}),
|
|
19
|
+
},
|
|
14
20
|
handler: async () => {
|
|
15
21
|
// This handler is special - it doesn't need an agent instance
|
|
16
22
|
// It just scaffolds files, so it will be handled by the CLI command
|
package/agent/lib/commands.js
CHANGED
|
@@ -302,10 +302,12 @@ const createCommands = (
|
|
|
302
302
|
`๐ assert() threshold: ${threshold} (cache ${threshold < 0 ? "DISABLED" : "ENABLED"}${cacheKey ? `, cacheKey: ${cacheKey.substring(0, 8)}...` : ""})`,
|
|
303
303
|
);
|
|
304
304
|
|
|
305
|
-
// Use v7 endpoint for assert with caching support
|
|
305
|
+
// Use v7 endpoint for assert with caching support.
|
|
306
|
+
// captureScreenImage returns { imageKey } (fast S3-key path) or { image }
|
|
307
|
+
// (base64 fallback) โ see system.captureScreenImage.
|
|
306
308
|
let response = await sdk.req("assert", {
|
|
307
309
|
expect: assertion,
|
|
308
|
-
|
|
310
|
+
...(await system.captureScreenImage()),
|
|
309
311
|
threshold,
|
|
310
312
|
cacheKey,
|
|
311
313
|
os,
|
|
@@ -815,7 +817,7 @@ const createCommands = (
|
|
|
815
817
|
|
|
816
818
|
let response = await sdk.req("find", {
|
|
817
819
|
element: description,
|
|
818
|
-
|
|
820
|
+
...(await system.captureScreenImage()),
|
|
819
821
|
});
|
|
820
822
|
|
|
821
823
|
if (!response || !response.coordinates) {
|
|
@@ -855,7 +857,7 @@ const createCommands = (
|
|
|
855
857
|
|
|
856
858
|
let response = await sdk.req("find", {
|
|
857
859
|
element: description,
|
|
858
|
-
|
|
860
|
+
...(await system.captureScreenImage()),
|
|
859
861
|
});
|
|
860
862
|
|
|
861
863
|
if (!response || !response.coordinates) {
|
|
@@ -1211,7 +1213,7 @@ const createCommands = (
|
|
|
1211
1213
|
while (durationPassed < timeout && !passed) {
|
|
1212
1214
|
const response = await sdk.req("find", {
|
|
1213
1215
|
element: text,
|
|
1214
|
-
|
|
1216
|
+
...(await system.captureScreenImage()),
|
|
1215
1217
|
});
|
|
1216
1218
|
|
|
1217
1219
|
passed = !!(response && response.coordinates);
|
|
@@ -1304,7 +1306,7 @@ const createCommands = (
|
|
|
1304
1306
|
while (scrollDistance <= maxDistance && !passed) {
|
|
1305
1307
|
const response = await sdk.req("find", {
|
|
1306
1308
|
element: text,
|
|
1307
|
-
|
|
1309
|
+
...(await system.captureScreenImage()),
|
|
1308
1310
|
});
|
|
1309
1311
|
|
|
1310
1312
|
passed = !!(response && response.coordinates);
|
package/agent/lib/system.js
CHANGED
|
@@ -53,15 +53,28 @@ const createSystem = (emitter, sandbox, config) => {
|
|
|
53
53
|
return Buffer.from(imageResponse.data).toString("base64");
|
|
54
54
|
};
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
// Capture a screenshot from the runner. Returns the raw runner response,
|
|
57
|
+
// which is one of:
|
|
58
|
+
// { s3Key, width, height } โ runner uploaded to S3 (Ably 64KB limit)
|
|
59
|
+
// { base64 } โ direct/local connection, bytes inline
|
|
60
|
+
const captureRaw = async () => {
|
|
61
|
+
return await sandbox.send({
|
|
62
|
+
type: "system.screenshot",
|
|
63
|
+
});
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const screenshot = async (options, rawResponse) => {
|
|
57
67
|
const MAX_RETRIES = 3;
|
|
58
68
|
let lastError;
|
|
59
69
|
|
|
60
70
|
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
61
71
|
try {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
72
|
+
// Reuse a response captured by the caller (so the key path and the
|
|
73
|
+
// base64 path don't each trigger a separate runner capture); otherwise
|
|
74
|
+
// capture fresh.
|
|
75
|
+
let response = attempt === 0 && rawResponse
|
|
76
|
+
? rawResponse
|
|
77
|
+
: await captureRaw();
|
|
65
78
|
|
|
66
79
|
let base64;
|
|
67
80
|
|
|
@@ -112,7 +125,7 @@ const createSystem = (emitter, sandbox, config) => {
|
|
|
112
125
|
return path.join(os.tmpdir(), `td-${Date.now()}-${randomUUID().slice(0, 8)}-${countImages}.png`);
|
|
113
126
|
};
|
|
114
127
|
|
|
115
|
-
const captureAndResize = async (scale = 1, silent = false, mouse = false) => {
|
|
128
|
+
const captureAndResize = async (scale = 1, silent = false, mouse = false, rawResponse = null) => {
|
|
116
129
|
try {
|
|
117
130
|
if (!silent) {
|
|
118
131
|
emitter.emit(events.screenCapture.start, {
|
|
@@ -125,7 +138,7 @@ const createSystem = (emitter, sandbox, config) => {
|
|
|
125
138
|
let step1 = tmpFilename();
|
|
126
139
|
let step2 = tmpFilename();
|
|
127
140
|
|
|
128
|
-
await screenshot({ filename: step1, format: "png" });
|
|
141
|
+
await screenshot({ filename: step1, format: "png" }, rawResponse);
|
|
129
142
|
|
|
130
143
|
// Load the screenshot image with Jimp
|
|
131
144
|
let image = await Jimp.read(step1);
|
|
@@ -187,6 +200,46 @@ const createSystem = (emitter, sandbox, config) => {
|
|
|
187
200
|
return await captureAndResize(scale, silent, mouse);
|
|
188
201
|
};
|
|
189
202
|
|
|
203
|
+
// Build the image payload to send to the API for a command (find/assert/etc).
|
|
204
|
+
//
|
|
205
|
+
// Fast path: when the runner uploaded the screenshot to S3 and it was already
|
|
206
|
+
// captured at the requested resolution, return { imageKey } so the API reads
|
|
207
|
+
// the bytes straight from S3 by key. This skips the redundant round-trip the
|
|
208
|
+
// base64 path pays per command โ SDK download from S3, Jimp re-encode, then a
|
|
209
|
+
// re-upload on the API side.
|
|
210
|
+
//
|
|
211
|
+
// Slow path (fallback): when bytes are inline (local/direct connection), when
|
|
212
|
+
// a mouse cursor must be composited, when scale != 1, or when the captured
|
|
213
|
+
// size differs from TD_RESOLUTION (so a resize is actually required), fall
|
|
214
|
+
// back to capturing + resizing locally and return { image } (base64).
|
|
215
|
+
const captureScreenImage = async (scale = 1, silent = false, mouse = false) => {
|
|
216
|
+
const raw = await captureRaw();
|
|
217
|
+
|
|
218
|
+
const [targetW, targetH] = config.TD_RESOLUTION || [];
|
|
219
|
+
const canUseKey =
|
|
220
|
+
raw &&
|
|
221
|
+
raw.s3Key &&
|
|
222
|
+
!mouse &&
|
|
223
|
+
scale === 1 &&
|
|
224
|
+
typeof raw.width === "number" &&
|
|
225
|
+
typeof raw.height === "number" &&
|
|
226
|
+
raw.width === targetW &&
|
|
227
|
+
raw.height === targetH;
|
|
228
|
+
|
|
229
|
+
if (canUseKey) {
|
|
230
|
+
if (!silent) {
|
|
231
|
+
emitter.emit(events.screenCapture.start, { scale, silent, display: primaryDisplay });
|
|
232
|
+
emitter.emit(events.screenCapture.end, { scale, silent, display: primaryDisplay });
|
|
233
|
+
}
|
|
234
|
+
return { imageKey: raw.s3Key };
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Fallback: download/resize locally and send base64. Pass the already
|
|
238
|
+
// captured runner response through so we don't capture the screen twice.
|
|
239
|
+
const step2 = await captureAndResize(scale, silent, mouse, raw);
|
|
240
|
+
return { image: fs.readFileSync(step2, "base64") };
|
|
241
|
+
};
|
|
242
|
+
|
|
190
243
|
const platform = () => {
|
|
191
244
|
return "windows";
|
|
192
245
|
};
|
|
@@ -213,6 +266,7 @@ const createSystem = (emitter, sandbox, config) => {
|
|
|
213
266
|
return {
|
|
214
267
|
captureScreenBase64,
|
|
215
268
|
captureScreenPNG,
|
|
269
|
+
captureScreenImage,
|
|
216
270
|
getMousePosition,
|
|
217
271
|
primaryDisplay,
|
|
218
272
|
activeWin,
|
package/docs/docs.json
CHANGED
|
@@ -99,10 +99,25 @@
|
|
|
99
99
|
}
|
|
100
100
|
]
|
|
101
101
|
},
|
|
102
|
+
{
|
|
103
|
+
"group": "AI",
|
|
104
|
+
"icon": "robot",
|
|
105
|
+
"pages": [
|
|
106
|
+
"/v7/ai/agent",
|
|
107
|
+
"/v7/ai/skills",
|
|
108
|
+
"/v7/ai/mcp"
|
|
109
|
+
]
|
|
110
|
+
},
|
|
102
111
|
{
|
|
103
112
|
"group": "Actions",
|
|
104
113
|
"pages": [
|
|
105
|
-
|
|
114
|
+
{
|
|
115
|
+
"group": "ai",
|
|
116
|
+
"tag": "Beta",
|
|
117
|
+
"pages": [
|
|
118
|
+
"/v7/ai"
|
|
119
|
+
]
|
|
120
|
+
},
|
|
106
121
|
"/v7/assert",
|
|
107
122
|
"/v7/captcha",
|
|
108
123
|
"/v7/click",
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Agent"
|
|
3
|
+
sidebarTitle: "Agent"
|
|
4
|
+
description: "The TestDriver test-creator agent that writes and debugs end-to-end tests for you"
|
|
5
|
+
icon: "robot"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
The **TestDriver agent** is an expert test-creator that runs inside your AI client (Claude Code, Cursor, VS Code, and others). It writes, runs, and debugs real end-to-end tests by driving your app the same way a person would โ using AI vision to find elements, click, type, and assert โ through the [TestDriver MCP Server](/v7/ai/mcp).
|
|
11
|
+
|
|
12
|
+
Unlike a chat assistant that only suggests code, the agent works **iteratively against a live sandbox**: it starts a session, performs each action, writes the generated code to your test file, verifies the result with a screenshot, and reruns the test until it passes.
|
|
13
|
+
|
|
14
|
+
## What it does
|
|
15
|
+
|
|
16
|
+
- **Builds tests from scratch** using TestDriver [skills](/v7/ai/skills) and best practices.
|
|
17
|
+
- **Drives a live sandbox** via MCP tools (`session_start`, `find`, `click`, `type`, `assert`, `check`, โฆ), getting a screenshot and generated code after every action.
|
|
18
|
+
- **Writes code immediately** to the test file after each successful step โ never all at once at the end.
|
|
19
|
+
- **Verifies visually** with `check` to confirm each action did what was intended.
|
|
20
|
+
- **Runs the test itself** with `vitest run` and iterates until it passes reliably.
|
|
21
|
+
- **Shares the run report** โ after each run it surfaces the `TESTDRIVER_RUN_URL` so you can watch the recording.
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
The agent is installed automatically by `testdriverai init`, alongside the [skills](/v7/ai/skills) and the [MCP server](/v7/ai/mcp):
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npx testdriverai init
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
During init you'll be asked which AI client(s) to install into. The agent is written to the location each client expects:
|
|
32
|
+
|
|
33
|
+
| Client | Agent location |
|
|
34
|
+
| --- | --- |
|
|
35
|
+
| Claude Code | `.claude/agents/testdriver.md` |
|
|
36
|
+
| VS Code (Copilot) | `.github/agents/testdriver.agent.md` |
|
|
37
|
+
| Cursor | `.cursor/rules/testdriver.mdc` |
|
|
38
|
+
| Windsurf | `.windsurf/rules/testdriver.md` |
|
|
39
|
+
| Codex | `AGENTS.md` |
|
|
40
|
+
| Zed | `.rules` |
|
|
41
|
+
|
|
42
|
+
To install for a specific client without the interactive picker:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npx testdriverai init --client claude-code
|
|
46
|
+
# or several at once
|
|
47
|
+
npx testdriverai init --client claude-code,cursor,vscode
|
|
48
|
+
# or everything
|
|
49
|
+
npx testdriverai init --client all
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
See the [MCP Server](/v7/ai/mcp) page for the full client matrix, including web-based clients (Lovable, Replit, v0) that require a few manual steps.
|
|
53
|
+
|
|
54
|
+
## Prerequisites
|
|
55
|
+
|
|
56
|
+
You need a TestDriver API key. Create one at [console.testdriver.ai/team](https://console.testdriver.ai/team) and `init` will save it to `.env` as `TD_API_KEY`.
|
|
57
|
+
|
|
58
|
+
## Using the agent
|
|
59
|
+
|
|
60
|
+
Once installed, invoke it from your client's chat:
|
|
61
|
+
|
|
62
|
+
```text
|
|
63
|
+
@testdriver write a test that logs in and verifies the dashboard loads
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The agent will spin up a sandbox, perform the steps live, write them into a test file under `tests/`, and run it for you.
|
|
67
|
+
|
|
68
|
+
## Related
|
|
69
|
+
|
|
70
|
+
- [Skills](/v7/ai/skills) โ the building blocks the agent composes tests from
|
|
71
|
+
- [MCP Server](/v7/ai/mcp) โ the tools the agent uses to drive your app
|
|
72
|
+
- [Generating tests](/v7/generating-tests) โ how test generation works end-to-end
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "MCP Server"
|
|
3
|
+
sidebarTitle: "MCP Server"
|
|
4
|
+
description: "Install the TestDriver MCP server in any AI client โ Claude Code, Cursor, VS Code, Windsurf, Codex, Zed, and more"
|
|
5
|
+
icon: "plug"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
The **TestDriver MCP server** exposes TestDriver's computer-use tools โ `session_start`, `find`, `click`, `type`, `assert`, `check`, `screenshot`, and more โ over the [Model Context Protocol](https://modelcontextprotocol.io). Any MCP-capable AI client can use it to drive a live sandbox and write real end-to-end tests.
|
|
11
|
+
|
|
12
|
+
It runs as a local stdio process:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npx -p testdriverai testdriverai-mcp
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
and authenticates with your `TD_API_KEY` (from [console.testdriver.ai/team](https://console.testdriver.ai/team)).
|
|
19
|
+
|
|
20
|
+
## Quick install (recommended)
|
|
21
|
+
|
|
22
|
+
`testdriverai init` wires up the MCP server, the [agent](/v7/ai/agent), and the [skills](/v7/ai/skills) for you, writing each client's config in the exact format and location it expects:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# interactive โ pick your client(s)
|
|
26
|
+
npx testdriverai init
|
|
27
|
+
|
|
28
|
+
# one client
|
|
29
|
+
npx testdriverai init --client claude-code
|
|
30
|
+
|
|
31
|
+
# several
|
|
32
|
+
npx testdriverai init --client claude-code,cursor,vscode
|
|
33
|
+
|
|
34
|
+
# everything
|
|
35
|
+
npx testdriverai init --client all
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
<Info>
|
|
39
|
+
`init` detects clients already present in your project and pre-selects them in the picker. Re-running `init` is safe โ it merges the TestDriver entry into existing config without overwriting your other servers.
|
|
40
|
+
</Info>
|
|
41
|
+
|
|
42
|
+
## Client support matrix
|
|
43
|
+
|
|
44
|
+
| Client | Auto-install | MCP config file | Config key |
|
|
45
|
+
| --- | --- | --- | --- |
|
|
46
|
+
| Claude Code | โ
| `.mcp.json` | `mcpServers` |
|
|
47
|
+
| Claude Desktop | โ
| OS-specific (see below) | `mcpServers` |
|
|
48
|
+
| Cursor | โ
| `.cursor/mcp.json` | `mcpServers` |
|
|
49
|
+
| VS Code (Copilot) | โ
| `.vscode/mcp.json` | `servers` |
|
|
50
|
+
| Windsurf | โ
| `~/.codeium/windsurf/mcp_config.json` | `mcpServers` |
|
|
51
|
+
| Codex | โ
| `~/.codex/config.toml` | `[mcp_servers]` |
|
|
52
|
+
| Zed | โ
| `.zed/settings.json` | `context_servers` |
|
|
53
|
+
| Lovable | โ๏ธ partial | GitHub `AGENTS.md` + UI | โ |
|
|
54
|
+
| Replit | โ๏ธ partial | `replit.md` + UI | โ |
|
|
55
|
+
| v0 (Vercel) | ๐ manual | web UI only | โ |
|
|
56
|
+
|
|
57
|
+
<Note>
|
|
58
|
+
Each client uses a **different top-level key** for MCP servers. The most common mistake when configuring by hand is using `mcpServers` for VS Code (it wants `servers`), Codex (TOML `[mcp_servers]`), or Zed (`context_servers`).
|
|
59
|
+
</Note>
|
|
60
|
+
|
|
61
|
+
## Manual installation
|
|
62
|
+
|
|
63
|
+
If you'd rather configure by hand, use the snippets below. The stdio command is the same everywhere; only the wrapper key and file location change.
|
|
64
|
+
|
|
65
|
+
<Tabs>
|
|
66
|
+
<Tab title="Claude Code">
|
|
67
|
+
Add to `.mcp.json` at your project root (or `~/.claude.json` for all projects):
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"mcpServers": {
|
|
72
|
+
"testdriver": {
|
|
73
|
+
"type": "stdio",
|
|
74
|
+
"command": "npx",
|
|
75
|
+
"args": ["-p", "testdriverai", "testdriverai-mcp"],
|
|
76
|
+
"env": { "TD_API_KEY": "${TD_API_KEY}" }
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
</Tab>
|
|
82
|
+
|
|
83
|
+
<Tab title="Claude Desktop">
|
|
84
|
+
Edit the Claude Desktop config file:
|
|
85
|
+
|
|
86
|
+
- **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
87
|
+
- **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
|
|
88
|
+
- **Linux:** `~/.config/Claude/claude_desktop_config.json`
|
|
89
|
+
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"mcpServers": {
|
|
93
|
+
"testdriver": {
|
|
94
|
+
"command": "npx",
|
|
95
|
+
"args": ["-p", "testdriverai", "testdriverai-mcp"],
|
|
96
|
+
"env": { "TD_API_KEY": "your_api_key" }
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Restart Claude Desktop after saving.
|
|
103
|
+
</Tab>
|
|
104
|
+
|
|
105
|
+
<Tab title="Cursor">
|
|
106
|
+
Add to `.cursor/mcp.json` (project) or `~/.cursor/mcp.json` (global):
|
|
107
|
+
|
|
108
|
+
```json
|
|
109
|
+
{
|
|
110
|
+
"mcpServers": {
|
|
111
|
+
"testdriver": {
|
|
112
|
+
"type": "stdio",
|
|
113
|
+
"command": "npx",
|
|
114
|
+
"args": ["-p", "testdriverai", "testdriverai-mcp"],
|
|
115
|
+
"env": { "TD_API_KEY": "${TD_API_KEY}" }
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
</Tab>
|
|
121
|
+
|
|
122
|
+
<Tab title="VS Code">
|
|
123
|
+
Add to `.vscode/mcp.json`. VS Code uses the `servers` key and an `inputs` prompt for secrets:
|
|
124
|
+
|
|
125
|
+
```json
|
|
126
|
+
{
|
|
127
|
+
"servers": {
|
|
128
|
+
"testdriver": {
|
|
129
|
+
"type": "stdio",
|
|
130
|
+
"command": "npx",
|
|
131
|
+
"args": ["-p", "testdriverai", "testdriverai-mcp"],
|
|
132
|
+
"env": { "TD_API_KEY": "${input:testdriver-api-key}" }
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
"inputs": [
|
|
136
|
+
{
|
|
137
|
+
"type": "promptString",
|
|
138
|
+
"id": "testdriver-api-key",
|
|
139
|
+
"description": "TestDriver API Key From https://console.testdriver.ai/team",
|
|
140
|
+
"password": true
|
|
141
|
+
}
|
|
142
|
+
]
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
</Tab>
|
|
146
|
+
|
|
147
|
+
<Tab title="Windsurf">
|
|
148
|
+
Windsurf reads MCP config globally. Add to `~/.codeium/windsurf/mcp_config.json`:
|
|
149
|
+
|
|
150
|
+
```json
|
|
151
|
+
{
|
|
152
|
+
"mcpServers": {
|
|
153
|
+
"testdriver": {
|
|
154
|
+
"command": "npx",
|
|
155
|
+
"args": ["-p", "testdriverai", "testdriverai-mcp"],
|
|
156
|
+
"env": { "TD_API_KEY": "${TD_API_KEY}" }
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
</Tab>
|
|
162
|
+
|
|
163
|
+
<Tab title="Codex">
|
|
164
|
+
Codex uses TOML. Add to `~/.codex/config.toml`:
|
|
165
|
+
|
|
166
|
+
```toml
|
|
167
|
+
[mcp_servers.testdriver]
|
|
168
|
+
command = "npx"
|
|
169
|
+
args = ["-p", "testdriverai", "testdriverai-mcp"]
|
|
170
|
+
env = { TD_API_KEY = "${TD_API_KEY}" }
|
|
171
|
+
```
|
|
172
|
+
</Tab>
|
|
173
|
+
|
|
174
|
+
<Tab title="Zed">
|
|
175
|
+
Zed calls them "context servers". Add to `.zed/settings.json` (project) or `~/.config/zed/settings.json` (global):
|
|
176
|
+
|
|
177
|
+
```json
|
|
178
|
+
{
|
|
179
|
+
"context_servers": {
|
|
180
|
+
"testdriver": {
|
|
181
|
+
"command": "npx",
|
|
182
|
+
"args": ["-p", "testdriverai", "testdriverai-mcp"],
|
|
183
|
+
"env": { "TD_API_KEY": "${TD_API_KEY}" }
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
</Tab>
|
|
189
|
+
</Tabs>
|
|
190
|
+
|
|
191
|
+
## Web-based clients
|
|
192
|
+
|
|
193
|
+
Lovable, Replit, and v0 run in the browser, so the MCP server can't be launched as a local process. Configure them through each product's UI.
|
|
194
|
+
|
|
195
|
+
<AccordionGroup>
|
|
196
|
+
<Accordion title="Lovable">
|
|
197
|
+
1. Connect your GitHub repo and run `npx testdriverai init --client lovable` โ this writes `AGENTS.md` and the skills into the repo so Lovable's agent picks them up.
|
|
198
|
+
2. In Lovable, open **Settings โ MCP** and add the TestDriver server.
|
|
199
|
+
</Accordion>
|
|
200
|
+
|
|
201
|
+
<Accordion title="Replit">
|
|
202
|
+
1. Run `npx testdriverai init --client replit` to write `replit.md` with the TestDriver agent guidance.
|
|
203
|
+
2. In Replit, open **Tools โ Integrations โ MCP** and add a custom MCP server.
|
|
204
|
+
</Accordion>
|
|
205
|
+
|
|
206
|
+
<Accordion title="v0 (Vercel)">
|
|
207
|
+
v0 is fully UI-driven and does not read repo files.
|
|
208
|
+
|
|
209
|
+
1. Open **[v0.app/chat/settings/mcp-connections](https://v0.app/chat/settings/mcp-connections)** and add the TestDriver MCP connection.
|
|
210
|
+
2. Paste the agent guidance into **Instructions** (the **+** in the prompt bar).
|
|
211
|
+
</Accordion>
|
|
212
|
+
</AccordionGroup>
|
|
213
|
+
|
|
214
|
+
## Verifying the install
|
|
215
|
+
|
|
216
|
+
After installing, open your client's chat and ask the agent to write a test:
|
|
217
|
+
|
|
218
|
+
```text
|
|
219
|
+
@testdriver write a test that opens the homepage and asserts the title
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
If the MCP server is wired up correctly, the agent will start a session and you'll see screenshots come back as it works. If tools don't appear, check that `TD_API_KEY` is set and restart the client.
|
|
223
|
+
|
|
224
|
+
## Related
|
|
225
|
+
|
|
226
|
+
- [Agent](/v7/ai/agent) โ the test-creator that uses these tools
|
|
227
|
+
- [Skills](/v7/ai/skills) โ instruction files describing each tool
|
|
228
|
+
- [CI/CD](/v7/ci-cd) โ running the generated tests in your pipeline
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Skills"
|
|
3
|
+
sidebarTitle: "Skills"
|
|
4
|
+
description: "Composable instruction files that teach any AI client how to use TestDriver"
|
|
5
|
+
icon: "puzzle-piece"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
**Skills** are small, focused instruction files โ one per TestDriver capability โ that teach your AI client exactly how to use each part of the TestDriver SDK and MCP tools. They follow the [Anthropic `SKILL.md` format](https://code.claude.com/docs/en/skills): a folder per skill, each containing a `SKILL.md` with YAML frontmatter and a markdown body.
|
|
11
|
+
|
|
12
|
+
There are **106 skills**, generated directly from the TestDriver documentation, covering every action and concept: `find`, `click`, `type`, `assert`, `check`, `scroll`, `press-keys`, `provision`, caching, secrets, CI/CD, and more.
|
|
13
|
+
|
|
14
|
+
```text
|
|
15
|
+
.claude/skills/
|
|
16
|
+
โโโ testdriver-click/
|
|
17
|
+
โ โโโ SKILL.md
|
|
18
|
+
โโโ testdriver-find/
|
|
19
|
+
โ โโโ SKILL.md
|
|
20
|
+
โโโ testdriver-assert/
|
|
21
|
+
โ โโโ SKILL.md
|
|
22
|
+
โโโ โฆ (103 more)
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Each `SKILL.md` looks like:
|
|
26
|
+
|
|
27
|
+
```markdown
|
|
28
|
+
---
|
|
29
|
+
name: testdriver:click
|
|
30
|
+
description: Click at specific coordinates or on elements
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Element Click
|
|
34
|
+
|
|
35
|
+
When called on an Element object, clicks on the located element.
|
|
36
|
+
โฆ
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Why skills
|
|
40
|
+
|
|
41
|
+
The [agent](/v7/ai/agent) is general; skills are specific. When the agent needs to perform an action โ say, locate an element โ it pulls in the `testdriver:find` skill, which contains the exact syntax, options, return shape, and gotchas for that one method. This keeps the agent accurate without bloating its base prompt, and lets clients load only the skills relevant to the current step.
|
|
42
|
+
|
|
43
|
+
## Installation
|
|
44
|
+
|
|
45
|
+
Skills are installed by `testdriverai init` along with the [agent](/v7/ai/agent) and [MCP server](/v7/ai/mcp):
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npx testdriverai init
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
They're written to the skills directory each client expects:
|
|
52
|
+
|
|
53
|
+
| Client | Skills location |
|
|
54
|
+
| --- | --- |
|
|
55
|
+
| Claude Code | `.claude/skills/<name>/SKILL.md` |
|
|
56
|
+
| Zed | `.agents/skills/<name>/SKILL.md` |
|
|
57
|
+
| Codex | referenced from `AGENTS.md` |
|
|
58
|
+
| VS Code ยท Cursor ยท Windsurf | folded into the agent rules/instructions |
|
|
59
|
+
|
|
60
|
+
Clients without a native skills concept (Cursor, VS Code, Windsurf) still get the agent definition, which references the same guidance inline.
|
|
61
|
+
|
|
62
|
+
## Authoring & regenerating
|
|
63
|
+
|
|
64
|
+
Skills are **generated, not hand-edited** โ each is built from a `.mdx` page in the docs. Do not edit `SKILL.md` files directly (they carry a `DO NOT EDIT` marker). To change a skill, edit the corresponding documentation page and regenerate:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
node docs/_scripts/generate-skills.js
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Related
|
|
71
|
+
|
|
72
|
+
- [Agent](/v7/ai/agent) โ composes skills into working tests
|
|
73
|
+
- [MCP Server](/v7/ai/mcp) โ the tools the skills describe how to use
|
package/docs/v7/find.mdx
CHANGED
|
@@ -402,6 +402,8 @@ await element.click();
|
|
|
402
402
|
|
|
403
403
|
## Cache Options
|
|
404
404
|
|
|
405
|
+
When a test completes successfully, the result of each `find()` is cached. On later runs, TestDriver reuses the cached match instead of making a fresh AI call, which significantly speeds up locating the same element. The cache lives in your [dashboard](https://console.testdriver.ai/cache) and is shared across runs โ see the [Cache](/v7/cache) page for how matching, thresholds, and invalidation work.
|
|
406
|
+
|
|
405
407
|
Control caching behavior to optimize performance, especially when using dynamic variables in prompts.
|
|
406
408
|
|
|
407
409
|
### Custom Cache Key
|