mirror of
https://github.com/microsoft/playwright-mcp.git
synced 2025-07-25 07:52:27 +08:00
153 lines
5.3 KiB
JavaScript
153 lines
5.3 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Copyright (c) Microsoft Corporation.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
// @ts-check
|
|
|
|
import fs from 'node:fs'
|
|
import path from 'node:path'
|
|
import url from 'node:url'
|
|
import zodToJsonSchema from 'zod-to-json-schema'
|
|
import { execSync } from 'node:child_process';
|
|
|
|
import { allTools } from '../lib/tools.js';
|
|
|
|
const capabilities = {
|
|
'core': 'Core automation',
|
|
'core-tabs': 'Tab management',
|
|
'core-install': 'Browser installation',
|
|
'vision': 'Coordinate-based (opt-in via --caps=vision)',
|
|
'pdf': 'PDF generation (opt-in via --caps=pdf)',
|
|
};
|
|
|
|
const toolsByCapability = Object.fromEntries(Object.entries(capabilities).map(([capability, title]) => [title, allTools.filter(tool => tool.capability === capability).sort((a, b) => a.schema.name.localeCompare(b.schema.name))]));
|
|
|
|
// NOTE: Can be removed when we drop Node.js 18 support and changed to import.meta.filename.
|
|
const __filename = url.fileURLToPath(import.meta.url);
|
|
|
|
/**
|
|
* @param {import('../src/tools/tool.js').ToolSchema<any>} tool
|
|
* @returns {string[]}
|
|
*/
|
|
function formatToolForReadme(tool) {
|
|
const lines = /** @type {string[]} */ ([]);
|
|
lines.push(`<!-- NOTE: This has been generated via ${path.basename(__filename)} -->`);
|
|
lines.push(``);
|
|
lines.push(`- **${tool.name}**`);
|
|
lines.push(` - Title: ${tool.title}`);
|
|
lines.push(` - Description: ${tool.description}`);
|
|
|
|
const inputSchema = /** @type {any} */ (zodToJsonSchema(tool.inputSchema || {}));
|
|
const requiredParams = inputSchema.required || [];
|
|
if (inputSchema.properties && Object.keys(inputSchema.properties).length) {
|
|
lines.push(` - Parameters:`);
|
|
Object.entries(inputSchema.properties).forEach(([name, param]) => {
|
|
const optional = !requiredParams.includes(name);
|
|
const meta = /** @type {string[]} */ ([]);
|
|
if (param.type)
|
|
meta.push(param.type);
|
|
if (optional)
|
|
meta.push('optional');
|
|
lines.push(` - \`${name}\` ${meta.length ? `(${meta.join(', ')})` : ''}: ${param.description}`);
|
|
});
|
|
} else {
|
|
lines.push(` - Parameters: None`);
|
|
}
|
|
lines.push(` - Read-only: **${tool.type === 'readOnly'}**`);
|
|
lines.push('');
|
|
return lines;
|
|
}
|
|
|
|
/**
|
|
* @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...');
|
|
|
|
const generatedLines = /** @type {string[]} */ ([]);
|
|
for (const [capability, tools] of Object.entries(toolsByCapability)) {
|
|
console.log('Updating tools for capability:', capability);
|
|
generatedLines.push(`<details>\n<summary><b>${capability}</b></summary>`);
|
|
generatedLines.push('');
|
|
for (const tool of tools)
|
|
generatedLines.push(...formatToolForReadme(tool.schema));
|
|
generatedLines.push(`</details>`);
|
|
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 readmeContent = await fs.promises.readFile(readmePath, 'utf-8');
|
|
const withTools = await updateTools(readmeContent);
|
|
const withOptions = await updateOptions(withTools);
|
|
await fs.promises.writeFile(readmePath, withOptions, 'utf-8');
|
|
console.log('README updated successfully');
|
|
}
|
|
|
|
updateReadme().catch(err => {
|
|
console.error('Error updating README:', err);
|
|
process.exit(1);
|
|
});
|