import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js"; // @ts-ignore import { chromium } from "playwright"; const server = new Server( { name: "playwright-mcp-server", version: "1.0.0", }, { capabilities: { tools: {}, }, } ); let browser: any = null; let currentPage: any = null; async function getBrowser(): Promise { if (!browser) { browser = await chromium.launch({ headless: true }); } return browser; } async function getPage(): Promise { const b = await getBrowser(); if (!currentPage) { currentPage = await b.newPage(); } return currentPage; } const tools = [ { name: "navigate", description: "Navigate to a URL and get the page title", inputSchema: { type: "object", properties: { url: { type: "string", description: "The URL to navigate to" }, }, required: ["url"], }, handler: async ({ url }: { url: string }) => { const page = await getPage(); await page.goto(url); const title = await page.title(); return { content: [{ type: "text", text: JSON.stringify({ url, title, success: true }) }], }; }, }, { name: "getPageContent", description: "Get text content from the current page", inputSchema: { type: "object", properties: {} }, handler: async () => { const page = await getPage(); const content = await page.textContent("body"); return { content: [{ type: "text", text: content || "" }] }; }, }, { name: "click", description: "Click an element by CSS selector", inputSchema: { type: "object", properties: { selector: { type: "string", description: "CSS selector" } }, required: ["selector"], }, handler: async ({ selector }: { selector: string }) => { const page = await getPage(); await page.click(selector); return { content: [{ type: "text", text: JSON.stringify({ success: true, action: "clicked", selector }) }], }; }, }, { name: "fillForm", description: "Fill a form input field", inputSchema: { type: "object", properties: { selector: { type: "string" }, value: { type: "string" }, }, required: ["selector", "value"], }, handler: async ({ selector, value }: { selector: string; value: string }) => { const page = await getPage(); await page.fill(selector, value); return { content: [{ type: "text", text: JSON.stringify({ success: true, action: "filled", selector, value }) }], }; }, }, { name: "screenshot", description: "Take a screenshot of the current page", inputSchema: { type: "object", properties: { path: { type: "string" } }, required: ["path"], }, handler: async ({ path }: { path: string }) => { const page = await getPage(); await page.screenshot({ path }); return { content: [{ type: "text", text: JSON.stringify({ success: true, path }) }] }; }, }, { name: "closeBrowser", description: "Close the browser", inputSchema: { type: "object", properties: {} }, handler: async () => { if (browser) { await browser.close(); browser = null; currentPage = null; } return { content: [{ type: "text", text: JSON.stringify({ success: true }) }] }; }, }, ]; server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: tools.map((t) => ({ name: t.name, description: t.description, inputSchema: t.inputSchema })), })); server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; const tool = tools.find((t) => t.name === name); if (!tool) return { content: [{ type: "text", text: `Tool not found: ${name}` }], isError: true }; try { // @ts-ignore return await tool.handler(args || {}); } catch (error) { return { content: [{ type: "text", text: JSON.stringify({ success: false, error: String(error) }) }], isError: true, }; } }); async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error("Playwright MCP Server started"); } main().catch(console.error);