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,259 @@
1
+ import { BrowserToolBase } from './base.js';
2
+ import { createSuccessResponse, createErrorResponse } from '../common/types.js';
3
+ import { setGlobalPage } from '../../toolHandler.js';
4
+ /**
5
+ * Tool for clicking elements on the page
6
+ */
7
+ export class ClickTool extends BrowserToolBase {
8
+ /**
9
+ * Execute the click tool
10
+ */
11
+ async execute(args, context) {
12
+ this.recordInteraction();
13
+ return this.safeExecute(context, async (page) => {
14
+ const selector = this.normalizeSelector(args.selector);
15
+ await page.click(selector);
16
+ return createSuccessResponse(`Clicked element: ${args.selector}`);
17
+ });
18
+ }
19
+ }
20
+ /**
21
+ * Tool for clicking a link and switching to the new tab
22
+ */
23
+ export class ClickAndSwitchTabTool extends BrowserToolBase {
24
+ /**
25
+ * Execute the click and switch tab tool
26
+ */
27
+ async execute(args, context) {
28
+ this.recordInteraction();
29
+ return this.safeExecute(context, async (page) => {
30
+ const selector = this.normalizeSelector(args.selector);
31
+ // Listen for a new tab to open
32
+ const [newPage] = await Promise.all([
33
+ //context.browser.waitForEvent('page'), // Wait for a new page (tab) to open
34
+ page.context().waitForEvent('page'), // Wait for a new page (tab) to open
35
+ page.click(selector), // Click the link that opens the new tab
36
+ ]);
37
+ // Wait for the new page to load
38
+ await newPage.waitForLoadState('domcontentloaded');
39
+ // Switch control to the new tab
40
+ setGlobalPage(newPage);
41
+ //page= newPage; // Update the current page to the new tab
42
+ //context.page = newPage;
43
+ //context.page.bringToFront(); // Bring the new tab to the front
44
+ return createSuccessResponse(`Clicked link and switched to new tab: ${newPage.url()}`);
45
+ //return createSuccessResponse(`Clicked link and switched to new tab: ${context.page.url()}`);
46
+ });
47
+ }
48
+ }
49
+ /**
50
+ * Tool for clicking elements inside iframes
51
+ */
52
+ export class IframeClickTool extends BrowserToolBase {
53
+ /**
54
+ * Execute the iframe click tool
55
+ */
56
+ async execute(args, context) {
57
+ this.recordInteraction();
58
+ return this.safeExecute(context, async (page) => {
59
+ const iframeSelector = this.normalizeSelector(args.iframeSelector);
60
+ const selector = this.normalizeSelector(args.selector);
61
+ const frame = page.frameLocator(iframeSelector);
62
+ if (!frame) {
63
+ return createErrorResponse(`Iframe not found: ${args.iframeSelector}`);
64
+ }
65
+ await frame.locator(selector).click();
66
+ return createSuccessResponse(`Clicked element ${args.selector} inside iframe ${args.iframeSelector}`);
67
+ });
68
+ }
69
+ }
70
+ /**
71
+ * Tool for filling elements inside iframes
72
+ */
73
+ export class IframeFillTool extends BrowserToolBase {
74
+ /**
75
+ * Execute the iframe fill tool
76
+ */
77
+ async execute(args, context) {
78
+ this.recordInteraction();
79
+ return this.safeExecute(context, async (page) => {
80
+ const iframeSelector = this.normalizeSelector(args.iframeSelector);
81
+ const selector = this.normalizeSelector(args.selector);
82
+ const frame = page.frameLocator(iframeSelector);
83
+ if (!frame) {
84
+ return createErrorResponse(`Iframe not found: ${args.iframeSelector}`);
85
+ }
86
+ await frame.locator(selector).fill(args.value);
87
+ return createSuccessResponse(`Filled element ${args.selector} inside iframe ${args.iframeSelector} with: ${args.value}`);
88
+ });
89
+ }
90
+ }
91
+ /**
92
+ * Tool for filling form fields
93
+ */
94
+ export class FillTool extends BrowserToolBase {
95
+ /**
96
+ * Execute the fill tool
97
+ */
98
+ async execute(args, context) {
99
+ this.recordInteraction();
100
+ return this.safeExecute(context, async (page) => {
101
+ const selector = this.normalizeSelector(args.selector);
102
+ await page.waitForSelector(selector);
103
+ await page.fill(selector, args.value);
104
+ return createSuccessResponse(`Filled ${args.selector} with: ${args.value}`);
105
+ });
106
+ }
107
+ }
108
+ /**
109
+ * Tool for selecting options from dropdown menus
110
+ */
111
+ export class SelectTool extends BrowserToolBase {
112
+ /**
113
+ * Execute the select tool
114
+ */
115
+ async execute(args, context) {
116
+ this.recordInteraction();
117
+ return this.safeExecute(context, async (page) => {
118
+ const selector = this.normalizeSelector(args.selector);
119
+ await page.waitForSelector(selector);
120
+ await page.selectOption(selector, args.value);
121
+ return createSuccessResponse(`Selected ${args.selector} with: ${args.value}`);
122
+ });
123
+ }
124
+ }
125
+ /**
126
+ * Tool for hovering over elements
127
+ */
128
+ export class HoverTool extends BrowserToolBase {
129
+ /**
130
+ * Execute the hover tool
131
+ */
132
+ async execute(args, context) {
133
+ this.recordInteraction();
134
+ return this.safeExecute(context, async (page) => {
135
+ const selector = this.normalizeSelector(args.selector);
136
+ await page.waitForSelector(selector);
137
+ await page.hover(selector);
138
+ return createSuccessResponse(`Hovered ${args.selector}`);
139
+ });
140
+ }
141
+ }
142
+ /**
143
+ * Tool for uploading files
144
+ */
145
+ export class UploadFileTool extends BrowserToolBase {
146
+ /**
147
+ * Execute the upload file tool
148
+ */
149
+ async execute(args, context) {
150
+ this.recordInteraction();
151
+ return this.safeExecute(context, async (page) => {
152
+ const selector = this.normalizeSelector(args.selector);
153
+ await page.waitForSelector(selector);
154
+ await page.setInputFiles(selector, args.filePath);
155
+ return createSuccessResponse(`Uploaded file '${args.filePath}' to '${args.selector}'`);
156
+ });
157
+ }
158
+ }
159
+ /**
160
+ * Tool for executing JavaScript in the browser
161
+ */
162
+ export class EvaluateTool extends BrowserToolBase {
163
+ /**
164
+ * Execute the evaluate tool
165
+ */
166
+ async execute(args, context) {
167
+ this.recordInteraction();
168
+ return this.safeExecute(context, async (page) => {
169
+ const result = await page.evaluate(args.script);
170
+ // Convert result to string for display
171
+ let resultStr;
172
+ try {
173
+ resultStr = JSON.stringify(result, null, 2);
174
+ }
175
+ catch (error) {
176
+ resultStr = String(result);
177
+ }
178
+ return createSuccessResponse([
179
+ `Executed JavaScript:`,
180
+ `${args.script}`,
181
+ `Result:`,
182
+ `${resultStr}`
183
+ ]);
184
+ });
185
+ }
186
+ }
187
+ /**
188
+ * Tool for dragging elements on the page
189
+ */
190
+ export class DragTool extends BrowserToolBase {
191
+ /**
192
+ * Execute the drag tool
193
+ */
194
+ async execute(args, context) {
195
+ this.recordInteraction();
196
+ return this.safeExecute(context, async (page) => {
197
+ const sourceSelector = this.normalizeSelector(args.sourceSelector);
198
+ const targetSelector = this.normalizeSelector(args.targetSelector);
199
+ const sourceElement = await page.waitForSelector(sourceSelector);
200
+ const targetElement = await page.waitForSelector(targetSelector);
201
+ const sourceBound = await sourceElement.boundingBox();
202
+ const targetBound = await targetElement.boundingBox();
203
+ if (!sourceBound || !targetBound) {
204
+ return createErrorResponse("Could not get element positions for drag operation");
205
+ }
206
+ await page.mouse.move(sourceBound.x + sourceBound.width / 2, sourceBound.y + sourceBound.height / 2);
207
+ await page.mouse.down();
208
+ await page.mouse.move(targetBound.x + targetBound.width / 2, targetBound.y + targetBound.height / 2);
209
+ await page.mouse.up();
210
+ return createSuccessResponse(`Dragged element from ${args.sourceSelector} to ${args.targetSelector}`);
211
+ });
212
+ }
213
+ }
214
+ /**
215
+ * Tool for pressing keyboard keys
216
+ */
217
+ export class PressKeyTool extends BrowserToolBase {
218
+ /**
219
+ * Execute the key press tool
220
+ */
221
+ async execute(args, context) {
222
+ this.recordInteraction();
223
+ return this.safeExecute(context, async (page) => {
224
+ if (args.selector) {
225
+ const selector = this.normalizeSelector(args.selector);
226
+ await page.waitForSelector(selector);
227
+ await page.focus(selector);
228
+ }
229
+ await page.keyboard.press(args.key);
230
+ return createSuccessResponse(`Pressed key: ${args.key}`);
231
+ });
232
+ }
233
+ }
234
+ /**
235
+ * Tool for switching browser tabs
236
+ */
237
+ // export class SwitchTabTool extends BrowserToolBase {
238
+ // /**
239
+ // * Switch the tab to the specified index
240
+ // */
241
+ // async execute(args: any, context: ToolContext): Promise<ToolResponse> {
242
+ // return this.safeExecute(context, async (page) => {
243
+ // const tabs = await browser.page;
244
+ // // Validate the tab index
245
+ // const tabIndex = Number(args.index);
246
+ // if (isNaN(tabIndex)) {
247
+ // return createErrorResponse(`Invalid tab index: ${args.index}. It must be a number.`);
248
+ // }
249
+ // if (tabIndex >= 0 && tabIndex < tabs.length) {
250
+ // await tabs[tabIndex].bringToFront();
251
+ // return createSuccessResponse(`Switched to tab with index ${tabIndex}`);
252
+ // } else {
253
+ // return createErrorResponse(
254
+ // `Tab index out of range: ${tabIndex}. Available tabs: 0 to ${tabs.length - 1}.`
255
+ // );
256
+ // }
257
+ // });
258
+ // }
259
+ // }
@@ -0,0 +1,10 @@
1
+ import type { ToolContext, ToolResponse } from '../common/types.js';
2
+ import { BrowserToolBase } from './base.js';
3
+ interface ListNetworkRequestsArgs {
4
+ type?: string;
5
+ limit?: number;
6
+ }
7
+ export declare class ListNetworkRequestsTool extends BrowserToolBase {
8
+ execute(args: ListNetworkRequestsArgs, context: ToolContext): Promise<ToolResponse>;
9
+ }
10
+ export {};
@@ -0,0 +1,74 @@
1
+ import { BrowserToolBase } from './base.js';
2
+ import { getNetworkLog } from '../../toolHandler.js';
3
+ export class ListNetworkRequestsTool extends BrowserToolBase {
4
+ async execute(args, context) {
5
+ return this.safeExecute(context, async () => {
6
+ const { type, limit = 50 } = args;
7
+ const networkLog = getNetworkLog();
8
+ // Filter by resource type if specified
9
+ let filtered = type
10
+ ? networkLog.filter(req => req.resourceType === type)
11
+ : networkLog;
12
+ // Get most recent requests (reverse chronological)
13
+ filtered = filtered.slice(-limit).reverse();
14
+ if (filtered.length === 0) {
15
+ return {
16
+ content: [{
17
+ type: "text",
18
+ text: type
19
+ ? `No network requests found for type: ${type}`
20
+ : "No network requests captured yet"
21
+ }],
22
+ isError: false
23
+ };
24
+ }
25
+ // Format output in compact text format
26
+ const lines = [`Network Requests (${filtered.length} of ${networkLog.length}, recent first):\n`];
27
+ filtered.forEach(req => {
28
+ const statusInfo = req.status
29
+ ? `${req.status} ${req.statusText || 'OK'}`
30
+ : 'pending';
31
+ const timing = req.timing ? `${req.timing}ms` : '...';
32
+ // Check if cached
33
+ const cached = req.responseData?.headers['cache-control']?.includes('max-age') ||
34
+ req.responseData?.headers['x-cache'] === 'HIT'
35
+ ? 'cached'
36
+ : '';
37
+ // Get response size
38
+ let sizeInfo = '';
39
+ if (req.responseData?.body) {
40
+ const bytes = req.responseData.body.length;
41
+ if (bytes < 1024) {
42
+ sizeInfo = `${bytes}B`;
43
+ }
44
+ else {
45
+ sizeInfo = `${(bytes / 1024).toFixed(1)}KB`;
46
+ }
47
+ }
48
+ const parts = [
49
+ `[${req.index}]`,
50
+ req.method,
51
+ req.url.length > 80 ? req.url.substring(0, 77) + '...' : req.url,
52
+ statusInfo,
53
+ '|',
54
+ req.resourceType,
55
+ '|',
56
+ timing
57
+ ];
58
+ if (sizeInfo)
59
+ parts.push('|', sizeInfo);
60
+ if (cached)
61
+ parts.push('|', cached);
62
+ lines.push(parts.join(' '));
63
+ });
64
+ lines.push('\nUse get_request_details(index) for full info');
65
+ return {
66
+ content: [{
67
+ type: "text",
68
+ text: lines.join('\n')
69
+ }],
70
+ isError: false
71
+ };
72
+ });
73
+ }
74
+ }
@@ -0,0 +1,9 @@
1
+ import { ToolHandler } from '../common/types.js';
2
+ import { BrowserToolBase } from './base.js';
3
+ import type { ToolContext, ToolResponse } from '../common/types.js';
4
+ export interface MeasureElementArgs {
5
+ selector: string;
6
+ }
7
+ export declare class MeasureElementTool extends BrowserToolBase implements ToolHandler {
8
+ execute(args: MeasureElementArgs, context: ToolContext): Promise<ToolResponse>;
9
+ }
@@ -0,0 +1,139 @@
1
+ import { BrowserToolBase } from './base.js';
2
+ export class MeasureElementTool extends BrowserToolBase {
3
+ async execute(args, context) {
4
+ return this.safeExecute(context, async (page) => {
5
+ const normalizedSelector = this.normalizeSelector(args.selector);
6
+ // Find the element
7
+ const locator = page.locator(normalizedSelector);
8
+ const count = await locator.count();
9
+ if (count === 0) {
10
+ return {
11
+ content: [
12
+ {
13
+ type: 'text',
14
+ text: `✗ Element not found: ${args.selector}`
15
+ }
16
+ ],
17
+ isError: true
18
+ };
19
+ }
20
+ // Handle multiple matches by using first() - show warning (consistent with other tools)
21
+ const targetLocator = count > 1 ? locator.first() : locator;
22
+ let warning = '';
23
+ if (count > 1) {
24
+ warning = `⚠ Warning: Selector matched ${count} elements, using first\n\n`;
25
+ }
26
+ // Get element descriptor
27
+ const elementInfo = await targetLocator.evaluate((el) => {
28
+ const tag = el.tagName.toLowerCase();
29
+ const testId = el.getAttribute('data-testid') || el.getAttribute('data-test') || el.getAttribute('data-cy');
30
+ const id = el.id ? `#${el.id}` : '';
31
+ const classes = el.className && typeof el.className === 'string'
32
+ ? `.${el.className.split(' ').filter(c => c).slice(0, 2).join('.')}`
33
+ : '';
34
+ let descriptor = `<${tag}`;
35
+ if (testId)
36
+ descriptor += ` data-testid="${testId}"`;
37
+ else if (id)
38
+ descriptor += id;
39
+ else if (classes)
40
+ descriptor += classes;
41
+ descriptor += '>';
42
+ return { descriptor };
43
+ });
44
+ // Get box model measurements
45
+ const measurements = await targetLocator.evaluate((el) => {
46
+ const computed = window.getComputedStyle(el);
47
+ const rect = el.getBoundingClientRect();
48
+ const parseValue = (val) => parseFloat(val) || 0;
49
+ return {
50
+ x: Math.round(rect.x),
51
+ y: Math.round(rect.y),
52
+ width: parseValue(computed.width),
53
+ height: parseValue(computed.height),
54
+ marginTop: parseValue(computed.marginTop),
55
+ marginRight: parseValue(computed.marginRight),
56
+ marginBottom: parseValue(computed.marginBottom),
57
+ marginLeft: parseValue(computed.marginLeft),
58
+ paddingTop: parseValue(computed.paddingTop),
59
+ paddingRight: parseValue(computed.paddingRight),
60
+ paddingBottom: parseValue(computed.paddingBottom),
61
+ paddingLeft: parseValue(computed.paddingLeft),
62
+ borderTopWidth: parseValue(computed.borderTopWidth),
63
+ borderRightWidth: parseValue(computed.borderRightWidth),
64
+ borderBottomWidth: parseValue(computed.borderBottomWidth),
65
+ borderLeftWidth: parseValue(computed.borderLeftWidth),
66
+ borderStyle: computed.borderStyle,
67
+ borderColor: computed.borderColor
68
+ };
69
+ });
70
+ // Calculate dimensions
71
+ const contentWidth = Math.round(measurements.width - measurements.paddingLeft - measurements.paddingRight);
72
+ const contentHeight = Math.round(measurements.height - measurements.paddingTop - measurements.paddingBottom);
73
+ const boxWidth = Math.round(measurements.width);
74
+ const boxHeight = Math.round(measurements.height);
75
+ const totalWidth = Math.round(boxWidth + measurements.marginLeft + measurements.marginRight);
76
+ const totalHeight = Math.round(boxHeight + measurements.marginTop + measurements.marginBottom);
77
+ // Format border info
78
+ const formatBorder = () => {
79
+ const { borderTopWidth: top, borderRightWidth: right, borderBottomWidth: bottom, borderLeftWidth: left, borderStyle: style } = measurements;
80
+ // Check if all sides are the same
81
+ if (top === right && right === bottom && bottom === left) {
82
+ if (top === 0)
83
+ return ' Border: none';
84
+ return ` Border: ${top}px ${style}`;
85
+ }
86
+ // Different sides
87
+ const lines = [];
88
+ if (top > 0)
89
+ lines.push(`↑${top}px`);
90
+ if (right > 0)
91
+ lines.push(`→${right}px`);
92
+ if (bottom > 0)
93
+ lines.push(`↓${bottom}px`);
94
+ if (left > 0)
95
+ lines.push(`←${left}px`);
96
+ return lines.length > 0
97
+ ? ` Border: ${lines.join(' ')} ${style}`
98
+ : ' Border: none';
99
+ };
100
+ // Format spacing (margin/padding) with directional arrows
101
+ const formatSpacing = (top, right, bottom, left) => {
102
+ const parts = [];
103
+ if (top > 0)
104
+ parts.push(`↑${Math.round(top)}px`);
105
+ if (bottom > 0)
106
+ parts.push(`↓${Math.round(bottom)}px`);
107
+ if (left > 0)
108
+ parts.push(`←${Math.round(left)}px`);
109
+ if (right > 0)
110
+ parts.push(`→${Math.round(right)}px`);
111
+ return parts.length > 0 ? parts.join(' ') : '0px';
112
+ };
113
+ // Build output in compact text format
114
+ const sections = [];
115
+ if (warning) {
116
+ sections.push(warning.trim());
117
+ }
118
+ sections.push(`Element: ${elementInfo.descriptor}`);
119
+ sections.push(`@ (${measurements.x},${measurements.y}) ${boxWidth}x${boxHeight}px`);
120
+ sections.push('');
121
+ sections.push('Box Model:');
122
+ sections.push(` Content: ${contentWidth}x${contentHeight}px`);
123
+ sections.push(` Padding: ${formatSpacing(measurements.paddingTop, measurements.paddingRight, measurements.paddingBottom, measurements.paddingLeft)}`);
124
+ sections.push(formatBorder());
125
+ sections.push(` Margin: ${formatSpacing(measurements.marginTop, measurements.marginRight, measurements.marginBottom, measurements.marginLeft)}`);
126
+ sections.push('');
127
+ sections.push(`Total Space: ${totalWidth}x${totalHeight}px (with margin)`);
128
+ return {
129
+ content: [
130
+ {
131
+ type: 'text',
132
+ text: sections.join('\n')
133
+ }
134
+ ],
135
+ isError: false
136
+ };
137
+ });
138
+ }
139
+ }
@@ -0,0 +1,38 @@
1
+ import { BrowserToolBase } from './base.js';
2
+ import { ToolContext, ToolResponse } from '../common/types.js';
3
+ /**
4
+ * Tool for navigating to URLs
5
+ */
6
+ export declare class NavigationTool extends BrowserToolBase {
7
+ /**
8
+ * Execute the navigation tool
9
+ */
10
+ execute(args: any, context: ToolContext): Promise<ToolResponse>;
11
+ }
12
+ /**
13
+ * Tool for closing the browser
14
+ */
15
+ export declare class CloseBrowserTool extends BrowserToolBase {
16
+ /**
17
+ * Execute the close browser tool
18
+ */
19
+ execute(args: any, context: ToolContext): Promise<ToolResponse>;
20
+ }
21
+ /**
22
+ * Tool for navigating back in browser history
23
+ */
24
+ export declare class GoBackTool extends BrowserToolBase {
25
+ /**
26
+ * Execute the go back tool
27
+ */
28
+ execute(args: any, context: ToolContext): Promise<ToolResponse>;
29
+ }
30
+ /**
31
+ * Tool for navigating forward in browser history
32
+ */
33
+ export declare class GoForwardTool extends BrowserToolBase {
34
+ /**
35
+ * Execute the go forward tool
36
+ */
37
+ execute(args: any, context: ToolContext): Promise<ToolResponse>;
38
+ }
@@ -0,0 +1,109 @@
1
+ import { BrowserToolBase } from './base.js';
2
+ import { createSuccessResponse, createErrorResponse } from '../common/types.js';
3
+ import { resetBrowserState } from '../../toolHandler.js';
4
+ /**
5
+ * Tool for navigating to URLs
6
+ */
7
+ export class NavigationTool extends BrowserToolBase {
8
+ /**
9
+ * Execute the navigation tool
10
+ */
11
+ async execute(args, context) {
12
+ // Check if browser is available
13
+ if (!context.browser || !context.browser.isConnected()) {
14
+ // If browser is not connected, we need to reset the state to force recreation
15
+ resetBrowserState();
16
+ return createErrorResponse("Browser is not connected. The connection has been reset - please retry your navigation.");
17
+ }
18
+ // Check if page is available and not closed
19
+ if (!context.page || context.page.isClosed()) {
20
+ return createErrorResponse("Page is not available or has been closed. Please retry your navigation.");
21
+ }
22
+ this.recordNavigation();
23
+ return this.safeExecute(context, async (page) => {
24
+ try {
25
+ await page.goto(args.url, {
26
+ timeout: args.timeout || 30000,
27
+ waitUntil: args.waitUntil || "load"
28
+ });
29
+ return createSuccessResponse(`Navigated to ${args.url}`);
30
+ }
31
+ catch (error) {
32
+ const errorMessage = error.message;
33
+ // Check for common disconnection errors
34
+ if (errorMessage.includes("Target page, context or browser has been closed") ||
35
+ errorMessage.includes("Target closed") ||
36
+ errorMessage.includes("Browser has been disconnected")) {
37
+ // Reset browser state to force recreation on next attempt
38
+ resetBrowserState();
39
+ return createErrorResponse(`Browser connection issue: ${errorMessage}. Connection has been reset - please retry your navigation.`);
40
+ }
41
+ // For other errors, return the standard error
42
+ throw error;
43
+ }
44
+ });
45
+ }
46
+ }
47
+ /**
48
+ * Tool for closing the browser
49
+ */
50
+ export class CloseBrowserTool extends BrowserToolBase {
51
+ /**
52
+ * Execute the close browser tool
53
+ */
54
+ async execute(args, context) {
55
+ if (context.browser) {
56
+ try {
57
+ // Check if browser is still connected
58
+ if (context.browser.isConnected()) {
59
+ await context.browser.close().catch(error => {
60
+ console.error("Error while closing browser:", error);
61
+ });
62
+ }
63
+ else {
64
+ console.error("Browser already disconnected, cleaning up state");
65
+ }
66
+ }
67
+ catch (error) {
68
+ console.error("Error during browser close operation:", error);
69
+ // Continue with resetting state even if close fails
70
+ }
71
+ finally {
72
+ // Always reset the global browser and page references
73
+ resetBrowserState();
74
+ }
75
+ return createSuccessResponse("Browser closed successfully");
76
+ }
77
+ return createSuccessResponse("No browser instance to close");
78
+ }
79
+ }
80
+ /**
81
+ * Tool for navigating back in browser history
82
+ */
83
+ export class GoBackTool extends BrowserToolBase {
84
+ /**
85
+ * Execute the go back tool
86
+ */
87
+ async execute(args, context) {
88
+ this.recordNavigation();
89
+ return this.safeExecute(context, async (page) => {
90
+ await page.goBack();
91
+ return createSuccessResponse("Navigated back in browser history");
92
+ });
93
+ }
94
+ }
95
+ /**
96
+ * Tool for navigating forward in browser history
97
+ */
98
+ export class GoForwardTool extends BrowserToolBase {
99
+ /**
100
+ * Execute the go forward tool
101
+ */
102
+ async execute(args, context) {
103
+ this.recordNavigation();
104
+ return this.safeExecute(context, async (page) => {
105
+ await page.goForward();
106
+ return createSuccessResponse("Navigated forward in browser history");
107
+ });
108
+ }
109
+ }
@@ -0,0 +1,11 @@
1
+ import { BrowserToolBase } from './base.js';
2
+ import { ToolContext, ToolResponse } from '../common/types.js';
3
+ /**
4
+ * Tool for saving page as PDF
5
+ */
6
+ export declare class SaveAsPdfTool extends BrowserToolBase {
7
+ /**
8
+ * Execute the save as PDF tool
9
+ */
10
+ execute(args: any, context: ToolContext): Promise<ToolResponse>;
11
+ }
@@ -0,0 +1,29 @@
1
+ import { BrowserToolBase } from './base.js';
2
+ import { createSuccessResponse } from '../common/types.js';
3
+ import * as path from 'path';
4
+ /**
5
+ * Tool for saving page as PDF
6
+ */
7
+ export class SaveAsPdfTool extends BrowserToolBase {
8
+ /**
9
+ * Execute the save as PDF tool
10
+ */
11
+ async execute(args, context) {
12
+ return this.safeExecute(context, async (page) => {
13
+ const filename = args.filename || 'page.pdf';
14
+ const options = {
15
+ path: path.resolve(args.outputPath || '.', filename),
16
+ format: args.format || 'A4',
17
+ printBackground: args.printBackground !== false,
18
+ margin: args.margin || {
19
+ top: '1cm',
20
+ right: '1cm',
21
+ bottom: '1cm',
22
+ left: '1cm'
23
+ }
24
+ };
25
+ await page.pdf(options);
26
+ return createSuccessResponse(`Saved page as PDF: ${options.path}`);
27
+ });
28
+ }
29
+ }