@testdriverai/agent 7.11.229-test → 7.11.231-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.
Files changed (100) hide show
  1. package/docs/_data/examples-manifest.json +16 -16
  2. package/docs/_scripts/generate-examples.js +38 -45
  3. package/docs/agent.mdx +174 -93
  4. package/docs/assert.mdx +73 -269
  5. package/docs/aws-setup.mdx +196 -314
  6. package/docs/bring-your-own-machine.mdx +139 -206
  7. package/docs/cache.mdx +144 -151
  8. package/docs/caching.mdx +99 -89
  9. package/docs/captcha.mdx +126 -111
  10. package/docs/ci-cd.mdx +154 -543
  11. package/docs/click.mdx +74 -229
  12. package/docs/client.mdx +177 -231
  13. package/docs/copilot/auto-healing.mdx +86 -370
  14. package/docs/copilot/running-tests.mdx +121 -930
  15. package/docs/customizing-devices.mdx +74 -251
  16. package/docs/dashcam.mdx +72 -405
  17. package/docs/debugging-with-screenshots.mdx +105 -371
  18. package/docs/docs.json +75 -18
  19. package/docs/double-click.mdx +48 -68
  20. package/docs/errors.mdx +102 -176
  21. package/docs/events.mdx +170 -327
  22. package/docs/examples/{hover-text-with-description.mdx → add-to-cart.mdx} +30 -35
  23. package/docs/examples/assert.mdx +28 -41
  24. package/docs/examples/click-button.mdx +71 -0
  25. package/docs/examples/{hover-image.mdx → click-icon.mdx} +32 -28
  26. package/docs/examples/element-not-found.mdx +27 -26
  27. package/docs/examples/findall-coffee-icons.mdx +28 -32
  28. package/docs/examples/installer.mdx +61 -25
  29. package/docs/examples/launch-vscode-linux.mdx +28 -51
  30. package/docs/examples/navigate-and-scroll.mdx +82 -0
  31. package/docs/examples/parse.mdx +24 -28
  32. package/docs/examples/press-keys.mdx +28 -25
  33. package/docs/examples/scroll.mdx +25 -27
  34. package/docs/examples/type.mdx +26 -21
  35. package/docs/exec.mdx +92 -256
  36. package/docs/extract.mdx +69 -170
  37. package/docs/find.mdx +140 -736
  38. package/docs/focus-application.mdx +69 -246
  39. package/docs/generating-tests.mdx +103 -137
  40. package/docs/guide/best-practices-polling.mdx +65 -141
  41. package/docs/hosted.mdx +80 -144
  42. package/docs/hover.mdx +62 -235
  43. package/docs/locating-elements.mdx +74 -369
  44. package/docs/making-assertions.mdx +116 -74
  45. package/docs/mcp-setup.mdx +80 -63
  46. package/docs/mouse-down.mdx +48 -125
  47. package/docs/mouse-up.mdx +47 -129
  48. package/docs/options.mdx +75 -48
  49. package/docs/parse.mdx +95 -184
  50. package/docs/performing-actions.mdx +104 -94
  51. package/docs/press-keys.mdx +59 -299
  52. package/docs/provision.mdx +137 -182
  53. package/docs/quickstart-claude.mdx +85 -0
  54. package/docs/quickstart-github.mdx +62 -32
  55. package/docs/quickstart-manual.mdx +60 -33
  56. package/docs/quickstart-vscode.mdx +97 -0
  57. package/docs/quickstart.mdx +25 -9
  58. package/docs/redraw.mdx +101 -165
  59. package/docs/reusable-code.mdx +97 -200
  60. package/docs/right-click.mdx +47 -89
  61. package/docs/screenshot.mdx +47 -201
  62. package/docs/screenshots.mdx +87 -137
  63. package/docs/scroll.mdx +71 -154
  64. package/docs/secrets.mdx +96 -91
  65. package/docs/self-hosted.mdx +83 -142
  66. package/docs/snippets/enterprise.mdx +15 -0
  67. package/docs/styles.css +68 -0
  68. package/docs/test-results-json.mdx +167 -204
  69. package/docs/type.mdx +56 -304
  70. package/docs/variables.mdx +121 -81
  71. package/docs/wait.mdx +33 -31
  72. package/docs/what-is-testdriver.mdx +80 -32
  73. package/examples/{hover-text-with-description.test.mjs → add-to-cart.test.mjs} +4 -5
  74. package/examples/assert.test.mjs +3 -20
  75. package/examples/{hover-text.test.mjs → click-button.test.mjs} +3 -4
  76. package/examples/{hover-image.test.mjs → click-icon.test.mjs} +5 -6
  77. package/examples/element-not-found.test.mjs +3 -5
  78. package/examples/findall-coffee-icons.test.mjs +3 -3
  79. package/examples/installer.test.mjs +1 -1
  80. package/examples/launch-vscode-linux.test.mjs +6 -33
  81. package/examples/{scroll-keyboard.test.mjs → navigate-and-scroll.test.mjs} +4 -5
  82. package/examples/parse.test.mjs +1 -2
  83. package/examples/press-keys.test.mjs +4 -4
  84. package/examples/scroll.test.mjs +3 -6
  85. package/examples/type.test.mjs +5 -2
  86. package/lib/provision.js +6 -2
  87. package/package.json +1 -1
  88. package/sdk.d.ts +4 -2
  89. package/sdk.js +1 -1
  90. package/docs/claude-mcp-plugin.mdx +0 -244
  91. package/docs/elements.mdx +0 -606
  92. package/docs/examples/ai.mdx +0 -78
  93. package/docs/examples/captcha-api.mdx +0 -92
  94. package/docs/examples/chrome-extension.mdx +0 -156
  95. package/docs/examples/drag-and-drop.mdx +0 -100
  96. package/docs/examples/formatted-logging.mdx +0 -74
  97. package/docs/examples/hover-text.mdx +0 -70
  98. package/docs/examples/match-image.mdx +0 -97
  99. package/docs/examples/scroll-keyboard.mdx +0 -80
  100. package/docs/examples/scroll-until-text.mdx +0 -109
@@ -1,109 +1,151 @@
1
1
  ---
2
- title: "Verifying Results"
3
- sidebarTitle: "Verifying Results"
4
- description: "Locate elements and verify app state with AI-powered assertions"
2
+ title: "Make assertions"
3
+ sidebarTitle: "Verifying results"
4
+ description: "Check that your app reached the right state, in plain language or with expect()."
5
5
  icon: "circle-check"
6
6
  ---
7
7
 
8
- After a test runs, make sure that the app did the correct thing. Validation has two parts: to find the elements that you want to check, and to make assertions about the state of your app. TestDriver uses the AI as a judge. It returns a boolean and the reason. This shows if your app is in the correct state.
8
+ An assertion checks that your app did the right thing after a step. TestDriver gives you two ways to check:
9
9
 
10
- ## Locating Elements
10
+ - [`assert()`](/assert) asks the AI whether a statement about the screen is true, and fails the test if it isn't.
11
+ - [`extract()`](/extract) reads a value off the screen, so you can compare it yourself with `expect()`.
11
12
 
12
- ### Locating Single Elements
13
+ ```javascript
14
+ await testdriver.find('Add to cart button').click();
15
+ await testdriver.assert('the cart icon shows 1 item');
16
+ ```
13
17
 
14
- Use natural language to describe elements. A description must be specific enough to find the element. But it must not be so specific that it breaks with small UI changes. For example:
18
+ ## Check the screen with assert()
15
19
 
16
- ```javascript
17
- await testdriver.find('email input field');
18
- await testdriver.find('first product card in the grid');
19
- await testdriver.find('dropdown menu labeled "Country"');
20
+ Pass `assert()` a statement that should be true. The AI looks at the current screen and decides.
21
+
22
+ ```javascript tests/login.test.mjs
23
+ import { describe, it } from 'vitest';
24
+ import { TestDriver } from 'testdriverai/vitest/hooks';
25
+
26
+ describe('Login', () => {
27
+ it('signs in with valid credentials', async (context) => {
28
+ const testdriver = TestDriver(context);
29
+ await testdriver.provision.chrome({ url: 'https://testdriver-sandbox.vercel.app/login' });
30
+
31
+ // The sandbox login page shows its test password on screen
32
+ const password = await testdriver.extract('the password shown on the login page');
33
+
34
+ await testdriver.find('Username input field').click();
35
+ await testdriver.type('standard_user');
36
+ await testdriver.find('Password input field').click();
37
+ await testdriver.type(password, { secret: true });
38
+ await testdriver.find('Sign in button below the password field').click();
39
+
40
+ await testdriver.assert('the product list is shown');
41
+ });
42
+ });
20
43
  ```
21
44
 
22
- <Info>TestDriver caches the found elements for better performance on later calls. Read more about [element caching here](/caching).</Info>
45
+ When the statement is true, `assert()` resolves to `true`. When it's false, `assert()` throws and the test fails at that line. The error message starts with `AI Assertion failed:` and includes the AI's explanation.
23
46
 
24
- ### Debugging Found Elements
47
+ ## Write statements the AI can judge
25
48
 
26
- After TestDriver finds an element, you can look at its properties for debug:
49
+ The AI judges only what's visible on screen right now. Write statements a person could confirm by looking.
50
+
51
+ - **Name the thing and its state.** `'the Submit button is disabled'`, not `'the form is invalid'`.
52
+ - **Quote exact text** when the wording matters: `'the banner says "Order placed"'`.
53
+ - **Check one thing per assertion.** Two short assertions tell you exactly which part failed.
54
+ - **Assert outcomes, not steps.** Check what the user should see after an action, such as a confirmation message.
27
55
 
28
56
  ```javascript
29
- const button = await testdriver.find('submit button');
30
- console.log(button);
57
+ // Visibility
58
+ await testdriver.assert('the login form is visible');
59
+ await testdriver.assert('the loading spinner is not visible');
60
+
61
+ // Exact text
62
+ await testdriver.assert('the error message says "Invalid email address"');
63
+
64
+ // State of a control
65
+ await testdriver.assert('the "Remember me" checkbox is checked');
66
+ await testdriver.assert('the Country dropdown shows "United States"');
31
67
  ```
32
68
 
33
- This outputs all element properties:
69
+ ## Compare exact values with extract()
70
+
71
+ Use `extract()` when you need the value itself, for example a number to compare or reuse. It returns a string, so convert it before comparing numbers:
34
72
 
35
73
  ```javascript
36
- {
37
- description: 'submit button',
38
- found: true,
39
- x: 150,
40
- y: 300,
41
- coordinates: { x: 150, y: 300, centerX: 200, centerY: 320 },
42
- threshold: 0.8,
43
- confidence: 0.95,
44
- similarity: 0.92,
45
- selector: 'button[type="submit"]',
46
- cache: {
47
- hit: true,
48
- strategy: 'pixel-diff',
49
- createdAt: '2025-01-15T10:30:00Z',
50
- diffPercent: 0.02,
51
- imageUrl: 'https://...'
52
- }
53
- }
74
+ import { expect } from 'vitest';
75
+
76
+ const itemCount = await testdriver.extract('the number of items shown on the cart icon');
77
+ expect(Number(itemCount)).toBe(1);
54
78
  ```
55
79
 
56
- ### Working with Multiple Elements
80
+ Prefer `assert()` for yes-or-no checks. Reach for `extract()` when the test needs a value it can't know in advance, such as an order number:
57
81
 
58
- Find and interact with multiple elements:
82
+ ```javascript
83
+ const orderNumber = await testdriver.extract('the order confirmation number');
84
+ expect(orderNumber).toMatch(/^A-\d+$/);
85
+ ```
86
+
87
+ ## Check state before and after an action
88
+
89
+ An assertion after an action only proves the end state. Assert the starting state too, so the test catches an action that did nothing:
59
90
 
60
91
  ```javascript
61
- // Find all matching elements
62
- const products = await testdriver.findAll('product card');
63
- console.log(`Found ${products.length} products`);
92
+ await testdriver.assert('the cart is empty');
64
93
 
65
- // Interact with each
66
- for (const product of products) {
67
- const title = await product.find('title text');
68
- console.log('Product:', title.text);
94
+ await testdriver.find('Add to cart button under the TestDriver Hat').click();
95
+
96
+ await testdriver.assert('the cart icon shows 1 item');
97
+ ```
98
+
99
+ To check that a value changed, extract it on both sides of the action:
100
+
101
+ ```javascript
102
+ const before = await testdriver.extract('the cart total');
69
103
 
70
- await product.find('add to cart button').click();
71
- }
104
+ await testdriver.find('Add to cart button under the TestDriver Hat').click();
72
105
 
73
- // Or find specific element
74
- const firstProduct = products[0];
75
- await firstProduct.click();
106
+ const after = await testdriver.extract('the cart total');
107
+ expect(after).not.toBe(before);
76
108
  ```
77
109
 
78
- ## Making Assertions
110
+ ## Wait before you assert
79
111
 
80
- Use AI-powered assertions to verify application state. TestDriver acts as a judge: it evaluates your natural-language statement against the current state of the app and returns a boolean plus reasoning explaining the verdict.
112
+ `assert()` checks the screen once, at the moment you call it. If the result takes time to appear, wait for it with [`find()`](/find) first:
81
113
 
82
114
  ```javascript
83
- // Verify visibility
84
- await testdriver.assert('login page is displayed');
85
- await testdriver.assert('submit button is visible');
86
- await testdriver.assert('loading spinner is not visible');
87
-
88
- // Verify content
89
- await testdriver.assert('page title is "Welcome"');
90
- await testdriver.assert('success message says "Account created"');
91
- await testdriver.assert('error message contains "Invalid email"');
92
-
93
- // Verify state
94
- await testdriver.assert('checkbox is checked');
95
- await testdriver.assert('dropdown shows "United States"');
96
- await testdriver.assert('button is disabled');
97
-
98
- // Verify visual appearance
99
- await testdriver.assert('submit button is blue');
100
- await testdriver.assert('form has red border');
115
+ await testdriver.find('Place order button').click();
116
+
117
+ // Keep looking for up to 30 seconds while the confirmation page loads
118
+ await testdriver.find('Order confirmation heading', { timeout: 30000 });
119
+ await testdriver.assert('the confirmation shows an order number');
101
120
  ```
102
121
 
103
- <Info>Assertions are not cached and always re-evaluated to ensure accuracy.</Info>
122
+ See [Perform actions](/performing-actions#wait-for-a-changing-ui) for other ways to wait.
123
+
124
+ ## How assertions are cached
125
+
126
+ `assert()` results are [cached](/caching) like `find()` results. When the screen matches a passing earlier run, TestDriver reuses that result instead of asking the AI.
127
+
128
+ `extract()` is never cached. It reads the screen every time.
104
129
 
105
- ## Next
130
+ To always ask the AI for one assertion, pass `threshold: -1`:
131
+
132
+ ```javascript
133
+ await testdriver.assert('the clock shows the current time', { threshold: -1 });
134
+ ```
106
135
 
107
- <Card title="Adapt" icon="arrows-rotate" href="/performing-actions">
108
- Drive your app forward by performing actions on the elements you've located and validated.
109
- </Card>
136
+ ## Next steps
137
+
138
+ <CardGroup cols={2}>
139
+ <Card title="assert()" icon="check-circle" href="/assert">
140
+ Every option, return value, and error.
141
+ </Card>
142
+ <Card title="extract()" icon="wand-magic-sparkles" href="/extract">
143
+ Read values off the screen as strings.
144
+ </Card>
145
+ <Card title="Perform actions" icon="arrow-pointer" href="/performing-actions">
146
+ Click, type, and wait for a changing UI.
147
+ </Card>
148
+ <Card title="Debug with screenshots" icon="bug" href="/debugging-with-screenshots">
149
+ See what the screen showed when an assertion failed.
150
+ </Card>
151
+ </CardGroup>
@@ -1,44 +1,49 @@
1
1
  ---
2
2
  title: "MCP Setup"
3
- sidebarTitle: "MCP Setup"
4
- description: "Connect any AI client to TestDriver by adding one URL."
3
+ sidebarTitle: "MCP setup"
4
+ description: "Connect Claude, Cursor, VS Code, ChatGPT, or any MCP client to TestDriver with one URL."
5
5
  icon: "plug"
6
6
  ---
7
7
 
8
- TestDriver runs a hosted [Model Context Protocol](https://modelcontextprotocol.io) server. There is nothing to install and no API key to paste. Add one URL to your AI client, sign in through the browser, and the computer-use tools appear in chat.
8
+ Connect your AI client to TestDriver's hosted [Model Context Protocol](https://modelcontextprotocol.io) server, and you can drive a real sandbox from chat.
9
+
10
+ - Open your app.
11
+ - Find elements, click, type, and assert.
12
+ - Get a screenshot after every step.
13
+
14
+ There's nothing to install and no API key to copy. Add one URL and sign in through your browser:
9
15
 
10
16
  ```text
11
17
  https://mcp.testdriver.ai/mcp
12
18
  ```
13
19
 
14
- <Info>
15
- **Prerequisites**
20
+ ## Before you begin
16
21
 
17
- - An MCP-compatible AI client (Claude, Claude Code, VS Code, Cursor, ChatGPT, and others)
18
- - A TestDriver account. [Create one for free](https://console.testdriver.ai/settings). You get 60 device minutes, no credit card required.
19
- </Info>
22
+ - **An MCP client** that supports remote servers, such as Claude, Claude Code, Cursor, VS Code, or ChatGPT.
23
+ - **A TestDriver account.** [Sign up at console.testdriver.ai](https://console.testdriver.ai). The free plan includes 30 testing minutes and needs no credit card.
20
24
 
21
25
  ## Add the server
22
26
 
23
- Most clients accept the URL directly. Pick yours below.
24
-
25
27
  <Tabs>
26
28
  <Tab title="Claude">
29
+
27
30
  1. Open **Settings → Connectors → Add custom connector**.
28
31
  2. Paste `https://mcp.testdriver.ai/mcp`.
29
- 3. Click **Connect** and sign in with TestDriver when the browser opens.
30
- </Tab>
32
+ 3. Click **Connect**, and sign in to TestDriver when the browser opens.
31
33
 
34
+ </Tab>
32
35
  <Tab title="Claude Code">
36
+
33
37
  ```bash
34
38
  claude mcp add --transport http testdriver https://mcp.testdriver.ai/mcp
35
39
  ```
36
40
 
37
- Run `/mcp` in a session to start the browser login and confirm the tools are connected.
38
- </Tab>
41
+ Then run `/mcp` in a Claude Code session to sign in and confirm the tools are connected.
39
42
 
43
+ </Tab>
40
44
  <Tab title="VS Code">
41
- Add this to `.vscode/mcp.json` in your project. VS Code uses the `servers` key:
45
+
46
+ Add this to `.vscode/mcp.json` in your project:
42
47
 
43
48
  ```json .vscode/mcp.json
44
49
  {
@@ -51,11 +56,12 @@ Most clients accept the URL directly. Pick yours below.
51
56
  }
52
57
  ```
53
58
 
54
- VS Code prompts you to authorize the server the first time you use it.
55
- </Tab>
59
+ VS Code asks you to sign in the first time it uses the server.
56
60
 
61
+ </Tab>
57
62
  <Tab title="Cursor">
58
- Add this to `.cursor/mcp.json` (project) or `~/.cursor/mcp.json` (global):
63
+
64
+ Add this to `.cursor/mcp.json` in your project, or to `~/.cursor/mcp.json` for all projects:
59
65
 
60
66
  ```json .cursor/mcp.json
61
67
  {
@@ -66,16 +72,18 @@ Most clients accept the URL directly. Pick yours below.
66
72
  }
67
73
  }
68
74
  ```
69
- </Tab>
70
75
 
76
+ </Tab>
71
77
  <Tab title="ChatGPT">
78
+
72
79
  1. Open **Settings → Connectors → Create**.
73
80
  2. Paste `https://mcp.testdriver.ai/mcp` as the MCP server URL.
74
- 3. Choose OAuth for authentication and sign in with TestDriver.
75
- </Tab>
81
+ 3. Choose **OAuth** for authentication, and sign in to TestDriver.
76
82
 
83
+ </Tab>
77
84
  <Tab title="Other clients">
78
- Any spec-compliant client works. Point it at the URL using the streamable HTTP transport:
85
+
86
+ Any client that supports the Streamable HTTP transport and OAuth sign-in can connect. Point it at the URL:
79
87
 
80
88
  ```json
81
89
  {
@@ -86,58 +94,58 @@ Most clients accept the URL directly. Pick yours below.
86
94
  }
87
95
  }
88
96
  ```
97
+
89
98
  </Tab>
90
99
  </Tabs>
91
100
 
92
- <Note>
93
- Clients disagree on the top-level key. VS Code uses `servers`, most others use `mcpServers`, Zed uses `context_servers`, and Codex uses TOML `[mcp_servers]`. If the tools do not show up, check the key before anything else.
94
- </Note>
95
-
96
101
  ## Sign in
97
102
 
98
- The server speaks OAuth 2.1 and advertises its authorization server via [RFC 9728](https://www.rfc-editor.org/rfc/rfc9728) protected-resource metadata:
103
+ The first time your client connects, it opens a browser window where you sign in to TestDriver.
99
104
 
100
- ```text
101
- https://mcp.testdriver.ai/.well-known/oauth-protected-resource
102
- ```
105
+ Every tool call afterward runs against the team you signed in with. Its usage counts toward that team's plan.
103
106
 
104
- Spec-compliant clients read that metadata, open a browser, and complete the handshake for you. Every tool is scoped to the team you sign in with.
107
+ Clients find the sign-in page automatically. The server uses OAuth 2.1 and publishes its authorization details as [RFC 9728](https://www.rfc-editor.org/rfc/rfc9728) protected-resource metadata at `https://mcp.testdriver.ai/.well-known/oauth-protected-resource`.
105
108
 
106
- ## Verify it works
109
+ ## Try it
107
110
 
108
111
  Ask your client to drive a browser:
109
112
 
110
113
  ```text
111
- Use TestDriver to open example.com and assert the page title is visible.
114
+ Use TestDriver to open example.com and assert that the page heading is visible.
112
115
  ```
113
116
 
114
- The agent starts a sandbox and returns a screenshot with every action. Each connection gets its own isolated sandbox, so multiple people and multiple chats can run tests at the same time without interfering.
117
+ The agent starts a sandbox, opens Chrome, and returns a screenshot after each action.
115
118
 
116
- ## What you get
119
+ Every connection gets its own sandbox. Several people or chats can run at once without affecting each other.
117
120
 
118
- The hosted server exposes the full live tool set:
121
+ ## Available tools
119
122
 
120
- | Tool | Purpose |
123
+ | Tools | What they do |
121
124
  | --- | --- |
122
- | `session_start`, `session_status`, `session_extend` | Start a sandbox, check its health, add more time |
123
- | `find`, `findall`, `find_and_click` | Locate elements by plain-English description |
124
- | `click`, `hover`, `type`, `press_keys`, `scroll` | Perform actions |
125
- | `assert`, `check` | Ask yes/no questions about the screen |
126
- | `screenshot`, `exec`, `wait`, `focus_application` | Capture state, run commands, pause |
125
+ | `session_start`, `session_status`, `session_extend` | Start a sandbox, check how long it has left, and keep it alive longer |
126
+ | `find`, `findall`, `find_and_click` | Locate elements from a plain-language description |
127
+ | `click`, `hover`, `type`, `press_keys`, `scroll` | Act on the screen with the mouse and keyboard |
128
+ | `assert`, `check` | Ask yes-or-no questions about what's on screen |
129
+ | `screenshot`, `wait`, `exec`, `focus_application` | Capture the screen, pause, run a command, or switch apps |
130
+ | `list_test_runs`, `get_test_run_detail` | Look up past test runs and their results |
131
+
132
+ Each action also returns the SDK code for that step. The agent uses it to assemble a runnable [Vitest](https://vitest.dev) test as it works.
127
133
 
128
- Every action also returns the SDK code for that step, so the agent can write a runnable [Vitest](https://vitest.dev) test as it goes.
134
+ To turn that test into a file you run locally and in CI, [set up a project](/quickstart-manual).
129
135
 
130
- ## Local server for CI and automation
136
+ ## Run the server locally
131
137
 
132
- For headless automation, or when you would rather use an API key than a browser login, run the server as a local stdio process:
138
+ Run the server as a local stdio process for headless automation, or to authenticate with an API key instead of a browser sign-in:
133
139
 
134
140
  ```bash
135
141
  npx -p testdriverai testdriverai-mcp
136
142
  ```
137
143
 
138
- It reads `TD_API_KEY` from the environment. Generate a key at [console.testdriver.ai/settings](https://console.testdriver.ai/settings).
144
+ It reads your API key from the `TD_API_KEY` environment variable. Create a key at [console.testdriver.ai/team/api-keys](https://console.testdriver.ai/team/api-keys).
139
145
 
140
- ```json
146
+ Then configure your client to start the server:
147
+
148
+ ```json .mcp.json
141
149
  {
142
150
  "mcpServers": {
143
151
  "testdriver": {
@@ -151,42 +159,51 @@ It reads `TD_API_KEY` from the environment. Generate a key at [console.testdrive
151
159
  ```
152
160
 
153
161
  <Tip>
154
- `npx testdriverai init` writes this config for you, plus the TestDriver agent, skills, an example test, and a GitHub Actions workflow. See [Setting up your workspace](/quickstart-manual).
162
+ `npx testdriverai init` writes this config for your client, along with the TestDriver agent, skills, an example test, and a GitHub Actions workflow. See [Set up a project](/quickstart-manual).
155
163
  </Tip>
156
164
 
157
165
  ## Troubleshooting
158
166
 
159
167
  <AccordionGroup>
160
- <Accordion title="The tools do not appear in my client">
161
- Confirm the URL is exactly `https://mcp.testdriver.ai/mcp`, check that you used the correct top-level config key for your client, and restart the client. Most clients read their MCP config only at startup.
168
+ <Accordion title="The TestDriver tools don't appear in my client">
169
+
170
+ Check that the URL is exactly `https://mcp.testdriver.ai/mcp`. Then restart your client, since most clients read their MCP configuration only at startup.
171
+
162
172
  </Accordion>
173
+ <Accordion title="The browser sign-in never finishes">
174
+
175
+ Your client needs to support OAuth 2.1 with Dynamic Client Registration.
176
+
177
+ Clients that only support local (stdio) servers can't connect to the hosted URL. [Run the server locally](#run-the-server-locally) with an API key instead.
163
178
 
164
- <Accordion title="The browser login never completes">
165
- Your client must support OAuth 2.1 with Dynamic Client Registration. Older clients, and clients that only support stdio servers, cannot connect to the hosted URL. Use the local server with `TD_API_KEY` instead.
166
179
  </Accordion>
180
+ <Accordion title="The sandbox expired in the middle of a session">
181
+
182
+ A sandbox shuts down after 5 minutes with no activity.
183
+
184
+ Ask the agent to call `session_extend` to keep it alive, or `session_start` to get a new one.
167
185
 
168
- <Accordion title="I ran out of device minutes">
169
- Sandbox time is billed per minute. Check your usage and plan at [console.testdriver.ai](https://console.testdriver.ai).
170
186
  </Accordion>
187
+ <Accordion title="I ran out of testing minutes">
188
+
189
+ Sandbox time counts against your plan's testing minutes. Check your usage and upgrade at [console.testdriver.ai/usage](https://console.testdriver.ai/usage).
171
190
 
172
- <Accordion title="The sandbox expired mid-session">
173
- Sessions time out after a period of inactivity. Ask the agent to call `session_extend` before it expires, or `session_start` to get a fresh sandbox.
174
191
  </Accordion>
175
192
  </AccordionGroup>
176
193
 
177
194
  ## Next steps
178
195
 
179
196
  <CardGroup cols={2}>
180
- <Card title="Generating tests" icon="wand-magic-sparkles" href="/generating-tests" arrow horizontal>
181
- Prompting patterns that get the best tests out of the agent.
197
+ <Card title="Generate tests" icon="wand-magic-sparkles" href="/generating-tests">
198
+ Prompts that get the best tests out of the agent.
182
199
  </Card>
183
- <Card title="Setting up your workspace" icon="wrench" href="/quickstart-manual" arrow horizontal>
184
- Add the SDK to a project so generated tests run locally and in CI.
200
+ <Card title="Set up a project" icon="wrench" href="/quickstart-manual">
201
+ Save generated tests and run them locally and in CI.
185
202
  </Card>
186
- <Card title="Walkthrough" icon="map" href="/provision" arrow horizontal>
187
- Provision apps, locate elements, perform actions, and make assertions.
203
+ <Card title="Provision apps" icon="rocket" href="/provision">
204
+ Launch browsers, extensions, and desktop apps in the sandbox.
188
205
  </Card>
189
- <Card title="CI/CD" icon="circle-play" href="/ci-cd" arrow horizontal>
206
+ <Card title="Run in CI/CD" icon="code-branch" href="/ci-cd">
190
207
  Run your tests on every pull request.
191
208
  </Card>
192
209
  </CardGroup>