Skip to content

Commit 859a1c3

Browse files
committed
feat: support filePath in performance tools
1 parent ab2340f commit 859a1c3

File tree

3 files changed

+92
-9
lines changed

3 files changed

+92
-9
lines changed

docs/tool-reference.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -234,14 +234,17 @@
234234

235235
- **autoStop** (boolean) **(required)**: Determines if the trace recording should be automatically stopped.
236236
- **reload** (boolean) **(required)**: Determines if, once tracing has started, the page should be automatically reloaded.
237+
- **filePath** (string) _(optional)_: The absolute path, or a path relative to the current working directory, to save the raw trace data
237238

238239
---
239240

240241
### `performance_stop_trace`
241242

242243
**Description:** Stops the active performance trace recording on the selected page.
243244

244-
**Parameters:** None
245+
**Parameters:**
246+
247+
- **filePath** (string) _(optional)_: The absolute path, or a path relative to the current working directory, to save the raw trace data
245248

246249
---
247250

@@ -280,12 +283,12 @@ so returned values have to JSON-serializable.
280283
**Parameters:**
281284

282285
- **function** (string) **(required)**: A JavaScript function declaration to be executed by the tool in the currently selected page.
283-
Example without arguments: `() => {
286+
Example without arguments: `() => {
284287
return document.title
285288
}` or `async () => {
286289
return await fetch("example.com")
287290
}`.
288-
Example with arguments: `(el) => {
291+
Example with arguments: `(el) => {
289292
return el.innerText;
290293
}`
291294

src/tools/performance.ts

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export const startTrace = defineTool({
2525
'Starts a performance trace recording on the selected page. This can be used to look for performance problems and insights to improve the performance of the page. It will also report Core Web Vital (CWV) scores for the page.',
2626
annotations: {
2727
category: ToolCategory.PERFORMANCE,
28-
readOnlyHint: true,
28+
readOnlyHint: false,
2929
},
3030
schema: {
3131
reload: zod
@@ -38,6 +38,12 @@ export const startTrace = defineTool({
3838
.describe(
3939
'Determines if the trace recording should be automatically stopped.',
4040
),
41+
filePath: zod
42+
.string()
43+
.optional()
44+
.describe(
45+
'The absolute path, or a path relative to the current working directory, to save the raw trace data',
46+
),
4147
},
4248
handler: async (request, response, context) => {
4349
if (context.isRunningPerformanceTrace()) {
@@ -91,7 +97,7 @@ export const startTrace = defineTool({
9197

9298
if (request.params.autoStop) {
9399
await new Promise(resolve => setTimeout(resolve, 5_000));
94-
await stopTracingAndAppendOutput(page, response, context);
100+
await stopTracingAndAppendOutput(page, response, context, request.params.filePath);
95101
} else {
96102
response.appendResponseLine(
97103
`The performance trace is being recorded. Use performance_stop_trace to stop it.`,
@@ -106,15 +112,22 @@ export const stopTrace = defineTool({
106112
'Stops the active performance trace recording on the selected page.',
107113
annotations: {
108114
category: ToolCategory.PERFORMANCE,
109-
readOnlyHint: true,
115+
readOnlyHint: false,
116+
},
117+
schema: {
118+
filePath: zod
119+
.string()
120+
.optional()
121+
.describe(
122+
'The absolute path, or a path relative to the current working directory, to save the raw trace data',
123+
),
110124
},
111-
schema: {},
112-
handler: async (_request, response, context) => {
125+
handler: async (request, response, context) => {
113126
if (!context.isRunningPerformanceTrace()) {
114127
return;
115128
}
116129
const page = context.getSelectedPage();
117-
await stopTracingAndAppendOutput(page, response, context);
130+
await stopTracingAndAppendOutput(page, response, context, request.params.filePath);
118131
},
119132
});
120133

@@ -165,9 +178,14 @@ async function stopTracingAndAppendOutput(
165178
page: Page,
166179
response: Response,
167180
context: Context,
181+
filePath?: string
168182
): Promise<void> {
169183
try {
170184
const traceEventsBuffer = await page.tracing.stop();
185+
if (filePath && traceEventsBuffer) {
186+
const file = await context.saveFile(traceEventsBuffer, filePath);
187+
response.appendResponseLine(`The raw trace data was saved to ${file.filename}.`);
188+
}
171189
const result = await parseRawTraceBuffer(traceEventsBuffer);
172190
response.appendResponseLine('The performance trace has been stopped.');
173191
if (traceResultIsSuccess(result)) {

tests/tools/performance.test.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,42 @@ describe('performance', () => {
138138
);
139139
});
140140
});
141+
142+
it('supports filePath', async () => {
143+
const rawData = loadTraceAsBuffer('basic-trace.json.gz');
144+
await withMcpContext(async (response, context) => {
145+
const filePath = 'test-trace.json';
146+
const selectedPage = context.getSelectedPage();
147+
sinon.stub(selectedPage, 'url').callsFake(() => 'https://www.test.com');
148+
sinon.stub(selectedPage, 'goto').callsFake(() => Promise.resolve(null));
149+
sinon.stub(selectedPage.tracing, 'start');
150+
sinon.stub(selectedPage.tracing, 'stop').resolves(rawData);
151+
const saveFileStub = sinon
152+
.stub(context, 'saveFile')
153+
.resolves({filename: filePath});
154+
155+
const clock = sinon.useFakeTimers({shouldClearNativeTimers: true});
156+
try {
157+
const handlerPromise = startTrace.handler(
158+
{params: {reload: true, autoStop: true, filePath}},
159+
response,
160+
context,
161+
);
162+
await clock.tickAsync(6_000);
163+
await handlerPromise;
164+
165+
assert.ok(
166+
response.responseLines.includes(
167+
`The raw trace data was saved to ${filePath}.`,
168+
),
169+
);
170+
sinon.assert.calledOnce(saveFileStub);
171+
sinon.assert.calledWith(saveFileStub, rawData, filePath);
172+
} finally {
173+
clock.restore();
174+
}
175+
});
176+
});
141177
});
142178

143179
describe('performance_analyze_insight', () => {
@@ -275,5 +311,31 @@ describe('performance', () => {
275311
t.assert.snapshot?.(response.responseLines.join('\n'));
276312
});
277313
});
314+
315+
it('supports filePath', async () => {
316+
const rawData = loadTraceAsBuffer('basic-trace.json.gz');
317+
await withMcpContext(async (response, context) => {
318+
const filePath = 'test-trace.json';
319+
context.setIsRunningPerformanceTrace(true);
320+
const selectedPage = context.getSelectedPage();
321+
const stopTracingStub = sinon
322+
.stub(selectedPage.tracing, 'stop')
323+
.resolves(rawData);
324+
const saveFileStub = sinon
325+
.stub(context, 'saveFile')
326+
.resolves({filename: filePath});
327+
328+
await stopTrace.handler({params: {filePath}}, response, context);
329+
330+
sinon.assert.calledOnce(stopTracingStub);
331+
sinon.assert.calledOnce(saveFileStub);
332+
sinon.assert.calledWith(saveFileStub, rawData, filePath);
333+
assert.ok(
334+
response.responseLines.includes(
335+
`The raw trace data was saved to ${filePath}.`,
336+
),
337+
);
338+
});
339+
});
278340
});
279341
});

0 commit comments

Comments
 (0)