Skip to content

Commit 1c0ed3e

Browse files
committed
feat(mcp): add JSON Schema for MCP/CLI configuration files
Auto-generate a JSON Schema (draft-07) from config.d.ts using the TypeScript compiler API. The schema covers all Config fields including recursively expanded LaunchOptions and BrowserContextOptions. - Add utils/generate_mcp_config_schema.js generator script - Output mcp-config.schema.json alongside config.d.ts - Register in build.js onChanges and flint for CI staleness checks - Add config-schema.spec.ts with structural assertion tests - Fix config.d.ts / configIni.ts drift: add saveTrace, remove dead saveVideo, add timeouts.expect to longhandTypes - Document $schema usage in getting-started-mcp.md and getting-started-cli.md
1 parent 2d6be2d commit 1c0ed3e

9 files changed

Lines changed: 1334 additions & 4 deletions

File tree

docs/src/getting-started-cli.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,14 @@ playwright-cli --config path/to/config.json open example.com
275275
276276
The CLI also loads `.playwright/cli.config.json` automatically if present. The config file supports browser options, context options, network rules, timeouts, and more. Run `playwright-cli --help` for the full list of options.
277277
278+
A JSON Schema is available for IDE autocompletion. The `.playwright/cli.config.json` file is automatically associated via [SchemaStore](https://www.schemastore.org/). For other file names, add `$schema` manually:
279+
280+
```json
281+
{
282+
"$schema": "https://raw.githubusercontent.com/microsoft/playwright/main/packages/playwright-core/src/tools/mcp/mcp-config.schema.json"
283+
}
284+
```
285+
278286
### Browser extension
279287
280288
Connect to your existing browser tabs instead of launching a new browser:

docs/src/getting-started-mcp.md

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,13 +179,30 @@ Playwright MCP supports three profile modes:
179179

180180
### Configuration file
181181

182-
For advanced configuration, use a JSON config file:
182+
For advanced configuration, use a JSON or INI config file:
183183

184184
```bash
185185
npx @playwright/mcp@latest --config path/to/config.json
186186
```
187187

188-
The config file supports browser options, context options, network rules, timeouts, and more. See the [Playwright MCP repository](https://github.com/microsoft/playwright-mcp/blob/main/packages/playwright-mcp/config.d.ts) for the full schema.
188+
The config file supports browser options, context options, network rules, timeouts, and more. See the [Playwright MCP repository](https://github.com/microsoft/playwright-mcp/blob/main/packages/playwright-mcp/config.d.ts) for the full type definition.
189+
190+
#### JSON Schema for IDE autocompletion
191+
192+
A JSON Schema is available for configuration files. Add `$schema` to your config file for IDE autocompletion and validation:
193+
194+
```json
195+
{
196+
"$schema": "https://raw.githubusercontent.com/microsoft/playwright/main/packages/playwright-core/src/tools/mcp/mcp-config.schema.json",
197+
"browser": {
198+
"browserName": "chromium"
199+
}
200+
}
201+
```
202+
203+
If you are using `@playwright/cli`, the project-level config at `.playwright/cli.config.json` is automatically associated with the schema via [SchemaStore](https://www.schemastore.org/).
204+
205+
**Note:** The JSON Schema only validates JSON config files. INI format config files use the same options but are not validated by this schema.
189206

190207
### Standalone server
191208

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
"lint": "npm run eslint && npm run tsc && npm run doc && npm run check-deps && node utils/generate_channels.js && node utils/generate_types/ && npm run lint-tests && npm run test-types && npm run lint-packages",
3636
"lint-packages": "node utils/workspace.js --ensure-consistent",
3737
"lint-tests": "node utils/lint_tests.js",
38-
"flint": "concurrently \"npm run eslint\" \"npm run tsc\" \"npm run doc\" \"npm run check-deps\" \"node utils/generate_channels.js\" \"npm run lint-tests\" \"npm run test-types\" \"npm run lint-packages\" \"node utils/doclint/linting-code-snippets/cli.js --js-only\"",
38+
"flint": "concurrently \"npm run eslint\" \"npm run tsc\" \"npm run doc\" \"npm run check-deps\" \"node utils/generate_channels.js\" \"node utils/generate_mcp_config_schema.js\" \"npm run lint-tests\" \"npm run test-types\" \"npm run lint-packages\" \"node utils/doclint/linting-code-snippets/cli.js --js-only\"",
3939
"clean": "node utils/build/clean.js",
4040
"build": "node utils/build/build.js",
4141
"watch": "node utils/build/build.js --watch --lint",

packages/playwright-core/src/tools/mcp/config.d.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
* limitations under the License.
1515
*/
1616

17+
// JSON Schema is auto-generated from this file by utils/generate_mcp_config_schema.js.
18+
1719
import type * as playwright from '../../..';
1820

1921
export type ToolCapability =
@@ -137,6 +139,11 @@ export type Config = {
137139
*/
138140
saveSession?: boolean;
139141

142+
/**
143+
* Whether to save the Playwright trace into the output directory.
144+
*/
145+
saveTrace?: boolean;
146+
140147
/**
141148
* Reuse the same browser context between all connected HTTP clients.
142149
*/

packages/playwright-core/src/tools/mcp/configIni.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,6 @@ const longhandTypes: Record<string, LonghandType> = {
159159
'capabilities': 'string[]',
160160
'saveSession': 'boolean',
161161
'saveTrace': 'boolean',
162-
'saveVideo': 'size',
163162
'sharedBrowserContext': 'boolean',
164163
'outputDir': 'string',
165164
'imageResponses': 'string',
@@ -182,6 +181,7 @@ const longhandTypes: Record<string, LonghandType> = {
182181
// timeouts
183182
'timeouts.action': 'number',
184183
'timeouts.navigation': 'number',
184+
'timeouts.expect': 'number',
185185

186186
// snapshot
187187
'snapshot.mode': 'string',

packages/playwright-core/src/tools/mcp/mcp-config.schema.json

Lines changed: 858 additions & 0 deletions
Large diffs are not rendered by default.

tests/mcp/config-schema.spec.ts

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/**
2+
* Copyright (c) Microsoft Corporation.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import fs from 'node:fs';
18+
import path from 'node:path';
19+
20+
import { test, expect } from './fixtures';
21+
22+
const schemaPath = path.join(__dirname, '..', '..', 'packages', 'playwright-core', 'src', 'tools', 'mcp', 'mcp-config.schema.json');
23+
24+
test('schema is valid JSON and has expected structure', async () => {
25+
const content = fs.readFileSync(schemaPath, 'utf8');
26+
const schema = JSON.parse(content);
27+
28+
expect(schema.$schema).toBe('http://json-schema.org/draft-07/schema#');
29+
expect(schema.type).toBe('object');
30+
expect(schema.properties).toBeTruthy();
31+
expect(schema.title).toContain('Playwright');
32+
});
33+
34+
test('schema has all top-level config keys', async () => {
35+
const schema = JSON.parse(fs.readFileSync(schemaPath, 'utf8'));
36+
const props = Object.keys(schema.properties);
37+
38+
for (const key of [
39+
'browser', 'extension', 'server', 'capabilities', 'saveSession',
40+
'saveTrace', 'sharedBrowserContext', 'secrets', 'outputDir',
41+
'console', 'network', 'testIdAttribute', 'timeouts',
42+
'imageResponses', 'snapshot', 'allowUnrestrictedFileAccess', 'codegen',
43+
]) {
44+
expect(props, `missing top-level key: ${key}`).toContain(key);
45+
}
46+
});
47+
48+
test('schema browser.browserName is an enum', async () => {
49+
const schema = JSON.parse(fs.readFileSync(schemaPath, 'utf8'));
50+
const browserName = schema.properties.browser.properties.browserName;
51+
52+
expect(browserName.enum).toEqual(['chromium', 'firefox', 'webkit']);
53+
});
54+
55+
test('schema browser.launchOptions has expected properties', async () => {
56+
const schema = JSON.parse(fs.readFileSync(schemaPath, 'utf8'));
57+
const launchOpts = schema.properties.browser.properties.launchOptions.properties;
58+
59+
for (const key of ['headless', 'channel', 'executablePath', 'args', 'proxy', 'slowMo', 'timeout', 'chromiumSandbox']) {
60+
expect(Object.keys(launchOpts), `launchOptions missing: ${key}`).toContain(key);
61+
}
62+
});
63+
64+
test('schema browser.contextOptions has expected properties', async () => {
65+
const schema = JSON.parse(fs.readFileSync(schemaPath, 'utf8'));
66+
const ctxOpts = schema.properties.browser.properties.contextOptions.properties;
67+
68+
for (const key of ['viewport', 'locale', 'colorScheme', 'baseURL', 'storageState', 'permissions', 'geolocation']) {
69+
expect(Object.keys(ctxOpts), `contextOptions missing: ${key}`).toContain(key);
70+
}
71+
});
72+
73+
test('schema excludes non-JSON types (Logger, Buffer)', async () => {
74+
const schema = JSON.parse(fs.readFileSync(schemaPath, 'utf8'));
75+
const launchOpts = schema.properties.browser.properties.launchOptions.properties;
76+
const ctxOpts = schema.properties.browser.properties.contextOptions.properties;
77+
78+
expect(launchOpts.logger).toBeUndefined();
79+
expect(ctxOpts.logger).toBeUndefined();
80+
81+
const clientCerts = ctxOpts.clientCertificates?.items?.properties;
82+
if (clientCerts) {
83+
expect(clientCerts.cert).toBeUndefined();
84+
expect(clientCerts.key).toBeUndefined();
85+
expect(clientCerts.pfx).toBeUndefined();
86+
expect(clientCerts.certPath).toBeTruthy();
87+
expect(clientCerts.keyPath).toBeTruthy();
88+
expect(clientCerts.pfxPath).toBeTruthy();
89+
}
90+
});
91+
92+
test('schema colorScheme allows null', async () => {
93+
const schema = JSON.parse(fs.readFileSync(schemaPath, 'utf8'));
94+
const colorScheme = schema.properties.browser.properties.contextOptions.properties.colorScheme;
95+
96+
expect(colorScheme.oneOf).toBeTruthy();
97+
expect(colorScheme.oneOf.some((s: any) => s.type === 'null')).toBe(true);
98+
expect(colorScheme.oneOf.some((s: any) => s.enum?.includes('light'))).toBe(true);
99+
});
100+
101+
test('schema ignoreDefaultArgs allows boolean or string array', async () => {
102+
const schema = JSON.parse(fs.readFileSync(schemaPath, 'utf8'));
103+
const prop = schema.properties.browser.properties.launchOptions.properties.ignoreDefaultArgs;
104+
105+
expect(prop.oneOf).toBeTruthy();
106+
expect(prop.oneOf.some((s: any) => s.type === 'boolean')).toBe(true);
107+
expect(prop.oneOf.some((s: any) => s.type === 'array')).toBe(true);
108+
});
109+
110+
test('schema viewport allows null or object with width/height', async () => {
111+
const schema = JSON.parse(fs.readFileSync(schemaPath, 'utf8'));
112+
const viewport = schema.properties.browser.properties.contextOptions.properties.viewport;
113+
114+
expect(viewport.oneOf).toBeTruthy();
115+
expect(viewport.oneOf.some((s: any) => s.type === 'null')).toBe(true);
116+
const objSchema = viewport.oneOf.find((s: any) => s.type === 'object');
117+
expect(objSchema).toBeTruthy();
118+
expect(objSchema.properties.width.type).toBe('number');
119+
expect(objSchema.properties.height.type).toBe('number');
120+
});

utils/build/build.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,14 @@ onChanges.push({
632632
script: 'utils/generate_channels.js',
633633
});
634634

635+
// Generate MCP config JSON schema.
636+
onChanges.push({
637+
inputs: [
638+
'packages/playwright-core/src/tools/mcp/config.d.ts',
639+
],
640+
script: 'utils/generate_mcp_config_schema.js',
641+
});
642+
635643
// Generate types.
636644
onChanges.push({
637645
inputs: [

0 commit comments

Comments
 (0)