Skip to content

Commit 60c5619

Browse files
committed
Add automatic browser installation feature
- Automatically detects when browsers are not installed - Runs 'npx playwright install <browser>' automatically on first use - Handles browser installation for chromium, firefox, and webkit - Provides clear error messages if auto-installation fails - Updates README with browser installation information - No breaking changes - fully backward compatible Fixes issue where users get 'Executable doesn't exist' error on first run. Users no longer need to manually run 'npx playwright install' before using the server.
1 parent 961d715 commit 60c5619

2 files changed

Lines changed: 138 additions & 6 deletions

File tree

README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,39 @@ code-insiders --add-mcp '{"name":"playwright","command":"npx","args":["@executea
116116

117117
After installation, the ExecuteAutomation Playwright MCP server will be available for use with your GitHub Copilot agent in VS Code.
118118

119+
## Browser Installation
120+
121+
### Automatic Installation (Recommended)
122+
123+
The Playwright MCP Server **automatically installs browser binaries** when you first use it. When the server detects that a browser is missing, it will:
124+
125+
1. Automatically download and install the required browser (Chromium, Firefox, or WebKit)
126+
2. Display installation progress in the console
127+
3. Retry your request once installation completes
128+
129+
**No manual setup required!** Just start using the server, and it handles browser installation for you.
130+
131+
### Manual Installation (Optional)
132+
133+
If you prefer to install browsers manually or encounter any issues with automatic installation:
134+
135+
```bash
136+
# Install all browsers
137+
npx playwright install
138+
139+
# Or install specific browsers
140+
npx playwright install chromium
141+
npx playwright install firefox
142+
npx playwright install webkit
143+
```
144+
145+
### Browser Storage Location
146+
147+
Browsers are installed to:
148+
- **Windows:** `%USERPROFILE%\AppData\Local\ms-playwright`
149+
- **macOS:** `~/Library/Caches/ms-playwright`
150+
- **Linux:** `~/.cache/ms-playwright`
151+
119152
## Configuration to use Playwright Server
120153

121154
### Standard Mode (stdio)

src/toolHandler.ts

Lines changed: 105 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
44
import { BROWSER_TOOLS, API_TOOLS } from './tools.js';
55
import type { ToolContext } from './tools/common/types.js';
66
import { ActionRecorder } from './tools/codegen/recorder.js';
7+
import { spawn } from 'child_process';
78
import {
89
startCodegenSession,
910
endCodegenSession,
@@ -157,6 +158,64 @@ async function registerConsoleMessage(page) {
157158
});
158159
}
159160

161+
/**
162+
* Attempts to install browsers automatically
163+
*/
164+
async function installBrowsers(browserType: string = 'chromium'): Promise<{ success: boolean; message: string }> {
165+
return new Promise((resolve) => {
166+
console.error(`[Playwright MCP] Attempting to install ${browserType} browser...`);
167+
168+
const installProcess = spawn('npx', ['playwright', 'install', browserType], {
169+
stdio: ['ignore', 'pipe', 'pipe'],
170+
shell: true
171+
});
172+
173+
let output = '';
174+
let errorOutput = '';
175+
176+
installProcess.stdout?.on('data', (data) => {
177+
output += data.toString();
178+
});
179+
180+
installProcess.stderr?.on('data', (data) => {
181+
errorOutput += data.toString();
182+
});
183+
184+
installProcess.on('close', (code) => {
185+
if (code === 0) {
186+
console.error(`[Playwright MCP] Successfully installed ${browserType} browser`);
187+
resolve({
188+
success: true,
189+
message: `Successfully installed ${browserType} browser. Please try your request again.`
190+
});
191+
} else {
192+
console.error(`[Playwright MCP] Failed to install browser: ${errorOutput}`);
193+
resolve({
194+
success: false,
195+
message: `Failed to automatically install ${browserType} browser. Please run: npx playwright install ${browserType}`
196+
});
197+
}
198+
});
199+
200+
installProcess.on('error', (error) => {
201+
console.error(`[Playwright MCP] Error during browser installation: ${error.message}`);
202+
resolve({
203+
success: false,
204+
message: `Error during installation: ${error.message}. Please run: npx playwright install ${browserType}`
205+
});
206+
});
207+
208+
// Timeout after 2 minutes
209+
setTimeout(() => {
210+
installProcess.kill();
211+
resolve({
212+
success: false,
213+
message: `Browser installation timed out. Please run manually: npx playwright install ${browserType}`
214+
});
215+
}, 120000);
216+
});
217+
}
218+
160219
/**
161220
* Ensures a browser is launched and returns the page
162221
*/
@@ -204,12 +263,36 @@ export async function ensureBrowser(browserSettings?: BrowserSettings) {
204263

205264
const executablePath = process.env.CHROME_EXECUTABLE_PATH;
206265

207-
browser = await browserInstance.launch({
208-
headless,
209-
executablePath: executablePath
210-
});
211-
212-
currentBrowserType = browserType;
266+
try {
267+
browser = await browserInstance.launch({
268+
headless,
269+
executablePath: executablePath
270+
});
271+
272+
currentBrowserType = browserType;
273+
} catch (launchError: any) {
274+
// Check if error is due to missing browser executable
275+
if (launchError.message?.includes("Executable doesn't exist") ||
276+
launchError.message?.includes("Failed to launch") ||
277+
launchError.message?.includes("browserType.launch")) {
278+
279+
console.error(`[Playwright MCP] Browser not found, attempting auto-installation...`);
280+
const installResult = await installBrowsers(browserType);
281+
282+
if (installResult.success) {
283+
// Try launching again after installation
284+
browser = await browserInstance.launch({
285+
headless,
286+
executablePath: executablePath
287+
});
288+
currentBrowserType = browserType;
289+
} else {
290+
throw new Error(installResult.message);
291+
}
292+
} else {
293+
throw launchError;
294+
}
295+
}
213296

214297
// Add cleanup logic when browser is disconnected
215298
browser.on('disconnected', () => {
@@ -255,6 +338,22 @@ export async function ensureBrowser(browserSettings?: BrowserSettings) {
255338

256339
resetBrowserState();
257340

341+
// Check if error is due to missing browser, if so attempt install
342+
const errorMessage = (error as Error).message;
343+
if (errorMessage?.includes("Executable doesn't exist") ||
344+
errorMessage?.includes("Failed to launch") ||
345+
errorMessage?.includes("browserType.launch")) {
346+
347+
const { browserType = 'chromium' } = browserSettings ?? {};
348+
console.error(`[Playwright MCP] Browser not found in retry, attempting auto-installation...`);
349+
const installResult = await installBrowsers(browserType);
350+
351+
if (!installResult.success) {
352+
throw new Error(installResult.message);
353+
}
354+
// If installation successful, continue to retry
355+
}
356+
258357
// Try one more time from scratch
259358
const { viewport, userAgent, headless = false, browserType = 'chromium' } = browserSettings ?? {};
260359

0 commit comments

Comments
 (0)