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,137 @@
1
+ import { BrowserToolBase } from './base.js';
2
+ import { getNetworkLog } from '../../toolHandler.js';
3
+ export class GetRequestDetailsTool extends BrowserToolBase {
4
+ async execute(args, context) {
5
+ return this.safeExecute(context, async () => {
6
+ const { index } = args;
7
+ const networkLog = getNetworkLog();
8
+ if (index < 0 || index >= networkLog.length) {
9
+ return {
10
+ content: [{
11
+ type: "text",
12
+ text: `Error: Invalid index ${index}. Valid range: 0-${networkLog.length - 1}`
13
+ }],
14
+ isError: true
15
+ };
16
+ }
17
+ const req = networkLog[index];
18
+ // Build compact text response
19
+ const lines = [];
20
+ lines.push(`Request Details [${index}]:\n`);
21
+ lines.push(`${req.method} ${req.url}`);
22
+ if (req.status) {
23
+ lines.push(`Status: ${req.status} ${req.statusText || 'OK'} (took ${req.timing}ms)`);
24
+ }
25
+ else {
26
+ lines.push(`Status: Pending (no response yet)`);
27
+ }
28
+ // Calculate sizes
29
+ const requestSize = req.requestData.postData
30
+ ? req.requestData.postData.length
31
+ : 0;
32
+ const responseSize = req.responseData?.body
33
+ ? req.responseData.body.length
34
+ : 0;
35
+ const formatBytes = (bytes) => {
36
+ if (bytes === 0)
37
+ return '0 bytes';
38
+ if (bytes < 1024)
39
+ return `${bytes} bytes`;
40
+ return `${(bytes / 1024).toFixed(1)}KB`;
41
+ };
42
+ if (responseSize > 0) {
43
+ lines.push(`Size: ${formatBytes(requestSize)} → ${formatBytes(responseSize)}`);
44
+ }
45
+ else if (requestSize > 0) {
46
+ lines.push(`Size: ${formatBytes(requestSize)} →`);
47
+ }
48
+ // Request headers (show important ones)
49
+ const importantRequestHeaders = ['content-type', 'authorization', 'cookie', 'user-agent', 'accept'];
50
+ const reqHeaders = Object.entries(req.requestData.headers)
51
+ .filter(([key]) => importantRequestHeaders.includes(key.toLowerCase()));
52
+ if (reqHeaders.length > 0) {
53
+ lines.push('\nRequest Headers:');
54
+ reqHeaders.forEach(([key, value]) => {
55
+ // Truncate sensitive values
56
+ if (key.toLowerCase() === 'authorization' || key.toLowerCase() === 'cookie') {
57
+ const truncated = value.length > 20
58
+ ? value.substring(0, 17) + '...'
59
+ : value;
60
+ lines.push(` ${key}: ${truncated}`);
61
+ }
62
+ else {
63
+ lines.push(` ${key}: ${value}`);
64
+ }
65
+ });
66
+ }
67
+ // Request body
68
+ if (req.requestData.postData) {
69
+ lines.push('\nRequest Body:');
70
+ // Mask passwords in JSON
71
+ let displayData = req.requestData.postData;
72
+ try {
73
+ const parsed = JSON.parse(displayData);
74
+ if (parsed.password)
75
+ parsed.password = '***';
76
+ if (parsed.pass)
77
+ parsed.pass = '***';
78
+ displayData = JSON.stringify(parsed);
79
+ }
80
+ catch (e) {
81
+ // Not JSON, use as is
82
+ }
83
+ // Truncate at 500 chars
84
+ if (displayData.length > 500) {
85
+ lines.push(` ${displayData.substring(0, 500)}`);
86
+ lines.push(` ... [${displayData.length - 500} more chars]`);
87
+ }
88
+ else {
89
+ lines.push(` ${displayData}`);
90
+ }
91
+ }
92
+ // Response headers (show important ones)
93
+ const importantResponseHeaders = ['content-type', 'set-cookie', 'cache-control', 'location', 'x-cache'];
94
+ const respHeaders = req.responseData?.headers
95
+ ? Object.entries(req.responseData.headers)
96
+ .filter(([key]) => importantResponseHeaders.includes(key.toLowerCase()))
97
+ : [];
98
+ if (respHeaders.length > 0) {
99
+ lines.push('\nResponse Headers:');
100
+ respHeaders.forEach(([key, value]) => {
101
+ // Truncate cookies
102
+ if (key.toLowerCase() === 'set-cookie') {
103
+ const truncated = value.length > 60
104
+ ? value.substring(0, 57) + '...'
105
+ : value;
106
+ lines.push(` ${key}: ${truncated}`);
107
+ }
108
+ else {
109
+ lines.push(` ${key}: ${value}`);
110
+ }
111
+ });
112
+ }
113
+ // Response body
114
+ if (req.responseData?.body) {
115
+ lines.push('\nResponse Body (truncated at 500 chars):');
116
+ const body = req.responseData.body;
117
+ if (body.length > 500) {
118
+ lines.push(` ${body.substring(0, 500)}`);
119
+ lines.push(` ... [${body.length - 500} more chars]`);
120
+ }
121
+ else {
122
+ lines.push(` ${body}`);
123
+ }
124
+ }
125
+ else if (req.status) {
126
+ lines.push('\nResponse Body: (none or binary data)');
127
+ }
128
+ return {
129
+ content: [{
130
+ type: "text",
131
+ text: lines.join('\n')
132
+ }],
133
+ isError: false
134
+ };
135
+ });
136
+ }
137
+ }
@@ -0,0 +1,12 @@
1
+ import { BrowserToolBase } from './base.js';
2
+ import { ToolContext, ToolResponse } from '../common/types.js';
3
+ /**
4
+ * Tool for discovering all test identifiers on the page
5
+ * Returns compact text format showing all data-testid, data-test, data-cy attributes
6
+ */
7
+ export declare class GetTestIdsTool extends BrowserToolBase {
8
+ /**
9
+ * Execute the test ID discovery tool
10
+ */
11
+ execute(args: any, context: ToolContext): Promise<ToolResponse>;
12
+ }
@@ -0,0 +1,148 @@
1
+ import { BrowserToolBase } from './base.js';
2
+ import { createSuccessResponse, createErrorResponse } from '../common/types.js';
3
+ /**
4
+ * Tool for discovering all test identifiers on the page
5
+ * Returns compact text format showing all data-testid, data-test, data-cy attributes
6
+ */
7
+ export class GetTestIdsTool extends BrowserToolBase {
8
+ /**
9
+ * Execute the test ID discovery tool
10
+ */
11
+ async execute(args, context) {
12
+ return this.safeExecute(context, async (page) => {
13
+ const attributes = args.attributes
14
+ ? args.attributes.split(',').map((a) => a.trim())
15
+ : ['data-testid', 'data-test', 'data-cy'];
16
+ const showAll = args.showAll === true;
17
+ try {
18
+ // Discover all test IDs on the page
19
+ const discoveryData = await page.evaluate((attrs) => {
20
+ const byAttribute = {};
21
+ const duplicates = {};
22
+ let totalCount = 0;
23
+ attrs.forEach((attr) => {
24
+ const elements = document.querySelectorAll(`[${attr}]`);
25
+ const values = [];
26
+ const counts = {};
27
+ elements.forEach((el) => {
28
+ const value = el.getAttribute(attr);
29
+ if (value) {
30
+ values.push(value);
31
+ totalCount++;
32
+ // Track duplicates
33
+ counts[value] = (counts[value] || 0) + 1;
34
+ }
35
+ });
36
+ if (values.length > 0) {
37
+ byAttribute[attr] = values;
38
+ // Store duplicates (values that appear more than once)
39
+ const attrDuplicates = {};
40
+ Object.entries(counts).forEach(([value, count]) => {
41
+ if (count > 1) {
42
+ attrDuplicates[value] = count;
43
+ }
44
+ });
45
+ if (Object.keys(attrDuplicates).length > 0) {
46
+ duplicates[attr] = attrDuplicates;
47
+ }
48
+ }
49
+ });
50
+ return {
51
+ totalCount,
52
+ byAttribute,
53
+ duplicates,
54
+ };
55
+ }, attributes);
56
+ // Format compact text output
57
+ const lines = [];
58
+ if (discoveryData.totalCount === 0) {
59
+ lines.push('Found 0 test IDs');
60
+ lines.push('');
61
+ lines.push('⚠ No test ID attributes found on this page.');
62
+ lines.push('');
63
+ lines.push('Searched for:');
64
+ attributes.forEach((attr) => {
65
+ lines.push(` • ${attr}`);
66
+ });
67
+ lines.push('');
68
+ lines.push('Suggestions:');
69
+ lines.push(' - Use inspect_dom to see page structure');
70
+ lines.push(' - Consider adding test IDs to interactive elements');
71
+ lines.push(' - Example: <button data-testid="submit-button">Submit</button>');
72
+ }
73
+ else {
74
+ lines.push(`Found ${discoveryData.totalCount} test ID${discoveryData.totalCount > 1 ? 's' : ''}:`);
75
+ lines.push('');
76
+ // Group by attribute type
77
+ Object.entries(discoveryData.byAttribute).forEach(([attr, values]) => {
78
+ lines.push(`${attr} (${values.length}):`);
79
+ // Format values in a compact way
80
+ if (showAll || values.length <= 10) {
81
+ // Show all if requested or if 10 or fewer
82
+ lines.push(` ${values.join(', ')}`);
83
+ }
84
+ else {
85
+ // Show first 8, then indicate more
86
+ const shown = values.slice(0, 8);
87
+ const remaining = values.length - 8;
88
+ lines.push(` ${shown.join(', ')},`);
89
+ lines.push(` ... and ${remaining} more`);
90
+ lines.push(` 💡 Use showAll: true to see all ${values.length} test IDs`);
91
+ }
92
+ lines.push('');
93
+ });
94
+ // Add duplicate warnings
95
+ const hasDuplicates = Object.keys(discoveryData.duplicates).length > 0;
96
+ if (hasDuplicates) {
97
+ lines.push('⚠ Warning: Duplicate test IDs found (test IDs should be unique):');
98
+ lines.push('');
99
+ Object.entries(discoveryData.duplicates).forEach(([attr, dups]) => {
100
+ Object.entries(dups).forEach(([value, count]) => {
101
+ lines.push(` ${attr}: "${value}" appears ${count} times`);
102
+ });
103
+ });
104
+ lines.push('');
105
+ lines.push('⚠ Impact of Duplicate Test IDs:');
106
+ lines.push(' - Flaky tests (selectors match multiple elements)');
107
+ lines.push(' - Ambiguous interactions (which element to click?)');
108
+ lines.push(' - Test automation will fail or behave unpredictably');
109
+ lines.push('');
110
+ lines.push('🔧 How to Fix:');
111
+ lines.push(' 1. Use query_selector_all to locate all duplicates');
112
+ // Add example for the first duplicate
113
+ const firstDupAttr = Object.keys(discoveryData.duplicates)[0];
114
+ const firstDupValue = Object.keys(discoveryData.duplicates[firstDupAttr])[0];
115
+ const firstDupCount = discoveryData.duplicates[firstDupAttr][firstDupValue];
116
+ if (firstDupAttr === 'data-testid') {
117
+ lines.push(` query_selector_all({ selector: "testid:${firstDupValue}" })`);
118
+ }
119
+ else {
120
+ lines.push(` query_selector_all({ selector: "[${firstDupAttr}='${firstDupValue}']" })`);
121
+ }
122
+ lines.push(' 2. Identify which elements should keep the test ID');
123
+ lines.push(' 3. Rename duplicates to be unique and descriptive');
124
+ lines.push(` Example: "${firstDupValue}" → "${firstDupValue}-primary", "${firstDupValue}-mobile"`);
125
+ lines.push(' 4. If one is hidden/unused, consider removing it entirely');
126
+ lines.push('');
127
+ lines.push('💡 Best Practice: Test IDs must be unique across the entire page');
128
+ lines.push('');
129
+ }
130
+ // Add usage tip
131
+ lines.push('💡 Tip: Use these test IDs with selector shortcuts:');
132
+ const firstAttr = Object.keys(discoveryData.byAttribute)[0];
133
+ const firstValue = discoveryData.byAttribute[firstAttr][0];
134
+ if (firstAttr === 'data-testid') {
135
+ lines.push(` testid:${firstValue} → [data-testid="${firstValue}"]`);
136
+ }
137
+ else {
138
+ lines.push(` ${firstAttr}:${firstValue} → [${firstAttr}="${firstValue}"]`);
139
+ }
140
+ }
141
+ return createSuccessResponse(lines.join('\n'));
142
+ }
143
+ catch (error) {
144
+ return createErrorResponse(`Failed to discover test IDs: ${error.message}`);
145
+ }
146
+ });
147
+ }
148
+ }
@@ -0,0 +1,7 @@
1
+ export * from './base.js';
2
+ export * from './screenshot.js';
3
+ export * from './navigation.js';
4
+ export * from './console.js';
5
+ export * from './interaction.js';
6
+ export * from './response.js';
7
+ export * from './useragent.js';
@@ -0,0 +1,7 @@
1
+ export * from './base.js';
2
+ export * from './screenshot.js';
3
+ export * from './navigation.js';
4
+ export * from './console.js';
5
+ export * from './interaction.js';
6
+ export * from './response.js';
7
+ export * from './useragent.js';
@@ -0,0 +1,12 @@
1
+ import { BrowserToolBase } from './base.js';
2
+ import { ToolContext, ToolResponse } 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 declare class InspectDomTool extends BrowserToolBase {
8
+ /**
9
+ * Execute the DOM inspection tool
10
+ */
11
+ execute(args: any, context: ToolContext): Promise<ToolResponse>;
12
+ }