mirror of
https://github.com/microsoft/playwright-mcp.git
synced 2025-07-26 08:32:26 +08:00
chore: generate readme options (#411)
This commit is contained in:
parent
c506027aec
commit
ab20175826
81
README.md
81
README.md
@ -110,34 +110,54 @@ Follow the MCP install [guide](https://modelcontextprotocol.io/quickstart/user),
|
|||||||
|
|
||||||
Playwright MCP server supports following arguments. They can be provided in the JSON configuration above, as a part of the `"args"` list:
|
Playwright MCP server supports following arguments. They can be provided in the JSON configuration above, as a part of the `"args"` list:
|
||||||
|
|
||||||
- `--allowed-origins <origins>`: Semicolon-separated list of origins to allow the browser to request. Default is to allow all. Origins matching both `--allowed-origins` and `--blocked-origins` will be blocked
|
<!--- Options generated by update-readme.js -->
|
||||||
- `--blocked-origins <origins>`: Semicolon-separated list of origins to block the browser to request. Origins matching both `--allowed-origins` and `--blocked-origins` will be blocked
|
|
||||||
- `--block-service-workers`: Block service workers
|
```
|
||||||
- `--browser <browser>`: Browser or chrome channel to use. Possible values:
|
> npx @playwright/mcp@latest --help
|
||||||
- `chrome`, `firefox`, `webkit`, `msedge`
|
--allowed-origins <origins> semicolon-separated list of origins to allow the
|
||||||
- Chrome channels: `chrome-beta`, `chrome-canary`, `chrome-dev`
|
browser to request. Default is to allow all.
|
||||||
- Edge channels: `msedge-beta`, `msedge-canary`, `msedge-dev`
|
--blocked-origins <origins> semicolon-separated list of origins to block the
|
||||||
- Default: `chrome`
|
browser from requesting. Blocklist is evaluated
|
||||||
- `--caps <caps>`: Comma-separated list of capabilities to enable, possible values: tabs, pdf, history, wait, files, install. Default is all.
|
before allowlist. If used without the allowlist,
|
||||||
- `--cdp-endpoint <endpoint>`: CDP endpoint to connect to
|
requests not matching the blocklist are still
|
||||||
- `--config <path>`: Path to the configuration file
|
allowed.
|
||||||
- `--device`: Emulate mobile device
|
--block-service-workers block service workers
|
||||||
- `--executable-path <path>`: Path to the browser executable
|
--browser <browser> browser or chrome channel to use, possible
|
||||||
- `--headless`: Run browser in headless mode (headed by default)
|
values: chrome, firefox, webkit, msedge.
|
||||||
- `--host <host>`: Host to bind server to. Default is localhost. Use 0.0.0.0 to bind to all interfaces
|
--caps <caps> comma-separated list of capabilities to enable,
|
||||||
- `--ignore-https-errors`: Ignore https errors
|
possible values: tabs, pdf, history, wait, files,
|
||||||
- `--isolated`: Keep the browser profile in memory, do not save it to disk
|
install. Default is all.
|
||||||
- `--no-image-responses`: Do not send image responses to the client
|
--cdp-endpoint <endpoint> CDP endpoint to connect to.
|
||||||
- `--no-sandbox`: Disable the sandbox for all process types that are normally sandboxed
|
--config <path> path to the configuration file.
|
||||||
- `--output-dir`: Directory for output files
|
--device <device> device to emulate, for example: "iPhone 15"
|
||||||
- `--port <port>`: Port to listen on for SSE transport
|
--executable-path <path> path to the browser executable.
|
||||||
- `--proxy-bypass <bypass>`: Comma-separated domains to bypass proxy, for example ".com,chromium.org,.domain.com"'
|
--headless run browser in headless mode, headed by default
|
||||||
- `--proxy-server <proxy>`: Proxy server, for example "http://myproxy:3128" or "socks5://myproxy:8080"'
|
--host <host> host to bind server to. Default is localhost. Use
|
||||||
- `--storage-state <path>`: Path to the storage state file for isolated sessions
|
0.0.0.0 to bind to all interfaces.
|
||||||
- `--user-agent <ua string>`: Specify user agent string
|
--ignore-https-errors ignore https errors
|
||||||
- `--user-data-dir <path>`: Path to the user data directory. If not specified, a temporary directory will be created
|
--isolated keep the browser profile in memory, do not save
|
||||||
- `--viewport-size <size>`: Specify browser viewport size in pixels, for example "1280, 720"
|
it to disk.
|
||||||
- `--vision`: Run server that uses screenshots (Aria snapshots are used by default)
|
--no-image-responses do not send image responses to the client.
|
||||||
|
--no-sandbox disable the sandbox for all process types that
|
||||||
|
are normally sandboxed.
|
||||||
|
--output-dir <path> path to the directory for output files.
|
||||||
|
--port <port> port to listen on for SSE transport.
|
||||||
|
--proxy-bypass <bypass> comma-separated domains to bypass proxy, for
|
||||||
|
example ".com,chromium.org,.domain.com"
|
||||||
|
--proxy-server <proxy> specify proxy server, for example
|
||||||
|
"http://myproxy:3128" or "socks5://myproxy:8080"
|
||||||
|
--storage-state <path> path to the storage state file for isolated
|
||||||
|
sessions.
|
||||||
|
--user-agent <ua string> specify user agent string
|
||||||
|
--user-data-dir <path> path to the user data directory. If not
|
||||||
|
specified, a temporary directory will be created.
|
||||||
|
--viewport-size <size> specify browser viewport size in pixels, for
|
||||||
|
example "1280, 720"
|
||||||
|
--vision Run server that uses screenshots (Aria snapshots
|
||||||
|
are used by default)
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--- End of options generated section -->
|
||||||
|
|
||||||
### User profile
|
### User profile
|
||||||
|
|
||||||
@ -362,7 +382,7 @@ To use Vision Mode, add the `--vision` flag when starting the server:
|
|||||||
Vision Mode works best with the computer use models that are able to interact with elements using
|
Vision Mode works best with the computer use models that are able to interact with elements using
|
||||||
X Y coordinate space, based on the provided screenshot.
|
X Y coordinate space, based on the provided screenshot.
|
||||||
|
|
||||||
<!--- Generated by update-readme.js -->
|
<!--- Tools generated by update-readme.js -->
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><b>Interactions</b></summary>
|
<summary><b>Interactions</b></summary>
|
||||||
@ -728,4 +748,5 @@ X Y coordinate space, based on the provided screenshot.
|
|||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<!--- End of generated section -->
|
|
||||||
|
<!--- End of tools generated section -->
|
||||||
|
@ -36,8 +36,8 @@ import screenshotTools from '../lib/tools/screenshot.js';
|
|||||||
import testTools from '../lib/tools/testing.js';
|
import testTools from '../lib/tools/testing.js';
|
||||||
import visionTools from '../lib/tools/vision.js';
|
import visionTools from '../lib/tools/vision.js';
|
||||||
import waitTools from '../lib/tools/wait.js';
|
import waitTools from '../lib/tools/wait.js';
|
||||||
|
import { execSync } from 'node:child_process';
|
||||||
|
|
||||||
// Category definitions for tools
|
|
||||||
const categories = {
|
const categories = {
|
||||||
'Interactions': [
|
'Interactions': [
|
||||||
...snapshotTools,
|
...snapshotTools,
|
||||||
@ -77,24 +77,22 @@ const categories = {
|
|||||||
// NOTE: Can be removed when we drop Node.js 18 support and changed to import.meta.filename.
|
// NOTE: Can be removed when we drop Node.js 18 support and changed to import.meta.filename.
|
||||||
const __filename = url.fileURLToPath(import.meta.url);
|
const __filename = url.fileURLToPath(import.meta.url);
|
||||||
|
|
||||||
const kStartMarker = `<!--- Generated by ${path.basename(__filename)} -->`;
|
|
||||||
const kEndMarker = `<!--- End of generated section -->`;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {import('../src/tools/tool.js').ToolSchema<any>} tool
|
* @param {import('../src/tools/tool.js').ToolSchema<any>} tool
|
||||||
* @returns {string}
|
* @returns {string[]}
|
||||||
*/
|
*/
|
||||||
function formatToolForReadme(tool) {
|
function formatToolForReadme(tool) {
|
||||||
const lines = /** @type {string[]} */ ([]);
|
const lines = /** @type {string[]} */ ([]);
|
||||||
lines.push(`<!-- NOTE: This has been generated via ${path.basename(__filename)} -->\n\n`);
|
lines.push(`<!-- NOTE: This has been generated via ${path.basename(__filename)} -->`);
|
||||||
lines.push(`- **${tool.name}**\n`);
|
lines.push(``);
|
||||||
lines.push(` - Title: ${tool.title}\n`);
|
lines.push(`- **${tool.name}**`);
|
||||||
lines.push(` - Description: ${tool.description}\n`);
|
lines.push(` - Title: ${tool.title}`);
|
||||||
|
lines.push(` - Description: ${tool.description}`);
|
||||||
|
|
||||||
const inputSchema = /** @type {any} */ (zodToJsonSchema(tool.inputSchema || {}));
|
const inputSchema = /** @type {any} */ (zodToJsonSchema(tool.inputSchema || {}));
|
||||||
const requiredParams = inputSchema.required || [];
|
const requiredParams = inputSchema.required || [];
|
||||||
if (inputSchema.properties && Object.keys(inputSchema.properties).length) {
|
if (inputSchema.properties && Object.keys(inputSchema.properties).length) {
|
||||||
lines.push(` - Parameters:\n`);
|
lines.push(` - Parameters:`);
|
||||||
Object.entries(inputSchema.properties).forEach(([name, param]) => {
|
Object.entries(inputSchema.properties).forEach(([name, param]) => {
|
||||||
const optional = !requiredParams.includes(name);
|
const optional = !requiredParams.includes(name);
|
||||||
const meta = /** @type {string[]} */ ([]);
|
const meta = /** @type {string[]} */ ([]);
|
||||||
@ -102,55 +100,94 @@ function formatToolForReadme(tool) {
|
|||||||
meta.push(param.type);
|
meta.push(param.type);
|
||||||
if (optional)
|
if (optional)
|
||||||
meta.push('optional');
|
meta.push('optional');
|
||||||
lines.push(` - \`${name}\` ${meta.length ? `(${meta.join(', ')})` : ''}: ${param.description}\n`);
|
lines.push(` - \`${name}\` ${meta.length ? `(${meta.join(', ')})` : ''}: ${param.description}`);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
lines.push(` - Parameters: None\n`);
|
lines.push(` - Parameters: None`);
|
||||||
}
|
}
|
||||||
lines.push(` - Read-only: **${tool.type === 'readOnly'}**\n`);
|
lines.push(` - Read-only: **${tool.type === 'readOnly'}**`);
|
||||||
lines.push('\n');
|
lines.push('');
|
||||||
return lines.join('');
|
return lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateReadme() {
|
/**
|
||||||
|
* @param {string} content
|
||||||
|
* @param {string} startMarker
|
||||||
|
* @param {string} endMarker
|
||||||
|
* @param {string[]} generatedLines
|
||||||
|
* @returns {Promise<string>}
|
||||||
|
*/
|
||||||
|
async function updateSection(content, startMarker, endMarker, generatedLines) {
|
||||||
|
const startMarkerIndex = content.indexOf(startMarker);
|
||||||
|
const endMarkerIndex = content.indexOf(endMarker);
|
||||||
|
if (startMarkerIndex === -1 || endMarkerIndex === -1)
|
||||||
|
throw new Error('Markers for generated section not found in README');
|
||||||
|
|
||||||
|
return [
|
||||||
|
content.slice(0, startMarkerIndex + startMarker.length),
|
||||||
|
'',
|
||||||
|
generatedLines.join('\n'),
|
||||||
|
'',
|
||||||
|
content.slice(endMarkerIndex),
|
||||||
|
].join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} content
|
||||||
|
* @returns {Promise<string>}
|
||||||
|
*/
|
||||||
|
async function updateTools(content) {
|
||||||
console.log('Loading tool information from compiled modules...');
|
console.log('Loading tool information from compiled modules...');
|
||||||
|
|
||||||
// Count the tools processed
|
|
||||||
const totalTools = Object.values(categories).flat().length;
|
const totalTools = Object.values(categories).flat().length;
|
||||||
console.log(`Found ${totalTools} tools`);
|
console.log(`Found ${totalTools} tools`);
|
||||||
|
|
||||||
const generatedLines = /** @type {string[]} */ ([]);
|
const generatedLines = /** @type {string[]} */ ([]);
|
||||||
|
|
||||||
for (const [category, categoryTools] of Object.entries(categories)) {
|
for (const [category, categoryTools] of Object.entries(categories)) {
|
||||||
generatedLines.push(`<details>\n<summary><b>${category}</b></summary>\n\n`);
|
generatedLines.push(`<details>\n<summary><b>${category}</b></summary>`);
|
||||||
|
generatedLines.push('');
|
||||||
for (const tool of categoryTools)
|
for (const tool of categoryTools)
|
||||||
generatedLines.push(formatToolForReadme(tool.schema));
|
generatedLines.push(...formatToolForReadme(tool.schema));
|
||||||
|
generatedLines.push(`</details>`);
|
||||||
generatedLines.push(`</details>\n\n`);
|
generatedLines.push('');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const startMarker = `<!--- Tools generated by ${path.basename(__filename)} -->`;
|
||||||
|
const endMarker = `<!--- End of tools generated section -->`;
|
||||||
|
return updateSection(content, startMarker, endMarker, generatedLines);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} content
|
||||||
|
* @returns {Promise<string>}
|
||||||
|
*/
|
||||||
|
async function updateOptions(content) {
|
||||||
|
console.log('Listing options...');
|
||||||
|
const output = execSync('node cli.js --help');
|
||||||
|
const lines = output.toString().split('\n');
|
||||||
|
const firstLine = lines.findIndex(line => line.includes('--version'));
|
||||||
|
lines.splice(0, firstLine + 1);
|
||||||
|
const lastLine = lines.findIndex(line => line.includes('--help'));
|
||||||
|
lines.splice(lastLine);
|
||||||
|
const startMarker = `<!--- Options generated by ${path.basename(__filename)} -->`;
|
||||||
|
const endMarker = `<!--- End of options generated section -->`;
|
||||||
|
return updateSection(content, startMarker, endMarker, [
|
||||||
|
'```',
|
||||||
|
'> npx @playwright/mcp@latest --help',
|
||||||
|
...lines,
|
||||||
|
'```',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateReadme() {
|
||||||
const readmePath = path.join(path.dirname(__filename), '..', 'README.md');
|
const readmePath = path.join(path.dirname(__filename), '..', 'README.md');
|
||||||
const readmeContent = await fs.promises.readFile(readmePath, 'utf-8');
|
const readmeContent = await fs.promises.readFile(readmePath, 'utf-8');
|
||||||
const startMarker = readmeContent.indexOf(kStartMarker);
|
const withTools = await updateTools(readmeContent);
|
||||||
const endMarker = readmeContent.indexOf(kEndMarker);
|
const withOptions = await updateOptions(withTools);
|
||||||
if (startMarker === -1 || endMarker === -1)
|
await fs.promises.writeFile(readmePath, withOptions, 'utf-8');
|
||||||
throw new Error('Markers for generated section not found in README');
|
|
||||||
|
|
||||||
const newReadmeContent = [
|
|
||||||
readmeContent.slice(0, startMarker),
|
|
||||||
kStartMarker + '\n\n',
|
|
||||||
generatedLines.join(''),
|
|
||||||
kEndMarker,
|
|
||||||
readmeContent.slice(endMarker + kEndMarker.length),
|
|
||||||
].join('');
|
|
||||||
|
|
||||||
// Write updated README
|
|
||||||
await fs.promises.writeFile(readmePath, newReadmeContent, 'utf-8');
|
|
||||||
console.log('README updated successfully');
|
console.log('README updated successfully');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the update
|
|
||||||
updateReadme().catch(err => {
|
updateReadme().catch(err => {
|
||||||
console.error('Error updating README:', err);
|
console.error('Error updating README:', err);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user