mcp-web-inspector 0.1.0 → 0.1.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.
package/README.md CHANGED
@@ -449,15 +449,6 @@ Detailed visibility diagnostics showing exactly why elements are or aren't visib
449
449
  - Detecting elements covered by modals or overlays
450
450
  - Checking if elements are clipped by parent containers
451
451
 
452
- #### `get_position`
453
- Get precise element coordinates and dimensions (x, y, width, height). Shows position relative to viewport.
454
-
455
- **Use Cases:**
456
- - Finding exact click coordinates
457
- - Checking element layout
458
- - Calculating distances between elements
459
- - Debugging overlapping elements
460
-
461
452
  #### `compare_positions`
462
453
  Compare positions and alignment of two elements. Validates if elements are aligned (top, left, right, bottom) or have matching dimensions (width, height).
463
454
 
@@ -703,8 +694,8 @@ Regular CSS selectors, text selectors (`text=Login`), and Playwright selectors w
703
694
  → Found 3 matches, 2 are hidden (display:none)
704
695
  3. check_visibility({ selector: ".submit-button:nth-child(1)" })
705
696
  → Element is clipped by parent overflow:hidden
706
- 4. get_position({ selector: ".submit-button:nth-child(1)" })
707
- Element is at x:1500, y:300 (outside viewport)
697
+ 4. measure_element({ selector: ".submit-button:nth-child(1)" })
698
+ @ (1500,300) 100x40px (outside viewport)
708
699
  ```
709
700
 
710
701
  ### Understanding Page Structure
@@ -774,9 +765,9 @@ These step-by-step recipes show how to chain tools together for common testing a
774
765
  1. navigate({ url: "https://dashboard.example.com" })
775
766
  2. inspect_dom({ selector: "testid:sidebar" })
776
767
  → Understand the structure of the problematic area
777
- 3. get_position({ selector: "testid:logo" })
768
+ 3. measure_element({ selector: "testid:logo" })
778
769
  → @ (20,10) 150x40px
779
- 4. get_position({ selector: "testid:menu" })
770
+ 4. measure_element({ selector: "testid:menu" })
780
771
  → @ (20,60) 200x300px
781
772
  5. compare_positions({
782
773
  selector1: "testid:logo",
@@ -870,9 +861,9 @@ These step-by-step recipes show how to chain tools together for common testing a
870
861
  checkAlignment: "height"
871
862
  })
872
863
  → ✗ not aligned (difference: 20px)
873
- 5. get_position({ selector: ".card:nth-child(1)" })
864
+ 5. measure_element({ selector: ".card:nth-child(1)" })
874
865
  → @ (20,100) 400x300px
875
- 6. get_position({ selector: ".card:nth-child(2)" })
866
+ 6. measure_element({ selector: ".card:nth-child(2)" })
876
867
  → @ (440,100) 400x320px ← 20px taller!
877
868
  ```
878
869
 
@@ -926,7 +917,7 @@ These step-by-step recipes show how to chain tools together for common testing a
926
917
  3. wait_for_element({ selector: "testid:tooltip", state: "visible", timeout: 2000 })
927
918
  4. check_visibility({ selector: "testid:tooltip" })
928
919
  → ✓ visible
929
- 5. get_position({ selector: "testid:tooltip" })
920
+ 5. measure_element({ selector: "testid:tooltip" })
930
921
  → @ (300,150) 200x50px
931
922
  6. get_computed_styles({
932
923
  selector: "testid:tooltip",
@@ -4,7 +4,6 @@ import { ScreenshotTool, NavigationTool, CloseBrowserTool, ConsoleLogsTool } fro
4
4
  import { ClickTool, FillTool, SelectTool, HoverTool, EvaluateTool, UploadFileTool } from './tools/browser/interaction.js';
5
5
  import { VisibleTextTool, VisibleHtmlTool } from './tools/browser/visiblePage.js';
6
6
  import { ElementVisibilityTool } from './tools/browser/elementVisibility.js';
7
- import { ElementPositionTool } from './tools/browser/elementPosition.js';
8
7
  import { InspectDomTool } from './tools/browser/inspectDom.js';
9
8
  import { GetTestIdsTool } from './tools/browser/getTestIds.js';
10
9
  import { QuerySelectorAllTool } from './tools/browser/querySelectorAll.js';
@@ -12,7 +11,7 @@ import { FindByTextTool } from './tools/browser/findByText.js';
12
11
  import { GetComputedStylesTool } from './tools/browser/computedStyles.js';
13
12
  import { MeasureElementTool } from './tools/browser/measureElement.js';
14
13
  import { ElementExistsTool } from './tools/browser/elementExists.js';
15
- import { ComparePositionsTool } from './tools/browser/comparePositions.js';
14
+ import { CompareElementAlignmentTool } from './tools/browser/compareElementAlignment.js';
16
15
  import { GoBackTool, GoForwardTool } from './tools/browser/navigation.js';
17
16
  import { DragTool, PressKeyTool } from './tools/browser/interaction.js';
18
17
  import { WaitForElementTool } from './tools/browser/waitForElement.js';
@@ -90,7 +89,6 @@ let goForwardTool;
90
89
  let dragTool;
91
90
  let pressKeyTool;
92
91
  let elementVisibilityTool;
93
- let elementPositionTool;
94
92
  let inspectDomTool;
95
93
  let getTestIdsTool;
96
94
  let querySelectorAllTool;
@@ -98,7 +96,7 @@ let findByTextTool;
98
96
  let getComputedStylesTool;
99
97
  let measureElementTool;
100
98
  let elementExistsTool;
101
- let comparePositionsTool;
99
+ let compareElementAlignmentTool;
102
100
  let waitForElementTool;
103
101
  let waitForNetworkIdleTool;
104
102
  let listNetworkRequestsTool;
@@ -219,6 +217,21 @@ export async function ensureBrowser(browserSettings) {
219
217
  // Reset browser and page references
220
218
  resetBrowserState();
221
219
  }
220
+ // If browser exists and viewport settings changed, resize the viewport
221
+ if (browser && page && !page.isClosed() && browserSettings?.viewport) {
222
+ const { width, height } = browserSettings.viewport;
223
+ // Only resize if width or height are explicitly provided
224
+ if (width !== undefined || height !== undefined) {
225
+ const currentViewport = page.viewportSize();
226
+ const targetWidth = width ?? currentViewport?.width ?? 1280;
227
+ const targetHeight = height ?? currentViewport?.height ?? 720;
228
+ // Check if viewport size actually changed
229
+ if (!currentViewport || currentViewport.width !== targetWidth || currentViewport.height !== targetHeight) {
230
+ console.error(`Resizing viewport to ${targetWidth}x${targetHeight}`);
231
+ await page.setViewportSize({ width: targetWidth, height: targetHeight });
232
+ }
233
+ }
234
+ }
222
235
  // Launch new browser if needed
223
236
  if (!browser) {
224
237
  const { viewport, userAgent, headless = false, browserType = 'chromium', device } = browserSettings ?? {};
@@ -483,8 +496,6 @@ function initializeTools(server) {
483
496
  pressKeyTool = new PressKeyTool(server);
484
497
  if (!elementVisibilityTool)
485
498
  elementVisibilityTool = new ElementVisibilityTool(server);
486
- if (!elementPositionTool)
487
- elementPositionTool = new ElementPositionTool(server);
488
499
  if (!inspectDomTool)
489
500
  inspectDomTool = new InspectDomTool(server);
490
501
  if (!getTestIdsTool)
@@ -499,8 +510,8 @@ function initializeTools(server) {
499
510
  measureElementTool = new MeasureElementTool(server);
500
511
  if (!elementExistsTool)
501
512
  elementExistsTool = new ElementExistsTool(server);
502
- if (!comparePositionsTool)
503
- comparePositionsTool = new ComparePositionsTool(server);
513
+ if (!compareElementAlignmentTool)
514
+ compareElementAlignmentTool = new CompareElementAlignmentTool(server);
504
515
  if (!waitForElementTool)
505
516
  waitForElementTool = new WaitForElementTool(server);
506
517
  if (!waitForNetworkIdleTool)
@@ -626,8 +637,6 @@ export async function handleToolCall(name, args, server) {
626
637
  return await pressKeyTool.execute(args, context);
627
638
  case "check_visibility":
628
639
  return await elementVisibilityTool.execute(args, context);
629
- case "get_position":
630
- return await elementPositionTool.execute(args, context);
631
640
  case "inspect_dom":
632
641
  return await inspectDomTool.execute(args, context);
633
642
  case "get_test_ids":
@@ -642,8 +651,8 @@ export async function handleToolCall(name, args, server) {
642
651
  return await measureElementTool.execute(args, context);
643
652
  case "element_exists":
644
653
  return await elementExistsTool.execute(args, context);
645
- case "compare_positions":
646
- return await comparePositionsTool.execute(args, context);
654
+ case "compare_element_alignment":
655
+ return await compareElementAlignmentTool.execute(args, context);
647
656
  case "wait_for_element":
648
657
  return await waitForElementTool.execute(args, context);
649
658
  case "wait_for_network_idle":
@@ -0,0 +1,11 @@
1
+ import { BrowserToolBase } from './base.js';
2
+ import { ToolContext, ToolResponse } from '../common/types.js';
3
+ /**
4
+ * Tool for comparing alignment of two elements
5
+ */
6
+ export declare class CompareElementAlignmentTool extends BrowserToolBase {
7
+ /**
8
+ * Execute the compare element alignment tool
9
+ */
10
+ execute(args: any, context: ToolContext): Promise<ToolResponse>;
11
+ }
@@ -0,0 +1,171 @@
1
+ import { BrowserToolBase } from './base.js';
2
+ import { createSuccessResponse, createErrorResponse } from '../common/types.js';
3
+ /**
4
+ * Tool for comparing alignment of two elements
5
+ */
6
+ export class CompareElementAlignmentTool extends BrowserToolBase {
7
+ /**
8
+ * Execute the compare element alignment tool
9
+ */
10
+ async execute(args, context) {
11
+ return this.safeExecute(context, async (page) => {
12
+ const selector1 = this.normalizeSelector(args.selector1);
13
+ const selector2 = this.normalizeSelector(args.selector2);
14
+ try {
15
+ // Get locators for both elements
16
+ const locator1 = page.locator(selector1);
17
+ const locator2 = page.locator(selector2);
18
+ // Check if both elements exist
19
+ const count1 = await locator1.count();
20
+ const count2 = await locator2.count();
21
+ if (count1 === 0) {
22
+ return createErrorResponse(`First element not found: ${args.selector1}`);
23
+ }
24
+ if (count2 === 0) {
25
+ return createErrorResponse(`Second element not found: ${args.selector2}`);
26
+ }
27
+ // Handle multiple matches by using first() - show warning
28
+ const targetLocator1 = count1 > 1 ? locator1.first() : locator1;
29
+ const targetLocator2 = count2 > 1 ? locator2.first() : locator2;
30
+ let warnings = '';
31
+ if (count1 > 1) {
32
+ warnings += `⚠ Warning: First selector matched ${count1} elements, using first\n`;
33
+ }
34
+ if (count2 > 1) {
35
+ warnings += `⚠ Warning: Second selector matched ${count2} elements, using first\n`;
36
+ }
37
+ if (warnings) {
38
+ warnings += '\n';
39
+ }
40
+ // Get bounding boxes
41
+ const box1 = await targetLocator1.boundingBox();
42
+ const box2 = await targetLocator2.boundingBox();
43
+ // Get element descriptors
44
+ const getDescriptor = async (locator) => {
45
+ return await locator.evaluate((element) => {
46
+ const tagName = element.tagName.toLowerCase();
47
+ const testId = element.getAttribute('data-testid') || element.getAttribute('data-test') || element.getAttribute('data-cy');
48
+ const id = element.id ? `#${element.id}` : '';
49
+ const classes = element.className && typeof element.className === 'string'
50
+ ? `.${element.className.split(' ').filter(c => c).join('.')}`
51
+ : '';
52
+ let descriptor = `<${tagName}`;
53
+ if (testId)
54
+ descriptor += ` data-testid="${testId}"`;
55
+ else if (id)
56
+ descriptor += id;
57
+ else if (classes)
58
+ descriptor += classes;
59
+ descriptor += '>';
60
+ return descriptor;
61
+ });
62
+ };
63
+ const descriptor1 = await getDescriptor(targetLocator1);
64
+ const descriptor2 = await getDescriptor(targetLocator2);
65
+ // Handle hidden elements
66
+ if (!box1) {
67
+ return createErrorResponse(`First element is hidden or has no dimensions: ${descriptor1}`);
68
+ }
69
+ if (!box2) {
70
+ return createErrorResponse(`Second element is hidden or has no dimensions: ${descriptor2}`);
71
+ }
72
+ // Extract short name from descriptor for compact output
73
+ const getShortName = (descriptor, selector) => {
74
+ const testIdMatch = descriptor.match(/data-testid="([^"]+)"/);
75
+ if (testIdMatch)
76
+ return testIdMatch[1];
77
+ const idMatch = descriptor.match(/#([^>]+)/);
78
+ if (idMatch)
79
+ return idMatch[1];
80
+ // Use original selector if available
81
+ return selector;
82
+ };
83
+ const name1 = getShortName(descriptor1, args.selector1);
84
+ const name2 = getShortName(descriptor2, args.selector2);
85
+ // Calculate all alignment values
86
+ const tolerance = 2; // Allow 2px tolerance for rounding
87
+ // Edge positions
88
+ const top1 = Math.round(box1.y);
89
+ const top2 = Math.round(box2.y);
90
+ const topDiff = Math.abs(top1 - top2);
91
+ const topAligned = topDiff <= tolerance;
92
+ const left1 = Math.round(box1.x);
93
+ const left2 = Math.round(box2.x);
94
+ const leftDiff = Math.abs(left1 - left2);
95
+ const leftAligned = leftDiff <= tolerance;
96
+ const right1 = Math.round(box1.x + box1.width);
97
+ const right2 = Math.round(box2.x + box2.width);
98
+ const rightDiff = Math.abs(right1 - right2);
99
+ const rightAligned = rightDiff <= tolerance;
100
+ const bottom1 = Math.round(box1.y + box1.height);
101
+ const bottom2 = Math.round(box2.y + box2.height);
102
+ const bottomDiff = Math.abs(bottom1 - bottom2);
103
+ const bottomAligned = bottomDiff <= tolerance;
104
+ // Center positions
105
+ const centerH1 = Math.round(box1.x + box1.width / 2);
106
+ const centerH2 = Math.round(box2.x + box2.width / 2);
107
+ const centerHDiff = Math.abs(centerH1 - centerH2);
108
+ const centerHAligned = centerHDiff <= tolerance;
109
+ const centerV1 = Math.round(box1.y + box1.height / 2);
110
+ const centerV2 = Math.round(box2.y + box2.height / 2);
111
+ const centerVDiff = Math.abs(centerV1 - centerV2);
112
+ const centerVAligned = centerVDiff <= tolerance;
113
+ // Dimensions
114
+ const width1 = Math.round(box1.width);
115
+ const width2 = Math.round(box2.width);
116
+ const widthDiff = Math.abs(width1 - width2);
117
+ const widthSame = widthDiff <= tolerance;
118
+ const height1 = Math.round(box1.height);
119
+ const height2 = Math.round(box2.height);
120
+ const heightDiff = Math.abs(height1 - height2);
121
+ const heightSame = heightDiff <= tolerance;
122
+ // Format compact output
123
+ const formatAlignment = (aligned, val1, val2, diff, unit = 'px') => {
124
+ const symbol = aligned ? '✓' : '✗';
125
+ const status = aligned ? 'aligned' : 'not aligned';
126
+ if (aligned) {
127
+ return `${symbol} ${status} (both @ ${val1}${unit})`;
128
+ }
129
+ else {
130
+ return `${symbol} ${status} (${val1}${unit} vs ${val2}${unit}, diff: ${diff}${unit})`;
131
+ }
132
+ };
133
+ const formatDimension = (same, val1, val2, diff, unit = 'px') => {
134
+ const symbol = same ? '✓' : '✗';
135
+ const status = same ? 'same' : 'different';
136
+ if (same) {
137
+ return `${symbol} ${status} (${val1}${unit})`;
138
+ }
139
+ else {
140
+ return `${symbol} ${status} (${val1}${unit} vs ${val2}${unit}, diff: ${diff}${unit})`;
141
+ }
142
+ };
143
+ // Build output
144
+ const lines = [
145
+ warnings,
146
+ `Alignment: ${descriptor1} vs ${descriptor2}`,
147
+ ` ${name1}: @ (${left1},${top1}) ${width1}×${height1}px`,
148
+ ` ${name2}: @ (${left2},${top2}) ${width2}×${height2}px`,
149
+ '',
150
+ 'Edges:',
151
+ ` Top: ${formatAlignment(topAligned, top1, top2, topDiff)}`,
152
+ ` Left: ${formatAlignment(leftAligned, left1, left2, leftDiff)}`,
153
+ ` Right: ${formatAlignment(rightAligned, right1, right2, rightDiff)}`,
154
+ ` Bottom: ${formatAlignment(bottomAligned, bottom1, bottom2, bottomDiff)}`,
155
+ '',
156
+ 'Centers:',
157
+ ` Horizontal: ${formatAlignment(centerHAligned, centerH1, centerH2, centerHDiff)}`,
158
+ ` Vertical: ${formatAlignment(centerVAligned, centerV1, centerV2, centerVDiff)}`,
159
+ '',
160
+ 'Dimensions:',
161
+ ` Width: ${formatDimension(widthSame, width1, width2, widthDiff)}`,
162
+ ` Height: ${formatDimension(heightSame, height1, height2, heightDiff)}`
163
+ ];
164
+ return createSuccessResponse(lines.filter(l => l !== undefined).join('\n'));
165
+ }
166
+ catch (error) {
167
+ return createErrorResponse(`Failed to compare element alignment: ${error.message}`);
168
+ }
169
+ });
170
+ }
171
+ }
@@ -391,6 +391,13 @@ export class InspectDomTool extends BrowserToolBase {
391
391
  lines.push(`${prefix} ${tag}${roleInfo}`);
392
392
  // Position
393
393
  lines.push(` @ (${child.position.x},${child.position.y}) ${child.position.width}x${child.position.height}px`);
394
+ // Calculate distances from all parent edges
395
+ const fromLeft = child.position.x - target.position.x;
396
+ const fromRight = (target.position.x + target.position.width) - (child.position.x + child.position.width);
397
+ const fromTop = child.position.y - target.position.y;
398
+ const fromBottom = (target.position.y + target.position.height) - (child.position.y + child.position.height);
399
+ // Format edge distances (centering is obvious: equal left/right = horizontal center, equal top/bottom = vertical center)
400
+ lines.push(` from edges: ←${fromLeft}px →${fromRight}px ↑${fromTop}px ↓${fromBottom}px`);
394
401
  // Calculate offset from previous sibling
395
402
  if (index > 0) {
396
403
  const prev = children[index - 1];
package/dist/tools.d.ts CHANGED
@@ -48,7 +48,7 @@ export declare function createToolDefinitions(sessionConfig?: SessionConfig): [{
48
48
  };
49
49
  }, {
50
50
  readonly name: "screenshot";
51
- readonly description: `Take a screenshot of the current page or a specific element. Screenshots are saved to ${string} by default. Example: { name: "login-page", fullPage: true } or { name: "submit-btn", selector: "testid:submit" }`;
51
+ readonly description: `\u26A0\uFE0F RARELY NEEDED: Screenshots are NOT useful for LLMs to analyze layouts, margins, or alignment issues. Use inspect_dom(), compare_positions(), or measure_element() instead - they provide precise numerical data. Only take screenshots for: (1) sharing with humans for visual confirmation, (2) documenting test results, or (3) verifying colors/images. Screenshots are saved to ${string}. Example: { name: "login-page", fullPage: true } or { name: "submit-btn", selector: "testid:submit" }`;
52
52
  readonly inputSchema: {
53
53
  readonly type: "object";
54
54
  readonly properties: {
@@ -150,7 +150,7 @@ export declare function createToolDefinitions(sessionConfig?: SessionConfig): [{
150
150
  };
151
151
  }, {
152
152
  readonly name: "evaluate";
153
- readonly description: "Execute JavaScript in the browser console";
153
+ readonly description: "Execute JavaScript in the browser console. ⚠️ AVOID for common tasks - use specialized tools instead: inspect_dom() for page structure, compare_positions() for alignment, measure_element() for spacing, query_selector() for finding elements. Only use evaluate() for custom logic not covered by other tools.";
154
154
  readonly inputSchema: {
155
155
  readonly type: "object";
156
156
  readonly properties: {
@@ -210,7 +210,7 @@ export declare function createToolDefinitions(sessionConfig?: SessionConfig): [{
210
210
  };
211
211
  }, {
212
212
  readonly name: "get_html";
213
- readonly description: "Get the HTML content of the current page. By default, all <script> tags are removed from the output unless removeScripts is explicitly set to false.";
213
+ readonly description: "Get raw HTML content of the page or specific element. ⚠️ For understanding page structure, use inspect_dom() instead - it's more efficient and filters out noise. Use get_html() only when you need the actual HTML markup (e.g., to check specific attributes or element nesting).";
214
214
  readonly inputSchema: {
215
215
  readonly type: "object";
216
216
  readonly properties: {
@@ -312,22 +312,9 @@ export declare function createToolDefinitions(sessionConfig?: SessionConfig): [{
312
312
  };
313
313
  readonly required: readonly ["selector"];
314
314
  };
315
- }, {
316
- readonly name: "get_position";
317
- readonly description: "Get the position and size of an element. Returns x, y coordinates and width/height in pixels. Useful for finding where to click or checking element layout. Supports testid shortcuts (e.g., 'testid:submit-button').";
318
- readonly inputSchema: {
319
- readonly type: "object";
320
- readonly properties: {
321
- readonly selector: {
322
- readonly type: "string";
323
- readonly description: "CSS selector, text selector, or testid shorthand (e.g., 'testid:login-button', '#submit', 'text=Click here')";
324
- };
325
- };
326
- readonly required: readonly ["selector"];
327
- };
328
315
  }, {
329
316
  readonly name: "inspect_dom";
330
- readonly description: "Progressive DOM inspection with semantic filtering and spatial layout info. This is the PRIMARY tool for understanding page structure. Returns immediate semantic children only (header, nav, main, form, button, elements with test IDs, ARIA roles, etc.) while automatically drilling through non-semantic wrapper elements (div, span, etc.) up to maxDepth levels. Use without selector for page overview, then drill down by calling again with a child's selector. Returns compact text format with position, visibility, and layout pattern detection. Supports testid shortcuts.";
317
+ readonly description: "START HERE FOR LAYOUT DEBUGGING: Progressive DOM inspection that shows parent-child relationships, centering issues, and spacing gaps. Skips wrapper divs and shows only semantic elements (header, nav, main, form, button, elements with test IDs, ARIA roles, etc.).\n\nWORKFLOW: Call without selector for page overview, then drill down by calling with child's selector.\n\nDETECTS: Parent-relative positioning, vertical/horizontal centering, sibling spacing gaps, layout patterns.\n\nOUTPUT FORMAT:\n[0] <button data-testid=\"menu\">\n @ (16,8) 40×40px ← Absolute viewport position (x,y) and size\n from edges: ←16px →1144px ↑8px ↓8px ← Distance from parent edges (↑8px = ↓8px means vertically centered)\n \"Menu\"\n ✓ visible, interactive\n\n[1] <div data-testid=\"title\">\n @ (260,2) 131×28px\n from edges: ←244px →244px ↑2px ↓42px ← Equal left/right (244px) = horizontally centered, unequal top/bottom = NOT vertically centered\n gap from [0]: →16px ← Spacing between siblings\n \"Title\"\n ✓ visible, 2 children\n\nSYMBOLS: ✓=visible, ✗=hidden, ⚡=interactive, ←→=horizontal edges, ↑↓=vertical edges\nCENTERING: Equal left/right distances = horizontally centered, equal top/bottom = vertically centered\n\nRELATED TOOLS: For comparing TWO elements' alignment (not parent-child), use compare_element_alignment(). For box model (padding/margin), use measure_element().\n\nMore efficient than get_html() or evaluate(). Supports testid shortcuts.";
331
318
  readonly inputSchema: {
332
319
  readonly type: "object";
333
320
  readonly properties: {
@@ -423,7 +410,7 @@ export declare function createToolDefinitions(sessionConfig?: SessionConfig): [{
423
410
  };
424
411
  }, {
425
412
  readonly name: "get_computed_styles";
426
- readonly description: "Get computed CSS styles for an element. Essential for understanding why elements behave unexpectedly and debugging layout issues. Returns styles grouped by category (Layout, Visibility, Spacing, Typography). Use properties parameter to request specific CSS properties, or omit for common layout properties.";
413
+ readonly description: "INSPECT CSS PROPERTIES: Get computed CSS values for specific properties (display, position, width, etc.). Use when you need raw CSS values or specific properties not shown by measure_element(). Returns styles grouped by category (Layout, Visibility, Spacing, Typography). For box model visualization (padding/margin), use measure_element() instead.";
427
414
  readonly inputSchema: {
428
415
  readonly type: "object";
429
416
  readonly properties: {
@@ -440,7 +427,7 @@ export declare function createToolDefinitions(sessionConfig?: SessionConfig): [{
440
427
  };
441
428
  }, {
442
429
  readonly name: "measure_element";
443
- readonly description: "Get box model measurements (position, size, margin, padding, border) for an element. Use for layout debugging, spacing validation, and understanding CSS box model. Returns compact visual representation of content, padding, border, and margin with directional arrows.";
430
+ readonly description: "DEBUG SPACING ISSUES: See padding, margin, and border measurements in visual box model format. Use when elements have unexpected spacing or size. Returns compact visual representation showing content padding border margin with directional arrows (↑24px for top margin, etc.). For parent-child centering issues, use inspect_dom() first (shows if child is centered in parent). For comparing alignment between two elements, use compare_element_alignment(). More readable than get_computed_styles() for box model debugging.";
444
431
  readonly inputSchema: {
445
432
  readonly type: "object";
446
433
  readonly properties: {
@@ -465,8 +452,8 @@ export declare function createToolDefinitions(sessionConfig?: SessionConfig): [{
465
452
  readonly required: readonly ["selector"];
466
453
  };
467
454
  }, {
468
- readonly name: "compare_positions";
469
- readonly description: "Compare positions and alignment of two elements. Validates layout consistency by checking if elements are aligned (top, left, right, bottom) or have the same dimensions (width, height). Essential for visual regression testing and ensuring consistent spacing across components. Returns compact text format with alignment status and difference in pixels.";
455
+ readonly name: "compare_element_alignment";
456
+ readonly description: "COMPARE TWO ELEMENTS: Get comprehensive alignment and dimension comparison in one call. Shows edge alignment (top, left, right, bottom), center alignment (horizontal, vertical), and dimensions (width, height). Perfect for debugging 'are these headers aligned?' or 'do these panels match?'. Returns all alignment info with ✓/✗ symbols and pixel differences. For parent-child centering, use inspect_dom() instead (automatically shows if children are centered in parent). More efficient than evaluate() with manual getBoundingClientRect() calculations.";
470
457
  readonly inputSchema: {
471
458
  readonly type: "object";
472
459
  readonly properties: {
@@ -478,13 +465,8 @@ export declare function createToolDefinitions(sessionConfig?: SessionConfig): [{
478
465
  readonly type: "string";
479
466
  readonly description: "CSS selector, text selector, or testid shorthand for the second element (e.g., 'testid:chat-header', '#secondary-header')";
480
467
  };
481
- readonly checkAlignment: {
482
- readonly type: "string";
483
- readonly description: "What to check: 'top', 'left', 'right', 'bottom' (edge alignment), or 'width', 'height' (dimension matching)";
484
- readonly enum: readonly ["top", "left", "right", "bottom", "width", "height"];
485
- };
486
468
  };
487
- readonly required: readonly ["selector1", "selector2", "checkAlignment"];
469
+ readonly required: readonly ["selector1", "selector2"];
488
470
  };
489
471
  }, {
490
472
  readonly name: "wait_for_element";
package/dist/tools.js CHANGED
@@ -31,7 +31,7 @@ export function createToolDefinitions(sessionConfig) {
31
31
  },
32
32
  {
33
33
  name: "screenshot",
34
- description: `Take a screenshot of the current page or a specific element. Screenshots are saved to ${screenshotsDir} by default. Example: { name: "login-page", fullPage: true } or { name: "submit-btn", selector: "testid:submit" }`,
34
+ description: `⚠️ RARELY NEEDED: Screenshots are NOT useful for LLMs to analyze layouts, margins, or alignment issues. Use inspect_dom(), compare_positions(), or measure_element() instead - they provide precise numerical data. Only take screenshots for: (1) sharing with humans for visual confirmation, (2) documenting test results, or (3) verifying colors/images. Screenshots are saved to ${screenshotsDir}. Example: { name: "login-page", fullPage: true } or { name: "submit-btn", selector: "testid:submit" }`,
35
35
  inputSchema: {
36
36
  type: "object",
37
37
  properties: {
@@ -115,7 +115,7 @@ export function createToolDefinitions(sessionConfig) {
115
115
  },
116
116
  {
117
117
  name: "evaluate",
118
- description: "Execute JavaScript in the browser console",
118
+ description: "Execute JavaScript in the browser console. ⚠️ AVOID for common tasks - use specialized tools instead: inspect_dom() for page structure, compare_positions() for alignment, measure_element() for spacing, query_selector() for finding elements. Only use evaluate() for custom logic not covered by other tools.",
119
119
  inputSchema: {
120
120
  type: "object",
121
121
  properties: {
@@ -176,7 +176,7 @@ export function createToolDefinitions(sessionConfig) {
176
176
  },
177
177
  {
178
178
  name: "get_html",
179
- description: "Get the HTML content of the current page. By default, all <script> tags are removed from the output unless removeScripts is explicitly set to false.",
179
+ description: "Get raw HTML content of the page or specific element. ⚠️ For understanding page structure, use inspect_dom() instead - it's more efficient and filters out noise. Use get_html() only when you need the actual HTML markup (e.g., to check specific attributes or element nesting).",
180
180
  inputSchema: {
181
181
  type: "object",
182
182
  properties: {
@@ -248,23 +248,34 @@ export function createToolDefinitions(sessionConfig) {
248
248
  required: ["selector"],
249
249
  },
250
250
  },
251
- {
252
- name: "get_position",
253
- description: "Get the position and size of an element. Returns x, y coordinates and width/height in pixels. Useful for finding where to click or checking element layout. Supports testid shortcuts (e.g., 'testid:submit-button').",
254
- inputSchema: {
255
- type: "object",
256
- properties: {
257
- selector: {
258
- type: "string",
259
- description: "CSS selector, text selector, or testid shorthand (e.g., 'testid:login-button', '#submit', 'text=Click here')"
260
- },
261
- },
262
- required: ["selector"],
263
- },
264
- },
265
251
  {
266
252
  name: "inspect_dom",
267
- description: "Progressive DOM inspection with semantic filtering and spatial layout info. This is the PRIMARY tool for understanding page structure. Returns immediate semantic children only (header, nav, main, form, button, elements with test IDs, ARIA roles, etc.) while automatically drilling through non-semantic wrapper elements (div, span, etc.) up to maxDepth levels. Use without selector for page overview, then drill down by calling again with a child's selector. Returns compact text format with position, visibility, and layout pattern detection. Supports testid shortcuts.",
253
+ description: `START HERE FOR LAYOUT DEBUGGING: Progressive DOM inspection that shows parent-child relationships, centering issues, and spacing gaps. Skips wrapper divs and shows only semantic elements (header, nav, main, form, button, elements with test IDs, ARIA roles, etc.).
254
+
255
+ WORKFLOW: Call without selector for page overview, then drill down by calling with child's selector.
256
+
257
+ DETECTS: Parent-relative positioning, vertical/horizontal centering, sibling spacing gaps, layout patterns.
258
+
259
+ OUTPUT FORMAT:
260
+ [0] <button data-testid="menu">
261
+ @ (16,8) 40×40px ← Absolute viewport position (x,y) and size
262
+ from edges: ←16px →1144px ↑8px ↓8px ← Distance from parent edges (↑8px = ↓8px means vertically centered)
263
+ "Menu"
264
+ ✓ visible, ⚡ interactive
265
+
266
+ [1] <div data-testid="title">
267
+ @ (260,2) 131×28px
268
+ from edges: ←244px →244px ↑2px ↓42px ← Equal left/right (244px) = horizontally centered, unequal top/bottom = NOT vertically centered
269
+ gap from [0]: →16px ← Spacing between siblings
270
+ "Title"
271
+ ✓ visible, 2 children
272
+
273
+ SYMBOLS: ✓=visible, ✗=hidden, ⚡=interactive, ←→=horizontal edges, ↑↓=vertical edges
274
+ CENTERING: Equal left/right distances = horizontally centered, equal top/bottom = vertically centered
275
+
276
+ RELATED TOOLS: For comparing TWO elements' alignment (not parent-child), use compare_element_alignment(). For box model (padding/margin), use measure_element().
277
+
278
+ More efficient than get_html() or evaluate(). Supports testid shortcuts.`,
268
279
  inputSchema: {
269
280
  type: "object",
270
281
  properties: {
@@ -364,7 +375,7 @@ export function createToolDefinitions(sessionConfig) {
364
375
  },
365
376
  {
366
377
  name: "get_computed_styles",
367
- description: "Get computed CSS styles for an element. Essential for understanding why elements behave unexpectedly and debugging layout issues. Returns styles grouped by category (Layout, Visibility, Spacing, Typography). Use properties parameter to request specific CSS properties, or omit for common layout properties.",
378
+ description: "INSPECT CSS PROPERTIES: Get computed CSS values for specific properties (display, position, width, etc.). Use when you need raw CSS values or specific properties not shown by measure_element(). Returns styles grouped by category (Layout, Visibility, Spacing, Typography). For box model visualization (padding/margin), use measure_element() instead.",
368
379
  inputSchema: {
369
380
  type: "object",
370
381
  properties: {
@@ -382,7 +393,7 @@ export function createToolDefinitions(sessionConfig) {
382
393
  },
383
394
  {
384
395
  name: "measure_element",
385
- description: "Get box model measurements (position, size, margin, padding, border) for an element. Use for layout debugging, spacing validation, and understanding CSS box model. Returns compact visual representation of content, padding, border, and margin with directional arrows.",
396
+ description: "DEBUG SPACING ISSUES: See padding, margin, and border measurements in visual box model format. Use when elements have unexpected spacing or size. Returns compact visual representation showing content padding border margin with directional arrows (↑24px for top margin, etc.). For parent-child centering issues, use inspect_dom() first (shows if child is centered in parent). For comparing alignment between two elements, use compare_element_alignment(). More readable than get_computed_styles() for box model debugging.",
386
397
  inputSchema: {
387
398
  type: "object",
388
399
  properties: {
@@ -409,8 +420,8 @@ export function createToolDefinitions(sessionConfig) {
409
420
  },
410
421
  },
411
422
  {
412
- name: "compare_positions",
413
- description: "Compare positions and alignment of two elements. Validates layout consistency by checking if elements are aligned (top, left, right, bottom) or have the same dimensions (width, height). Essential for visual regression testing and ensuring consistent spacing across components. Returns compact text format with alignment status and difference in pixels.",
423
+ name: "compare_element_alignment",
424
+ description: "COMPARE TWO ELEMENTS: Get comprehensive alignment and dimension comparison in one call. Shows edge alignment (top, left, right, bottom), center alignment (horizontal, vertical), and dimensions (width, height). Perfect for debugging 'are these headers aligned?' or 'do these panels match?'. Returns all alignment info with ✓/✗ symbols and pixel differences. For parent-child centering, use inspect_dom() instead (automatically shows if children are centered in parent). More efficient than evaluate() with manual getBoundingClientRect() calculations.",
414
425
  inputSchema: {
415
426
  type: "object",
416
427
  properties: {
@@ -421,14 +432,9 @@ export function createToolDefinitions(sessionConfig) {
421
432
  selector2: {
422
433
  type: "string",
423
434
  description: "CSS selector, text selector, or testid shorthand for the second element (e.g., 'testid:chat-header', '#secondary-header')"
424
- },
425
- checkAlignment: {
426
- type: "string",
427
- description: "What to check: 'top', 'left', 'right', 'bottom' (edge alignment), or 'width', 'height' (dimension matching)",
428
- enum: ["top", "left", "right", "bottom", "width", "height"]
429
435
  }
430
436
  },
431
- required: ["selector1", "selector2", "checkAlignment"],
437
+ required: ["selector1", "selector2"],
432
438
  },
433
439
  },
434
440
  {
@@ -516,8 +522,7 @@ export const BROWSER_TOOLS = [
516
522
  "find_by_text",
517
523
  // Visibility & Position
518
524
  "check_visibility",
519
- "get_position",
520
- "compare_positions",
525
+ "compare_element_alignment",
521
526
  "element_exists",
522
527
  "wait_for_element",
523
528
  "wait_for_network_idle",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-web-inspector",
3
- "version": "0.1.0",
3
+ "version": "0.1.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",