mcp-web-inspector 0.8.0 → 0.9.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.
Files changed (93) hide show
  1. package/README.md +1 -0
  2. package/dist/toolHandler.js +58 -36
  3. package/dist/tools/browser/console/__tests__/console.test.js +2 -2
  4. package/dist/tools/browser/content/__tests__/screenshot.test.js +5 -5
  5. package/dist/tools/browser/content/__tests__/visiblePage.test.js +5 -5
  6. package/dist/tools/common/confirm_output.js +8 -1
  7. package/dist/utils/browserCheck.d.ts +0 -8
  8. package/dist/utils/browserCheck.js +4 -62
  9. package/package.json +1 -4
  10. package/dist/evals/evals.d.ts +0 -5
  11. package/dist/evals/evals.js +0 -41
  12. package/dist/toolConstants.d.ts +0 -7
  13. package/dist/toolConstants.js +0 -46
  14. package/dist/tools/api/base.d.ts +0 -33
  15. package/dist/tools/api/base.js +0 -49
  16. package/dist/tools/api/index.d.ts +0 -2
  17. package/dist/tools/api/index.js +0 -3
  18. package/dist/tools/api/requests.d.ts +0 -47
  19. package/dist/tools/api/requests.js +0 -168
  20. package/dist/tools/browser/ancestorInspection.d.ts +0 -18
  21. package/dist/tools/browser/ancestorInspection.js +0 -372
  22. package/dist/tools/browser/cleanSession.d.ts +0 -10
  23. package/dist/tools/browser/cleanSession.js +0 -42
  24. package/dist/tools/browser/compareElementAlignment.d.ts +0 -11
  25. package/dist/tools/browser/compareElementAlignment.js +0 -180
  26. package/dist/tools/browser/comparePositions.d.ts +0 -11
  27. package/dist/tools/browser/comparePositions.js +0 -149
  28. package/dist/tools/browser/computedStyles.d.ts +0 -12
  29. package/dist/tools/browser/computedStyles.js +0 -115
  30. package/dist/tools/browser/console.d.ts +0 -37
  31. package/dist/tools/browser/console.js +0 -106
  32. package/dist/tools/browser/content/__tests__/output.test.d.ts +0 -1
  33. package/dist/tools/browser/content/__tests__/output.test.js +0 -108
  34. package/dist/tools/browser/elementExists.d.ts +0 -9
  35. package/dist/tools/browser/elementExists.js +0 -57
  36. package/dist/tools/browser/elementInspection.d.ts +0 -21
  37. package/dist/tools/browser/elementInspection.js +0 -151
  38. package/dist/tools/browser/elementPosition.d.ts +0 -11
  39. package/dist/tools/browser/elementPosition.js +0 -107
  40. package/dist/tools/browser/elementVisibility.d.ts +0 -12
  41. package/dist/tools/browser/elementVisibility.js +0 -225
  42. package/dist/tools/browser/findByText.d.ts +0 -13
  43. package/dist/tools/browser/findByText.js +0 -207
  44. package/dist/tools/browser/getRequestDetails.d.ts +0 -9
  45. package/dist/tools/browser/getRequestDetails.js +0 -137
  46. package/dist/tools/browser/getTestIds.d.ts +0 -12
  47. package/dist/tools/browser/getTestIds.js +0 -148
  48. package/dist/tools/browser/index.d.ts +0 -7
  49. package/dist/tools/browser/index.js +0 -7
  50. package/dist/tools/browser/inspectDom.d.ts +0 -12
  51. package/dist/tools/browser/inspectDom.js +0 -480
  52. package/dist/tools/browser/interaction.d.ts +0 -108
  53. package/dist/tools/browser/interaction.js +0 -353
  54. package/dist/tools/browser/listNetworkRequests.d.ts +0 -10
  55. package/dist/tools/browser/listNetworkRequests.js +0 -74
  56. package/dist/tools/browser/measureElement.d.ts +0 -10
  57. package/dist/tools/browser/measureElement.js +0 -134
  58. package/dist/tools/browser/navigation/go_back.d.ts +0 -9
  59. package/dist/tools/browser/navigation/go_back.js +0 -25
  60. package/dist/tools/browser/navigation/go_forward.d.ts +0 -9
  61. package/dist/tools/browser/navigation/go_forward.js +0 -25
  62. package/dist/tools/browser/navigation.d.ts +0 -38
  63. package/dist/tools/browser/navigation.js +0 -109
  64. package/dist/tools/browser/output.d.ts +0 -11
  65. package/dist/tools/browser/output.js +0 -29
  66. package/dist/tools/browser/querySelectorAll.d.ts +0 -12
  67. package/dist/tools/browser/querySelectorAll.js +0 -201
  68. package/dist/tools/browser/response.d.ts +0 -29
  69. package/dist/tools/browser/response.js +0 -67
  70. package/dist/tools/browser/screenshot.d.ts +0 -16
  71. package/dist/tools/browser/screenshot.js +0 -82
  72. package/dist/tools/browser/useragent.d.ts +0 -15
  73. package/dist/tools/browser/useragent.js +0 -32
  74. package/dist/tools/browser/visiblePage.d.ts +0 -20
  75. package/dist/tools/browser/visiblePage.js +0 -205
  76. package/dist/tools/browser/waitForElement.d.ts +0 -10
  77. package/dist/tools/browser/waitForElement.js +0 -38
  78. package/dist/tools/browser/waitForNetworkIdle.d.ts +0 -8
  79. package/dist/tools/browser/waitForNetworkIdle.js +0 -32
  80. package/dist/tools/codegen/generator.d.ts +0 -21
  81. package/dist/tools/codegen/generator.js +0 -158
  82. package/dist/tools/codegen/index.d.ts +0 -11
  83. package/dist/tools/codegen/index.js +0 -187
  84. package/dist/tools/codegen/recorder.d.ts +0 -14
  85. package/dist/tools/codegen/recorder.js +0 -62
  86. package/dist/tools/codegen/types.d.ts +0 -28
  87. package/dist/tools/codegen/types.js +0 -1
  88. package/dist/tools/common/confirmHelpers.d.ts +0 -16
  89. package/dist/tools/common/confirmHelpers.js +0 -37
  90. package/dist/tools/common/confirmStore.d.ts +0 -8
  91. package/dist/tools/common/confirmStore.js +0 -31
  92. package/dist/tools.d.ts +0 -14
  93. package/dist/tools.js +0 -14
package/README.md CHANGED
@@ -1121,6 +1121,7 @@ Return full output for a previously previewed large result using a one-time toke
1121
1121
 
1122
1122
  - Parameters:
1123
1123
  - token (string, required): One-time token obtained from a tool's preview response
1124
+ - reason (string, required): Explain why the full output is needed and how it will be used. This helps the user understand whether the action is reasonable and necessary.
1124
1125
 
1125
1126
  - Output Format:
1126
1127
  - Full original payload if token is valid (one-time)
@@ -1,5 +1,5 @@
1
1
  import { chromium, firefox, webkit, devices } from 'playwright';
2
- import { checkBrowsersInstalled, getInstallationInstructions } from './utils/browserCheck.js';
2
+ import { spawnSync } from 'node:child_process';
3
3
  import { getToolInstance, isBrowserTool, executeTool } from './tools/common/registry.js';
4
4
  // Global state
5
5
  let browser;
@@ -15,10 +15,6 @@ let sessionConfig = {
15
15
  exposeSensitiveNetworkData: false,
16
16
  };
17
17
  let colorSchemeOverride = null;
18
- // Resolve package root for child processes (like npx). Entry point sets
19
- // MCP_WEB_INSPECTOR_PACKAGE_ROOT using import.meta.url; tests and other
20
- // environments fall back to process.cwd().
21
- const PACKAGE_ROOT = process.env.MCP_WEB_INSPECTOR_PACKAGE_ROOT || process.cwd();
22
18
  /**
23
19
  * Sets the session configuration
24
20
  */
@@ -265,15 +261,13 @@ async function registerConsoleMessage(page) {
265
261
  });
266
262
  });
267
263
  }
268
- // Track if we've checked browser installation
269
- let browserInstallationChecked = false;
270
264
  /**
271
265
  * Gets the screen size using Playwright's API
272
266
  */
273
267
  async function getScreenSize() {
274
268
  try {
275
269
  // Launch a temporary browser to get screen size
276
- const tempBrowser = await chromium.launch({ headless: true });
270
+ const tempBrowser = await chromium.launch({ headless: true, executablePath: process.env.CHROME_EXECUTABLE_PATH || undefined });
277
271
  const tempContext = await tempBrowser.newContext();
278
272
  const tempPage = await tempContext.newPage();
279
273
  const screenSize = await tempPage.evaluate(() => {
@@ -300,32 +294,6 @@ async function getScreenSize() {
300
294
  */
301
295
  export async function ensureBrowser(browserSettings) {
302
296
  try {
303
- // Check if browsers are installed on first launch (only once)
304
- if (!browser && !browserInstallationChecked) {
305
- browserInstallationChecked = true;
306
- const browserCheck = checkBrowsersInstalled();
307
- if (!browserCheck.installed) {
308
- // Try to install browsers automatically
309
- console.warn('🎭 Playwright browsers not found. Installing automatically...');
310
- console.warn('⏳ This will download ~1GB of browser binaries. Please wait...');
311
- try {
312
- const { execSync } = await import('child_process');
313
- execSync('npx playwright install chromium firefox webkit', {
314
- stdio: 'inherit',
315
- encoding: 'utf8',
316
- cwd: PACKAGE_ROOT,
317
- });
318
- console.error('✅ Browsers installed successfully! Starting browser...');
319
- // Note: browser variable is still undefined here, which is correct.
320
- // The code below (line 342) will launch the browser after installation.
321
- }
322
- catch (installError) {
323
- // If auto-install fails, show instructions
324
- const instructions = getInstallationInstructions();
325
- throw new Error(`Playwright browsers not installed.\n\n${instructions}`);
326
- }
327
- }
328
- }
329
297
  // Check if browser exists but is disconnected
330
298
  if (browser && !browser.isConnected()) {
331
299
  console.warn("Browser exists but is disconnected. Cleaning up...");
@@ -410,7 +378,7 @@ export async function ensureBrowser(browserSettings) {
410
378
  browserInstance = chromium;
411
379
  break;
412
380
  }
413
- const executablePath = process.env.CHROME_EXECUTABLE_PATH;
381
+ const executablePath = process.env.CHROME_EXECUTABLE_PATH || undefined;
414
382
  // Determine viewport size
415
383
  let viewportWidth;
416
384
  let viewportHeight;
@@ -525,6 +493,60 @@ export async function ensureBrowser(browserSettings) {
525
493
  // Ignore errors during cleanup
526
494
  }
527
495
  resetBrowserState();
496
+ // If browser executable is missing, try installing it automatically
497
+ const errorMessage = error.message || '';
498
+ if (errorMessage.includes("Executable doesn't exist")) {
499
+ // Ensure we have a writable browsers path
500
+ const browsersPath = process.env.PLAYWRIGHT_BROWSERS_PATH;
501
+ let installPath = browsersPath;
502
+ if (installPath) {
503
+ try {
504
+ const { accessSync, constants, mkdirSync } = await import('node:fs');
505
+ mkdirSync(installPath, { recursive: true });
506
+ accessSync(installPath, constants.W_OK);
507
+ }
508
+ catch {
509
+ installPath = undefined; // not writable, fall back
510
+ }
511
+ }
512
+ if (!installPath) {
513
+ const { homedir } = await import('node:os');
514
+ installPath = `${homedir()}/.cache/ms-playwright`;
515
+ console.error(`Default browsers path not writable, using ${installPath}`);
516
+ }
517
+ console.error('Browser not found. Installing Chromium...');
518
+ const result = spawnSync('npx', ['playwright', 'install', 'chromium'], {
519
+ stdio: ['ignore', 'inherit', 'inherit'],
520
+ encoding: 'utf8',
521
+ env: { ...process.env, PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '', PLAYWRIGHT_BROWSERS_PATH: installPath },
522
+ });
523
+ if (result.status === 0) {
524
+ // Playwright caches PLAYWRIGHT_BROWSERS_PATH at import time, so setting
525
+ // process.env mid-process won't help. Find the actual chrome binary and
526
+ // set CHROME_EXECUTABLE_PATH so the retry uses it via executablePath.
527
+ const { readdirSync, existsSync } = await import('node:fs');
528
+ const { join } = await import('node:path');
529
+ try {
530
+ const dirs = readdirSync(installPath);
531
+ const chromiumDir = dirs.find(d => d.startsWith('chromium-') && !d.includes('headless'));
532
+ if (chromiumDir) {
533
+ const chromePath = join(installPath, chromiumDir, 'chrome-linux', 'chrome');
534
+ if (existsSync(chromePath)) {
535
+ process.env.CHROME_EXECUTABLE_PATH = chromePath;
536
+ console.error(`Chromium installed at ${chromePath}`);
537
+ }
538
+ }
539
+ }
540
+ catch {
541
+ // Fall back — also try setting PLAYWRIGHT_BROWSERS_PATH in case it helps
542
+ process.env.PLAYWRIGHT_BROWSERS_PATH = installPath;
543
+ console.error('Chromium installed successfully.');
544
+ }
545
+ }
546
+ else {
547
+ console.error('Failed to install Chromium. Retrying launch anyway...');
548
+ }
549
+ }
528
550
  // Try one more time from scratch
529
551
  const { viewport, userAgent, headless = sessionConfig.headlessDefault, browserType = 'chromium', device } = browserSettings ?? {};
530
552
  // Get device configuration if device preset is specified
@@ -557,7 +579,7 @@ export async function ensureBrowser(browserSettings) {
557
579
  browserInstance = chromium;
558
580
  break;
559
581
  }
560
- const executablePath = process.env.CHROME_EXECUTABLE_PATH;
582
+ const executablePath = process.env.CHROME_EXECUTABLE_PATH || undefined;
561
583
  // Determine viewport size for retry
562
584
  let retryViewportWidth;
563
585
  let retryViewportHeight;
@@ -209,7 +209,7 @@ describe('GetConsoleLogsTool', () => {
209
209
  // Confirm to retrieve full payload
210
210
  const { ConfirmOutputTool } = await import('../../../common/confirm_output.js');
211
211
  const confirmTool = new ConfirmOutputTool({});
212
- const full = await confirmTool.execute({ token }, mockContext);
212
+ const full = await confirmTool.execute({ token, reason: 'test' }, mockContext);
213
213
  expect(full.isError).toBe(false);
214
214
  const fullText = full.content.map(c => c.text).join('\n');
215
215
  // By default limit=20 → header should reflect 20 lines
@@ -229,7 +229,7 @@ describe('GetConsoleLogsTool', () => {
229
229
  const token = (previewText.match(/confirm_output\(\{ token: \"([\w\d]+)\" \}\)/) || [])[1];
230
230
  const { ConfirmOutputTool } = await import('../../../common/confirm_output.js');
231
231
  const confirmTool = new ConfirmOutputTool({});
232
- const full = await confirmTool.execute({ token }, mockContext);
232
+ const full = await confirmTool.execute({ token, reason: 'test' }, mockContext);
233
233
  expect(full.isError).toBe(false);
234
234
  const fullText = full.content.map(c => c.text).join('\n');
235
235
  // Header reflects number of groups shown (limit default 20)
@@ -70,7 +70,7 @@ describe('ScreenshotTool', () => {
70
70
  const confirmTool = new ConfirmOutputTool({});
71
71
  const screenshotBuffer = Buffer.from('mock-screenshot');
72
72
  mockScreenshot.mockImplementationOnce(() => Promise.resolve(screenshotBuffer));
73
- const finalResult = await confirmTool.execute({ token }, mockContext);
73
+ const finalResult = await confirmTool.execute({ token, reason: 'test' }, mockContext);
74
74
  expect(finalResult.isError).toBe(false);
75
75
  expect(finalResult.content[0].text).toContain('Screenshot saved to');
76
76
  expect(mockScreenshot).toHaveBeenCalledWith(expect.objectContaining({ fullPage: true, type: 'png' }));
@@ -90,7 +90,7 @@ describe('ScreenshotTool', () => {
90
90
  const confirmTool = new ConfirmOutputTool({});
91
91
  const screenshotBuffer = Buffer.from('mock-element-screenshot');
92
92
  mockLocatorScreenshot.mockImplementationOnce(() => Promise.resolve(screenshotBuffer));
93
- const finalResult = await confirmTool.execute({ token }, mockContext);
93
+ const finalResult = await confirmTool.execute({ token, reason: 'test' }, mockContext);
94
94
  expect(finalResult.isError).toBe(false);
95
95
  expect(finalResult.content[0].text).toContain('Screenshot saved to');
96
96
  });
@@ -108,7 +108,7 @@ describe('ScreenshotTool', () => {
108
108
  const confirmTool = new ConfirmOutputTool({});
109
109
  // Mock a screenshot error on confirmation
110
110
  mockScreenshot.mockImplementationOnce(() => Promise.reject(new Error('Screenshot failed')));
111
- const finalResult = await confirmTool.execute({ token }, mockContext);
111
+ const finalResult = await confirmTool.execute({ token, reason: 'test' }, mockContext);
112
112
  expect(finalResult.isError).toBe(true);
113
113
  expect(finalResult.content[0].text).toContain('Screenshot failed');
114
114
  });
@@ -136,7 +136,7 @@ describe('ScreenshotTool', () => {
136
136
  const { ConfirmOutputTool } = await import('../../../common/confirm_output.js');
137
137
  const confirmTool = new ConfirmOutputTool({});
138
138
  mockScreenshot.mockImplementationOnce(() => Promise.resolve(Buffer.from('mock-screenshot')));
139
- await confirmTool.execute({ token }, mockContext);
139
+ await confirmTool.execute({ token, reason: 'test' }, mockContext);
140
140
  // Check that the screenshot was stored in the map
141
141
  const screenshots = screenshotTool.getScreenshots();
142
142
  expect(screenshots.has('test-screenshot')).toBe(true);
@@ -152,7 +152,7 @@ describe('ScreenshotTool', () => {
152
152
  const { ConfirmOutputTool } = await import('../../../common/confirm_output.js');
153
153
  const confirmTool = new ConfirmOutputTool({});
154
154
  mockScreenshot.mockImplementationOnce(() => Promise.resolve(Buffer.from('mock-screenshot')));
155
- const result = await confirmTool.execute({ token }, mockContext);
155
+ const result = await confirmTool.execute({ token, reason: 'test' }, mockContext);
156
156
  expect(mockScreenshot).toHaveBeenCalled();
157
157
  expect(result.isError).toBe(false);
158
158
  expect(result.content[0].text).toContain('Screenshot saved to');
@@ -408,14 +408,14 @@ describe('GetHtmlTool', () => {
408
408
  const token = tokenMatch[1];
409
409
  const { ConfirmOutputTool } = await import('../../../common/confirm_output.js');
410
410
  const confirmTool = new ConfirmOutputTool({});
411
- const fullResult = await confirmTool.execute({ token }, {});
411
+ const fullResult = await confirmTool.execute({ token, reason: 'test' }, {});
412
412
  expect(fullResult.isError).toBe(false);
413
413
  expect(fullResult.content[0].text).toContain(largeHtml);
414
414
  });
415
415
  test('confirm_output should error on invalid token', async () => {
416
416
  const { ConfirmOutputTool } = await import('../../../common/confirm_output.js');
417
417
  const confirmTool = new ConfirmOutputTool({});
418
- const res = await confirmTool.execute({ token: 'invalid123' }, {});
418
+ const res = await confirmTool.execute({ token: 'invalid123', reason: 'test' }, {});
419
419
  expect(res.isError).toBe(true);
420
420
  expect(res.content[0].text).toContain('Invalid or expired token');
421
421
  });
@@ -443,11 +443,11 @@ describe('GetHtmlTool', () => {
443
443
  const { ConfirmOutputTool } = await import('../../../common/confirm_output.js');
444
444
  const confirmTool = new ConfirmOutputTool({});
445
445
  // Second call - use token (should work)
446
- const fullResult = await confirmTool.execute({ token }, {});
446
+ const fullResult = await confirmTool.execute({ token, reason: 'test' }, {});
447
447
  expect(fullResult.isError).toBe(false);
448
448
  expect(fullResult.content[0].text).toContain(largeHtml);
449
449
  // Third call - try to reuse same token (should fail)
450
- const retryResult = await confirmTool.execute({ token }, {});
450
+ const retryResult = await confirmTool.execute({ token, reason: 'test' }, {});
451
451
  expect(retryResult.isError).toBe(true);
452
452
  expect(retryResult.content[0].text).toContain('Invalid or expired token');
453
453
  });
@@ -470,7 +470,7 @@ describe('GetHtmlTool', () => {
470
470
  // Second call - with token via confirm tool
471
471
  const { ConfirmOutputTool } = await import('../../../common/confirm_output.js');
472
472
  const confirmTool = new ConfirmOutputTool({});
473
- const fullResult = await confirmTool.execute({ token }, {});
473
+ const fullResult = await confirmTool.execute({ token, reason: 'test' }, {});
474
474
  expect(fullResult.isError).toBe(false);
475
475
  expect(fullResult.content[0].text).toContain(largeHtml);
476
476
  selectSpy.mockRestore();
@@ -87,8 +87,12 @@ export class ConfirmOutputTool {
87
87
  type: 'string',
88
88
  description: "One-time token obtained from a tool's preview response",
89
89
  },
90
+ reason: {
91
+ type: 'string',
92
+ description: "Explain why the full output is needed and how it will be used. This helps the user understand whether the action is reasonable and necessary.",
93
+ },
90
94
  },
91
- required: ['token'],
95
+ required: ['token', 'reason'],
92
96
  },
93
97
  priority: 2,
94
98
  category: 'Other',
@@ -98,6 +102,9 @@ export class ConfirmOutputTool {
98
102
  const token = typeof args.token === 'string' ? args.token : '';
99
103
  if (!token)
100
104
  return createErrorResponse('Token is required');
105
+ const reason = typeof args.reason === 'string' ? args.reason : '';
106
+ if (!reason)
107
+ return createErrorResponse('Reason is required');
101
108
  const res = consumeThunk(token);
102
109
  if (!res.ok) {
103
110
  const err = res.error;
@@ -1,11 +1,3 @@
1
- /**
2
- * Check if Playwright browsers are installed
3
- * Returns true if browsers are available, false otherwise
4
- */
5
- export declare function checkBrowsersInstalled(): {
6
- installed: boolean;
7
- message?: string;
8
- };
9
1
  /**
10
2
  * Get installation instructions for the current context
11
3
  */
@@ -1,72 +1,14 @@
1
- import { execSync } from 'child_process';
2
- import { existsSync } from 'fs';
3
- import { join } from 'path';
4
- // Resolve package root so npx uses this package's Playwright.
5
- // Entry point sets MCP_WEB_INSPECTOR_PACKAGE_ROOT using import.meta.url;
6
- // tests and other environments fall back to process.cwd().
7
- const PACKAGE_ROOT = process.env.MCP_WEB_INSPECTOR_PACKAGE_ROOT || process.cwd();
8
- /**
9
- * Check if Playwright browsers are installed
10
- * Returns true if browsers are available, false otherwise
11
- */
12
- export function checkBrowsersInstalled() {
13
- try {
14
- // Check if playwright is available
15
- const result = execSync('npx playwright --version', {
16
- encoding: 'utf8',
17
- stdio: 'pipe',
18
- cwd: PACKAGE_ROOT,
19
- });
20
- // If we got here, playwright CLI is available
21
- // Now check if browsers are actually installed
22
- // Playwright stores browsers in a cache directory
23
- const homeDir = process.env.HOME || process.env.USERPROFILE;
24
- if (!homeDir) {
25
- return {
26
- installed: false,
27
- message: 'Could not determine home directory to check for browsers'
28
- };
29
- }
30
- // Common browser cache locations
31
- const possibleCacheDirs = [
32
- join(homeDir, '.cache', 'ms-playwright'), // Linux
33
- join(homeDir, 'Library', 'Caches', 'ms-playwright'), // macOS
34
- join(homeDir, 'AppData', 'Local', 'ms-playwright'), // Windows
35
- ];
36
- const cacheExists = possibleCacheDirs.some(dir => existsSync(dir));
37
- if (!cacheExists) {
38
- return {
39
- installed: false,
40
- message: 'Playwright browsers not found in cache directories'
41
- };
42
- }
43
- return { installed: true };
44
- }
45
- catch (error) {
46
- return {
47
- installed: false,
48
- message: `Playwright check failed: ${error.message}`
49
- };
50
- }
51
- }
52
1
  /**
53
2
  * Get installation instructions for the current context
54
3
  */
55
4
  export function getInstallationInstructions() {
56
5
  return `
57
- Playwright browsers are not installed. To fix this, run one of the following commands:
58
-
59
- 1. In your project directory:
60
- npx playwright install chromium firefox webkit
6
+ Chromium is not available. To fix this:
61
7
 
62
- 2. If you installed mcp-web-inspector globally:
63
- cd $(npm root -g)/mcp-web-inspector && npx playwright install chromium firefox webkit
8
+ 1. Run: npm install (installs bundled Chromium via @playwright/browser-chromium)
64
9
 
65
- 3. Or install system-wide with dependencies (requires admin/sudo):
66
- npx playwright install --with-deps chromium firefox webkit
10
+ 2. Or set CHROME_EXECUTABLE_PATH to your Chrome/Chromium binary
67
11
 
68
- For GitHub Copilot or VS Code users, you may need to:
69
- - Close and reopen your IDE after installation
70
- - Or run: npx playwright install chromium
12
+ 3. For Firefox/WebKit (optional): npx playwright install firefox webkit
71
13
  `.trim();
72
14
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-web-inspector",
3
- "version": "0.8.0",
3
+ "version": "0.9.2",
4
4
  "description": "Web Inspector MCP: Give LLMs visual superpowers to see, debug, and test any web page.",
5
5
  "license": "MIT",
6
6
  "author": "Anton",
@@ -30,9 +30,6 @@
30
30
  "dependencies": {
31
31
  "@modelcontextprotocol/sdk": "1.21.0",
32
32
  "@playwright/browser-chromium": "1.56.1",
33
- "@playwright/browser-firefox": "1.56.1",
34
- "@playwright/browser-webkit": "1.56.1",
35
- "@playwright/test": "^1.56.1",
36
33
  "playwright": "1.56.1",
37
34
  "uuid": "13.0.0"
38
35
  },
@@ -1,5 +0,0 @@
1
- import { EvalConfig } from 'mcp-evals';
2
- import { EvalFunction } from "mcp-evals";
3
- declare const config: EvalConfig;
4
- export default config;
5
- export declare const evals: EvalFunction[];
@@ -1,41 +0,0 @@
1
- //evals.ts
2
- import { openai } from "@ai-sdk/openai";
3
- import { grade } from "mcp-evals";
4
- const startCodegenSessionEval = {
5
- name: 'startCodegenSession Evaluation',
6
- description: 'Evaluates the start codegen session tool',
7
- run: async () => {
8
- const result = await grade(openai("gpt-4"), "Please start a new code generation session with an output path of /my/test/path, a testNamePrefix of MyPrefix, and comments enabled. Confirm the session was created successfully.");
9
- return JSON.parse(result);
10
- }
11
- };
12
- const end_codegen_sessionEval = {
13
- name: 'end_codegen_session Evaluation',
14
- description: 'Evaluates the end_codegen_session tool functionality',
15
- run: async () => {
16
- const result = await grade(openai("gpt-4"), "Please end the code generation session with ID session123 and generate the Playwright test code");
17
- return JSON.parse(result);
18
- }
19
- };
20
- const get_codegen_sessionEval = {
21
- name: 'get_codegen_session Tool Evaluation',
22
- description: 'Evaluates the retrieval of code generation session details',
23
- run: async () => {
24
- const result = await grade(openai("gpt-4"), "Please retrieve the code generation session details using session ID abc123.");
25
- return JSON.parse(result);
26
- }
27
- };
28
- const clearCodegenSessionEval = {
29
- name: 'clear_codegen_session Evaluation',
30
- description: 'Evaluates the functionality of clearing a code generation session',
31
- run: async () => {
32
- const result = await grade(openai("gpt-4"), "Please clear the code generation session with the ID testSession_123 to verify removal.");
33
- return JSON.parse(result);
34
- }
35
- };
36
- const config = {
37
- model: openai("gpt-4"),
38
- evals: [startCodegenSessionEval, end_codegen_sessionEval, get_codegen_sessionEval, clearCodegenSessionEval]
39
- };
40
- export default config;
41
- export const evals = [startCodegenSessionEval, end_codegen_sessionEval, get_codegen_sessionEval, clearCodegenSessionEval];
@@ -1,7 +0,0 @@
1
- /**
2
- * Tool constants - separate file to avoid circular dependencies
3
- */
4
- /**
5
- * Get list of all browser tool names
6
- */
7
- export declare const BROWSER_TOOLS: string[];
@@ -1,46 +0,0 @@
1
- /**
2
- * Tool constants - separate file to avoid circular dependencies
3
- */
4
- /**
5
- * Get list of all browser tool names
6
- */
7
- export const BROWSER_TOOLS = [
8
- // Navigation & Control
9
- "navigate",
10
- "go_back",
11
- "go_forward",
12
- "screenshot",
13
- // DOM Inspection (PRIMARY)
14
- "inspect_dom",
15
- "get_test_ids",
16
- "query_selector",
17
- "find_by_text",
18
- // Visibility & Position
19
- "check_visibility",
20
- "compare_element_alignment",
21
- "inspect_ancestors",
22
- "element_exists",
23
- "wait_for_element",
24
- "wait_for_network_idle",
25
- // Style & Content
26
- "get_computed_styles",
27
- "measure_element",
28
- "get_text",
29
- "get_html",
30
- "get_console_logs",
31
- // Network Monitoring
32
- "list_network_requests",
33
- "get_request_details",
34
- // Interactions (for debugging/testing workflows)
35
- "click",
36
- "fill",
37
- "hover",
38
- "select",
39
- "upload_file",
40
- "drag",
41
- "press_key",
42
- // JavaScript Execution
43
- "evaluate",
44
- // Cleanup
45
- "close"
46
- ];
@@ -1,33 +0,0 @@
1
- import type { APIRequestContext } from 'playwright';
2
- import { ToolHandler, ToolContext, ToolResponse } from '../common/types.js';
3
- /**
4
- * Base class for all API-based tools
5
- * Provides common functionality and error handling
6
- */
7
- export declare abstract class ApiToolBase implements ToolHandler {
8
- protected server: any;
9
- constructor(server: any);
10
- /**
11
- * Main execution method that all tools must implement
12
- */
13
- abstract execute(args: any, context: ToolContext): Promise<ToolResponse>;
14
- /**
15
- * Ensures an API context is available and returns it
16
- * @param context The tool context containing apiContext
17
- * @returns The apiContext or null if not available
18
- */
19
- protected ensureApiContext(context: ToolContext): APIRequestContext | null;
20
- /**
21
- * Validates that an API context is available and returns an error response if not
22
- * @param context The tool context
23
- * @returns Either null if apiContext is available, or an error response
24
- */
25
- protected validateApiContextAvailable(context: ToolContext): ToolResponse | null;
26
- /**
27
- * Safely executes an API operation with proper error handling
28
- * @param context The tool context
29
- * @param operation The async operation to perform
30
- * @returns The tool response
31
- */
32
- protected safeExecute(context: ToolContext, operation: (apiContext: APIRequestContext) => Promise<ToolResponse>): Promise<ToolResponse>;
33
- }
@@ -1,49 +0,0 @@
1
- import { createErrorResponse } from '../common/types.js';
2
- /**
3
- * Base class for all API-based tools
4
- * Provides common functionality and error handling
5
- */
6
- export class ApiToolBase {
7
- constructor(server) {
8
- this.server = server;
9
- }
10
- /**
11
- * Ensures an API context is available and returns it
12
- * @param context The tool context containing apiContext
13
- * @returns The apiContext or null if not available
14
- */
15
- ensureApiContext(context) {
16
- if (!context.apiContext) {
17
- return null;
18
- }
19
- return context.apiContext;
20
- }
21
- /**
22
- * Validates that an API context is available and returns an error response if not
23
- * @param context The tool context
24
- * @returns Either null if apiContext is available, or an error response
25
- */
26
- validateApiContextAvailable(context) {
27
- if (!this.ensureApiContext(context)) {
28
- return createErrorResponse("API context not initialized");
29
- }
30
- return null;
31
- }
32
- /**
33
- * Safely executes an API operation with proper error handling
34
- * @param context The tool context
35
- * @param operation The async operation to perform
36
- * @returns The tool response
37
- */
38
- async safeExecute(context, operation) {
39
- const apiError = this.validateApiContextAvailable(context);
40
- if (apiError)
41
- return apiError;
42
- try {
43
- return await operation(context.apiContext);
44
- }
45
- catch (error) {
46
- return createErrorResponse(`API operation failed: ${error.message}`);
47
- }
48
- }
49
- }
@@ -1,2 +0,0 @@
1
- export * from './base.js';
2
- export * from './requests.js';
@@ -1,3 +0,0 @@
1
- export * from './base.js';
2
- export * from './requests.js';
3
- // TODO: Add exports for other API tools as they are implemented
@@ -1,47 +0,0 @@
1
- import { ApiToolBase } from './base.js';
2
- import { ToolContext, ToolResponse } from '../common/types.js';
3
- /**
4
- * Tool for making GET requests
5
- */
6
- export declare class GetRequestTool extends ApiToolBase {
7
- /**
8
- * Execute the GET request tool
9
- */
10
- execute(args: any, context: ToolContext): Promise<ToolResponse>;
11
- }
12
- /**
13
- * Tool for making POST requests
14
- */
15
- export declare class PostRequestTool extends ApiToolBase {
16
- /**
17
- * Execute the POST request tool
18
- */
19
- execute(args: any, context: ToolContext): Promise<ToolResponse>;
20
- }
21
- /**
22
- * Tool for making PUT requests
23
- */
24
- export declare class PutRequestTool extends ApiToolBase {
25
- /**
26
- * Execute the PUT request tool
27
- */
28
- execute(args: any, context: ToolContext): Promise<ToolResponse>;
29
- }
30
- /**
31
- * Tool for making PATCH requests
32
- */
33
- export declare class PatchRequestTool extends ApiToolBase {
34
- /**
35
- * Execute the PATCH request tool
36
- */
37
- execute(args: any, context: ToolContext): Promise<ToolResponse>;
38
- }
39
- /**
40
- * Tool for making DELETE requests
41
- */
42
- export declare class DeleteRequestTool extends ApiToolBase {
43
- /**
44
- * Execute the DELETE request tool
45
- */
46
- execute(args: any, context: ToolContext): Promise<ToolResponse>;
47
- }