chrome-devtools-mcp 0.21.0 → 0.22.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/README.md +83 -21
- package/build/src/HeapSnapshotManager.js +94 -0
- package/build/src/McpContext.js +26 -56
- package/build/src/McpPage.js +16 -0
- package/build/src/McpResponse.js +145 -11
- package/build/src/PageCollector.js +10 -24
- package/build/src/WaitForHelper.js +31 -0
- package/build/src/bin/check-latest-version.js +25 -0
- package/build/src/bin/chrome-devtools-mcp-cli-options.js +24 -10
- package/build/src/bin/chrome-devtools-mcp-main.js +2 -0
- package/build/src/bin/chrome-devtools.js +3 -0
- package/build/src/bin/cliDefinitions.js +14 -8
- package/build/src/daemon/client.js +1 -1
- package/build/src/daemon/daemon.js +0 -4
- package/build/src/formatters/HeapSnapshotFormatter.js +38 -0
- package/build/src/formatters/NetworkFormatter.js +24 -7
- package/build/src/index.js +12 -1
- package/build/src/telemetry/ClearcutLogger.js +34 -12
- package/build/src/telemetry/flagUtils.js +46 -4
- package/build/src/telemetry/toolMetricsUtils.js +88 -0
- package/build/src/telemetry/watchdog/ClearcutSender.js +4 -3
- package/build/src/third_party/THIRD_PARTY_NOTICES +32 -32
- package/build/src/third_party/bundled-packages.json +5 -4
- package/build/src/third_party/devtools-formatter-worker.js +61 -64
- package/build/src/third_party/devtools-heap-snapshot-worker.js +9690 -0
- package/build/src/third_party/index.js +61443 -59378
- package/build/src/third_party/lighthouse-devtools-mcp-bundle.js +3501 -2658
- package/build/src/tools/categories.js +3 -0
- package/build/src/tools/console.js +42 -39
- package/build/src/tools/emulation.js +1 -1
- package/build/src/tools/extensions.js +5 -11
- package/build/src/tools/inPage.js +27 -6
- package/build/src/tools/input.js +15 -16
- package/build/src/tools/lighthouse.js +2 -2
- package/build/src/tools/memory.js +48 -3
- package/build/src/tools/network.js +2 -2
- package/build/src/tools/pages.js +8 -5
- package/build/src/tools/performance.js +1 -1
- package/build/src/tools/screencast.js +2 -1
- package/build/src/tools/screenshot.js +3 -3
- package/build/src/tools/script.js +22 -16
- package/build/src/tools/tools.js +2 -0
- package/build/src/tools/webmcp.js +63 -0
- package/build/src/utils/check-for-updates.js +73 -0
- package/build/src/utils/files.js +4 -0
- package/build/src/utils/id.js +15 -0
- package/build/src/version.js +1 -1
- package/package.json +12 -8
- package/build/src/utils/ExtensionRegistry.js +0 -35
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { stableIdSymbol } from '../utils/id.js';
|
|
7
|
+
export class HeapSnapshotFormatter {
|
|
8
|
+
#aggregates;
|
|
9
|
+
constructor(aggregates) {
|
|
10
|
+
this.#aggregates = aggregates;
|
|
11
|
+
}
|
|
12
|
+
#getSortedAggregates() {
|
|
13
|
+
return Object.values(this.#aggregates).sort((a, b) => b.self - a.self);
|
|
14
|
+
}
|
|
15
|
+
toString() {
|
|
16
|
+
const sorted = this.#getSortedAggregates();
|
|
17
|
+
const lines = [];
|
|
18
|
+
lines.push('uid,className,count,selfSize,maxRetainedSize');
|
|
19
|
+
for (const info of sorted) {
|
|
20
|
+
const uid = info[stableIdSymbol] ?? '';
|
|
21
|
+
lines.push(`${uid},"${info.name}",${info.count},${info.self},${info.maxRet}`);
|
|
22
|
+
}
|
|
23
|
+
return lines.join('\n');
|
|
24
|
+
}
|
|
25
|
+
toJSON() {
|
|
26
|
+
const sorted = this.#getSortedAggregates();
|
|
27
|
+
return sorted.map(info => ({
|
|
28
|
+
uid: info[stableIdSymbol],
|
|
29
|
+
className: info.name,
|
|
30
|
+
count: info.count,
|
|
31
|
+
selfSize: info.self,
|
|
32
|
+
retainedSize: info.maxRet,
|
|
33
|
+
}));
|
|
34
|
+
}
|
|
35
|
+
static sort(aggregates) {
|
|
36
|
+
return Object.entries(aggregates).sort((a, b) => b[1].self - a[1].self);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
* */
|
|
6
6
|
import { isUtf8 } from 'node:buffer';
|
|
7
|
+
import { DevTools, } from '../third_party/index.js';
|
|
7
8
|
const BODY_CONTEXT_SIZE_LIMIT = 10000;
|
|
8
9
|
export class NetworkFormatter {
|
|
9
10
|
#request;
|
|
@@ -40,8 +41,8 @@ export class NetworkFormatter {
|
|
|
40
41
|
throw new Error('saveFile is not provided');
|
|
41
42
|
}
|
|
42
43
|
if (data) {
|
|
43
|
-
await this.#options.saveFile(Buffer.from(data), this.#options.requestFilePath);
|
|
44
|
-
this.#requestBodyFilePath =
|
|
44
|
+
const result = await this.#options.saveFile(Buffer.from(data), this.#options.requestFilePath, '.network-request');
|
|
45
|
+
this.#requestBodyFilePath = result.filename;
|
|
45
46
|
}
|
|
46
47
|
else {
|
|
47
48
|
this.#requestBody = requestBodyNotAvailableMessage;
|
|
@@ -66,8 +67,8 @@ export class NetworkFormatter {
|
|
|
66
67
|
if (!this.#options.saveFile) {
|
|
67
68
|
throw new Error('saveFile is not provided');
|
|
68
69
|
}
|
|
69
|
-
await this.#options.saveFile(buffer, this.#options.responseFilePath);
|
|
70
|
-
this.#responseBodyFilePath =
|
|
70
|
+
const result = await this.#options.saveFile(buffer, this.#options.responseFilePath, '.network-response');
|
|
71
|
+
this.#responseBodyFilePath = result.filename;
|
|
71
72
|
}
|
|
72
73
|
catch {
|
|
73
74
|
// Flatten error handling for buffer() failure and save failure
|
|
@@ -96,6 +97,16 @@ export class NetworkFormatter {
|
|
|
96
97
|
selectedInDevToolsUI: this.#options.selectedInDevToolsUI,
|
|
97
98
|
};
|
|
98
99
|
}
|
|
100
|
+
#redactNetworkHeaders(headers) {
|
|
101
|
+
const headersList = Object.entries(headers).map(item => {
|
|
102
|
+
return { name: item[0], value: item[1] };
|
|
103
|
+
});
|
|
104
|
+
const redacted = DevTools.NetworkRequestFormatter.sanitizeHeaders(headersList);
|
|
105
|
+
return redacted.reduce((acc, item) => {
|
|
106
|
+
acc[item.name] = item.value;
|
|
107
|
+
return acc;
|
|
108
|
+
}, {});
|
|
109
|
+
}
|
|
99
110
|
toJSONDetailed() {
|
|
100
111
|
const redirectChain = this.#request.redirectChain();
|
|
101
112
|
const formattedRedirectChain = redirectChain.reverse().map(request => {
|
|
@@ -105,15 +116,21 @@ export class NetworkFormatter {
|
|
|
105
116
|
const formatter = new NetworkFormatter(request, {
|
|
106
117
|
requestId: id,
|
|
107
118
|
saveFile: this.#options.saveFile,
|
|
119
|
+
redactNetworkHeaders: this.#options.redactNetworkHeaders,
|
|
108
120
|
});
|
|
109
121
|
return formatter.toJSON();
|
|
110
122
|
});
|
|
123
|
+
const responseHeaders = this.#request.response()?.headers();
|
|
111
124
|
return {
|
|
112
125
|
...this.toJSON(),
|
|
113
|
-
requestHeaders: this.#
|
|
126
|
+
requestHeaders: this.#options.redactNetworkHeaders
|
|
127
|
+
? this.#redactNetworkHeaders(this.#request.headers())
|
|
128
|
+
: this.#request.headers(),
|
|
114
129
|
requestBody: this.#requestBody,
|
|
115
130
|
requestBodyFilePath: this.#requestBodyFilePath,
|
|
116
|
-
responseHeaders: this.#
|
|
131
|
+
responseHeaders: this.#options.redactNetworkHeaders && responseHeaders
|
|
132
|
+
? this.#redactNetworkHeaders(responseHeaders)
|
|
133
|
+
: this.#request.response()?.headers(),
|
|
117
134
|
responseBody: this.#responseBody,
|
|
118
135
|
responseBodyFilePath: this.#responseBodyFilePath,
|
|
119
136
|
failure: this.#request.failure()?.errorText,
|
|
@@ -210,7 +227,7 @@ function converNetworkRequestDetailedToStringDetailed(data) {
|
|
|
210
227
|
response.push(`### Redirect chain`);
|
|
211
228
|
let indent = 0;
|
|
212
229
|
for (const request of redirectChain.reverse()) {
|
|
213
|
-
response.push(`${' '.repeat(indent)}${convertNetworkRequestConciseToString(request)}
|
|
230
|
+
response.push(`${' '.repeat(indent)}${convertNetworkRequestConciseToString(request)}`);
|
|
214
231
|
indent++;
|
|
215
232
|
}
|
|
216
233
|
}
|
package/build/src/index.js
CHANGED
|
@@ -101,7 +101,7 @@ export async function createMcpServer(serverArgs, options) {
|
|
|
101
101
|
return;
|
|
102
102
|
}
|
|
103
103
|
if (tool.annotations.category === ToolCategory.EXTENSIONS &&
|
|
104
|
-
|
|
104
|
+
serverArgs.categoryExtensions === false) {
|
|
105
105
|
return;
|
|
106
106
|
}
|
|
107
107
|
if (tool.annotations.category === ToolCategory.IN_PAGE &&
|
|
@@ -112,6 +112,10 @@ export async function createMcpServer(serverArgs, options) {
|
|
|
112
112
|
!serverArgs.experimentalVision) {
|
|
113
113
|
return;
|
|
114
114
|
}
|
|
115
|
+
if (tool.annotations.conditions?.includes('experimentalMemory') &&
|
|
116
|
+
!serverArgs.experimentalMemory) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
115
119
|
if (tool.annotations.conditions?.includes('experimentalInteropTools') &&
|
|
116
120
|
!serverArgs.experimentalInteropTools) {
|
|
117
121
|
return;
|
|
@@ -120,6 +124,10 @@ export async function createMcpServer(serverArgs, options) {
|
|
|
120
124
|
!serverArgs.experimentalScreencast) {
|
|
121
125
|
return;
|
|
122
126
|
}
|
|
127
|
+
if (tool.annotations.conditions?.includes('experimentalWebmcp') &&
|
|
128
|
+
!serverArgs.experimentalWebmcp) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
123
131
|
const schema = 'pageScoped' in tool &&
|
|
124
132
|
tool.pageScoped &&
|
|
125
133
|
serverArgs.experimentalPageIdRouting &&
|
|
@@ -142,6 +150,7 @@ export async function createMcpServer(serverArgs, options) {
|
|
|
142
150
|
const response = serverArgs.slim
|
|
143
151
|
? new SlimMcpResponse(serverArgs)
|
|
144
152
|
: new McpResponse(serverArgs);
|
|
153
|
+
response.setRedactNetworkHeaders(serverArgs.redactNetworkHeaders);
|
|
145
154
|
if ('pageScoped' in tool && tool.pageScoped) {
|
|
146
155
|
const page = serverArgs.experimentalPageIdRouting &&
|
|
147
156
|
params.pageId &&
|
|
@@ -190,6 +199,8 @@ export async function createMcpServer(serverArgs, options) {
|
|
|
190
199
|
finally {
|
|
191
200
|
void clearcutLogger?.logToolInvocation({
|
|
192
201
|
toolName: tool.name,
|
|
202
|
+
params,
|
|
203
|
+
schema,
|
|
193
204
|
success,
|
|
194
205
|
latencyMs: bucketizeLatency(Date.now() - startTime),
|
|
195
206
|
});
|
|
@@ -10,7 +10,7 @@ import { FilePersistence } from './persistence.js';
|
|
|
10
10
|
import { McpClient, WatchdogMessageType, OsType, } from './types.js';
|
|
11
11
|
import { WatchdogClient } from './WatchdogClient.js';
|
|
12
12
|
const MS_PER_DAY = 24 * 60 * 60 * 1000;
|
|
13
|
-
const PARAM_BLOCKLIST = new Set(['uid']);
|
|
13
|
+
export const PARAM_BLOCKLIST = new Set(['uid', 'reqid', 'msgid']);
|
|
14
14
|
const SUPPORTED_ZOD_TYPES = [
|
|
15
15
|
'ZodString',
|
|
16
16
|
'ZodNumber',
|
|
@@ -21,7 +21,7 @@ const SUPPORTED_ZOD_TYPES = [
|
|
|
21
21
|
function isZodType(type) {
|
|
22
22
|
return SUPPORTED_ZOD_TYPES.includes(type);
|
|
23
23
|
}
|
|
24
|
-
function getZodType(zodType) {
|
|
24
|
+
export function getZodType(zodType) {
|
|
25
25
|
const def = zodType._def;
|
|
26
26
|
const typeName = def.typeName;
|
|
27
27
|
if (typeName === 'ZodOptional' ||
|
|
@@ -37,15 +37,31 @@ function getZodType(zodType) {
|
|
|
37
37
|
}
|
|
38
38
|
throw new Error(`Unsupported zod type for tool parameter: ${typeName}`);
|
|
39
39
|
}
|
|
40
|
-
function
|
|
40
|
+
export function transformArgName(zodType, name) {
|
|
41
|
+
const snakeCaseName = name.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`);
|
|
41
42
|
if (zodType === 'ZodString') {
|
|
42
|
-
return `${
|
|
43
|
+
return `${snakeCaseName}_length`;
|
|
43
44
|
}
|
|
44
45
|
else if (zodType === 'ZodArray') {
|
|
45
|
-
return `${
|
|
46
|
+
return `${snakeCaseName}_count`;
|
|
46
47
|
}
|
|
47
48
|
else {
|
|
48
|
-
return
|
|
49
|
+
return snakeCaseName;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export function transformArgType(zodType) {
|
|
53
|
+
if (zodType === 'ZodString' || zodType === 'ZodArray') {
|
|
54
|
+
return 'number';
|
|
55
|
+
}
|
|
56
|
+
switch (zodType) {
|
|
57
|
+
case 'ZodNumber':
|
|
58
|
+
return 'number';
|
|
59
|
+
case 'ZodBoolean':
|
|
60
|
+
return 'boolean';
|
|
61
|
+
case 'ZodEnum':
|
|
62
|
+
return 'enum';
|
|
63
|
+
default:
|
|
64
|
+
throw new Error(`Unsupported zod type for tool parameter: ${zodType}`);
|
|
49
65
|
}
|
|
50
66
|
}
|
|
51
67
|
function transformValue(zodType, value) {
|
|
@@ -91,7 +107,7 @@ export function sanitizeParams(params, schema) {
|
|
|
91
107
|
if (!hasEquivalentType(zodType, value)) {
|
|
92
108
|
throw new Error(`parameter ${name} has type ${zodType} but value ${value} is not of equivalent type`);
|
|
93
109
|
}
|
|
94
|
-
const transformedName =
|
|
110
|
+
const transformedName = transformArgName(zodType, name);
|
|
95
111
|
const transformedValue = transformValue(zodType, value);
|
|
96
112
|
transformed[transformedName] = transformedValue;
|
|
97
113
|
}
|
|
@@ -153,15 +169,21 @@ export class ClearcutLogger {
|
|
|
153
169
|
}
|
|
154
170
|
}
|
|
155
171
|
async logToolInvocation(args) {
|
|
172
|
+
const tool_invocation = {
|
|
173
|
+
tool_name: args.toolName,
|
|
174
|
+
success: args.success,
|
|
175
|
+
latency_ms: args.latencyMs,
|
|
176
|
+
};
|
|
177
|
+
if (Object.keys(args.params).length > 0) {
|
|
178
|
+
tool_invocation.tool_params = {
|
|
179
|
+
[`${args.toolName}_params`]: sanitizeParams(args.params, args.schema),
|
|
180
|
+
};
|
|
181
|
+
}
|
|
156
182
|
this.#watchdog.send({
|
|
157
183
|
type: WatchdogMessageType.LOG_EVENT,
|
|
158
184
|
payload: {
|
|
159
185
|
mcp_client: this.#mcpClient,
|
|
160
|
-
tool_invocation:
|
|
161
|
-
tool_name: args.toolName,
|
|
162
|
-
success: args.success,
|
|
163
|
-
latency_ms: args.latencyMs,
|
|
164
|
-
},
|
|
186
|
+
tool_invocation: tool_invocation,
|
|
165
187
|
},
|
|
166
188
|
});
|
|
167
189
|
}
|
|
@@ -4,6 +4,14 @@
|
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
import { toSnakeCase } from '../utils/string.js';
|
|
7
|
+
/**
|
|
8
|
+
* For enums, log the value as uppercase.
|
|
9
|
+
* We're going to have an enum for such flags with choices represented
|
|
10
|
+
* as an `enum` where the keys of the enum will map to the uppercase `choice`.
|
|
11
|
+
*/
|
|
12
|
+
function formatEnumChoice(snakeCaseName, choice) {
|
|
13
|
+
return `${snakeCaseName}_${choice}`.toUpperCase();
|
|
14
|
+
}
|
|
7
15
|
/**
|
|
8
16
|
* Computes telemetry flag usage from parsed arguments and CLI options.
|
|
9
17
|
*
|
|
@@ -14,6 +22,8 @@ import { toSnakeCase } from '../utils/string.js';
|
|
|
14
22
|
* - The provided value differs from the default value.
|
|
15
23
|
* - Boolean flags are logged with their literal value.
|
|
16
24
|
* - String flags with defined `choices` (Enums) are logged as their uppercase value.
|
|
25
|
+
*
|
|
26
|
+
* IMPORTANT: keep getPossibleFlagMetrics() in sync with this function.
|
|
17
27
|
*/
|
|
18
28
|
export function computeFlagUsage(args, options) {
|
|
19
29
|
const usage = {};
|
|
@@ -35,11 +45,43 @@ export function computeFlagUsage(args, options) {
|
|
|
35
45
|
typeof value === 'string' &&
|
|
36
46
|
'choices' in config &&
|
|
37
47
|
config.choices) {
|
|
38
|
-
|
|
39
|
-
// We're going to have an enum for such flags with choices represented
|
|
40
|
-
// as an `enum` where the keys of the enum will map to the uppercase `choice`.
|
|
41
|
-
usage[snakeCaseName] = `${snakeCaseName}_${value}`.toUpperCase();
|
|
48
|
+
usage[snakeCaseName] = formatEnumChoice(snakeCaseName, value);
|
|
42
49
|
}
|
|
43
50
|
}
|
|
44
51
|
return usage;
|
|
45
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Computes the list of possible flag metrics based on the CLI options.
|
|
55
|
+
*
|
|
56
|
+
* IMPORTANT: keep this function in sync with computeFlagUsage().
|
|
57
|
+
*/
|
|
58
|
+
export function getPossibleFlagMetrics(options) {
|
|
59
|
+
const metrics = [];
|
|
60
|
+
for (const [flagName, config] of Object.entries(options)) {
|
|
61
|
+
const snakeCaseName = toSnakeCase(flagName);
|
|
62
|
+
// _present is always a possible metric
|
|
63
|
+
metrics.push({
|
|
64
|
+
name: `${snakeCaseName}_present`,
|
|
65
|
+
flagType: 'boolean',
|
|
66
|
+
});
|
|
67
|
+
if (config.type === 'boolean') {
|
|
68
|
+
metrics.push({
|
|
69
|
+
name: snakeCaseName,
|
|
70
|
+
flagType: 'boolean',
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
else if (config.type === 'string' &&
|
|
74
|
+
'choices' in config &&
|
|
75
|
+
config.choices) {
|
|
76
|
+
metrics.push({
|
|
77
|
+
name: snakeCaseName,
|
|
78
|
+
flagType: 'enum',
|
|
79
|
+
choices: [
|
|
80
|
+
`${snakeCaseName.toUpperCase()}_UNSPECIFIED`,
|
|
81
|
+
...config.choices.map(choice => formatEnumChoice(snakeCaseName, choice)),
|
|
82
|
+
],
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return metrics;
|
|
87
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { transformArgName, transformArgType, getZodType, PARAM_BLOCKLIST, } from './ClearcutLogger.js';
|
|
7
|
+
/**
|
|
8
|
+
* Validates that all values in an enum are of the homogeneous primitive type.
|
|
9
|
+
* Returns the primitive type string. Throws an error if heterogeneous.
|
|
10
|
+
*/
|
|
11
|
+
export function validateEnumHomogeneity(values) {
|
|
12
|
+
const firstType = typeof values[0];
|
|
13
|
+
for (const val of values) {
|
|
14
|
+
if (typeof val !== firstType) {
|
|
15
|
+
throw new Error('Heterogeneous enum types found');
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return firstType;
|
|
19
|
+
}
|
|
20
|
+
export function applyToExistingMetrics(existing, update) {
|
|
21
|
+
const updated = applyToExisting(existing, update);
|
|
22
|
+
const existingByName = new Map(existing.map(tool => [tool.name, tool]));
|
|
23
|
+
const updatedByName = new Map(update.map(tool => [tool.name, tool]));
|
|
24
|
+
return updated.map(tool => {
|
|
25
|
+
const existingTool = existingByName.get(tool.name);
|
|
26
|
+
const updatedTool = updatedByName.get(tool.name);
|
|
27
|
+
// If the tool still exists in the update, we will update the args.
|
|
28
|
+
if (existingTool && updatedTool) {
|
|
29
|
+
const updatedArgs = applyToExisting(existingTool.args, updatedTool.args);
|
|
30
|
+
return { ...tool, args: updatedArgs };
|
|
31
|
+
}
|
|
32
|
+
return tool;
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
function applyToExisting(existing, update) {
|
|
36
|
+
const existingNames = new Set(existing.map(item => item.name));
|
|
37
|
+
const updatedNames = new Set(update.map(item => item.name));
|
|
38
|
+
const result = [];
|
|
39
|
+
// Keep the original ordering.
|
|
40
|
+
for (const entry of existing) {
|
|
41
|
+
const toAdd = { ...entry };
|
|
42
|
+
if (!updatedNames.has(entry.name)) {
|
|
43
|
+
toAdd.isDeprecated = true;
|
|
44
|
+
}
|
|
45
|
+
result.push(toAdd);
|
|
46
|
+
}
|
|
47
|
+
// New entries must be added to the very back of the list.
|
|
48
|
+
for (const entry of update) {
|
|
49
|
+
if (!existingNames.has(entry.name)) {
|
|
50
|
+
result.push({ ...entry });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Generates tool metrics from tool definitions.
|
|
57
|
+
*/
|
|
58
|
+
export function generateToolMetrics(tools) {
|
|
59
|
+
return tools.map(tool => {
|
|
60
|
+
const args = [];
|
|
61
|
+
for (const [name, schema] of Object.entries(tool.schema)) {
|
|
62
|
+
if (PARAM_BLOCKLIST.has(name)) {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
const zodType = getZodType(schema);
|
|
66
|
+
const transformedName = transformArgName(zodType, name);
|
|
67
|
+
let argType = transformArgType(zodType);
|
|
68
|
+
if (argType === 'enum') {
|
|
69
|
+
let values;
|
|
70
|
+
if (schema._def.values?.length > 0) {
|
|
71
|
+
values = schema._def.values;
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
values = schema._def.innerType._def.values;
|
|
75
|
+
}
|
|
76
|
+
argType = validateEnumHomogeneity(values);
|
|
77
|
+
}
|
|
78
|
+
args.push({
|
|
79
|
+
name: transformedName,
|
|
80
|
+
argType,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
name: tool.name,
|
|
85
|
+
args,
|
|
86
|
+
};
|
|
87
|
+
});
|
|
88
|
+
}
|
|
@@ -42,13 +42,14 @@ export class ClearcutSender {
|
|
|
42
42
|
this.#sessionId = crypto.randomUUID();
|
|
43
43
|
this.#sessionCreated = Date.now();
|
|
44
44
|
}
|
|
45
|
-
|
|
46
|
-
this.#addToBuffer({
|
|
45
|
+
const eventToSend = {
|
|
47
46
|
...event,
|
|
48
47
|
session_id: this.#sessionId,
|
|
49
48
|
app_version: this.#appVersion,
|
|
50
49
|
os_type: this.#osType,
|
|
51
|
-
}
|
|
50
|
+
};
|
|
51
|
+
logger('Enqueing telemetry event', JSON.stringify(eventToSend, null, 2));
|
|
52
|
+
this.#addToBuffer(eventToSend);
|
|
52
53
|
if (!this.#timerStarted) {
|
|
53
54
|
this.#timerStarted = true;
|
|
54
55
|
this.#scheduleFlush(this.#flushIntervalMs);
|
|
@@ -293,6 +293,30 @@ Permission to use, copy, modify, and/or distribute this software for any purpose
|
|
|
293
293
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
294
294
|
|
|
295
295
|
|
|
296
|
+
-------------------- DEPENDENCY DIVIDER --------------------
|
|
297
|
+
|
|
298
|
+
Name: semver
|
|
299
|
+
URL: git+https://github.com/npm/node-semver.git
|
|
300
|
+
Version: 7.7.4
|
|
301
|
+
License: ISC
|
|
302
|
+
|
|
303
|
+
The ISC License
|
|
304
|
+
|
|
305
|
+
Copyright (c) Isaac Z. Schlueter and Contributors
|
|
306
|
+
|
|
307
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
308
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
309
|
+
copyright notice and this permission notice appear in all copies.
|
|
310
|
+
|
|
311
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
312
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
313
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
314
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
315
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
316
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
|
317
|
+
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
318
|
+
|
|
319
|
+
|
|
296
320
|
-------------------- DEPENDENCY DIVIDER --------------------
|
|
297
321
|
|
|
298
322
|
Name: debug
|
|
@@ -422,7 +446,7 @@ SOFTWARE.
|
|
|
422
446
|
|
|
423
447
|
Name: @modelcontextprotocol/sdk
|
|
424
448
|
URL: https://modelcontextprotocol.io
|
|
425
|
-
Version: 1.
|
|
449
|
+
Version: 1.29.0
|
|
426
450
|
License: MIT
|
|
427
451
|
|
|
428
452
|
MIT License
|
|
@@ -818,7 +842,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
818
842
|
|
|
819
843
|
Name: puppeteer-core
|
|
820
844
|
URL: https://github.com/puppeteer/puppeteer/tree/main/packages/puppeteer-core
|
|
821
|
-
Version: 24.
|
|
845
|
+
Version: 24.42.0
|
|
822
846
|
License: Apache-2.0
|
|
823
847
|
|
|
824
848
|
-------------------- DEPENDENCY DIVIDER --------------------
|
|
@@ -828,30 +852,6 @@ URL: https://github.com/puppeteer/puppeteer/tree/main/packages/browsers
|
|
|
828
852
|
Version: 2.13.0
|
|
829
853
|
License: Apache-2.0
|
|
830
854
|
|
|
831
|
-
-------------------- DEPENDENCY DIVIDER --------------------
|
|
832
|
-
|
|
833
|
-
Name: semver
|
|
834
|
-
URL: git+https://github.com/npm/node-semver.git
|
|
835
|
-
Version: 7.7.4
|
|
836
|
-
License: ISC
|
|
837
|
-
|
|
838
|
-
The ISC License
|
|
839
|
-
|
|
840
|
-
Copyright (c) Isaac Z. Schlueter and Contributors
|
|
841
|
-
|
|
842
|
-
Permission to use, copy, modify, and/or distribute this software for any
|
|
843
|
-
purpose with or without fee is hereby granted, provided that the above
|
|
844
|
-
copyright notice and this permission notice appear in all copies.
|
|
845
|
-
|
|
846
|
-
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
847
|
-
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
848
|
-
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
849
|
-
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
850
|
-
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
851
|
-
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
|
852
|
-
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
853
|
-
|
|
854
|
-
|
|
855
855
|
-------------------- DEPENDENCY DIVIDER --------------------
|
|
856
856
|
|
|
857
857
|
Name: proxy-agent
|
|
@@ -1237,7 +1237,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
1237
1237
|
|
|
1238
1238
|
Name: basic-ftp
|
|
1239
1239
|
URL: https://github.com/patrickjuchli/basic-ftp.git
|
|
1240
|
-
Version: 5.
|
|
1240
|
+
Version: 5.3.0
|
|
1241
1241
|
License: MIT
|
|
1242
1242
|
|
|
1243
1243
|
Copyright (c) 2019 Patrick Juchli
|
|
@@ -2459,7 +2459,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
|
|
|
2459
2459
|
|
|
2460
2460
|
Name: @paulirish/trace_engine
|
|
2461
2461
|
URL: N/A
|
|
2462
|
-
Version: 0.0.
|
|
2462
|
+
Version: 0.0.64
|
|
2463
2463
|
License: BSD-3-Clause
|
|
2464
2464
|
|
|
2465
2465
|
// Copyright 2014 The Chromium Authors
|
|
@@ -2525,7 +2525,7 @@ SOFTWARE.
|
|
|
2525
2525
|
|
|
2526
2526
|
Name: axe-core
|
|
2527
2527
|
URL: https://www.deque.com/axe/
|
|
2528
|
-
Version: 4.11.
|
|
2528
|
+
Version: 4.11.2
|
|
2529
2529
|
License: MPL-2.0
|
|
2530
2530
|
|
|
2531
2531
|
Mozilla Public License, version 2.0
|
|
@@ -3961,7 +3961,7 @@ SOFTWARE.
|
|
|
3961
3961
|
|
|
3962
3962
|
Name: puppeteer-core
|
|
3963
3963
|
URL: https://github.com/puppeteer/puppeteer/tree/main/packages/puppeteer-core
|
|
3964
|
-
Version: 24.
|
|
3964
|
+
Version: 24.40.0
|
|
3965
3965
|
License: Apache-2.0
|
|
3966
3966
|
|
|
3967
3967
|
-------------------- DEPENDENCY DIVIDER --------------------
|
|
@@ -4027,7 +4027,7 @@ SOFTWARE.
|
|
|
4027
4027
|
|
|
4028
4028
|
Name: tldts-core
|
|
4029
4029
|
URL: https://github.com/remusao/tldts#readme
|
|
4030
|
-
Version: 7.0.
|
|
4030
|
+
Version: 7.0.27
|
|
4031
4031
|
License: MIT
|
|
4032
4032
|
|
|
4033
4033
|
Copyright (c) 2017 Thomas Parisot, 2018 Rémi Berson
|
|
@@ -4049,7 +4049,7 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTH
|
|
|
4049
4049
|
|
|
4050
4050
|
Name: tldts-icann
|
|
4051
4051
|
URL: https://github.com/remusao/tldts#readme
|
|
4052
|
-
Version: 7.0.
|
|
4052
|
+
Version: 7.0.27
|
|
4053
4053
|
License: MIT
|
|
4054
4054
|
|
|
4055
4055
|
Copyright (c) 2017 Thomas Parisot, 2018 Rémi Berson
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
|
-
"@modelcontextprotocol/sdk": "1.
|
|
3
|
-
"chrome-devtools-frontend": "1.0.
|
|
2
|
+
"@modelcontextprotocol/sdk": "1.29.0",
|
|
3
|
+
"chrome-devtools-frontend": "1.0.1613625",
|
|
4
4
|
"core-js": "3.49.0",
|
|
5
5
|
"debug": "4.4.3",
|
|
6
|
-
"lighthouse": "13.0
|
|
6
|
+
"lighthouse": "13.1.0",
|
|
7
|
+
"semver": "^7.7.4",
|
|
7
8
|
"yargs": "18.0.0",
|
|
8
|
-
"puppeteer-core": "24.
|
|
9
|
+
"puppeteer-core": "24.42.0"
|
|
9
10
|
}
|