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 +166 -7
- package/dist/index.js +5 -0
- package/dist/toolHandler.d.ts +6 -1
- package/dist/toolHandler.js +124 -13
- package/dist/tools/browser/ancestorInspection.d.ts +18 -0
- package/dist/tools/browser/ancestorInspection.js +362 -0
- package/dist/tools/browser/compareElementAlignment.js +9 -0
- package/dist/tools/browser/elementVisibility.js +5 -0
- package/dist/tools/browser/inspectDom.js +6 -0
- package/dist/tools/browser/interaction.d.ts +4 -0
- package/dist/tools/browser/interaction.js +62 -8
- package/dist/tools/browser/measureElement.js +8 -0
- package/dist/tools/browser/screenshot.js +13 -1
- package/dist/tools/browser/visiblePage.js +80 -68
- package/dist/tools.d.ts +36 -29
- package/dist/tools.js +47 -14
- package/dist/utils/browserCheck.d.ts +12 -0
- package/dist/utils/browserCheck.js +67 -0
- package/package.json +1 -1
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
|
-
-
|
|
387
|
-
- Use `headless:
|
|
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() {
|
package/dist/toolHandler.d.ts
CHANGED
|
@@ -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;
|
package/dist/toolHandler.js
CHANGED
|
@@ -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
|
-
|
|
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 =
|
|
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:
|
|
291
|
-
height:
|
|
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:
|
|
335
|
-
height:
|
|
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 =
|
|
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:
|
|
408
|
-
height:
|
|
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:
|
|
448
|
-
height:
|
|
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
|
+
}
|