@ricardodeazambuja/browser-mcp-server 1.3.0 → 1.4.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.
- package/CHANGELOG-v1.4.0.md +8 -0
- package/README.md +87 -6
- package/package.json +3 -2
- package/src/browser.js +2 -0
- package/src/cdp.js +58 -0
- package/src/index.js +2 -2
- package/src/tools/docs.js +798 -0
- package/src/tools/index.js +5 -1
- package/src/tools/network.js +552 -0
- package/src/tools/performance.js +517 -0
- package/src/tools/security.js +470 -0
- package/src/tools/storage.js +467 -0
- package/src/tools/system.js +5 -1
- package/src/utils.js +12 -1
- package/tests/fixtures/test-network.html +48 -0
- package/tests/fixtures/test-performance.html +61 -0
- package/tests/fixtures/test-security.html +33 -0
- package/tests/fixtures/test-storage.html +76 -0
- package/tests/run-all.js +50 -0
- package/tests/test-mcp.js +3 -2
- package/tests/test-network.js +212 -0
- package/tests/test-performance.js +254 -0
- package/tests/test-security.js +203 -0
- package/tests/test-storage.js +192 -0
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Storage & Service Workers Tools (CDP-based)
|
|
3
|
+
* IndexedDB, Cache Storage, Service Worker inspection and control
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { connectToBrowser } = require('../browser');
|
|
7
|
+
const { getCDPSession } = require('../cdp');
|
|
8
|
+
const { debugLog } = require('../utils');
|
|
9
|
+
|
|
10
|
+
const definitions = [
|
|
11
|
+
{
|
|
12
|
+
name: 'browser_storage_get_indexeddb',
|
|
13
|
+
description: 'Inspect IndexedDB databases and their data (see browser_docs)',
|
|
14
|
+
inputSchema: {
|
|
15
|
+
type: 'object',
|
|
16
|
+
properties: {
|
|
17
|
+
databaseName: {
|
|
18
|
+
type: 'string',
|
|
19
|
+
description: 'Specific database to inspect (optional)'
|
|
20
|
+
},
|
|
21
|
+
objectStoreName: {
|
|
22
|
+
type: 'string',
|
|
23
|
+
description: 'Specific object store to query (optional, requires databaseName)'
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
additionalProperties: false,
|
|
27
|
+
$schema: 'http://json-schema.org/draft-07/schema#'
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: 'browser_storage_get_cache_storage',
|
|
32
|
+
description: 'List Cache Storage API caches and their entries (see browser_docs)',
|
|
33
|
+
inputSchema: {
|
|
34
|
+
type: 'object',
|
|
35
|
+
properties: {
|
|
36
|
+
cacheName: {
|
|
37
|
+
type: 'string',
|
|
38
|
+
description: 'Specific cache to inspect (optional)'
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
additionalProperties: false,
|
|
42
|
+
$schema: 'http://json-schema.org/draft-07/schema#'
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: 'browser_storage_delete_cache',
|
|
47
|
+
description: 'Delete a specific cache from Cache Storage (see browser_docs)',
|
|
48
|
+
inputSchema: {
|
|
49
|
+
type: 'object',
|
|
50
|
+
properties: {
|
|
51
|
+
cacheName: {
|
|
52
|
+
type: 'string',
|
|
53
|
+
description: 'Cache name to delete'
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
required: ['cacheName'],
|
|
57
|
+
additionalProperties: false,
|
|
58
|
+
$schema: 'http://json-schema.org/draft-07/schema#'
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: 'browser_storage_get_service_workers',
|
|
63
|
+
description: 'Get service worker registrations and their state (see browser_docs)',
|
|
64
|
+
inputSchema: {
|
|
65
|
+
type: 'object',
|
|
66
|
+
properties: {},
|
|
67
|
+
additionalProperties: false,
|
|
68
|
+
$schema: 'http://json-schema.org/draft-07/schema#'
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: 'browser_storage_unregister_service_worker',
|
|
73
|
+
description: 'Unregister a service worker (see browser_docs)',
|
|
74
|
+
inputSchema: {
|
|
75
|
+
type: 'object',
|
|
76
|
+
properties: {
|
|
77
|
+
scopeURL: {
|
|
78
|
+
type: 'string',
|
|
79
|
+
description: 'Scope URL of service worker to unregister'
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
required: ['scopeURL'],
|
|
83
|
+
additionalProperties: false,
|
|
84
|
+
$schema: 'http://json-schema.org/draft-07/schema#'
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
];
|
|
88
|
+
|
|
89
|
+
const handlers = {
|
|
90
|
+
browser_storage_get_indexeddb: async (args) => {
|
|
91
|
+
try {
|
|
92
|
+
const { page } = await connectToBrowser();
|
|
93
|
+
const cdp = await getCDPSession();
|
|
94
|
+
|
|
95
|
+
// Get security origin for current page
|
|
96
|
+
const origin = await page.evaluate(() => window.location.origin);
|
|
97
|
+
|
|
98
|
+
// Request database names
|
|
99
|
+
const { databaseNames } = await cdp.send('IndexedDB.requestDatabaseNames', {
|
|
100
|
+
securityOrigin: origin
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
if (databaseNames.length === 0) {
|
|
104
|
+
return {
|
|
105
|
+
content: [{
|
|
106
|
+
type: 'text',
|
|
107
|
+
text: 'No IndexedDB databases found for this origin.\n\nThe page may not be using IndexedDB.'
|
|
108
|
+
}]
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// If no specific database requested, list all databases
|
|
113
|
+
if (!args.databaseName) {
|
|
114
|
+
return {
|
|
115
|
+
content: [{
|
|
116
|
+
type: 'text',
|
|
117
|
+
text: `📊 IndexedDB Databases:\n\n${JSON.stringify({ origin, databases: databaseNames }, null, 2)}\n\nUse databaseName parameter to inspect a specific database.`
|
|
118
|
+
}]
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Get database structure
|
|
123
|
+
const dbInfo = await cdp.send('IndexedDB.requestDatabase', {
|
|
124
|
+
securityOrigin: origin,
|
|
125
|
+
databaseName: args.databaseName
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
if (!args.objectStoreName) {
|
|
129
|
+
// Return database structure
|
|
130
|
+
const structure = {
|
|
131
|
+
name: dbInfo.databaseWithObjectStores.name,
|
|
132
|
+
version: dbInfo.databaseWithObjectStores.version,
|
|
133
|
+
objectStores: dbInfo.databaseWithObjectStores.objectStores.map(store => ({
|
|
134
|
+
name: store.name,
|
|
135
|
+
keyPath: store.keyPath,
|
|
136
|
+
autoIncrement: store.autoIncrement,
|
|
137
|
+
indexes: store.indexes.map(idx => ({
|
|
138
|
+
name: idx.name,
|
|
139
|
+
keyPath: idx.keyPath,
|
|
140
|
+
unique: idx.unique,
|
|
141
|
+
multiEntry: idx.multiEntry
|
|
142
|
+
}))
|
|
143
|
+
}))
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
content: [{
|
|
148
|
+
type: 'text',
|
|
149
|
+
text: `📊 IndexedDB Database Structure:\n\n${JSON.stringify(structure, null, 2)}\n\nUse objectStoreName parameter to query data from a specific object store.`
|
|
150
|
+
}]
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Get object store data
|
|
155
|
+
const { objectStoreDataEntries, hasMore } = await cdp.send('IndexedDB.requestData', {
|
|
156
|
+
securityOrigin: origin,
|
|
157
|
+
databaseName: args.databaseName,
|
|
158
|
+
objectStoreName: args.objectStoreName,
|
|
159
|
+
indexName: '',
|
|
160
|
+
skipCount: 0,
|
|
161
|
+
pageSize: 100
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
const data = {
|
|
165
|
+
objectStore: args.objectStoreName,
|
|
166
|
+
entries: objectStoreDataEntries.length,
|
|
167
|
+
hasMore: hasMore,
|
|
168
|
+
data: objectStoreDataEntries.map(entry => ({
|
|
169
|
+
key: entry.key,
|
|
170
|
+
primaryKey: entry.primaryKey,
|
|
171
|
+
value: entry.value
|
|
172
|
+
}))
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
return {
|
|
176
|
+
content: [{
|
|
177
|
+
type: 'text',
|
|
178
|
+
text: `📊 IndexedDB Data:\n\n${JSON.stringify(data, null, 2)}\n\nNote: Limited to 100 entries.${hasMore ? ' More entries available.' : ''}`
|
|
179
|
+
}]
|
|
180
|
+
};
|
|
181
|
+
} catch (error) {
|
|
182
|
+
debugLog(`CDP error in browser_storage_get_indexeddb: ${error.message}`);
|
|
183
|
+
return {
|
|
184
|
+
content: [{
|
|
185
|
+
type: 'text',
|
|
186
|
+
text: `❌ CDP Error: ${error.message}\n\nPossible causes:\n- IndexedDB not accessible\n- Invalid database or object store name\n- CDP session disconnected`
|
|
187
|
+
}],
|
|
188
|
+
isError: true
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
|
|
193
|
+
browser_storage_get_cache_storage: async (args) => {
|
|
194
|
+
try {
|
|
195
|
+
const { page } = await connectToBrowser();
|
|
196
|
+
const cdp = await getCDPSession();
|
|
197
|
+
|
|
198
|
+
// Get security origin
|
|
199
|
+
const origin = await page.evaluate(() => window.location.origin);
|
|
200
|
+
|
|
201
|
+
// Request cache names
|
|
202
|
+
const { caches } = await cdp.send('CacheStorage.requestCacheNames', {
|
|
203
|
+
securityOrigin: origin
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
if (caches.length === 0) {
|
|
207
|
+
return {
|
|
208
|
+
content: [{
|
|
209
|
+
type: 'text',
|
|
210
|
+
text: 'No Cache Storage caches found for this origin.\n\nThe page may not be using Cache Storage API.'
|
|
211
|
+
}]
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// If no specific cache requested, list all caches
|
|
216
|
+
if (!args.cacheName) {
|
|
217
|
+
return {
|
|
218
|
+
content: [{
|
|
219
|
+
type: 'text',
|
|
220
|
+
text: `📊 Cache Storage Caches:\n\n${JSON.stringify({ origin, caches: caches.map(c => c.cacheName) }, null, 2)}\n\nUse cacheName parameter to inspect entries in a specific cache.`
|
|
221
|
+
}]
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Find the cache ID
|
|
226
|
+
const cache = caches.find(c => c.cacheName === args.cacheName);
|
|
227
|
+
if (!cache) {
|
|
228
|
+
return {
|
|
229
|
+
content: [{
|
|
230
|
+
type: 'text',
|
|
231
|
+
text: `⚠️ Cache "${args.cacheName}" not found.\n\nAvailable caches:\n${caches.map(c => ` • ${c.cacheName}`).join('\n')}`
|
|
232
|
+
}]
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Get cache entries
|
|
237
|
+
const { cacheDataEntries, returnCount } = await cdp.send('CacheStorage.requestEntries', {
|
|
238
|
+
cacheId: cache.cacheId,
|
|
239
|
+
skipCount: 0,
|
|
240
|
+
pageSize: 50
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
const entries = {
|
|
244
|
+
cacheName: args.cacheName,
|
|
245
|
+
entryCount: returnCount,
|
|
246
|
+
entries: cacheDataEntries.map(entry => ({
|
|
247
|
+
requestURL: entry.requestURL,
|
|
248
|
+
requestMethod: entry.requestMethod,
|
|
249
|
+
responseStatus: entry.responseStatus,
|
|
250
|
+
responseStatusText: entry.responseStatusText,
|
|
251
|
+
responseType: entry.responseType
|
|
252
|
+
}))
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
return {
|
|
256
|
+
content: [{
|
|
257
|
+
type: 'text',
|
|
258
|
+
text: `📊 Cache Storage Entries:\n\n${JSON.stringify(entries, null, 2)}\n\nNote: Limited to 50 entries.`
|
|
259
|
+
}]
|
|
260
|
+
};
|
|
261
|
+
} catch (error) {
|
|
262
|
+
debugLog(`CDP error in browser_storage_get_cache_storage: ${error.message}`);
|
|
263
|
+
return {
|
|
264
|
+
content: [{
|
|
265
|
+
type: 'text',
|
|
266
|
+
text: `❌ CDP Error: ${error.message}\n\nPossible causes:\n- Cache Storage not accessible\n- Invalid cache name\n- CDP session disconnected`
|
|
267
|
+
}],
|
|
268
|
+
isError: true
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
},
|
|
272
|
+
|
|
273
|
+
browser_storage_delete_cache: async (args) => {
|
|
274
|
+
try {
|
|
275
|
+
const { page } = await connectToBrowser();
|
|
276
|
+
const cdp = await getCDPSession();
|
|
277
|
+
|
|
278
|
+
// Get security origin
|
|
279
|
+
const origin = await page.evaluate(() => window.location.origin);
|
|
280
|
+
|
|
281
|
+
// Request cache names to find the cache ID
|
|
282
|
+
const { caches } = await cdp.send('CacheStorage.requestCacheNames', {
|
|
283
|
+
securityOrigin: origin
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
const cache = caches.find(c => c.cacheName === args.cacheName);
|
|
287
|
+
if (!cache) {
|
|
288
|
+
return {
|
|
289
|
+
content: [{
|
|
290
|
+
type: 'text',
|
|
291
|
+
text: `⚠️ Cache "${args.cacheName}" not found.\n\nAvailable caches:\n${caches.map(c => ` • ${c.cacheName}`).join('\n')}`
|
|
292
|
+
}]
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Delete the cache
|
|
297
|
+
await cdp.send('CacheStorage.deleteCache', {
|
|
298
|
+
cacheId: cache.cacheId
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
debugLog(`Deleted cache: ${args.cacheName}`);
|
|
302
|
+
|
|
303
|
+
return {
|
|
304
|
+
content: [{
|
|
305
|
+
type: 'text',
|
|
306
|
+
text: `✅ Cache deleted successfully: ${args.cacheName}`
|
|
307
|
+
}]
|
|
308
|
+
};
|
|
309
|
+
} catch (error) {
|
|
310
|
+
debugLog(`CDP error in browser_storage_delete_cache: ${error.message}`);
|
|
311
|
+
return {
|
|
312
|
+
content: [{
|
|
313
|
+
type: 'text',
|
|
314
|
+
text: `❌ CDP Error: ${error.message}\n\nFailed to delete cache.`
|
|
315
|
+
}],
|
|
316
|
+
isError: true
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
},
|
|
320
|
+
|
|
321
|
+
browser_storage_get_service_workers: async (args) => {
|
|
322
|
+
try {
|
|
323
|
+
const { page } = await connectToBrowser();
|
|
324
|
+
const cdp = await getCDPSession();
|
|
325
|
+
|
|
326
|
+
// Enable ServiceWorker domain
|
|
327
|
+
await cdp.send('ServiceWorker.enable');
|
|
328
|
+
|
|
329
|
+
// Get service worker registrations
|
|
330
|
+
const { registrations } = await cdp.send('ServiceWorker.getServiceWorker', {});
|
|
331
|
+
|
|
332
|
+
if (registrations.length === 0) {
|
|
333
|
+
await cdp.send('ServiceWorker.disable');
|
|
334
|
+
return {
|
|
335
|
+
content: [{
|
|
336
|
+
type: 'text',
|
|
337
|
+
text: 'No service workers found.\n\nThe page may not have registered any service workers.'
|
|
338
|
+
}]
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const workers = registrations.map(reg => ({
|
|
343
|
+
registrationId: reg.registrationId,
|
|
344
|
+
scopeURL: reg.scopeURL,
|
|
345
|
+
isDeleted: reg.isDeleted
|
|
346
|
+
}));
|
|
347
|
+
|
|
348
|
+
await cdp.send('ServiceWorker.disable');
|
|
349
|
+
|
|
350
|
+
return {
|
|
351
|
+
content: [{
|
|
352
|
+
type: 'text',
|
|
353
|
+
text: `📊 Service Workers:\n\n${JSON.stringify(workers, null, 2)}`
|
|
354
|
+
}]
|
|
355
|
+
};
|
|
356
|
+
} catch (error) {
|
|
357
|
+
debugLog(`CDP error in browser_storage_get_service_workers: ${error.message}`);
|
|
358
|
+
|
|
359
|
+
// Try alternative approach using page.evaluate
|
|
360
|
+
try {
|
|
361
|
+
const { page } = await connectToBrowser();
|
|
362
|
+
const swInfo = await page.evaluate(async () => {
|
|
363
|
+
if (!('serviceWorker' in navigator)) {
|
|
364
|
+
return { supported: false };
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const registrations = await navigator.serviceWorker.getRegistrations();
|
|
368
|
+
return {
|
|
369
|
+
supported: true,
|
|
370
|
+
registrations: registrations.map(reg => ({
|
|
371
|
+
scope: reg.scope,
|
|
372
|
+
active: reg.active ? {
|
|
373
|
+
scriptURL: reg.active.scriptURL,
|
|
374
|
+
state: reg.active.state
|
|
375
|
+
} : null,
|
|
376
|
+
installing: reg.installing ? {
|
|
377
|
+
scriptURL: reg.installing.scriptURL,
|
|
378
|
+
state: reg.installing.state
|
|
379
|
+
} : null,
|
|
380
|
+
waiting: reg.waiting ? {
|
|
381
|
+
scriptURL: reg.waiting.scriptURL,
|
|
382
|
+
state: reg.waiting.state
|
|
383
|
+
} : null
|
|
384
|
+
}))
|
|
385
|
+
};
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
if (!swInfo.supported) {
|
|
389
|
+
return {
|
|
390
|
+
content: [{
|
|
391
|
+
type: 'text',
|
|
392
|
+
text: '⚠️ Service Workers not supported in this browser/context.'
|
|
393
|
+
}]
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
return {
|
|
398
|
+
content: [{
|
|
399
|
+
type: 'text',
|
|
400
|
+
text: `📊 Service Workers:\n\n${JSON.stringify(swInfo.registrations, null, 2)}`
|
|
401
|
+
}]
|
|
402
|
+
};
|
|
403
|
+
} catch (fallbackError) {
|
|
404
|
+
debugLog(`Fallback error in browser_storage_get_service_workers: ${fallbackError.message}`);
|
|
405
|
+
return {
|
|
406
|
+
content: [{
|
|
407
|
+
type: 'text',
|
|
408
|
+
text: `❌ Error: ${error.message}\n\nCould not retrieve service worker information.`
|
|
409
|
+
}],
|
|
410
|
+
isError: true
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
},
|
|
415
|
+
|
|
416
|
+
browser_storage_unregister_service_worker: async (args) => {
|
|
417
|
+
try {
|
|
418
|
+
const { page } = await connectToBrowser();
|
|
419
|
+
|
|
420
|
+
// Use page.evaluate to unregister via JavaScript API
|
|
421
|
+
const result = await page.evaluate(async (scopeURL) => {
|
|
422
|
+
if (!('serviceWorker' in navigator)) {
|
|
423
|
+
return { success: false, error: 'Service Workers not supported' };
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
const registrations = await navigator.serviceWorker.getRegistrations();
|
|
427
|
+
const registration = registrations.find(reg => reg.scope === scopeURL);
|
|
428
|
+
|
|
429
|
+
if (!registration) {
|
|
430
|
+
return { success: false, error: 'Service worker not found for scope: ' + scopeURL };
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
const unregistered = await registration.unregister();
|
|
434
|
+
return { success: unregistered };
|
|
435
|
+
}, args.scopeURL);
|
|
436
|
+
|
|
437
|
+
if (!result.success) {
|
|
438
|
+
return {
|
|
439
|
+
content: [{
|
|
440
|
+
type: 'text',
|
|
441
|
+
text: `⚠️ ${result.error}`
|
|
442
|
+
}]
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
debugLog(`Unregistered service worker: ${args.scopeURL}`);
|
|
447
|
+
|
|
448
|
+
return {
|
|
449
|
+
content: [{
|
|
450
|
+
type: 'text',
|
|
451
|
+
text: `✅ Service worker unregistered successfully\n\nScope: ${args.scopeURL}`
|
|
452
|
+
}]
|
|
453
|
+
};
|
|
454
|
+
} catch (error) {
|
|
455
|
+
debugLog(`Error in browser_storage_unregister_service_worker: ${error.message}`);
|
|
456
|
+
return {
|
|
457
|
+
content: [{
|
|
458
|
+
type: 'text',
|
|
459
|
+
text: `❌ Error: ${error.message}\n\nFailed to unregister service worker.`
|
|
460
|
+
}],
|
|
461
|
+
isError: true
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
};
|
|
466
|
+
|
|
467
|
+
module.exports = { definitions, handlers };
|
package/src/tools/system.js
CHANGED
|
@@ -104,6 +104,10 @@ const handlers = {
|
|
|
104
104
|
browserProfile = process.env.MCP_BROWSER_PROFILE || `${os.tmpdir()}/chrome-mcp-profile`;
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
+
// Get dynamic tool count (loaded at runtime to avoid circular dependency)
|
|
108
|
+
const { tools } = require('./index');
|
|
109
|
+
const toolCount = tools.length;
|
|
110
|
+
|
|
107
111
|
return {
|
|
108
112
|
content: [{
|
|
109
113
|
type: 'text',
|
|
@@ -113,7 +117,7 @@ const handlers = {
|
|
|
113
117
|
`✅ Chrome: Port 9222\n` +
|
|
114
118
|
`✅ Profile: ${browserProfile}\n` +
|
|
115
119
|
`✅ Current page: ${url}\n\n` +
|
|
116
|
-
`All
|
|
120
|
+
`All ${toolCount} browser tools are ready to use!`
|
|
117
121
|
}]
|
|
118
122
|
};
|
|
119
123
|
},
|
package/src/utils.js
CHANGED
|
@@ -4,6 +4,16 @@
|
|
|
4
4
|
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
const os = require('os');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
|
|
9
|
+
// Get version from package.json
|
|
10
|
+
let version = 'unknown';
|
|
11
|
+
try {
|
|
12
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8'));
|
|
13
|
+
version = pkg.version;
|
|
14
|
+
} catch (error) {
|
|
15
|
+
// Fallback if package.json cannot be read
|
|
16
|
+
}
|
|
7
17
|
|
|
8
18
|
// Log file location
|
|
9
19
|
const logFile = `${os.tmpdir()}/mcp-browser-server.log`;
|
|
@@ -116,5 +126,6 @@ module.exports = {
|
|
|
116
126
|
loadPlaywright,
|
|
117
127
|
getPlaywrightPath,
|
|
118
128
|
findChromeExecutable,
|
|
119
|
-
logFile
|
|
129
|
+
logFile,
|
|
130
|
+
version
|
|
120
131
|
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Network Test Page</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<h1>Network Test Page</h1>
|
|
10
|
+
<p>This page makes multiple network requests for testing.</p>
|
|
11
|
+
<div id="status"></div>
|
|
12
|
+
|
|
13
|
+
<script>
|
|
14
|
+
async function makeNetworkRequests() {
|
|
15
|
+
const status = document.getElementById('status');
|
|
16
|
+
status.innerHTML = 'Making network requests...<br>';
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
// Make multiple fetch requests
|
|
20
|
+
const urls = [
|
|
21
|
+
'https://jsonplaceholder.typicode.com/posts/1',
|
|
22
|
+
'https://jsonplaceholder.typicode.com/posts/2',
|
|
23
|
+
'https://jsonplaceholder.typicode.com/users/1'
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
for (const url of urls) {
|
|
27
|
+
try {
|
|
28
|
+
const response = await fetch(url);
|
|
29
|
+
const data = await response.json();
|
|
30
|
+
status.innerHTML += `✓ Fetched: ${url}<br>`;
|
|
31
|
+
} catch (e) {
|
|
32
|
+
status.innerHTML += `✗ Failed: ${url} - ${e.message}<br>`;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
status.innerHTML += 'Network requests completed!';
|
|
37
|
+
} catch (error) {
|
|
38
|
+
status.innerHTML += `Error: ${error.message}`;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Auto-run after page load
|
|
43
|
+
window.addEventListener('load', () => {
|
|
44
|
+
setTimeout(makeNetworkRequests, 500);
|
|
45
|
+
});
|
|
46
|
+
</script>
|
|
47
|
+
</body>
|
|
48
|
+
</html>
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Performance Test Page</title>
|
|
7
|
+
<style>
|
|
8
|
+
body {
|
|
9
|
+
font-family: Arial, sans-serif;
|
|
10
|
+
padding: 20px;
|
|
11
|
+
}
|
|
12
|
+
.container {
|
|
13
|
+
max-width: 800px;
|
|
14
|
+
margin: 0 auto;
|
|
15
|
+
}
|
|
16
|
+
</style>
|
|
17
|
+
</head>
|
|
18
|
+
<body>
|
|
19
|
+
<div class="container">
|
|
20
|
+
<h1>Performance Test Page</h1>
|
|
21
|
+
<p>This page is used for testing CPU profiling and performance metrics.</p>
|
|
22
|
+
<button id="heavy-compute">Run Heavy Computation</button>
|
|
23
|
+
<div id="output"></div>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<script>
|
|
27
|
+
// Heavy computation for CPU profiling test
|
|
28
|
+
function fibonacci(n) {
|
|
29
|
+
if (n <= 1) return n;
|
|
30
|
+
return fibonacci(n - 1) + fibonacci(n - 2);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function performHeavyComputation() {
|
|
34
|
+
const start = Date.now();
|
|
35
|
+
const result = fibonacci(35);
|
|
36
|
+
const duration = Date.now() - start;
|
|
37
|
+
document.getElementById('output').innerHTML =
|
|
38
|
+
`Computed fibonacci(35) = ${result} in ${duration}ms`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Auto-run computation for testing
|
|
42
|
+
document.getElementById('heavy-compute').addEventListener('click', performHeavyComputation);
|
|
43
|
+
|
|
44
|
+
// Run automatically after a short delay
|
|
45
|
+
setTimeout(performHeavyComputation, 100);
|
|
46
|
+
|
|
47
|
+
// Create some DOM nodes for metrics testing
|
|
48
|
+
for (let i = 0; i < 100; i++) {
|
|
49
|
+
const div = document.createElement('div');
|
|
50
|
+
div.textContent = `Node ${i}`;
|
|
51
|
+
div.style.display = 'none';
|
|
52
|
+
document.body.appendChild(div);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Add event listeners for metrics testing
|
|
56
|
+
for (let i = 0; i < 50; i++) {
|
|
57
|
+
document.body.addEventListener('click', () => {});
|
|
58
|
+
}
|
|
59
|
+
</script>
|
|
60
|
+
</body>
|
|
61
|
+
</html>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<meta http-equiv="Content-Security-Policy" content="default-src 'self' 'unsafe-inline'">
|
|
7
|
+
<title>Security Test Page</title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<h1>Security Test Page</h1>
|
|
11
|
+
<p>This page is used for testing security features.</p>
|
|
12
|
+
<div id="status"></div>
|
|
13
|
+
|
|
14
|
+
<script>
|
|
15
|
+
// This inline script is allowed by CSP
|
|
16
|
+
document.getElementById('status').innerHTML = 'Page loaded successfully';
|
|
17
|
+
|
|
18
|
+
// Attempt to trigger CSP violation by trying to load external script
|
|
19
|
+
// (This will be blocked and create a CSP violation)
|
|
20
|
+
try {
|
|
21
|
+
const script = document.createElement('script');
|
|
22
|
+
script.src = 'https://example.com/external.js';
|
|
23
|
+
document.head.appendChild(script);
|
|
24
|
+
} catch (e) {
|
|
25
|
+
console.log('CSP blocked external script');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Log security info
|
|
29
|
+
console.log('Page protocol:', window.location.protocol);
|
|
30
|
+
console.log('Page origin:', window.location.origin);
|
|
31
|
+
</script>
|
|
32
|
+
</body>
|
|
33
|
+
</html>
|