mcp-web-inspector 0.1.2 โ†’ 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,6 +10,7 @@ Modern web applications are complex. Elements are hidden, layouts break, selecto
10
10
 
11
11
  - ๐Ÿ” **Understand any page structure** - Progressive DOM inspection that drills through wrapper divs to find semantic elements
12
12
  - ๐ŸŽฏ **Debug visibility issues** - Detailed diagnostics showing exactly why clicks fail (clipped, covered, scrolled out of view)
13
+ - ๐Ÿ”ผ **Trace layout constraints** - Walk up the DOM tree to find where unexpected margins, width limits, and overflow clipping come from
13
14
  - ๐Ÿ“ **Validate layouts** - Compare element positions to ensure consistent alignment and spacing
14
15
  - ๐Ÿงช **Test selector reliability** - See all matching elements with their visibility status before writing tests
15
16
  - ๐ŸŽจ **Inspect styles** - Get computed CSS to understand why elements behave unexpectedly
@@ -139,6 +140,17 @@ code-insiders --add-mcp '{"name":"web-inspector","command":"npx","args":["mcp-we
139
140
  4. MCP only works in **Agent mode** - switch to agent mode in the chat interface
140
141
  5. Open the `mcp.json` file and click the **"Start"** button next to the server
141
142
 
143
+ ### First-Time Browser Setup
144
+
145
+ When you first use the server with `npx`, Playwright browsers will be **automatically installed** on first tool use if not already present. The installation happens once and browsers are stored in your home directory, shared across all projects.
146
+
147
+ If automatic installation doesn't work (firewall, permissions, etc.), you'll see clear instructions to run:
148
+ ```bash
149
+ npx playwright install chromium firefox webkit
150
+ ```
151
+
152
+ Then restart VS Code to use the server.
153
+
142
154
  ### Note about Embedded Browser
143
155
 
144
156
  GitHub Copilot and VS Code may have an embedded browser feature. If you experience conflicts or prefer using Web Inspector MCP for all web inspection tasks, you may want to disable the built-in browser:
@@ -277,6 +289,7 @@ Customize server behavior with command line flags:
277
289
 
278
290
  - **`--no-save-session`** - Disable automatic session persistence (start with fresh browser state each time)
279
291
  - **`--user-data-dir <path>`** - Custom directory for session data (default: `./.mcp-web-inspector`)
292
+ - **`--headless`** - Run browser in headless mode by default (no visible window)
280
293
 
281
294
  **Example usage:**
282
295
  ```json
@@ -290,6 +303,30 @@ Customize server behavior with command line flags:
290
303
  }
291
304
  ```
292
305
 
306
+ **Run in headless mode for automation/CI:**
307
+ ```json
308
+ {
309
+ "mcpServers": {
310
+ "web-inspector": {
311
+ "command": "npx",
312
+ "args": ["-y", "mcp-web-inspector", "--headless"]
313
+ }
314
+ }
315
+ }
316
+ ```
317
+
318
+ **Combine multiple flags:**
319
+ ```json
320
+ {
321
+ "mcpServers": {
322
+ "web-inspector": {
323
+ "command": "npx",
324
+ "args": ["-y", "mcp-web-inspector", "--headless", "--no-save-session"]
325
+ }
326
+ }
327
+ }
328
+ ```
329
+
293
330
  ---
294
331
 
295
332
  ## Session Persistence & Data Storage
@@ -383,8 +420,8 @@ rm -rf ./.mcp-web-inspector/screenshots # Clear screenshots only
383
420
  - Session files can be large and bloat your git history
384
421
 
385
422
  **Best practices:**
386
- - Use `headless: true` for automation and CI/CD environments
387
- - Use `headless: false` only when debugging interactively
423
+ - **Default is visible browser** (`headless: false`) for interactive debugging
424
+ - Use `headless: true` explicitly for automation and CI/CD environments
388
425
  - Clear session data after testing sensitive applications
389
426
  - Use `--no-save-session` flag when testing on shared/public sites
390
427
 
@@ -458,6 +495,32 @@ Compare positions and alignment of two elements. Validates if elements are align
458
495
  - Validating grid layouts
459
496
  - Checking responsive design consistency
460
497
 
498
+ #### `inspect_ancestors` โญ **DEBUG LAYOUT CONSTRAINTS**
499
+ Walk up the DOM tree to find where width constraints, margins, borders, and overflow clipping come from. Shows position, size, and layout-critical CSS for each ancestor up to `<body>`.
500
+
501
+ **Key Features:**
502
+ - Default depth: 10 levels (reaches `<body>` in most React apps)
503
+ - Only shows non-default values (omits `border:none`, `overflow:visible`)
504
+ - Auto-detects overflow:hidden clipping, width constraints, auto-margin centering
505
+ - Token-efficient compact text format with diagnostic annotations (๐ŸŽฏโš ๏ธ)
506
+
507
+ **Use Cases:**
508
+ - Finding where unexpected margins come from (auto-centering)
509
+ - Discovering parent max-width constraints
510
+ - Locating overflow:hidden containers that clip elements
511
+ - Understanding why elements have constrained widths
512
+ - Debugging deeply nested component library layouts (Material-UI, Chakra, Ant Design)
513
+
514
+ **Example:**
515
+ ```
516
+ inspect_ancestors({ selector: "testid:header" })
517
+ โ†’ Shows: [0] header (896px, margins: 160px)
518
+ [1] div (1216px, max-w constraint)
519
+ [2] body (1920px, overflow-x: hidden)
520
+ ๐ŸŽฏ WIDTH CONSTRAINT found at parent
521
+ โš  Auto margins centering (160px each side)
522
+ ```
523
+
461
524
  ### ๐ŸŽจ Style & Content Inspection
462
525
 
463
526
  #### `get_computed_styles`
@@ -532,11 +595,16 @@ Ultra-lightweight existence check. Returns simple โœ“ exists or โœ— not found st
532
595
  Navigate to a URL with full browser configuration options.
533
596
 
534
597
  **Parameters:**
535
- - `browserType` - chromium, firefox, or webkit
536
- - `width`, `height` - Viewport dimensions
537
- - `headless` - Run in headless mode
538
- - `timeout` - Navigation timeout
539
- - `waitUntil` - Navigation wait condition
598
+ - `browserType` - chromium, firefox, or webkit (default: chromium)
599
+ - `width`, `height` - Viewport dimensions (default: auto-detected screen size)
600
+ - `headless` - Run in headless mode (default: **false** - browser window visible)
601
+ - `timeout` - Navigation timeout in ms (default: 30000)
602
+ - `waitUntil` - Navigation wait condition (default: "load")
603
+
604
+ **Default Behavior:**
605
+ - Browser window is **visible by default** for interactive debugging
606
+ - Use `headless: true` for automation, CI/CD, or when you don't need visual feedback
607
+ - Use `headless: false` (or omit) when debugging interactively
540
608
 
541
609
  #### `go_back`
542
610
  Navigate back in browser history. Essential for testing navigation flows and multi-step forms.
@@ -785,6 +853,32 @@ These step-by-step recipes show how to chain tools together for common testing a
785
853
 
786
854
  **Why this works**: Progressive inspection + precise measurements reveal layout problems.
787
855
 
856
+ ### Recipe 2a: Finding Where Unexpected Margins Come From
857
+
858
+ ```
859
+ 1. navigate({ url: "https://app.example.com" })
860
+ 2. inspect_dom({ selector: "main" })
861
+ โ†’ Shows header element with test ID
862
+ 3. measure_element({ selector: "testid:event-mode-header" })
863
+ โ†’ @ (160,0) 896x56px
864
+ โ†’ Margin: โ†160px โ†’160px (unexpected!)
865
+ ๐Ÿ’ก Unexpected spacing detected. Check parent constraints
866
+ 4. inspect_ancestors({ selector: "testid:event-mode-header" })
867
+ โ†’ [0] <header testid:event-mode-header>
868
+ @ (160,0) 896x56px | w:896px max-w:896px m:0 160px
869
+ border-bottom: 1px solid #e5e7eb
870
+ โš  Auto margins centering (160px each side)
871
+ โ†’ [1] <div>
872
+ @ (0,0) 1216x56px | w:1216px
873
+ โ†’ [2] <div> flex max-w-[1600px]
874
+ @ (352,60) 1216x900px
875
+ max-width: 1600px
876
+ ๐ŸŽฏ WIDTH CONSTRAINT
877
+ 5. Solution: Remove mx-auto from header (centering already handled by parent)
878
+ ```
879
+
880
+ **Why this works**: `inspect_ancestors` traces the layout constraint chain to find the root cause of unexpected spacing in deeply nested React components.
881
+
788
882
  ### Recipe 3: API Response Testing
789
883
 
790
884
  ```
@@ -953,6 +1047,71 @@ These step-by-step recipes show how to chain tools together for common testing a
953
1047
 
954
1048
  **Why this works**: DOM inspection + attribute queries reveal accessibility issues.
955
1049
 
1050
+ ## Troubleshooting
1051
+
1052
+ ### Browser Installation Issues
1053
+
1054
+ **Symptom**: Error message about Playwright browsers not being installed, or browser fails to launch.
1055
+
1056
+ **How it works**: Browsers are **automatically installed on first use** when you run any navigation tool. The installation happens once (~1GB download) and browsers are stored in your home directory, shared across all projects.
1057
+
1058
+ **What you'll see on first use**:
1059
+ ```
1060
+ ๐ŸŽญ Playwright browsers not found. Installing automatically...
1061
+ โณ This will download ~1GB of browser binaries. Please wait...
1062
+ [Installation progress...]
1063
+ โœ… Browsers installed successfully! Starting browser...
1064
+ ```
1065
+
1066
+ **If automatic installation fails** (firewall, permissions, etc.):
1067
+
1068
+ ```bash
1069
+ # Manual installation - run this command:
1070
+ npx playwright install chromium firefox webkit
1071
+
1072
+ # With system dependencies (requires admin/sudo):
1073
+ npx playwright install --with-deps chromium firefox webkit
1074
+ ```
1075
+
1076
+ **For GitHub Copilot / VS Code users**:
1077
+ - First tool use will auto-install browsers (1-2 minute wait)
1078
+ - Subsequent uses are instant
1079
+ - Installation happens in the background with progress messages
1080
+ - If manual installation is needed, restart your IDE after running the command
1081
+
1082
+ ### Server Not Loading
1083
+
1084
+ **Symptom**: Tools from web-inspector are not available in your AI assistant.
1085
+
1086
+ **Solutions**:
1087
+ 1. Verify the configuration file is correct (see AI Tool Setup section above)
1088
+ 2. Restart your AI tool completely (not just reload window)
1089
+ 3. Check server logs (location depends on your AI tool)
1090
+ 4. Try removing and re-adding the server configuration
1091
+
1092
+ ### Permission Issues
1093
+
1094
+ **Symptom**: Permission denied errors when installing browsers.
1095
+
1096
+ **Solutions**:
1097
+ ```bash
1098
+ # If using global installation, you may need sudo (Linux/macOS)
1099
+ sudo npm install -g mcp-web-inspector
1100
+
1101
+ # Or use npx without global installation (recommended)
1102
+ # Just configure with "npx -y mcp-web-inspector" as shown in setup
1103
+ ```
1104
+
1105
+ ### Browser Crashes or Disconnects
1106
+
1107
+ **Symptom**: Browser becomes unresponsive or disconnects during use.
1108
+
1109
+ **The MCP server automatically handles this**:
1110
+ - Detects disconnected browsers
1111
+ - Resets state and provides clear error messages
1112
+ - Instructs you to retry the navigation/action
1113
+ - No manual intervention needed - just retry your command
1114
+
956
1115
  ## Development
957
1116
 
958
1117
  ### Testing
package/dist/index.js CHANGED
@@ -16,6 +16,10 @@ const { values } = parseArgs({
16
16
  type: 'string',
17
17
  default: './.mcp-web-inspector',
18
18
  },
19
+ 'headless': {
20
+ type: 'boolean',
21
+ default: false,
22
+ },
19
23
  },
20
24
  strict: false,
21
25
  });
@@ -25,6 +29,7 @@ const sessionConfig = {
25
29
  saveSession: !Boolean(values['no-save-session']),
26
30
  userDataDir: `${baseDir}/user-data`,
27
31
  screenshotsDir: `${baseDir}/screenshots`,
32
+ headlessDefault: Boolean(values['headless']),
28
33
  };
29
34
  setSessionConfig(sessionConfig);
30
35
  async function runServer() {
@@ -22,6 +22,7 @@ interface SessionConfig {
22
22
  saveSession: boolean;
23
23
  userDataDir: string;
24
24
  screenshotsDir: string;
25
+ headlessDefault: boolean;
25
26
  }
26
27
  /**
27
28
  * Sets the session configuration
@@ -31,6 +32,10 @@ export declare function setSessionConfig(config: Partial<SessionConfig>): void;
31
32
  * Gets the screenshots directory
32
33
  */
33
34
  export declare function getScreenshotsDir(): string;
35
+ /**
36
+ * Gets the default headless setting
37
+ */
38
+ export declare function getHeadlessDefault(): boolean;
34
39
  /**
35
40
  * Resets browser and page variables
36
41
  * Used when browser is closed
@@ -48,7 +53,7 @@ export declare function clearNetworkLog(): void;
48
53
  * Sets the provided page to the global page variable
49
54
  * @param newPage The Page object to set as the global page
50
55
  */
51
- export declare function setGlobalPage(newPage: Page): void;
56
+ export declare function setGlobalPage(newPage: Page): Promise<void>;
52
57
  interface BrowserSettings {
53
58
  viewport?: {
54
59
  width?: number;
@@ -1,5 +1,6 @@
1
1
  import { chromium, firefox, webkit, devices } from 'playwright';
2
2
  import { BROWSER_TOOLS } from './tools.js';
3
+ import { checkBrowsersInstalled, getInstallationInstructions } from './utils/browserCheck.js';
3
4
  import { ScreenshotTool, NavigationTool, CloseBrowserTool, ConsoleLogsTool } from './tools/browser/index.js';
4
5
  import { ClickTool, FillTool, SelectTool, HoverTool, EvaluateTool, UploadFileTool } from './tools/browser/interaction.js';
5
6
  import { VisibleTextTool, VisibleHtmlTool } from './tools/browser/visiblePage.js';
@@ -12,6 +13,7 @@ import { GetComputedStylesTool } from './tools/browser/computedStyles.js';
12
13
  import { MeasureElementTool } from './tools/browser/measureElement.js';
13
14
  import { ElementExistsTool } from './tools/browser/elementExists.js';
14
15
  import { CompareElementAlignmentTool } from './tools/browser/compareElementAlignment.js';
16
+ import { InspectAncestorsTool } from './tools/browser/ancestorInspection.js';
15
17
  import { GoBackTool, GoForwardTool } from './tools/browser/navigation.js';
16
18
  import { DragTool, PressKeyTool } from './tools/browser/interaction.js';
17
19
  import { WaitForElementTool } from './tools/browser/waitForElement.js';
@@ -27,6 +29,7 @@ let sessionConfig = {
27
29
  saveSession: false,
28
30
  userDataDir: './.mcp-web-inspector/user-data',
29
31
  screenshotsDir: './.mcp-web-inspector/screenshots',
32
+ headlessDefault: false,
30
33
  };
31
34
  /**
32
35
  * Sets the session configuration
@@ -40,6 +43,12 @@ export function setSessionConfig(config) {
40
43
  export function getScreenshotsDir() {
41
44
  return sessionConfig.screenshotsDir;
42
45
  }
46
+ /**
47
+ * Gets the default headless setting
48
+ */
49
+ export function getHeadlessDefault() {
50
+ return sessionConfig.headlessDefault;
51
+ }
43
52
  /**
44
53
  * Resets browser and page variables
45
54
  * Used when browser is closed
@@ -66,10 +75,13 @@ export function clearNetworkLog() {
66
75
  * Sets the provided page to the global page variable
67
76
  * @param newPage The Page object to set as the global page
68
77
  */
69
- export function setGlobalPage(newPage) {
78
+ export async function setGlobalPage(newPage) {
70
79
  page = newPage;
80
+ // Register console message handlers and network listeners for the new page
81
+ await registerConsoleMessage(page);
82
+ await registerNetworkListeners(page);
71
83
  page.bringToFront(); // Bring the new tab to the front
72
- console.log("Global page has been updated.");
84
+ console.log("Global page has been updated with listeners registered.");
73
85
  }
74
86
  // Tool instances
75
87
  let screenshotTool;
@@ -97,6 +109,7 @@ let getComputedStylesTool;
97
109
  let measureElementTool;
98
110
  let elementExistsTool;
99
111
  let compareElementAlignmentTool;
112
+ let inspectAncestorsTool;
100
113
  let waitForElementTool;
101
114
  let waitForNetworkIdleTool;
102
115
  let listNetworkRequestsTool;
@@ -162,13 +175,21 @@ async function registerConsoleMessage(page) {
162
175
  page.on("console", (msg) => {
163
176
  if (consoleLogsTool) {
164
177
  const type = msg.type();
165
- const text = msg.text();
178
+ let text = msg.text();
166
179
  // "Unhandled Rejection In Promise" we injected
167
180
  if (text.startsWith("[Playwright]")) {
168
181
  const payload = text.replace("[Playwright]", "");
169
182
  consoleLogsTool.registerConsoleMessage("exception", payload);
170
183
  }
171
184
  else {
185
+ // Truncate stack traces for error messages to keep output compact
186
+ if (type === 'error' && text.includes('\n')) {
187
+ const lines = text.split('\n');
188
+ // Keep first line (error message) and up to 3 stack trace lines
189
+ if (lines.length > 4) {
190
+ text = lines.slice(0, 4).join('\n') + '\n ...[stack trace truncated]';
191
+ }
192
+ }
172
193
  consoleLogsTool.registerConsoleMessage(type, text);
173
194
  }
174
195
  }
@@ -200,11 +221,66 @@ async function registerConsoleMessage(page) {
200
221
  });
201
222
  });
202
223
  }
224
+ // Track if we've checked browser installation
225
+ let browserInstallationChecked = false;
226
+ /**
227
+ * Gets the screen size using Playwright's API
228
+ */
229
+ async function getScreenSize() {
230
+ try {
231
+ // Launch a temporary browser to get screen size
232
+ const tempBrowser = await chromium.launch({ headless: true });
233
+ const tempContext = await tempBrowser.newContext();
234
+ const tempPage = await tempContext.newPage();
235
+ const screenSize = await tempPage.evaluate(() => {
236
+ return {
237
+ width: window.screen.width,
238
+ height: window.screen.height
239
+ };
240
+ });
241
+ await tempBrowser.close();
242
+ // Validate the screen size values
243
+ if (!screenSize || typeof screenSize.width !== 'number' || typeof screenSize.height !== 'number') {
244
+ console.error('Invalid screen size detected, using defaults');
245
+ return { width: 1280, height: 720 };
246
+ }
247
+ return screenSize;
248
+ }
249
+ catch (error) {
250
+ console.error('Failed to detect screen size, using defaults:', error);
251
+ return { width: 1280, height: 720 };
252
+ }
253
+ }
203
254
  /**
204
255
  * Ensures a browser is launched and returns the page
205
256
  */
206
257
  export async function ensureBrowser(browserSettings) {
207
258
  try {
259
+ // Check if browsers are installed on first launch (only once)
260
+ if (!browser && !browserInstallationChecked) {
261
+ browserInstallationChecked = true;
262
+ const browserCheck = checkBrowsersInstalled();
263
+ if (!browserCheck.installed) {
264
+ // Try to install browsers automatically
265
+ console.error('๐ŸŽญ Playwright browsers not found. Installing automatically...');
266
+ console.error('โณ This will download ~1GB of browser binaries. Please wait...');
267
+ try {
268
+ const { execSync } = await import('child_process');
269
+ execSync('npx playwright install chromium firefox webkit', {
270
+ stdio: 'inherit',
271
+ encoding: 'utf8'
272
+ });
273
+ console.error('โœ… Browsers installed successfully! Starting browser...');
274
+ // Note: browser variable is still undefined here, which is correct.
275
+ // The code below (line 342) will launch the browser after installation.
276
+ }
277
+ catch (installError) {
278
+ // If auto-install fails, show instructions
279
+ const instructions = getInstallationInstructions();
280
+ throw new Error(`Playwright browsers not installed.\n\n${instructions}`);
281
+ }
282
+ }
283
+ }
208
284
  // Check if browser exists but is disconnected
209
285
  if (browser && !browser.isConnected()) {
210
286
  console.error("Browser exists but is disconnected. Cleaning up...");
@@ -234,7 +310,7 @@ export async function ensureBrowser(browserSettings) {
234
310
  }
235
311
  // Launch new browser if needed
236
312
  if (!browser) {
237
- const { viewport, userAgent, headless = false, browserType = 'chromium', device } = browserSettings ?? {};
313
+ const { viewport, userAgent, headless = sessionConfig.headlessDefault, browserType = 'chromium', device } = browserSettings ?? {};
238
314
  // If browser type is changing, force a new browser instance
239
315
  if (browser && currentBrowserType !== browserType) {
240
316
  try {
@@ -273,6 +349,23 @@ export async function ensureBrowser(browserSettings) {
273
349
  break;
274
350
  }
275
351
  const executablePath = process.env.CHROME_EXECUTABLE_PATH;
352
+ // Determine viewport size
353
+ let viewportWidth;
354
+ let viewportHeight;
355
+ if (viewport?.width !== undefined || viewport?.height !== undefined) {
356
+ // If any viewport dimension is specified, use specified values or defaults
357
+ viewportWidth = viewport?.width ?? 1280;
358
+ viewportHeight = viewport?.height ?? 720;
359
+ }
360
+ else {
361
+ // If no viewport specified, detect screen size
362
+ const screenSize = await getScreenSize();
363
+ viewportWidth = screenSize?.width ?? 1280;
364
+ viewportHeight = screenSize?.height ?? 720;
365
+ if (screenSize && screenSize.width > 0 && screenSize.height > 0) {
366
+ console.error(`No viewport specified, using screen size: ${viewportWidth}x${viewportHeight}`);
367
+ }
368
+ }
276
369
  // Prepare context options
277
370
  const contextOptions = {
278
371
  headless,
@@ -287,8 +380,8 @@ export async function ensureBrowser(browserSettings) {
287
380
  contextOptions.userAgent = userAgent;
288
381
  }
289
382
  contextOptions.viewport = {
290
- width: viewport?.width ?? 1280,
291
- height: viewport?.height ?? 720,
383
+ width: viewportWidth,
384
+ height: viewportHeight,
292
385
  };
293
386
  contextOptions.deviceScaleFactor = 1;
294
387
  }
@@ -331,8 +424,8 @@ export async function ensureBrowser(browserSettings) {
331
424
  newContextOptions.userAgent = userAgent;
332
425
  }
333
426
  newContextOptions.viewport = {
334
- width: viewport?.width ?? 1280,
335
- height: viewport?.height ?? 720,
427
+ width: viewportWidth,
428
+ height: viewportHeight,
336
429
  };
337
430
  newContextOptions.deviceScaleFactor = 1;
338
431
  }
@@ -368,7 +461,7 @@ export async function ensureBrowser(browserSettings) {
368
461
  }
369
462
  resetBrowserState();
370
463
  // Try one more time from scratch
371
- const { viewport, userAgent, headless = false, browserType = 'chromium', device } = browserSettings ?? {};
464
+ const { viewport, userAgent, headless = sessionConfig.headlessDefault, browserType = 'chromium', device } = browserSettings ?? {};
372
465
  // Get device configuration if device preset is specified
373
466
  let deviceConfig = null;
374
467
  if (device && DEVICE_PRESETS[device]) {
@@ -390,6 +483,20 @@ export async function ensureBrowser(browserSettings) {
390
483
  break;
391
484
  }
392
485
  const executablePath = process.env.CHROME_EXECUTABLE_PATH;
486
+ // Determine viewport size for retry
487
+ let retryViewportWidth;
488
+ let retryViewportHeight;
489
+ if (viewport?.width !== undefined || viewport?.height !== undefined) {
490
+ // If any viewport dimension is specified, use specified values or defaults
491
+ retryViewportWidth = viewport?.width ?? 1280;
492
+ retryViewportHeight = viewport?.height ?? 720;
493
+ }
494
+ else {
495
+ // If no viewport specified, detect screen size
496
+ const screenSize = await getScreenSize();
497
+ retryViewportWidth = screenSize?.width ?? 1280;
498
+ retryViewportHeight = screenSize?.height ?? 720;
499
+ }
393
500
  // Prepare context options
394
501
  const retryContextOptions = {
395
502
  headless,
@@ -404,8 +511,8 @@ export async function ensureBrowser(browserSettings) {
404
511
  retryContextOptions.userAgent = userAgent;
405
512
  }
406
513
  retryContextOptions.viewport = {
407
- width: viewport?.width ?? 1280,
408
- height: viewport?.height ?? 720,
514
+ width: retryViewportWidth,
515
+ height: retryViewportHeight,
409
516
  };
410
517
  retryContextOptions.deviceScaleFactor = 1;
411
518
  }
@@ -444,8 +551,8 @@ export async function ensureBrowser(browserSettings) {
444
551
  retryNewContextOptions.userAgent = userAgent;
445
552
  }
446
553
  retryNewContextOptions.viewport = {
447
- width: viewport?.width ?? 1280,
448
- height: viewport?.height ?? 720,
554
+ width: retryViewportWidth,
555
+ height: retryViewportHeight,
449
556
  };
450
557
  retryNewContextOptions.deviceScaleFactor = 1;
451
558
  }
@@ -512,6 +619,8 @@ function initializeTools(server) {
512
619
  elementExistsTool = new ElementExistsTool(server);
513
620
  if (!compareElementAlignmentTool)
514
621
  compareElementAlignmentTool = new CompareElementAlignmentTool(server);
622
+ if (!inspectAncestorsTool)
623
+ inspectAncestorsTool = new InspectAncestorsTool(server);
515
624
  if (!waitForElementTool)
516
625
  waitForElementTool = new WaitForElementTool(server);
517
626
  if (!waitForNetworkIdleTool)
@@ -653,6 +762,8 @@ export async function handleToolCall(name, args, server) {
653
762
  return await elementExistsTool.execute(args, context);
654
763
  case "compare_element_alignment":
655
764
  return await compareElementAlignmentTool.execute(args, context);
765
+ case "inspect_ancestors":
766
+ return await inspectAncestorsTool.execute(args, context);
656
767
  case "wait_for_element":
657
768
  return await waitForElementTool.execute(args, context);
658
769
  case "wait_for_network_idle":
@@ -0,0 +1,18 @@
1
+ import { BrowserToolBase } from "./base.js";
2
+ import type { ToolResponse, ToolContext } from "../common/types.js";
3
+ /**
4
+ * Tool to inspect ancestor chain of an element
5
+ * Shows parent elements up the DOM tree with layout-critical CSS properties
6
+ */
7
+ export declare class InspectAncestorsTool extends BrowserToolBase {
8
+ execute(args: {
9
+ selector: string;
10
+ limit?: number;
11
+ }, context: ToolContext): Promise<ToolResponse>;
12
+ private formatAncestorChain;
13
+ private formatBorder;
14
+ private formatOverflow;
15
+ private formatLayoutContext;
16
+ private formatMarginDetails;
17
+ private generateDiagnostics;
18
+ }