Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,10 @@ The Chrome DevTools MCP server supports the following configuration option:
Exposes experimental screencast tools (requires ffmpeg). Install ffmpeg https://www.ffmpeg.org/download.html and ensure it is available in the MCP server PATH.
- **Type:** boolean

- **`--experimentalFfmpegPath`/ `--experimental-ffmpeg-path`**
Path to ffmpeg executable for screencast recording.
- **Type:** string

- **`--experimentalWebmcp`/ `--experimental-webmcp`**
Set to true to enable debugging WebMCP tools. Requires Chrome 149+ with the following flags: `--enable-features=WebMCPTesting,DevToolsWebMCPSupport`
- **Type:** boolean
Expand Down
5 changes: 5 additions & 0 deletions src/bin/chrome-devtools-mcp-cli-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,11 @@ export const cliOptions = {
describe:
'Exposes experimental screencast tools (requires ffmpeg). Install ffmpeg https://www.ffmpeg.org/download.html and ensure it is available in the MCP server PATH.',
},
experimentalFfmpegPath: {
type: 'string',
describe: 'Path to ffmpeg executable for screencast recording.',
implies: 'experimentalScreencast',
},
experimentalWebmcp: {
type: 'boolean',
describe:
Expand Down
4 changes: 4 additions & 0 deletions src/telemetry/flag_usage_metrics.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@
"name": "experimental_devtools_present",
"flagType": "boolean"
},
{
"name": "experimental_ffmpeg_path_present",
"flagType": "boolean"
},
{
"name": "experimental_include_all_pages",
"flagType": "boolean"
Expand Down
5 changes: 3 additions & 2 deletions src/tools/screencast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ async function generateTempFilePath(): Promise<string> {
return path.join(dir, `screencast.mp4`);
}

export const startScreencast = definePageTool({
export const startScreencast = definePageTool(args => ({
name: 'screencast_start',
description:
'Starts recording a screencast (video) of the selected page in mp4 format.',
Expand Down Expand Up @@ -56,6 +56,7 @@ export const startScreencast = definePageTool({
recorder = await page.pptrPage.screencast({
path: resolvedPath as `${string}.mp4`,
format: 'mp4' as const,
ffmpegPath: args?.experimentalFfmpegPath,
});
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
Expand All @@ -74,7 +75,7 @@ export const startScreencast = definePageTool({
`Screencast recording started. The recording will be saved to ${resolvedPath}. Use ${stopScreencast.name} to stop recording.`,
);
},
});
}));

export const stopScreencast = definePageTool({
name: 'screencast_stop',
Expand Down
32 changes: 28 additions & 4 deletions tests/tools/screencast.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {describe, it, afterEach} from 'node:test';

import sinon from 'sinon';

import type {ParsedArguments} from '../../src/bin/chrome-devtools-mcp-cli-options.js';
import {startScreencast, stopScreencast} from '../../src/tools/screencast.js';
import {withMcpContext} from '../utils.js';

Expand All @@ -32,7 +33,7 @@ describe('screencast', () => {
.stub(selectedPage, 'screencast')
.resolves(mockRecorder as never);

await startScreencast.handler(
await startScreencast().handler(
{
params: {path: '/tmp/test-recording.mp4'},
page: context.getSelectedMcpPage(),
Expand Down Expand Up @@ -63,7 +64,7 @@ describe('screencast', () => {
.stub(selectedPage, 'screencast')
.resolves(mockRecorder as never);

await startScreencast.handler(
await startScreencast().handler(
{params: {}, page: context.getSelectedMcpPage()},
response,
context,
Expand All @@ -88,7 +89,7 @@ describe('screencast', () => {
const selectedPage = context.getSelectedPptrPage();
const screencastStub = sinon.stub(selectedPage, 'screencast');

await startScreencast.handler(
await startScreencast().handler(
{params: {}, page: context.getSelectedMcpPage()},
response,
context,
Expand All @@ -110,7 +111,7 @@ describe('screencast', () => {
sinon.stub(selectedPage, 'screencast').rejects(error);

await assert.rejects(
startScreencast.handler(
startScreencast().handler(
{
params: {path: '/tmp/test.mp4'},
page: context.getSelectedMcpPage(),
Expand All @@ -124,6 +125,29 @@ describe('screencast', () => {
assert.strictEqual(context.getScreenRecorder(), null);
});
});

it('passes ffmpegPath from args to puppeteer', async () => {
await withMcpContext(async (response, context) => {
const mockRecorder = createMockRecorder();
const selectedPage = context.getSelectedPptrPage();
const screencastStub = sinon
.stub(selectedPage, 'screencast')
.resolves(mockRecorder as never);

const experimentalFfmpegPath = '/custom/path/to/ffmpeg';
await startScreencast({
experimentalFfmpegPath,
} as ParsedArguments).handler(
{params: {}, page: context.getSelectedMcpPage()},
response,
context,
);

sinon.assert.calledOnce(screencastStub);
const callArgs = screencastStub.firstCall.args[0];
assert.strictEqual(callArgs?.ffmpegPath, experimentalFfmpegPath);
});
});
});

describe('screencast_stop', () => {
Expand Down
Loading