mcp-web-inspector 0.1.0

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 (85) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1017 -0
  3. package/dist/evals/evals.d.ts +5 -0
  4. package/dist/evals/evals.js +41 -0
  5. package/dist/index.d.ts +2 -0
  6. package/dist/index.js +62 -0
  7. package/dist/requestHandler.d.ts +3 -0
  8. package/dist/requestHandler.js +53 -0
  9. package/dist/toolHandler.d.ts +91 -0
  10. package/dist/toolHandler.js +725 -0
  11. package/dist/tools/api/base.d.ts +33 -0
  12. package/dist/tools/api/base.js +49 -0
  13. package/dist/tools/api/index.d.ts +2 -0
  14. package/dist/tools/api/index.js +3 -0
  15. package/dist/tools/api/requests.d.ts +47 -0
  16. package/dist/tools/api/requests.js +168 -0
  17. package/dist/tools/browser/base.d.ts +51 -0
  18. package/dist/tools/browser/base.js +111 -0
  19. package/dist/tools/browser/cleanSession.d.ts +10 -0
  20. package/dist/tools/browser/cleanSession.js +42 -0
  21. package/dist/tools/browser/comparePositions.d.ts +11 -0
  22. package/dist/tools/browser/comparePositions.js +149 -0
  23. package/dist/tools/browser/computedStyles.d.ts +11 -0
  24. package/dist/tools/browser/computedStyles.js +128 -0
  25. package/dist/tools/browser/console.d.ts +37 -0
  26. package/dist/tools/browser/console.js +106 -0
  27. package/dist/tools/browser/elementExists.d.ts +9 -0
  28. package/dist/tools/browser/elementExists.js +57 -0
  29. package/dist/tools/browser/elementInspection.d.ts +21 -0
  30. package/dist/tools/browser/elementInspection.js +151 -0
  31. package/dist/tools/browser/elementPosition.d.ts +11 -0
  32. package/dist/tools/browser/elementPosition.js +107 -0
  33. package/dist/tools/browser/elementVisibility.d.ts +12 -0
  34. package/dist/tools/browser/elementVisibility.js +224 -0
  35. package/dist/tools/browser/findByText.d.ts +13 -0
  36. package/dist/tools/browser/findByText.js +207 -0
  37. package/dist/tools/browser/getRequestDetails.d.ts +9 -0
  38. package/dist/tools/browser/getRequestDetails.js +137 -0
  39. package/dist/tools/browser/getTestIds.d.ts +12 -0
  40. package/dist/tools/browser/getTestIds.js +148 -0
  41. package/dist/tools/browser/index.d.ts +7 -0
  42. package/dist/tools/browser/index.js +7 -0
  43. package/dist/tools/browser/inspectDom.d.ts +12 -0
  44. package/dist/tools/browser/inspectDom.js +447 -0
  45. package/dist/tools/browser/interaction.d.ts +104 -0
  46. package/dist/tools/browser/interaction.js +259 -0
  47. package/dist/tools/browser/listNetworkRequests.d.ts +10 -0
  48. package/dist/tools/browser/listNetworkRequests.js +74 -0
  49. package/dist/tools/browser/measureElement.d.ts +9 -0
  50. package/dist/tools/browser/measureElement.js +139 -0
  51. package/dist/tools/browser/navigation.d.ts +38 -0
  52. package/dist/tools/browser/navigation.js +109 -0
  53. package/dist/tools/browser/output.d.ts +11 -0
  54. package/dist/tools/browser/output.js +29 -0
  55. package/dist/tools/browser/querySelectorAll.d.ts +12 -0
  56. package/dist/tools/browser/querySelectorAll.js +201 -0
  57. package/dist/tools/browser/response.d.ts +29 -0
  58. package/dist/tools/browser/response.js +67 -0
  59. package/dist/tools/browser/screenshot.d.ts +16 -0
  60. package/dist/tools/browser/screenshot.js +70 -0
  61. package/dist/tools/browser/useragent.d.ts +15 -0
  62. package/dist/tools/browser/useragent.js +32 -0
  63. package/dist/tools/browser/visiblePage.d.ts +20 -0
  64. package/dist/tools/browser/visiblePage.js +170 -0
  65. package/dist/tools/browser/waitForElement.d.ts +10 -0
  66. package/dist/tools/browser/waitForElement.js +38 -0
  67. package/dist/tools/browser/waitForNetworkIdle.d.ts +8 -0
  68. package/dist/tools/browser/waitForNetworkIdle.js +32 -0
  69. package/dist/tools/codegen/generator.d.ts +21 -0
  70. package/dist/tools/codegen/generator.js +158 -0
  71. package/dist/tools/codegen/index.d.ts +11 -0
  72. package/dist/tools/codegen/index.js +187 -0
  73. package/dist/tools/codegen/recorder.d.ts +14 -0
  74. package/dist/tools/codegen/recorder.js +62 -0
  75. package/dist/tools/codegen/types.d.ts +28 -0
  76. package/dist/tools/codegen/types.js +1 -0
  77. package/dist/tools/common/types.d.ts +17 -0
  78. package/dist/tools/common/types.js +20 -0
  79. package/dist/tools/index.d.ts +2 -0
  80. package/dist/tools/index.js +2 -0
  81. package/dist/tools.d.ts +557 -0
  82. package/dist/tools.js +554 -0
  83. package/dist/types.d.ts +16 -0
  84. package/dist/types.js +1 -0
  85. package/package.json +60 -0
@@ -0,0 +1,447 @@
1
+ import { BrowserToolBase } from './base.js';
2
+ import { createSuccessResponse, createErrorResponse } from '../common/types.js';
3
+ /**
4
+ * Tool for progressive DOM inspection with semantic filtering and spatial layout
5
+ * This is the PRIMARY tool for understanding page structure
6
+ */
7
+ export class InspectDomTool extends BrowserToolBase {
8
+ /**
9
+ * Execute the DOM inspection tool
10
+ */
11
+ async execute(args, context) {
12
+ return this.safeExecute(context, async (page) => {
13
+ const selector = args.selector ? this.normalizeSelector(args.selector) : 'body';
14
+ const includeHidden = args.includeHidden ?? false;
15
+ const maxChildren = args.maxChildren ?? 20;
16
+ const maxDepth = args.maxDepth ?? 5;
17
+ try {
18
+ // Get the target element and its semantic children
19
+ const inspectionData = await page.evaluate(({ sel, hidden, max, maxDepth }) => {
20
+ const target = document.querySelector(sel);
21
+ if (!target) {
22
+ return { error: `Element not found: ${sel}` };
23
+ }
24
+ // Get element info
25
+ const getElementInfo = (el) => {
26
+ const rect = el.getBoundingClientRect();
27
+ const styles = window.getComputedStyle(el);
28
+ const isVisible = styles.display !== 'none' &&
29
+ styles.visibility !== 'hidden' &&
30
+ parseFloat(styles.opacity) > 0 &&
31
+ rect.width > 0 &&
32
+ rect.height > 0;
33
+ return {
34
+ rect: {
35
+ x: Math.round(rect.x),
36
+ y: Math.round(rect.y),
37
+ width: Math.round(rect.width),
38
+ height: Math.round(rect.height),
39
+ },
40
+ isVisible,
41
+ styles: {
42
+ display: styles.display,
43
+ visibility: styles.visibility,
44
+ opacity: parseFloat(styles.opacity),
45
+ },
46
+ };
47
+ };
48
+ // Check if element is semantic (worth showing)
49
+ const isSemanticElement = (el) => {
50
+ const tag = el.tagName.toLowerCase();
51
+ // Semantic HTML tags
52
+ const semanticTags = new Set([
53
+ 'header',
54
+ 'nav',
55
+ 'main',
56
+ 'article',
57
+ 'section',
58
+ 'aside',
59
+ 'footer',
60
+ 'form',
61
+ 'button',
62
+ 'input',
63
+ 'select',
64
+ 'textarea',
65
+ 'a',
66
+ 'h1',
67
+ 'h2',
68
+ 'h3',
69
+ 'h4',
70
+ 'h5',
71
+ 'h6',
72
+ 'p',
73
+ 'ul',
74
+ 'ol',
75
+ 'li',
76
+ 'table',
77
+ 'img',
78
+ 'video',
79
+ 'audio',
80
+ 'svg',
81
+ 'canvas',
82
+ 'iframe',
83
+ 'dialog',
84
+ 'details',
85
+ 'summary',
86
+ ]);
87
+ if (semanticTags.has(tag))
88
+ return true;
89
+ // Elements with test IDs
90
+ if (el.hasAttribute('data-testid') ||
91
+ el.hasAttribute('data-test') ||
92
+ el.hasAttribute('data-cy')) {
93
+ return true;
94
+ }
95
+ // Elements with ARIA roles
96
+ if (el.hasAttribute('role'))
97
+ return true;
98
+ // Interactive elements
99
+ if (el.hasAttribute('onclick') || el.hasAttribute('contenteditable'))
100
+ return true;
101
+ // Containers with significant direct text (>10 chars)
102
+ const directText = Array.from(el.childNodes)
103
+ .filter((node) => node.nodeType === Node.TEXT_NODE)
104
+ .map((node) => node.textContent?.trim() || '')
105
+ .join(' ')
106
+ .trim();
107
+ if (directText.length > 10)
108
+ return true;
109
+ return false;
110
+ };
111
+ // Get selector for element
112
+ const getSelector = (el) => {
113
+ // Prefer test IDs
114
+ if (el.hasAttribute('data-testid')) {
115
+ return `[data-testid="${el.getAttribute('data-testid')}"]`;
116
+ }
117
+ if (el.hasAttribute('data-test')) {
118
+ return `[data-test="${el.getAttribute('data-test')}"]`;
119
+ }
120
+ if (el.hasAttribute('data-cy')) {
121
+ return `[data-cy="${el.getAttribute('data-cy')}"]`;
122
+ }
123
+ // Use ID if available
124
+ if (el.id) {
125
+ return `#${el.id}`;
126
+ }
127
+ // Use class + tag for common patterns
128
+ const tag = el.tagName.toLowerCase();
129
+ const classes = Array.from(el.classList)
130
+ .slice(0, 2)
131
+ .join('.');
132
+ if (classes) {
133
+ return `${tag}.${classes}`;
134
+ }
135
+ return tag;
136
+ };
137
+ // Check if element is interactive
138
+ const isInteractive = (el) => {
139
+ const tag = el.tagName.toLowerCase();
140
+ const interactiveTags = new Set(['button', 'a', 'input', 'select', 'textarea']);
141
+ return (interactiveTags.has(tag) ||
142
+ el.hasAttribute('onclick') ||
143
+ el.hasAttribute('contenteditable') ||
144
+ el.getAttribute('role') === 'button');
145
+ };
146
+ // Get target element info
147
+ const targetInfo = getElementInfo(target);
148
+ // Get all immediate children
149
+ const allChildren = Array.from(target.children);
150
+ const semanticChildren = [];
151
+ let skippedWrappers = 0;
152
+ // Count elements for summary
153
+ const elementCounts = {};
154
+ const interactiveCounts = {};
155
+ // Helper to count elements in entire subtree (for overview)
156
+ const countElementsInTree = (root) => {
157
+ const counts = {};
158
+ const interactiveCounts = {};
159
+ const traverse = (el) => {
160
+ const tag = el.tagName.toLowerCase();
161
+ counts[tag] = (counts[tag] || 0) + 1;
162
+ if (isInteractive(el)) {
163
+ interactiveCounts[tag] = (interactiveCounts[tag] || 0) + 1;
164
+ }
165
+ Array.from(el.children).forEach(traverse);
166
+ };
167
+ traverse(root);
168
+ return { counts, interactiveCounts };
169
+ };
170
+ // Recursive helper to collect semantic children, drilling through non-semantic wrappers
171
+ const collectSemanticChildren = (elements, depth = 0) => {
172
+ for (const child of elements) {
173
+ const childInfo = getElementInfo(child);
174
+ const tag = child.tagName.toLowerCase();
175
+ // Count all immediate children for summary (depth 0 only)
176
+ if (depth === 0) {
177
+ elementCounts[tag] = (elementCounts[tag] || 0) + 1;
178
+ }
179
+ // Skip hidden elements unless includeHidden is true
180
+ if (!hidden && !childInfo.isVisible) {
181
+ if (depth === 0)
182
+ skippedWrappers++;
183
+ continue;
184
+ }
185
+ // Check if this element is semantic
186
+ const isSemantic = isSemanticElement(child);
187
+ if (isSemantic) {
188
+ // This is a semantic element - add it to the list and stop drilling
189
+ const text = child.textContent?.trim().slice(0, 100) || '';
190
+ const testId = child.getAttribute('data-testid') ||
191
+ child.getAttribute('data-test') ||
192
+ child.getAttribute('data-cy') ||
193
+ undefined;
194
+ const semanticChild = {
195
+ tag: child.tagName.toLowerCase(),
196
+ selector: getSelector(child),
197
+ testId,
198
+ role: child.getAttribute('role') || undefined,
199
+ text,
200
+ position: childInfo.rect,
201
+ isVisible: childInfo.isVisible,
202
+ isInteractive: isInteractive(child),
203
+ childCount: child.children.length,
204
+ };
205
+ semanticChildren.push(semanticChild);
206
+ // Count this semantic element in interactiveCounts if it's interactive
207
+ if (isInteractive(child)) {
208
+ interactiveCounts[tag] = (interactiveCounts[tag] || 0) + 1;
209
+ }
210
+ }
211
+ else if (depth < maxDepth) {
212
+ // This is a non-semantic wrapper - drill through it to find semantic children
213
+ if (depth === 0)
214
+ skippedWrappers++;
215
+ // Recursively look for semantic children inside this wrapper
216
+ collectSemanticChildren(Array.from(child.children), depth + 1);
217
+ }
218
+ else {
219
+ // Hit max depth - count as skipped wrapper
220
+ if (depth === 0)
221
+ skippedWrappers++;
222
+ }
223
+ }
224
+ };
225
+ // Start collecting semantic children from immediate children
226
+ collectSemanticChildren(allChildren);
227
+ // For body/main containers, also count elements in entire tree
228
+ const targetTag = target.tagName.toLowerCase();
229
+ const isTopLevelContainer = targetTag === 'body' || sel.includes('main-layout') || sel.includes('main');
230
+ let treeCounts = null;
231
+ if (isTopLevelContainer) {
232
+ const treeData = countElementsInTree(target);
233
+ const testIdCount = target.querySelectorAll('[data-testid], [data-test], [data-cy]').length;
234
+ treeCounts = {
235
+ ...treeData,
236
+ testIdCount,
237
+ };
238
+ }
239
+ // Limit children shown
240
+ const totalSemantic = semanticChildren.length;
241
+ const shownChildren = semanticChildren.slice(0, max);
242
+ const omittedCount = Math.max(0, totalSemantic - max);
243
+ // Detect layout pattern
244
+ let layoutPattern = 'unknown';
245
+ if (shownChildren.length >= 2) {
246
+ const first = shownChildren[0].position;
247
+ const second = shownChildren[1].position;
248
+ const horizontalGap = Math.abs(second.x - (first.x + first.width));
249
+ const verticalGap = Math.abs(second.y - (first.y + first.height));
250
+ if (horizontalGap < 50 && verticalGap > 20) {
251
+ layoutPattern = 'vertical';
252
+ }
253
+ else if (verticalGap < 50 && horizontalGap > 20) {
254
+ layoutPattern = 'horizontal';
255
+ }
256
+ else if (horizontalGap < 50 && verticalGap < 50) {
257
+ layoutPattern = 'grid';
258
+ }
259
+ }
260
+ return {
261
+ target: {
262
+ tag: target.tagName.toLowerCase(),
263
+ selector: getSelector(target),
264
+ position: targetInfo.rect,
265
+ isVisible: targetInfo.isVisible,
266
+ },
267
+ children: shownChildren,
268
+ stats: {
269
+ totalChildren: allChildren.length,
270
+ semanticCount: totalSemantic,
271
+ shownCount: shownChildren.length,
272
+ omittedCount,
273
+ skippedWrappers,
274
+ },
275
+ elementCounts,
276
+ interactiveCounts,
277
+ treeCounts,
278
+ layoutPattern,
279
+ };
280
+ }, { sel: selector, hidden: includeHidden, max: maxChildren, maxDepth });
281
+ // Check for errors from evaluate
282
+ if ('error' in inspectionData) {
283
+ return createErrorResponse(inspectionData.error);
284
+ }
285
+ // Format compact text output
286
+ const lines = [];
287
+ const { target, children, stats, layoutPattern, elementCounts, interactiveCounts, treeCounts } = inspectionData;
288
+ // Header
289
+ lines.push(`DOM Inspection: <${target.tag}${target.selector ? ' ' + target.selector : ''}>`);
290
+ lines.push(`@ (${target.position.x},${target.position.y}) ${target.position.width}x${target.position.height}px`);
291
+ lines.push('');
292
+ // Add page/section overview for top-level containers
293
+ if (treeCounts) {
294
+ lines.push('Page Overview:');
295
+ // Show semantic structure counts
296
+ const semanticStructure = ['header', 'nav', 'main', 'article', 'section', 'aside', 'footer'];
297
+ const structureCounts = semanticStructure
298
+ .filter(tag => (treeCounts.counts[tag] || 0) > 0)
299
+ .map(tag => `${treeCounts.counts[tag]} ${tag}${treeCounts.counts[tag] > 1 ? 's' : ''}`)
300
+ .join(', ');
301
+ if (structureCounts) {
302
+ lines.push(` Structure: ${structureCounts}`);
303
+ }
304
+ // Show interactive element counts
305
+ const interactiveTypes = ['button', 'a', 'input', 'select', 'textarea'];
306
+ const interactiveSummary = interactiveTypes
307
+ .filter(tag => (treeCounts.interactiveCounts[tag] || 0) > 0)
308
+ .map(tag => {
309
+ const count = treeCounts.interactiveCounts[tag];
310
+ const label = tag === 'a' ? 'link' : tag;
311
+ return `${count} ${label}${count > 1 ? 's' : ''}`;
312
+ })
313
+ .join(', ');
314
+ if (interactiveSummary) {
315
+ lines.push(` Interactive: ${interactiveSummary}`);
316
+ }
317
+ // Show form counts
318
+ const formCount = treeCounts.counts.form || 0;
319
+ const inputCount = (treeCounts.counts.input || 0) + (treeCounts.counts.select || 0) + (treeCounts.counts.textarea || 0);
320
+ if (formCount > 0) {
321
+ lines.push(` Forms: ${formCount} form${formCount > 1 ? 's' : ''} with ${inputCount} input${inputCount !== 1 ? 's' : ''}`);
322
+ }
323
+ // Show test coverage
324
+ if (treeCounts.testIdCount && treeCounts.testIdCount > 0) {
325
+ lines.push(` Test Coverage: ${treeCounts.testIdCount} element${treeCounts.testIdCount > 1 ? 's' : ''} with test IDs`);
326
+ }
327
+ lines.push('');
328
+ }
329
+ // Children summary
330
+ if (stats.semanticCount === 0) {
331
+ lines.push(`Children (0 semantic, skipped ${stats.skippedWrappers} wrapper divs):`);
332
+ lines.push('');
333
+ // Show interactive element summary if available
334
+ const hasInteractive = Object.keys(interactiveCounts).length > 0;
335
+ if (hasInteractive) {
336
+ lines.push('Interactive Elements Found:');
337
+ const interactiveTypes = ['button', 'a', 'input', 'select', 'textarea'];
338
+ interactiveTypes.forEach(tag => {
339
+ const count = interactiveCounts[tag] || 0;
340
+ if (count > 0) {
341
+ const label = tag === 'a' ? 'link' : tag;
342
+ lines.push(` • ${count} ${label}${count > 1 ? 's' : ''}`);
343
+ }
344
+ });
345
+ lines.push('');
346
+ lines.push(`💡 Tip: Use maxChildren parameter or drill down with specific selectors (e.g., "button", "a")`);
347
+ lines.push(` to inspect these elements. They were skipped because they lack test IDs or semantic containers.`);
348
+ }
349
+ else {
350
+ lines.push('⚠ No semantic or interactive elements found at this level.');
351
+ lines.push('');
352
+ lines.push('The page uses generic <div> wrappers without semantic HTML, test IDs, or ARIA roles.');
353
+ }
354
+ lines.push('');
355
+ lines.push('Suggestions:');
356
+ lines.push(`1. Use get_visible_html({ selector: "${args.selector || 'body'}" }) to see raw HTML`);
357
+ lines.push('2. Look for interactive elements by class/id (e.g., .button, #submit-btn)');
358
+ lines.push('3. Recommend adding data-testid attributes for better testability');
359
+ lines.push('');
360
+ lines.push('To improve this page\'s structure, consider:');
361
+ lines.push(' - Adding semantic HTML: <header>, <main>, <nav>, <button>');
362
+ lines.push(' - Adding test IDs: data-testid="submit-button"');
363
+ lines.push(' - Adding ARIA roles: role="button", role="navigation"');
364
+ // Add drill-down suggestions when Page Overview shows interactive but Children shows none
365
+ if (treeCounts && Object.keys(interactiveCounts).length === 0 && Object.keys(treeCounts.interactiveCounts).length > 0) {
366
+ lines.push('');
367
+ lines.push('💡 Try drilling down to find interactive elements:');
368
+ const currentSelector = args.selector || 'body';
369
+ // Suggest specific selectors based on what's in the tree
370
+ if (treeCounts.interactiveCounts.button && treeCounts.interactiveCounts.button > 0) {
371
+ lines.push(` inspect_dom({ selector: "${currentSelector} button" })`);
372
+ }
373
+ if (treeCounts.interactiveCounts.input && treeCounts.interactiveCounts.input > 0) {
374
+ lines.push(` inspect_dom({ selector: "${currentSelector} input" })`);
375
+ }
376
+ if (treeCounts.interactiveCounts.a && treeCounts.interactiveCounts.a > 0) {
377
+ lines.push(` inspect_dom({ selector: "${currentSelector} a" })`);
378
+ }
379
+ }
380
+ }
381
+ else {
382
+ lines.push(`Children (${stats.shownCount} of ${stats.semanticCount}${stats.skippedWrappers > 0 ? `, skipped ${stats.skippedWrappers} wrappers` : ''}):`);
383
+ lines.push('');
384
+ // List children
385
+ children.forEach((child, index) => {
386
+ const prefix = `[${index}]`;
387
+ const tag = child.testId
388
+ ? `<${child.tag} data-testid="${child.testId}">`
389
+ : `<${child.tag}${child.selector ? ' ' + child.selector : ''}>`;
390
+ const roleInfo = child.role ? ` | ${child.role}` : '';
391
+ lines.push(`${prefix} ${tag}${roleInfo}`);
392
+ // Position
393
+ lines.push(` @ (${child.position.x},${child.position.y}) ${child.position.width}x${child.position.height}px`);
394
+ // Calculate offset from previous sibling
395
+ if (index > 0) {
396
+ const prev = children[index - 1];
397
+ const horizontalGap = child.position.x - (prev.position.x + prev.position.width);
398
+ const verticalGap = child.position.y - (prev.position.y + prev.position.height);
399
+ if (Math.abs(horizontalGap) < 50 && verticalGap > 10) {
400
+ // Vertical layout
401
+ lines.push(` gap from [${index - 1}]: ↓${Math.round(verticalGap)}px (vertical layout)`);
402
+ }
403
+ else if (Math.abs(verticalGap) < 50 && horizontalGap > 10) {
404
+ // Horizontal layout
405
+ lines.push(` gap from [${index - 1}]: →${Math.round(horizontalGap)}px (horizontal layout)`);
406
+ }
407
+ }
408
+ // Text content
409
+ if (child.text) {
410
+ lines.push(` "${child.text}"`);
411
+ }
412
+ // Status symbols
413
+ const statusParts = [];
414
+ statusParts.push(child.isVisible ? '✓ visible' : '✗ hidden');
415
+ if (child.isInteractive)
416
+ statusParts.push('⚡ interactive');
417
+ if (child.childCount > 0)
418
+ statusParts.push(`${child.childCount} children`);
419
+ if (child.testId)
420
+ statusParts.push('has test ID');
421
+ lines.push(` ${statusParts.join(', ')}`);
422
+ lines.push('');
423
+ });
424
+ // Omitted elements notice
425
+ if (stats.omittedCount > 0) {
426
+ lines.push(`... ${stats.omittedCount} more semantic children omitted (use maxChildren to show more)`);
427
+ lines.push('');
428
+ }
429
+ // Layout pattern
430
+ if (layoutPattern !== 'unknown') {
431
+ lines.push(`Layout: ${layoutPattern}`);
432
+ }
433
+ // Mixed structure tip
434
+ if (stats.skippedWrappers > 0 && stats.semanticCount > 0) {
435
+ lines.push('');
436
+ lines.push(`💡 Tip: Some elements found, but ${stats.skippedWrappers} wrapper divs were skipped.`);
437
+ lines.push(' Consider adding test IDs to key elements for easier selection.');
438
+ }
439
+ }
440
+ return createSuccessResponse(lines.join('\n'));
441
+ }
442
+ catch (error) {
443
+ return createErrorResponse(`Failed to inspect DOM: ${error.message}`);
444
+ }
445
+ });
446
+ }
447
+ }
@@ -0,0 +1,104 @@
1
+ import { BrowserToolBase } from './base.js';
2
+ import { ToolContext, ToolResponse } from '../common/types.js';
3
+ /**
4
+ * Tool for clicking elements on the page
5
+ */
6
+ export declare class ClickTool extends BrowserToolBase {
7
+ /**
8
+ * Execute the click tool
9
+ */
10
+ execute(args: any, context: ToolContext): Promise<ToolResponse>;
11
+ }
12
+ /**
13
+ * Tool for clicking a link and switching to the new tab
14
+ */
15
+ export declare class ClickAndSwitchTabTool extends BrowserToolBase {
16
+ /**
17
+ * Execute the click and switch tab tool
18
+ */
19
+ execute(args: any, context: ToolContext): Promise<ToolResponse>;
20
+ }
21
+ /**
22
+ * Tool for clicking elements inside iframes
23
+ */
24
+ export declare class IframeClickTool extends BrowserToolBase {
25
+ /**
26
+ * Execute the iframe click tool
27
+ */
28
+ execute(args: any, context: ToolContext): Promise<ToolResponse>;
29
+ }
30
+ /**
31
+ * Tool for filling elements inside iframes
32
+ */
33
+ export declare class IframeFillTool extends BrowserToolBase {
34
+ /**
35
+ * Execute the iframe fill tool
36
+ */
37
+ execute(args: any, context: ToolContext): Promise<ToolResponse>;
38
+ }
39
+ /**
40
+ * Tool for filling form fields
41
+ */
42
+ export declare class FillTool extends BrowserToolBase {
43
+ /**
44
+ * Execute the fill tool
45
+ */
46
+ execute(args: any, context: ToolContext): Promise<ToolResponse>;
47
+ }
48
+ /**
49
+ * Tool for selecting options from dropdown menus
50
+ */
51
+ export declare class SelectTool extends BrowserToolBase {
52
+ /**
53
+ * Execute the select tool
54
+ */
55
+ execute(args: any, context: ToolContext): Promise<ToolResponse>;
56
+ }
57
+ /**
58
+ * Tool for hovering over elements
59
+ */
60
+ export declare class HoverTool extends BrowserToolBase {
61
+ /**
62
+ * Execute the hover tool
63
+ */
64
+ execute(args: any, context: ToolContext): Promise<ToolResponse>;
65
+ }
66
+ /**
67
+ * Tool for uploading files
68
+ */
69
+ export declare class UploadFileTool extends BrowserToolBase {
70
+ /**
71
+ * Execute the upload file tool
72
+ */
73
+ execute(args: any, context: ToolContext): Promise<ToolResponse>;
74
+ }
75
+ /**
76
+ * Tool for executing JavaScript in the browser
77
+ */
78
+ export declare class EvaluateTool extends BrowserToolBase {
79
+ /**
80
+ * Execute the evaluate tool
81
+ */
82
+ execute(args: any, context: ToolContext): Promise<ToolResponse>;
83
+ }
84
+ /**
85
+ * Tool for dragging elements on the page
86
+ */
87
+ export declare class DragTool extends BrowserToolBase {
88
+ /**
89
+ * Execute the drag tool
90
+ */
91
+ execute(args: any, context: ToolContext): Promise<ToolResponse>;
92
+ }
93
+ /**
94
+ * Tool for pressing keyboard keys
95
+ */
96
+ export declare class PressKeyTool extends BrowserToolBase {
97
+ /**
98
+ * Execute the key press tool
99
+ */
100
+ execute(args: any, context: ToolContext): Promise<ToolResponse>;
101
+ }
102
+ /**
103
+ * Tool for switching browser tabs
104
+ */