playwright-checkpoint 0.1.0-beta.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/bin.cjs +1 -1
- package/dist/cli/bin.cjs.map +1 -1
- package/dist/index.cjs +4 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/mcp/index.cjs +1 -1
- package/dist/mcp/index.cjs.map +1 -1
- package/dist/mcp/index.js +1 -1
- package/dist/mcp/index.js.map +1 -1
- package/package.json +1 -1
package/dist/mcp/index.js
CHANGED
|
@@ -520,7 +520,7 @@ async function startMcpProxy(options = {}) {
|
|
|
520
520
|
inputSchema: t.inputSchema
|
|
521
521
|
}));
|
|
522
522
|
const allTools = [...checkpointTools, ...upstreamTools];
|
|
523
|
-
const server = new Server({ name: "playwright-checkpoint", version: "0.1.0
|
|
523
|
+
const server = new Server({ name: "playwright-checkpoint", version: "0.1.0" }, { capabilities: { tools: {} } });
|
|
524
524
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
525
525
|
return { tools: allTools };
|
|
526
526
|
});
|
package/dist/mcp/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/mcp/transport.ts","../../src/mcp/upstream.ts","../../src/mcp/browser-connect.ts","../../src/mcp/tools.ts","../../src/mcp/index.ts"],"sourcesContent":["/**\n * Minimal MCP stdio transport — inlined from @modelcontextprotocol/sdk/shared/stdio\n * to avoid SDK subpath resolution issues. This matches the SDK's protocol exactly.\n */\n\nimport type { JSONRPCMessage } from '@modelcontextprotocol/sdk/types';\n\nfunction serializeMessage(message: JSONRPCMessage): string {\n return JSON.stringify(message) + '\\n';\n}\n\nclass ReadBuffer {\n private _buffer?: Buffer;\n\n append(chunk: Buffer): void {\n this._buffer = this._buffer ? Buffer.concat([this._buffer, chunk]) : chunk;\n }\n\n readMessage(): JSONRPCMessage | null {\n if (!this._buffer) return null;\n const index = this._buffer.indexOf('\\n');\n if (index === -1) return null;\n const line = this._buffer.toString('utf8', 0, index).replace(/\\r$/, '');\n this._buffer = this._buffer.subarray(index + 1);\n try {\n return JSON.parse(line) as JSONRPCMessage;\n } catch {\n return null;\n }\n }\n\n clear(): void {\n this._buffer = undefined;\n }\n}\n\n/**\n * Minimal stdio transport for the MCP server.\n * Communicates with the MCP client via stdin/stdout using newline-delimited JSON-RPC.\n */\nexport class StdioServerTransport {\n private readonly _stdin: NodeJS.ReadableStream;\n private readonly _stdout: NodeJS.WritableStream;\n private readonly _readBuffer = new ReadBuffer();\n private _started = false;\n\n constructor(stdin?: NodeJS.ReadableStream, stdout?: NodeJS.WritableStream) {\n this._stdin = stdin ?? process.stdin;\n this._stdout = stdout ?? process.stdout;\n }\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n\n async start(): Promise<void> {\n if (this._started) {\n throw new Error('StdioServerTransport already started!');\n }\n this._started = true;\n\n this._stdin.on('data', (chunk: Buffer) => {\n this._readBuffer.append(chunk);\n this.#processReadBuffer();\n });\n\n this._stdin.on('error', (error: Error) => {\n this.onerror?.(error);\n });\n }\n\n #processReadBuffer(): void {\n while (true) {\n try {\n const message = this._readBuffer.readMessage();\n if (message === null) break;\n this.onmessage?.(message);\n } catch (error) {\n this.onerror?.(error instanceof Error ? error : new Error(String(error)));\n }\n }\n }\n\n async close(): Promise<void> {\n (this._stdin as NodeJS.ReadableStream & { pause?: () => void }).pause?.();\n this._readBuffer.clear();\n this.onclose?.();\n }\n\n async send(message: JSONRPCMessage): Promise<void> {\n const json = serializeMessage(message);\n if (this._stdout.write(json)) {\n return Promise.resolve();\n }\n return new Promise((resolve) => {\n this._stdout.once('drain', resolve);\n });\n }\n}\n","/**\n * Upstream Playwright MCP detection and spawning.\n *\n * Auto-detection priority:\n * 1. Explicit `--upstream @playwright/mcp` flag\n * 2. Scan node_modules for `@playwright/mcp`\n * 3. Scan node_modules for `playwright-mcp-advanced`\n * 4. null — standalone mode (no upstream proxying)\n */\n\nimport { spawn } from 'node:child_process';\nimport type { ChildProcess } from 'node:child_process';\nimport type { Readable, Writable } from 'node:stream';\n\n/* eslint-disable @typescript-eslint/no-require-imports */\nfunction getNodeModuleCreateRequire(): NodeJS.Require {\n const { createRequire } = require('node:module');\n return createRequire(process.cwd() + '/noop.js');\n}\n/* eslint-enable @typescript-eslint/no-require-imports */\n\ninterface ToolDescriptor {\n name: string;\n description?: string;\n inputSchema: Record<string, unknown>;\n outputSchema?: Record<string, unknown>;\n}\n\ninterface ListToolsResult {\n tools: ToolDescriptor[];\n}\n\ninterface CallToolResult {\n content: Array<{ type: string; [key: string]: unknown }>;\n isError?: boolean;\n structuredContent?: Record<string, unknown>;\n}\n\n// ---------------------------------------------------------------------------\n// Package detection\n// ---------------------------------------------------------------------------\n\n/** Attempt to resolve a package's package.json from the current working directory. */\nfunction resolvePackageJson(pkg: string): string | null {\n try {\n const req = getNodeModuleCreateRequire();\n return req.resolve(`${pkg}/package.json`);\n } catch {\n return null;\n }\n}\n\n/**\n * Resolve the upstream MCP package to use.\n *\n * Returns the package name to spawn, or null for standalone mode.\n */\nexport function resolveUpstream(options: { upstream?: string }): string | null {\n // Explicit override always wins\n if (options.upstream) {\n return options.upstream;\n }\n\n // Auto-detect\n if (resolvePackageJson('@playwright/mcp')) {\n return '@playwright/mcp';\n }\n if (resolvePackageJson('playwright-mcp-advanced')) {\n return 'playwright-mcp-advanced';\n }\n\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Stdio transport helpers (minimal, no external deps beyond Node built-ins)\n// ---------------------------------------------------------------------------\n\ntype MessageCallback = (message: unknown) => void;\n\n/**\n * Minimal stdio transport that wraps a ChildProcess's stdin/stdout as an\n * MCP transport using the same newline-delimited JSON-RPC format as the SDK.\n */\nclass ChildProcessTransport {\n private pendingRequests = new Map<string, { resolve: (v: unknown) => void; reject: (e: Error) => void }>();\n private id = 0;\n\n constructor(\n private stdin: Writable,\n private stdout: Readable,\n private stderr: Readable | null,\n private onMessage: MessageCallback,\n private onClose?: () => void,\n ) {}\n\n start(): void {\n // Drain stdout line-by-line into JSON-RPC messages\n let buffer = '';\n this.stdout.on('data', (chunk: Buffer) => {\n buffer += chunk.toString();\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n for (const line of lines) {\n if (line.trim()) {\n try {\n const msg = JSON.parse(line);\n this.handleMessage(msg);\n } catch {\n // ignore parse errors\n }\n }\n }\n });\n\n this.stdout.on('end', () => {\n this.onClose?.();\n });\n\n this.stderr?.on('data', (chunk: Buffer) => {\n // Pass stderr through to parent stderr so Chrome debug URLs are visible\n process.stderr.write(chunk);\n });\n }\n\n private handleMessage(\n \n msg: { id?: unknown; result?: unknown; error?: unknown; method?: string; params?: unknown },\n ): void {\n // Response to one of our requests\n if (msg.id !== undefined) {\n const pending = this.pendingRequests.get(String(msg.id));\n if (pending) {\n this.pendingRequests.delete(String(msg.id));\n if (msg.error) {\n pending.reject(new Error(String(msg.error)));\n } else {\n pending.resolve(msg.result);\n }\n }\n return;\n }\n // Server-to-client notification (shouldn't happen in our use case but handle gracefully)\n if (msg.method) {\n this.onMessage(msg);\n }\n }\n\n send(message: { method: string; params?: unknown; id?: unknown }): void {\n const json = JSON.stringify(message) + '\\n';\n this.stdin.write(json);\n }\n\n request(method: string, params?: unknown): Promise<unknown> {\n const id = String(++this.id);\n const promise = new Promise((resolve, reject) => {\n this.pendingRequests.set(id, { resolve: resolve as (v: unknown) => void, reject });\n });\n this.send({ method, params, id });\n return promise;\n }\n\n close(): void {\n this.stdin.end();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Upstream connection\n// ---------------------------------------------------------------------------\n\nexport interface UpstreamConnection {\n /** List all tools the upstream server provides. */\n listTools(): Promise<ListToolsResult>;\n /** Call a tool on the upstream server. */\n callTool(name: string, args?: Record<string, unknown>): Promise<CallToolResult>;\n /** The underlying child process (may be null in standalone mode). */\n process: ChildProcess | null;\n /** Close the upstream connection. */\n close(): void;\n}\n\n// ---------------------------------------------------------------------------\n// CDP port injection\n// ---------------------------------------------------------------------------\n\n/**\n * Inject `--remote-debugging-port=9222` into the args array if no existing\n * remote-debugging-port or cdp-endpoint flag is present.\n */\nexport function injectDebugPort(args: string[]): string[] {\n const hasDebugFlag = args.some(\n (a) => a.startsWith('--remote-debugging-port') || a.startsWith('--cdp-endpoint'),\n );\n if (hasDebugFlag) {\n return args;\n }\n return ['--remote-debugging-port=9222', ...args];\n}\n\n// ---------------------------------------------------------------------------\n// Spawn and connect\n// ---------------------------------------------------------------------------\n\n/**\n * Spawn the upstream MCP server as a child process and return a connection handle.\n */\nexport function spawnUpstream(pkg: string, passthroughArgs: string[]): UpstreamConnection {\n // Spawn via npx so scoped packages work without a local install\n const npxBin = process.platform === 'win32' ? 'npx.cmd' : 'npx';\n const npxArgs = [pkg, ...passthroughArgs];\n\n const child = spawn(npxBin, npxArgs, {\n stdio: ['pipe', 'pipe', 'pipe'],\n env: {\n ...process.env,\n // Prevent npx from prompting or caching in CI\n NPM_CONFIG_YES: 'true',\n NPM_CONFIG_INTERACTIVE: 'false',\n },\n shell: false,\n windowsHide: true,\n });\n\n const transport = new ChildProcessTransport(\n child.stdin!,\n child.stdout!,\n child.stderr!,\n (_msg: unknown) => {\n // Notifications from upstream — not expected in proxy use, ignore\n },\n () => {\n // Process ended\n },\n );\n transport.start();\n\n const connection: UpstreamConnection = {\n process: child,\n\n async listTools(): Promise<ListToolsResult> {\n const result = await transport.request('tools/list');\n return result as ListToolsResult;\n },\n\n async callTool(name: string, args?: Record<string, unknown>): Promise<CallToolResult> {\n const result = await transport.request('tools/call', { name, arguments: args ?? {} });\n return result as CallToolResult;\n },\n\n close(): void {\n transport.close();\n },\n };\n\n return connection;\n}\n","/**\n * CDP connection to the browser that the upstream Playwright MCP controls.\n *\n * Strategy (in priority order):\n * 1. Explicit `--cdp-endpoint` flag (highest priority)\n * 2. Parse CDP URL from upstream stderr — Chrome prints `DevTools listening on ws://...`\n * 3. Try well-known debug port 9222 as fallback\n * 4. Auto-inject `--remote-debugging-port=9222` into upstream passthrough args\n */\n\nimport type { Browser, Page } from 'playwright-core';\nimport type { ChildProcess } from 'node:child_process';\n\n// CDP WebSocket URL regex — Chrome prints this to stderr when remote debugging is on\nconst CDP_WS_REGEX = /DevTools listening on (ws:\\/\\/[^\\s]+)/;\n\nlet cachedConnection: { browser: Browser; page: Page } | null = null;\n\n/**\n * Find the CDP WebSocket URL in the upstream process's stderr output.\n */\nexport function extractCdpUrlFromStderr(data: string): string | null {\n const match = CDP_WS_REGEX.exec(data);\n return match ? (match[1] ?? null) : null;\n}\n\n/**\n * Lazily connect to the upstream browser via CDP and return a shared Page handle.\n *\n * The connection is cached — subsequent calls return the same handle.\n */\nexport async function getUpstreamPage(\n upstreamProcess: ChildProcess | null,\n options: { cdpEndpoint?: string; debugPort?: number },\n): Promise<{ browser: Browser; page: Page }> {\n if (cachedConnection) {\n return cachedConnection;\n }\n\n let cdpEndpoint = options.cdpEndpoint;\n\n // 1. Explicit endpoint from flags\n if (!cdpEndpoint) {\n // 2. Parse from upstream stderr if we have the process\n if (upstreamProcess?.stderr) {\n const stderrLines: string[] = [];\n upstreamProcess.stderr.on('data', (chunk: Buffer) => {\n stderrLines.push(chunk.toString());\n });\n\n // Give Chrome a moment to emit the listening message\n await new Promise<void>((resolve) => setTimeout(resolve, 500));\n\n for (const line of stderrLines) {\n const url = extractCdpUrlFromStderr(line);\n if (url) {\n cdpEndpoint = url;\n break;\n }\n }\n }\n }\n\n // 3. Fall back to well-known debug port\n if (!cdpEndpoint) {\n const port = options.debugPort ?? 9222;\n cdpEndpoint = `http://localhost:${port}`;\n }\n\n // Connect via CDP\n const browser = await chromiumConnect(cdpEndpoint);\n const pages = await browser.contexts()[0]?.pages() ?? [];\n const page = pages[0] ?? (await browser.newPage());\n\n cachedConnection = { browser, page };\n return cachedConnection;\n}\n\n/* eslint-disable @typescript-eslint/no-require-imports */\nfunction getNodeModuleCreateRequire(): NodeJS.Require {\n const { createRequire } = require('node:module');\n return createRequire(import.meta.url);\n}\n/* eslint-enable @typescript-eslint/no-require-imports */\n\n/**\n * Thin wrapper around playwright-core's CDP connect to allow swapping\n * the implementation during tests.\n */\nexport async function chromiumConnect(endpoint: string): Promise<Browser> {\n const req = getNodeModuleCreateRequire();\n const pw = req('playwright-core') as unknown as { chromium: { connectOverCDP: (url: string) => Promise<Browser> } };\n return pw.chromium.connectOverCDP(endpoint);\n}\n\nexport function resetCachedConnection(): void {\n cachedConnection = null;\n}\n","/**\n * Checkpoint tool definitions and the handler that runs them locally.\n */\n\nimport type { Page } from 'playwright-core';\nimport type {\n CheckpointRecord,\n WebVitalsSnapshot,\n} from '../types';\nimport { captureCheckpoint, sanitizeSegment } from '../core';\nimport { runReporters } from '../report';\nimport type { CheckpointConfig } from '../types';\n\n// ---------------------------------------------------------------------------\n// Tool input schemas (JSON Schema for MCP tool registration)\n// ---------------------------------------------------------------------------\n\nconst browserCheckpointSchema = {\n type: 'object' as const,\n properties: {\n name: { type: 'string', description: 'Unique name for this checkpoint (e.g. \"homepage\", \"after-login\").' },\n description: {\n type: 'string',\n description: 'Long-form description of what this checkpoint captures.',\n },\n highlightSelector: {\n type: 'string',\n description: 'CSS selector for a region to highlight in the annotated screenshot.',\n },\n fullPage: {\n type: 'boolean',\n description: 'Capture the full page (default: true). Set false for viewport-only.',\n },\n collectors: {\n type: 'object',\n description: 'Per-collector overrides. Set to false to disable, or an options object to configure.',\n additionalProperties: true,\n },\n },\n required: ['name'],\n};\n\nconst browserCheckpointReportSchema = {\n type: 'object' as const,\n properties: {\n outputDir: {\n type: 'string',\n description: 'Directory to write report files (default: ./report).',\n },\n format: {\n type: 'string',\n enum: ['html', 'markdown', 'mdx'],\n description: 'Report format(s) to generate.',\n },\n },\n};\n\nconst browserCheckpointCompareSchema = {\n type: 'object' as const,\n properties: {\n baseline: { type: 'string', description: 'Path or name of the baseline checkpoint manifest.' },\n current: { type: 'string', description: 'Path or name of the current checkpoint manifest.' },\n },\n required: ['baseline', 'current'],\n};\n\nexport const CHECKPOINT_TOOL_NAME = 'browser_checkpoint';\nexport const REPORT_TOOL_NAME = 'browser_checkpoint_report';\nexport const COMPARE_TOOL_NAME = 'browser_checkpoint_compare';\n\nexport const CHECKPOINT_TOOLS = [\n {\n name: CHECKPOINT_TOOL_NAME,\n description:\n 'Capture a structured snapshot of the current browser page: screenshot, accessibility violations, Web Vitals, console errors, and network failures. Returns an LLM-friendly summary plus artifact paths.',\n inputSchema: browserCheckpointSchema,\n },\n {\n name: REPORT_TOOL_NAME,\n description: 'Generate an HTML, Markdown, or MDX report from all checkpoint manifests in a directory.',\n inputSchema: browserCheckpointReportSchema,\n },\n {\n name: COMPARE_TOOL_NAME,\n description: 'Compare two checkpoint runs to surface differences (not yet implemented).',\n inputSchema: browserCheckpointCompareSchema,\n },\n] as const;\n\n// ---------------------------------------------------------------------------\n// Tool implementations\n// ---------------------------------------------------------------------------\n\nexport type ToolContext = {\n page: Page;\n outputDir: string;\n};\n\nexport async function handleBrowserCheckpoint(\n args: {\n name: string;\n description?: string;\n highlightSelector?: string;\n fullPage?: boolean;\n collectors?: Record<string, unknown>;\n },\n ctx: ToolContext,\n): Promise<string> {\n const slug = sanitizeSegment(args.name);\n\n const record = await captureCheckpoint(ctx.page, args.name, {\n outputDir: ctx.outputDir,\n highlightSelector: args.highlightSelector,\n fullPage: args.fullPage ?? true,\n description: args.description,\n collectors: args.collectors as Record<string, boolean | Record<string, unknown>> | undefined,\n });\n\n void slug; // reserved for future path-building use\n return formatCheckpointSummary(record);\n}\n\nexport async function handleBrowserCheckpointReport(\n args: {\n outputDir?: string;\n format?: 'html' | 'markdown' | 'mdx';\n },\n testResultsDir = 'test-results',\n): Promise<string> {\n const outputDir = args.outputDir ?? 'report';\n const config: CheckpointConfig = {\n reporters: {\n html: args.format === undefined || args.format === 'html',\n markdown: args.format === 'markdown',\n mdx: args.format === 'mdx',\n },\n };\n\n const { resolve } = await import('node:path');\n\n const results = await runReporters(\n config,\n resolve(process.cwd(), testResultsDir),\n resolve(process.cwd(), outputDir),\n );\n\n const lines = Object.entries(results).map(([name, result]) => `- ${name}: ${result.summary}`);\n if (lines.length === 0) {\n return 'No reports generated.';\n }\n return `Report generation complete:\\n${lines.join('\\n')}`;\n}\n\nexport function handleBrowserCheckpointCompare(\n _args: { baseline: string; current: string },\n): string {\n return 'browser_checkpoint_compare is not yet implemented.';\n}\n\n// ---------------------------------------------------------------------------\n// Summary formatter\n// ---------------------------------------------------------------------------\n\n/**\n * Format a CheckpointRecord into a clean, LLM-friendly text summary.\n */\nexport function formatCheckpointSummary(record: CheckpointRecord): string {\n const lines: string[] = [];\n\n lines.push(`Checkpoint \"${record.name}\" captured.`);\n lines.push(`URL: ${record.url}`);\n lines.push(`Title: ${record.title}`);\n lines.push('');\n\n // ── Accessibility ──────────────────────────────────────────────────────\n const axeData = record.collectors['axe'];\n if (axeData?.data) {\n const axe = axeData.data as { skipped?: boolean; violations?: number; reason?: string | null };\n if (axe.skipped) {\n lines.push(`Accessibility: skipped (${axe.reason ?? 'unknown reason'})`);\n } else {\n const violations = axe.violations ?? 0;\n lines.push(`Accessibility: ${violations} violation${violations !== 1 ? 's' : ''}`);\n }\n }\n\n // ── Web Vitals ────────────────────────────────────────────────────────\n const wvData = record.collectors['web-vitals'];\n if (wvData?.data) {\n const wv = wvData.data as WebVitalsSnapshot;\n lines.push('Web Vitals:');\n\n const metricLines: string[] = [];\n for (const [key, metric] of Object.entries(wv)) {\n if (!metric || typeof metric !== 'object') continue;\n const m = metric as { value: number | null; rating: string };\n if (m.value === null) continue;\n\n const ratingIcon = m.rating === 'good' ? '✅' : m.rating === 'needs-improvement' ? '⚠️' : m.rating === 'poor' ? '❌' : '';\n const label = key.replace(/Ms$/, '').toUpperCase();\n const formatted =\n key.endsWith('Ms') ? `${Math.round(m.value)}ms` : m.value.toFixed ? m.value.toFixed(3) : String(m.value);\n metricLines.push(` ${label}: ${formatted} (${m.rating} ${ratingIcon})`.trim());\n }\n if (metricLines.length > 0) {\n lines.push(metricLines.join('\\n'));\n }\n }\n\n // ── Console ────────────────────────────────────────────────────────────\n const consoleData = record.collectors['console'];\n if (consoleData?.data) {\n const entries = consoleData.data as Array<{ type: string; text: string }>;\n const errors = entries.filter((e) => e.type === 'error' || e.type === 'pageerror');\n if (errors.length > 0) {\n lines.push(`Console: ${errors.length} error${errors.length !== 1 ? 's' : ''}`);\n for (const err of errors.slice(0, 3)) {\n lines.push(` ${err.text}`);\n }\n if (errors.length > 3) {\n lines.push(` ... and ${errors.length - 3} more`);\n }\n } else {\n lines.push('Console: no errors');\n }\n }\n\n // ── Network ────────────────────────────────────────────────────────────\n const netData = record.collectors['network'];\n if (netData?.data) {\n const requests = netData.data as Array<{\n url: string;\n status: number | null;\n failureText: string | null;\n }>;\n const failed = requests.filter((r) => r.status === null || r.status >= 400 || r.failureText);\n if (failed.length > 0) {\n lines.push(`Network: ${failed.length} failed request${failed.length !== 1 ? 's' : ''}`);\n for (const req of failed.slice(0, 3)) {\n const reason = req.failureText ?? `${req.status} ${req.url}`;\n lines.push(` ${req.url} → ${reason}`);\n }\n if (failed.length > 3) {\n lines.push(` ... and ${failed.length - 3} more`);\n }\n } else {\n lines.push('Network: 0 failed requests');\n }\n }\n\n // ── Screenshot ─────────────────────────────────────────────────────────\n const ssData = record.collectors['screenshot'];\n if (ssData?.summary) {\n const ss = ssData.summary as { screenshotPath?: string };\n if (ss.screenshotPath) {\n lines.push(`Screenshot: ${ss.screenshotPath}`);\n }\n }\n\n return lines.join('\\n');\n}\n","/**\n * playwright-checkpoint MCP proxy server.\n *\n * Proxies all tools from an upstream Playwright MCP server while adding\n * the browser_checkpoint, browser_checkpoint_report, and\n * browser_checkpoint_compare tools handled locally.\n *\n * The StdioServerTransport is inlined locally. The SDK is loaded at runtime\n * (not bundled) so this module works even when the SDK is not installed,\n * with a clear error message when startMcpProxy() is actually called.\n */\n\nimport { StdioServerTransport } from './transport';\n\nimport { resolveUpstream, spawnUpstream, injectDebugPort } from './upstream';\nimport { getUpstreamPage, resetCachedConnection } from './browser-connect';\nimport {\n CHECKPOINT_TOOLS,\n CHECKPOINT_TOOL_NAME,\n REPORT_TOOL_NAME,\n COMPARE_TOOL_NAME,\n handleBrowserCheckpoint,\n handleBrowserCheckpointReport,\n handleBrowserCheckpointCompare,\n} from './tools';\n\n// ---------------------------------------------------------------------------\n// SDK availability check\n// ---------------------------------------------------------------------------\n\nfunction ensureMcpSdk(): void {\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require('@modelcontextprotocol/sdk');\n } catch {\n throw new Error(\n 'playwright-checkpoint MCP mode requires @modelcontextprotocol/sdk.\\n' +\n 'Install it: npm install @modelcontextprotocol/sdk',\n );\n }\n}\n\n/* eslint-disable @typescript-eslint/no-require-imports */\nfunction getNodeModuleCreateRequire(): (specifier: string) => NodeJS.Require {\n const { createRequire } = require('node:module');\n return createRequire(import.meta.url);\n}\n/* eslint-enable @typescript-eslint/no-require-imports */\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\nexport type McpProxyOptions = {\n /** Explicit upstream package to proxy (e.g. \"@playwright/mcp\"). */\n upstream?: string;\n /** Run in standalone mode — no upstream proxying. */\n standalone?: boolean;\n /** CDP endpoint to connect to the browser directly (overrides auto-detection). */\n cdpEndpoint?: string;\n /** Directory for checkpoint output files (default: ./checkpoints). */\n outputDir?: string;\n /** Arguments to pass through to the upstream server (everything after --). */\n passthrough?: string[];\n};\n\n// ---------------------------------------------------------------------------\n// Server\n// ---------------------------------------------------------------------------\n\nexport async function startMcpProxy(options: McpProxyOptions = {}): Promise<void> {\n ensureMcpSdk();\n\n // Load SDK at runtime — it is a peer-optional dep so we do not bundle it.\n const req = getNodeModuleCreateRequire();\n const sdk: { ListToolsRequestSchema: unknown; CallToolRequestSchema: unknown } = req('@modelcontextprotocol/sdk') as unknown as {\n ListToolsRequestSchema: unknown;\n CallToolRequestSchema: unknown;\n };\n const sdkServer: { Server: unknown } = req('@modelcontextprotocol/sdk/server') as unknown as {\n Server: new (opts: unknown, capabilities?: unknown) => unknown\n };\n\n const ListToolsRequestSchema = sdk.ListToolsRequestSchema;\n const CallToolRequestSchema = sdk.CallToolRequestSchema;\n \n const Server = sdkServer.Server as new (opts: unknown, capabilities?: unknown) => any;\n\n const outputDir = options.outputDir ?? './checkpoints';\n\n // ── Resolve upstream ────────────────────────────────────────────────────\n let upstreamConnection: ReturnType<typeof spawnUpstream> | null = null;\n const upstreamTools: Array<{\n name: string;\n description?: string;\n inputSchema: Record<string, unknown>;\n }> = [];\n\n const upstreamPkg = options.standalone ? null : resolveUpstream({ upstream: options.upstream });\n\n if (upstreamPkg) {\n console.error(`[playwright-checkpoint MCP] Proxying upstream: ${upstreamPkg}`);\n\n let passthroughArgs = options.passthrough ?? [];\n\n // If no explicit CDP endpoint and no debug port in args, inject one\n // so we can auto-detect the browser\n if (!options.cdpEndpoint) {\n passthroughArgs = injectDebugPort(passthroughArgs);\n }\n\n upstreamConnection = spawnUpstream(upstreamPkg, passthroughArgs);\n\n // Wait briefly for the upstream to initialize, then fetch its tool list\n await new Promise<void>((resolve) => setTimeout(resolve, 500));\n try {\n const result = await upstreamConnection.listTools();\n for (const tool of result.tools) {\n upstreamTools.push({\n name: tool.name,\n description: tool.description,\n inputSchema: tool.inputSchema,\n });\n }\n } catch (err) {\n console.error('[playwright-checkpoint MCP] Warning: could not list upstream tools:', err);\n }\n } else {\n console.error('[playwright-checkpoint MCP] Running in standalone mode (no upstream).');\n }\n\n // ── Build merged tool list ──────────────────────────────────────────────\n const checkpointTools = CHECKPOINT_TOOLS.map((t) => ({\n name: t.name,\n description: t.description,\n inputSchema: t.inputSchema as Record<string, unknown>,\n }));\n\n const allTools = [...checkpointTools, ...upstreamTools];\n\n // ── Create MCP server ─────────────────────────────────────────────────\n \n const server = new Server({ name: 'playwright-checkpoint', version: '0.1.0-beta.0' }, { capabilities: { tools: {} } });\n\n \n server.setRequestHandler(ListToolsRequestSchema as any, async () => {\n return { tools: allTools };\n });\n\n \n server.setRequestHandler(CallToolRequestSchema as any, async (request: {\n params: { name: string; arguments?: Record<string, unknown> };\n }) => {\n const { name, arguments: args = {} } = request.params;\n\n // ── Handle checkpoint tools locally ──────────────────────────────────\n if (name === CHECKPOINT_TOOL_NAME) {\n const { page } = await getUpstreamPage(upstreamConnection?.process ?? null, {\n cdpEndpoint: options.cdpEndpoint,\n debugPort: 9222,\n });\n\n const result = await handleBrowserCheckpoint(\n args as Parameters<typeof handleBrowserCheckpoint>[0],\n { page, outputDir },\n );\n\n return { content: [{ type: 'text', text: result }] };\n }\n\n if (name === REPORT_TOOL_NAME) {\n const result = await handleBrowserCheckpointReport(\n args as Parameters<typeof handleBrowserCheckpointReport>[0],\n );\n return { content: [{ type: 'text', text: result }] };\n }\n\n if (name === COMPARE_TOOL_NAME) {\n const result = handleBrowserCheckpointCompare(\n args as Parameters<typeof handleBrowserCheckpointCompare>[0],\n );\n return { content: [{ type: 'text', text: result }], isError: true };\n }\n\n // ── Forward to upstream ──────────────────────────────────────────────\n if (upstreamConnection) {\n try {\n const result = await upstreamConnection.callTool(name, args as Record<string, unknown>);\n return {\n content: result.content as Array<{ type: string; [key: string]: unknown }>,\n isError: result.isError,\n structuredContent: result.structuredContent,\n };\n } catch (err) {\n return {\n content: [\n {\n type: 'text',\n text: `Upstream tool \"${name}\" failed: ${err instanceof Error ? err.message : String(err)}`,\n },\n ],\n isError: true,\n };\n }\n }\n\n // No upstream and tool not found\n return { content: [{ type: 'text', text: `Tool \"${name}\" is not available.` }], isError: true };\n });\n\n // ── Connect transport and start ───────────────────────────────────────\n const transport = new StdioServerTransport();\n \n await (server as any).connect(transport);\n\n // Clean up on exit\n const cleanup = (): void => {\n resetCachedConnection();\n upstreamConnection?.close();\n };\n process.on('SIGINT', cleanup);\n process.on('SIGTERM', cleanup);\n}\n"],"mappings":";;;;;;;;;;;;AAOA,SAAS,iBAAiB,SAAiC;AACzD,SAAO,KAAK,UAAU,OAAO,IAAI;AACnC;AAEA,IAAM,aAAN,MAAiB;AAAA,EACP;AAAA,EAER,OAAO,OAAqB;AAC1B,SAAK,UAAU,KAAK,UAAU,OAAO,OAAO,CAAC,KAAK,SAAS,KAAK,CAAC,IAAI;AAAA,EACvE;AAAA,EAEA,cAAqC;AACnC,QAAI,CAAC,KAAK,QAAS,QAAO;AAC1B,UAAM,QAAQ,KAAK,QAAQ,QAAQ,IAAI;AACvC,QAAI,UAAU,GAAI,QAAO;AACzB,UAAM,OAAO,KAAK,QAAQ,SAAS,QAAQ,GAAG,KAAK,EAAE,QAAQ,OAAO,EAAE;AACtE,SAAK,UAAU,KAAK,QAAQ,SAAS,QAAQ,CAAC;AAC9C,QAAI;AACF,aAAO,KAAK,MAAM,IAAI;AAAA,IACxB,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,QAAc;AACZ,SAAK,UAAU;AAAA,EACjB;AACF;AAMO,IAAM,uBAAN,MAA2B;AAAA,EACf;AAAA,EACA;AAAA,EACA,cAAc,IAAI,WAAW;AAAA,EACtC,WAAW;AAAA,EAEnB,YAAY,OAA+B,QAAgC;AACzE,SAAK,SAAS,SAAS,QAAQ;AAC/B,SAAK,UAAU,UAAU,QAAQ;AAAA,EACnC;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EAEA,MAAM,QAAuB;AAC3B,QAAI,KAAK,UAAU;AACjB,YAAM,IAAI,MAAM,uCAAuC;AAAA,IACzD;AACA,SAAK,WAAW;AAEhB,SAAK,OAAO,GAAG,QAAQ,CAAC,UAAkB;AACxC,WAAK,YAAY,OAAO,KAAK;AAC7B,WAAK,mBAAmB;AAAA,IAC1B,CAAC;AAED,SAAK,OAAO,GAAG,SAAS,CAAC,UAAiB;AACxC,WAAK,UAAU,KAAK;AAAA,IACtB,CAAC;AAAA,EACH;AAAA,EAEA,qBAA2B;AACzB,WAAO,MAAM;AACX,UAAI;AACF,cAAM,UAAU,KAAK,YAAY,YAAY;AAC7C,YAAI,YAAY,KAAM;AACtB,aAAK,YAAY,OAAO;AAAA,MAC1B,SAAS,OAAO;AACd,aAAK,UAAU,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;AAAA,MAC1E;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,QAAuB;AAC3B,IAAC,KAAK,OAA0D,QAAQ;AACxE,SAAK,YAAY,MAAM;AACvB,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,MAAM,KAAK,SAAwC;AACjD,UAAM,OAAO,iBAAiB,OAAO;AACrC,QAAI,KAAK,QAAQ,MAAM,IAAI,GAAG;AAC5B,aAAO,QAAQ,QAAQ;AAAA,IACzB;AACA,WAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,WAAK,QAAQ,KAAK,SAAS,OAAO;AAAA,IACpC,CAAC;AAAA,EACH;AACF;;;ACxFA,SAAS,aAAa;AAKtB,SAAS,6BAA6C;AACpD,QAAM,EAAE,cAAc,IAAI,UAAQ,QAAa;AAC/C,SAAO,cAAc,QAAQ,IAAI,IAAI,UAAU;AACjD;AAyBA,SAAS,mBAAmB,KAA4B;AACtD,MAAI;AACF,UAAM,MAAM,2BAA2B;AACvC,WAAO,IAAI,QAAQ,GAAG,GAAG,eAAe;AAAA,EAC1C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAOO,SAAS,gBAAgB,SAA+C;AAE7E,MAAI,QAAQ,UAAU;AACpB,WAAO,QAAQ;AAAA,EACjB;AAGA,MAAI,mBAAmB,iBAAiB,GAAG;AACzC,WAAO;AAAA,EACT;AACA,MAAI,mBAAmB,yBAAyB,GAAG;AACjD,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAYA,IAAM,wBAAN,MAA4B;AAAA,EAI1B,YACU,OACA,QACA,QACA,WACA,SACR;AALQ;AACA;AACA;AACA;AACA;AAAA,EACP;AAAA,EALO;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EARF,kBAAkB,oBAAI,IAA2E;AAAA,EACjG,KAAK;AAAA,EAUb,QAAc;AAEZ,QAAI,SAAS;AACb,SAAK,OAAO,GAAG,QAAQ,CAAC,UAAkB;AACxC,gBAAU,MAAM,SAAS;AACzB,YAAM,QAAQ,OAAO,MAAM,IAAI;AAC/B,eAAS,MAAM,IAAI,KAAK;AACxB,iBAAW,QAAQ,OAAO;AACxB,YAAI,KAAK,KAAK,GAAG;AACf,cAAI;AACF,kBAAM,MAAM,KAAK,MAAM,IAAI;AAC3B,iBAAK,cAAc,GAAG;AAAA,UACxB,QAAQ;AAAA,UAER;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AAED,SAAK,OAAO,GAAG,OAAO,MAAM;AAC1B,WAAK,UAAU;AAAA,IACjB,CAAC;AAED,SAAK,QAAQ,GAAG,QAAQ,CAAC,UAAkB;AAEzC,cAAQ,OAAO,MAAM,KAAK;AAAA,IAC5B,CAAC;AAAA,EACH;AAAA,EAEQ,cAEN,KACM;AAEN,QAAI,IAAI,OAAO,QAAW;AACxB,YAAM,UAAU,KAAK,gBAAgB,IAAI,OAAO,IAAI,EAAE,CAAC;AACvD,UAAI,SAAS;AACX,aAAK,gBAAgB,OAAO,OAAO,IAAI,EAAE,CAAC;AAC1C,YAAI,IAAI,OAAO;AACb,kBAAQ,OAAO,IAAI,MAAM,OAAO,IAAI,KAAK,CAAC,CAAC;AAAA,QAC7C,OAAO;AACL,kBAAQ,QAAQ,IAAI,MAAM;AAAA,QAC5B;AAAA,MACF;AACA;AAAA,IACF;AAEA,QAAI,IAAI,QAAQ;AACd,WAAK,UAAU,GAAG;AAAA,IACpB;AAAA,EACF;AAAA,EAEA,KAAK,SAAmE;AACtE,UAAM,OAAO,KAAK,UAAU,OAAO,IAAI;AACvC,SAAK,MAAM,MAAM,IAAI;AAAA,EACvB;AAAA,EAEA,QAAQ,QAAgB,QAAoC;AAC1D,UAAM,KAAK,OAAO,EAAE,KAAK,EAAE;AAC3B,UAAM,UAAU,IAAI,QAAQ,CAAC,SAAS,WAAW;AAC/C,WAAK,gBAAgB,IAAI,IAAI,EAAE,SAA0C,OAAO,CAAC;AAAA,IACnF,CAAC;AACD,SAAK,KAAK,EAAE,QAAQ,QAAQ,GAAG,CAAC;AAChC,WAAO;AAAA,EACT;AAAA,EAEA,QAAc;AACZ,SAAK,MAAM,IAAI;AAAA,EACjB;AACF;AAyBO,SAAS,gBAAgB,MAA0B;AACxD,QAAM,eAAe,KAAK;AAAA,IACxB,CAAC,MAAM,EAAE,WAAW,yBAAyB,KAAK,EAAE,WAAW,gBAAgB;AAAA,EACjF;AACA,MAAI,cAAc;AAChB,WAAO;AAAA,EACT;AACA,SAAO,CAAC,gCAAgC,GAAG,IAAI;AACjD;AASO,SAAS,cAAc,KAAa,iBAA+C;AAExF,QAAM,SAAS,QAAQ,aAAa,UAAU,YAAY;AAC1D,QAAM,UAAU,CAAC,KAAK,GAAG,eAAe;AAExC,QAAM,QAAQ,MAAM,QAAQ,SAAS;AAAA,IACnC,OAAO,CAAC,QAAQ,QAAQ,MAAM;AAAA,IAC9B,KAAK;AAAA,MACH,GAAG,QAAQ;AAAA;AAAA,MAEX,gBAAgB;AAAA,MAChB,wBAAwB;AAAA,IAC1B;AAAA,IACA,OAAO;AAAA,IACP,aAAa;AAAA,EACf,CAAC;AAED,QAAM,YAAY,IAAI;AAAA,IACpB,MAAM;AAAA,IACN,MAAM;AAAA,IACN,MAAM;AAAA,IACN,CAAC,SAAkB;AAAA,IAEnB;AAAA,IACA,MAAM;AAAA,IAEN;AAAA,EACF;AACA,YAAU,MAAM;AAEhB,QAAM,aAAiC;AAAA,IACrC,SAAS;AAAA,IAET,MAAM,YAAsC;AAC1C,YAAM,SAAS,MAAM,UAAU,QAAQ,YAAY;AACnD,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,SAAS,MAAc,MAAyD;AACpF,YAAM,SAAS,MAAM,UAAU,QAAQ,cAAc,EAAE,MAAM,WAAW,QAAQ,CAAC,EAAE,CAAC;AACpF,aAAO;AAAA,IACT;AAAA,IAEA,QAAc;AACZ,gBAAU,MAAM;AAAA,IAClB;AAAA,EACF;AAEA,SAAO;AACT;;;AClPA,IAAM,eAAe;AAErB,IAAI,mBAA4D;AAKzD,SAAS,wBAAwB,MAA6B;AACnE,QAAM,QAAQ,aAAa,KAAK,IAAI;AACpC,SAAO,QAAS,MAAM,CAAC,KAAK,OAAQ;AACtC;AAOA,eAAsB,gBACpB,iBACA,SAC2C;AAC3C,MAAI,kBAAkB;AACpB,WAAO;AAAA,EACT;AAEA,MAAI,cAAc,QAAQ;AAG1B,MAAI,CAAC,aAAa;AAEhB,QAAI,iBAAiB,QAAQ;AAC3B,YAAM,cAAwB,CAAC;AAC/B,sBAAgB,OAAO,GAAG,QAAQ,CAAC,UAAkB;AACnD,oBAAY,KAAK,MAAM,SAAS,CAAC;AAAA,MACnC,CAAC;AAGD,YAAM,IAAI,QAAc,CAAC,YAAY,WAAW,SAAS,GAAG,CAAC;AAE7D,iBAAW,QAAQ,aAAa;AAC9B,cAAM,MAAM,wBAAwB,IAAI;AACxC,YAAI,KAAK;AACP,wBAAc;AACd;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,MAAI,CAAC,aAAa;AAChB,UAAM,OAAO,QAAQ,aAAa;AAClC,kBAAc,oBAAoB,IAAI;AAAA,EACxC;AAGA,QAAM,UAAU,MAAM,gBAAgB,WAAW;AACjD,QAAM,QAAQ,MAAM,QAAQ,SAAS,EAAE,CAAC,GAAG,MAAM,KAAK,CAAC;AACvD,QAAM,OAAO,MAAM,CAAC,KAAM,MAAM,QAAQ,QAAQ;AAEhD,qBAAmB,EAAE,SAAS,KAAK;AACnC,SAAO;AACT;AAGA,SAASA,8BAA6C;AACpD,QAAM,EAAE,cAAc,IAAI,UAAQ,QAAa;AAC/C,SAAO,cAAc,YAAY,GAAG;AACtC;AAOA,eAAsB,gBAAgB,UAAoC;AACxE,QAAM,MAAMA,4BAA2B;AACvC,QAAM,KAAK,IAAI,iBAAiB;AAChC,SAAO,GAAG,SAAS,eAAe,QAAQ;AAC5C;AAEO,SAAS,wBAA8B;AAC5C,qBAAmB;AACrB;;;AChFA,IAAM,0BAA0B;AAAA,EAC9B,MAAM;AAAA,EACN,YAAY;AAAA,IACV,MAAM,EAAE,MAAM,UAAU,aAAa,oEAAoE;AAAA,IACzG,aAAa;AAAA,MACX,MAAM;AAAA,MACN,aAAa;AAAA,IACf;AAAA,IACA,mBAAmB;AAAA,MACjB,MAAM;AAAA,MACN,aAAa;AAAA,IACf;AAAA,IACA,UAAU;AAAA,MACR,MAAM;AAAA,MACN,aAAa;AAAA,IACf;AAAA,IACA,YAAY;AAAA,MACV,MAAM;AAAA,MACN,aAAa;AAAA,MACb,sBAAsB;AAAA,IACxB;AAAA,EACF;AAAA,EACA,UAAU,CAAC,MAAM;AACnB;AAEA,IAAM,gCAAgC;AAAA,EACpC,MAAM;AAAA,EACN,YAAY;AAAA,IACV,WAAW;AAAA,MACT,MAAM;AAAA,MACN,aAAa;AAAA,IACf;AAAA,IACA,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,MAAM,CAAC,QAAQ,YAAY,KAAK;AAAA,MAChC,aAAa;AAAA,IACf;AAAA,EACF;AACF;AAEA,IAAM,iCAAiC;AAAA,EACrC,MAAM;AAAA,EACN,YAAY;AAAA,IACV,UAAU,EAAE,MAAM,UAAU,aAAa,oDAAoD;AAAA,IAC7F,SAAS,EAAE,MAAM,UAAU,aAAa,mDAAmD;AAAA,EAC7F;AAAA,EACA,UAAU,CAAC,YAAY,SAAS;AAClC;AAEO,IAAM,uBAAuB;AAC7B,IAAM,mBAAmB;AACzB,IAAM,oBAAoB;AAE1B,IAAM,mBAAmB;AAAA,EAC9B;AAAA,IACE,MAAM;AAAA,IACN,aACE;AAAA,IACF,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aAAa;AAAA,EACf;AACF;AAWA,eAAsB,wBACpB,MAOA,KACiB;AACjB,QAAM,OAAO,gBAAgB,KAAK,IAAI;AAEtC,QAAM,SAAS,MAAM,kBAAkB,IAAI,MAAM,KAAK,MAAM;AAAA,IAC1D,WAAW,IAAI;AAAA,IACf,mBAAmB,KAAK;AAAA,IACxB,UAAU,KAAK,YAAY;AAAA,IAC3B,aAAa,KAAK;AAAA,IAClB,YAAY,KAAK;AAAA,EACnB,CAAC;AAED,OAAK;AACL,SAAO,wBAAwB,MAAM;AACvC;AAEA,eAAsB,8BACpB,MAIA,iBAAiB,gBACA;AACjB,QAAM,YAAY,KAAK,aAAa;AACpC,QAAM,SAA2B;AAAA,IAC/B,WAAW;AAAA,MACT,MAAM,KAAK,WAAW,UAAa,KAAK,WAAW;AAAA,MACnD,UAAU,KAAK,WAAW;AAAA,MAC1B,KAAK,KAAK,WAAW;AAAA,IACvB;AAAA,EACF;AAEA,QAAM,EAAE,QAAQ,IAAI,MAAM,OAAO,MAAW;AAE5C,QAAM,UAAU,MAAM;AAAA,IACpB;AAAA,IACA,QAAQ,QAAQ,IAAI,GAAG,cAAc;AAAA,IACrC,QAAQ,QAAQ,IAAI,GAAG,SAAS;AAAA,EAClC;AAEA,QAAM,QAAQ,OAAO,QAAQ,OAAO,EAAE,IAAI,CAAC,CAAC,MAAM,MAAM,MAAM,KAAK,IAAI,KAAK,OAAO,OAAO,EAAE;AAC5F,MAAI,MAAM,WAAW,GAAG;AACtB,WAAO;AAAA,EACT;AACA,SAAO;AAAA,EAAgC,MAAM,KAAK,IAAI,CAAC;AACzD;AAEO,SAAS,+BACd,OACQ;AACR,SAAO;AACT;AASO,SAAS,wBAAwB,QAAkC;AACxE,QAAM,QAAkB,CAAC;AAEzB,QAAM,KAAK,eAAe,OAAO,IAAI,aAAa;AAClD,QAAM,KAAK,QAAQ,OAAO,GAAG,EAAE;AAC/B,QAAM,KAAK,UAAU,OAAO,KAAK,EAAE;AACnC,QAAM,KAAK,EAAE;AAGb,QAAM,UAAU,OAAO,WAAW,KAAK;AACvC,MAAI,SAAS,MAAM;AACjB,UAAM,MAAM,QAAQ;AACpB,QAAI,IAAI,SAAS;AACf,YAAM,KAAK,2BAA2B,IAAI,UAAU,gBAAgB,GAAG;AAAA,IACzE,OAAO;AACL,YAAM,aAAa,IAAI,cAAc;AACrC,YAAM,KAAK,kBAAkB,UAAU,aAAa,eAAe,IAAI,MAAM,EAAE,EAAE;AAAA,IACnF;AAAA,EACF;AAGA,QAAM,SAAS,OAAO,WAAW,YAAY;AAC7C,MAAI,QAAQ,MAAM;AAChB,UAAM,KAAK,OAAO;AAClB,UAAM,KAAK,aAAa;AAExB,UAAM,cAAwB,CAAC;AAC/B,eAAW,CAAC,KAAK,MAAM,KAAK,OAAO,QAAQ,EAAE,GAAG;AAC9C,UAAI,CAAC,UAAU,OAAO,WAAW,SAAU;AAC3C,YAAM,IAAI;AACV,UAAI,EAAE,UAAU,KAAM;AAEtB,YAAM,aAAa,EAAE,WAAW,SAAS,WAAM,EAAE,WAAW,sBAAsB,iBAAO,EAAE,WAAW,SAAS,WAAM;AACrH,YAAM,QAAQ,IAAI,QAAQ,OAAO,EAAE,EAAE,YAAY;AACjD,YAAM,YACJ,IAAI,SAAS,IAAI,IAAI,GAAG,KAAK,MAAM,EAAE,KAAK,CAAC,OAAO,EAAE,MAAM,UAAU,EAAE,MAAM,QAAQ,CAAC,IAAI,OAAO,EAAE,KAAK;AACzG,kBAAY,KAAK,KAAK,KAAK,KAAK,SAAS,KAAK,EAAE,MAAM,IAAI,UAAU,IAAI,KAAK,CAAC;AAAA,IAChF;AACA,QAAI,YAAY,SAAS,GAAG;AAC1B,YAAM,KAAK,YAAY,KAAK,IAAI,CAAC;AAAA,IACnC;AAAA,EACF;AAGA,QAAM,cAAc,OAAO,WAAW,SAAS;AAC/C,MAAI,aAAa,MAAM;AACrB,UAAM,UAAU,YAAY;AAC5B,UAAM,SAAS,QAAQ,OAAO,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,SAAS,WAAW;AACjF,QAAI,OAAO,SAAS,GAAG;AACrB,YAAM,KAAK,YAAY,OAAO,MAAM,SAAS,OAAO,WAAW,IAAI,MAAM,EAAE,EAAE;AAC7E,iBAAW,OAAO,OAAO,MAAM,GAAG,CAAC,GAAG;AACpC,cAAM,KAAK,KAAK,IAAI,IAAI,EAAE;AAAA,MAC5B;AACA,UAAI,OAAO,SAAS,GAAG;AACrB,cAAM,KAAK,aAAa,OAAO,SAAS,CAAC,OAAO;AAAA,MAClD;AAAA,IACF,OAAO;AACL,YAAM,KAAK,oBAAoB;AAAA,IACjC;AAAA,EACF;AAGA,QAAM,UAAU,OAAO,WAAW,SAAS;AAC3C,MAAI,SAAS,MAAM;AACjB,UAAM,WAAW,QAAQ;AAKzB,UAAM,SAAS,SAAS,OAAO,CAAC,MAAM,EAAE,WAAW,QAAQ,EAAE,UAAU,OAAO,EAAE,WAAW;AAC3F,QAAI,OAAO,SAAS,GAAG;AACrB,YAAM,KAAK,YAAY,OAAO,MAAM,kBAAkB,OAAO,WAAW,IAAI,MAAM,EAAE,EAAE;AACtF,iBAAW,OAAO,OAAO,MAAM,GAAG,CAAC,GAAG;AACpC,cAAM,SAAS,IAAI,eAAe,GAAG,IAAI,MAAM,IAAI,IAAI,GAAG;AAC1D,cAAM,KAAK,KAAK,IAAI,GAAG,WAAM,MAAM,EAAE;AAAA,MACvC;AACA,UAAI,OAAO,SAAS,GAAG;AACrB,cAAM,KAAK,aAAa,OAAO,SAAS,CAAC,OAAO;AAAA,MAClD;AAAA,IACF,OAAO;AACL,YAAM,KAAK,4BAA4B;AAAA,IACzC;AAAA,EACF;AAGA,QAAM,SAAS,OAAO,WAAW,YAAY;AAC7C,MAAI,QAAQ,SAAS;AACnB,UAAM,KAAK,OAAO;AAClB,QAAI,GAAG,gBAAgB;AACrB,YAAM,KAAK,eAAe,GAAG,cAAc,EAAE;AAAA,IAC/C;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;;;ACtOA,SAAS,eAAqB;AAC5B,MAAI;AAEF,cAAQ,2BAA2B;AAAA,EACrC,QAAQ;AACN,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AACF;AAGA,SAASC,8BAAoE;AAC3E,QAAM,EAAE,cAAc,IAAI,UAAQ,QAAa;AAC/C,SAAO,cAAc,YAAY,GAAG;AACtC;AAwBA,eAAsB,cAAc,UAA2B,CAAC,GAAkB;AAChF,eAAa;AAGb,QAAM,MAAMA,4BAA2B;AACvC,QAAM,MAA2E,IAAI,2BAA2B;AAIhH,QAAM,YAAiC,IAAI,kCAAkC;AAI7E,QAAM,yBAAyB,IAAI;AACnC,QAAM,wBAAwB,IAAI;AAElC,QAAM,SAAS,UAAU;AAEzB,QAAM,YAAY,QAAQ,aAAa;AAGvC,MAAI,qBAA8D;AAClE,QAAM,gBAID,CAAC;AAEN,QAAM,cAAc,QAAQ,aAAa,OAAO,gBAAgB,EAAE,UAAU,QAAQ,SAAS,CAAC;AAE9F,MAAI,aAAa;AACf,YAAQ,MAAM,kDAAkD,WAAW,EAAE;AAE7E,QAAI,kBAAkB,QAAQ,eAAe,CAAC;AAI9C,QAAI,CAAC,QAAQ,aAAa;AACxB,wBAAkB,gBAAgB,eAAe;AAAA,IACnD;AAEA,yBAAqB,cAAc,aAAa,eAAe;AAG/D,UAAM,IAAI,QAAc,CAAC,YAAY,WAAW,SAAS,GAAG,CAAC;AAC7D,QAAI;AACF,YAAM,SAAS,MAAM,mBAAmB,UAAU;AAClD,iBAAW,QAAQ,OAAO,OAAO;AAC/B,sBAAc,KAAK;AAAA,UACjB,MAAM,KAAK;AAAA,UACX,aAAa,KAAK;AAAA,UAClB,aAAa,KAAK;AAAA,QACpB,CAAC;AAAA,MACH;AAAA,IACF,SAAS,KAAK;AACZ,cAAQ,MAAM,uEAAuE,GAAG;AAAA,IAC1F;AAAA,EACF,OAAO;AACL,YAAQ,MAAM,uEAAuE;AAAA,EACvF;AAGA,QAAM,kBAAkB,iBAAiB,IAAI,CAAC,OAAO;AAAA,IACnD,MAAM,EAAE;AAAA,IACR,aAAa,EAAE;AAAA,IACf,aAAa,EAAE;AAAA,EACjB,EAAE;AAEF,QAAM,WAAW,CAAC,GAAG,iBAAiB,GAAG,aAAa;AAItD,QAAM,SAAS,IAAI,OAAO,EAAE,MAAM,yBAAyB,SAAS,eAAe,GAAG,EAAE,cAAc,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC;AAGrH,SAAO,kBAAkB,wBAA+B,YAAY;AAClE,WAAO,EAAE,OAAO,SAAS;AAAA,EAC3B,CAAC;AAGD,SAAO,kBAAkB,uBAA8B,OAAO,YAExD;AACJ,UAAM,EAAE,MAAM,WAAW,OAAO,CAAC,EAAE,IAAI,QAAQ;AAG/C,QAAI,SAAS,sBAAsB;AACjC,YAAM,EAAE,KAAK,IAAI,MAAM,gBAAgB,oBAAoB,WAAW,MAAM;AAAA,QAC1E,aAAa,QAAQ;AAAA,QACrB,WAAW;AAAA,MACb,CAAC;AAED,YAAM,SAAS,MAAM;AAAA,QACnB;AAAA,QACA,EAAE,MAAM,UAAU;AAAA,MACpB;AAEA,aAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,OAAO,CAAC,EAAE;AAAA,IACrD;AAEA,QAAI,SAAS,kBAAkB;AAC7B,YAAM,SAAS,MAAM;AAAA,QACnB;AAAA,MACF;AACA,aAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,OAAO,CAAC,EAAE;AAAA,IACrD;AAEA,QAAI,SAAS,mBAAmB;AAC9B,YAAM,SAAS;AAAA,QACb;AAAA,MACF;AACA,aAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,OAAO,CAAC,GAAG,SAAS,KAAK;AAAA,IACpE;AAGA,QAAI,oBAAoB;AACtB,UAAI;AACF,cAAM,SAAS,MAAM,mBAAmB,SAAS,MAAM,IAA+B;AACtF,eAAO;AAAA,UACL,SAAS,OAAO;AAAA,UAChB,SAAS,OAAO;AAAA,UAChB,mBAAmB,OAAO;AAAA,QAC5B;AAAA,MACF,SAAS,KAAK;AACZ,eAAO;AAAA,UACL,SAAS;AAAA,YACP;AAAA,cACE,MAAM;AAAA,cACN,MAAM,kBAAkB,IAAI,aAAa,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,YAC3F;AAAA,UACF;AAAA,UACA,SAAS;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAGA,WAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,SAAS,IAAI,sBAAsB,CAAC,GAAG,SAAS,KAAK;AAAA,EAChG,CAAC;AAGD,QAAM,YAAY,IAAI,qBAAqB;AAE3C,QAAO,OAAe,QAAQ,SAAS;AAGvC,QAAM,UAAU,MAAY;AAC1B,0BAAsB;AACtB,wBAAoB,MAAM;AAAA,EAC5B;AACA,UAAQ,GAAG,UAAU,OAAO;AAC5B,UAAQ,GAAG,WAAW,OAAO;AAC/B;","names":["getNodeModuleCreateRequire","getNodeModuleCreateRequire"]}
|
|
1
|
+
{"version":3,"sources":["../../src/mcp/transport.ts","../../src/mcp/upstream.ts","../../src/mcp/browser-connect.ts","../../src/mcp/tools.ts","../../src/mcp/index.ts"],"sourcesContent":["/**\n * Minimal MCP stdio transport — inlined from @modelcontextprotocol/sdk/shared/stdio\n * to avoid SDK subpath resolution issues. This matches the SDK's protocol exactly.\n */\n\nimport type { JSONRPCMessage } from '@modelcontextprotocol/sdk/types';\n\nfunction serializeMessage(message: JSONRPCMessage): string {\n return JSON.stringify(message) + '\\n';\n}\n\nclass ReadBuffer {\n private _buffer?: Buffer;\n\n append(chunk: Buffer): void {\n this._buffer = this._buffer ? Buffer.concat([this._buffer, chunk]) : chunk;\n }\n\n readMessage(): JSONRPCMessage | null {\n if (!this._buffer) return null;\n const index = this._buffer.indexOf('\\n');\n if (index === -1) return null;\n const line = this._buffer.toString('utf8', 0, index).replace(/\\r$/, '');\n this._buffer = this._buffer.subarray(index + 1);\n try {\n return JSON.parse(line) as JSONRPCMessage;\n } catch {\n return null;\n }\n }\n\n clear(): void {\n this._buffer = undefined;\n }\n}\n\n/**\n * Minimal stdio transport for the MCP server.\n * Communicates with the MCP client via stdin/stdout using newline-delimited JSON-RPC.\n */\nexport class StdioServerTransport {\n private readonly _stdin: NodeJS.ReadableStream;\n private readonly _stdout: NodeJS.WritableStream;\n private readonly _readBuffer = new ReadBuffer();\n private _started = false;\n\n constructor(stdin?: NodeJS.ReadableStream, stdout?: NodeJS.WritableStream) {\n this._stdin = stdin ?? process.stdin;\n this._stdout = stdout ?? process.stdout;\n }\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n\n async start(): Promise<void> {\n if (this._started) {\n throw new Error('StdioServerTransport already started!');\n }\n this._started = true;\n\n this._stdin.on('data', (chunk: Buffer) => {\n this._readBuffer.append(chunk);\n this.#processReadBuffer();\n });\n\n this._stdin.on('error', (error: Error) => {\n this.onerror?.(error);\n });\n }\n\n #processReadBuffer(): void {\n while (true) {\n try {\n const message = this._readBuffer.readMessage();\n if (message === null) break;\n this.onmessage?.(message);\n } catch (error) {\n this.onerror?.(error instanceof Error ? error : new Error(String(error)));\n }\n }\n }\n\n async close(): Promise<void> {\n (this._stdin as NodeJS.ReadableStream & { pause?: () => void }).pause?.();\n this._readBuffer.clear();\n this.onclose?.();\n }\n\n async send(message: JSONRPCMessage): Promise<void> {\n const json = serializeMessage(message);\n if (this._stdout.write(json)) {\n return Promise.resolve();\n }\n return new Promise((resolve) => {\n this._stdout.once('drain', resolve);\n });\n }\n}\n","/**\n * Upstream Playwright MCP detection and spawning.\n *\n * Auto-detection priority:\n * 1. Explicit `--upstream @playwright/mcp` flag\n * 2. Scan node_modules for `@playwright/mcp`\n * 3. Scan node_modules for `playwright-mcp-advanced`\n * 4. null — standalone mode (no upstream proxying)\n */\n\nimport { spawn } from 'node:child_process';\nimport type { ChildProcess } from 'node:child_process';\nimport type { Readable, Writable } from 'node:stream';\n\n/* eslint-disable @typescript-eslint/no-require-imports */\nfunction getNodeModuleCreateRequire(): NodeJS.Require {\n const { createRequire } = require('node:module');\n return createRequire(process.cwd() + '/noop.js');\n}\n/* eslint-enable @typescript-eslint/no-require-imports */\n\ninterface ToolDescriptor {\n name: string;\n description?: string;\n inputSchema: Record<string, unknown>;\n outputSchema?: Record<string, unknown>;\n}\n\ninterface ListToolsResult {\n tools: ToolDescriptor[];\n}\n\ninterface CallToolResult {\n content: Array<{ type: string; [key: string]: unknown }>;\n isError?: boolean;\n structuredContent?: Record<string, unknown>;\n}\n\n// ---------------------------------------------------------------------------\n// Package detection\n// ---------------------------------------------------------------------------\n\n/** Attempt to resolve a package's package.json from the current working directory. */\nfunction resolvePackageJson(pkg: string): string | null {\n try {\n const req = getNodeModuleCreateRequire();\n return req.resolve(`${pkg}/package.json`);\n } catch {\n return null;\n }\n}\n\n/**\n * Resolve the upstream MCP package to use.\n *\n * Returns the package name to spawn, or null for standalone mode.\n */\nexport function resolveUpstream(options: { upstream?: string }): string | null {\n // Explicit override always wins\n if (options.upstream) {\n return options.upstream;\n }\n\n // Auto-detect\n if (resolvePackageJson('@playwright/mcp')) {\n return '@playwright/mcp';\n }\n if (resolvePackageJson('playwright-mcp-advanced')) {\n return 'playwright-mcp-advanced';\n }\n\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Stdio transport helpers (minimal, no external deps beyond Node built-ins)\n// ---------------------------------------------------------------------------\n\ntype MessageCallback = (message: unknown) => void;\n\n/**\n * Minimal stdio transport that wraps a ChildProcess's stdin/stdout as an\n * MCP transport using the same newline-delimited JSON-RPC format as the SDK.\n */\nclass ChildProcessTransport {\n private pendingRequests = new Map<string, { resolve: (v: unknown) => void; reject: (e: Error) => void }>();\n private id = 0;\n\n constructor(\n private stdin: Writable,\n private stdout: Readable,\n private stderr: Readable | null,\n private onMessage: MessageCallback,\n private onClose?: () => void,\n ) {}\n\n start(): void {\n // Drain stdout line-by-line into JSON-RPC messages\n let buffer = '';\n this.stdout.on('data', (chunk: Buffer) => {\n buffer += chunk.toString();\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n for (const line of lines) {\n if (line.trim()) {\n try {\n const msg = JSON.parse(line);\n this.handleMessage(msg);\n } catch {\n // ignore parse errors\n }\n }\n }\n });\n\n this.stdout.on('end', () => {\n this.onClose?.();\n });\n\n this.stderr?.on('data', (chunk: Buffer) => {\n // Pass stderr through to parent stderr so Chrome debug URLs are visible\n process.stderr.write(chunk);\n });\n }\n\n private handleMessage(\n \n msg: { id?: unknown; result?: unknown; error?: unknown; method?: string; params?: unknown },\n ): void {\n // Response to one of our requests\n if (msg.id !== undefined) {\n const pending = this.pendingRequests.get(String(msg.id));\n if (pending) {\n this.pendingRequests.delete(String(msg.id));\n if (msg.error) {\n pending.reject(new Error(String(msg.error)));\n } else {\n pending.resolve(msg.result);\n }\n }\n return;\n }\n // Server-to-client notification (shouldn't happen in our use case but handle gracefully)\n if (msg.method) {\n this.onMessage(msg);\n }\n }\n\n send(message: { method: string; params?: unknown; id?: unknown }): void {\n const json = JSON.stringify(message) + '\\n';\n this.stdin.write(json);\n }\n\n request(method: string, params?: unknown): Promise<unknown> {\n const id = String(++this.id);\n const promise = new Promise((resolve, reject) => {\n this.pendingRequests.set(id, { resolve: resolve as (v: unknown) => void, reject });\n });\n this.send({ method, params, id });\n return promise;\n }\n\n close(): void {\n this.stdin.end();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Upstream connection\n// ---------------------------------------------------------------------------\n\nexport interface UpstreamConnection {\n /** List all tools the upstream server provides. */\n listTools(): Promise<ListToolsResult>;\n /** Call a tool on the upstream server. */\n callTool(name: string, args?: Record<string, unknown>): Promise<CallToolResult>;\n /** The underlying child process (may be null in standalone mode). */\n process: ChildProcess | null;\n /** Close the upstream connection. */\n close(): void;\n}\n\n// ---------------------------------------------------------------------------\n// CDP port injection\n// ---------------------------------------------------------------------------\n\n/**\n * Inject `--remote-debugging-port=9222` into the args array if no existing\n * remote-debugging-port or cdp-endpoint flag is present.\n */\nexport function injectDebugPort(args: string[]): string[] {\n const hasDebugFlag = args.some(\n (a) => a.startsWith('--remote-debugging-port') || a.startsWith('--cdp-endpoint'),\n );\n if (hasDebugFlag) {\n return args;\n }\n return ['--remote-debugging-port=9222', ...args];\n}\n\n// ---------------------------------------------------------------------------\n// Spawn and connect\n// ---------------------------------------------------------------------------\n\n/**\n * Spawn the upstream MCP server as a child process and return a connection handle.\n */\nexport function spawnUpstream(pkg: string, passthroughArgs: string[]): UpstreamConnection {\n // Spawn via npx so scoped packages work without a local install\n const npxBin = process.platform === 'win32' ? 'npx.cmd' : 'npx';\n const npxArgs = [pkg, ...passthroughArgs];\n\n const child = spawn(npxBin, npxArgs, {\n stdio: ['pipe', 'pipe', 'pipe'],\n env: {\n ...process.env,\n // Prevent npx from prompting or caching in CI\n NPM_CONFIG_YES: 'true',\n NPM_CONFIG_INTERACTIVE: 'false',\n },\n shell: false,\n windowsHide: true,\n });\n\n const transport = new ChildProcessTransport(\n child.stdin!,\n child.stdout!,\n child.stderr!,\n (_msg: unknown) => {\n // Notifications from upstream — not expected in proxy use, ignore\n },\n () => {\n // Process ended\n },\n );\n transport.start();\n\n const connection: UpstreamConnection = {\n process: child,\n\n async listTools(): Promise<ListToolsResult> {\n const result = await transport.request('tools/list');\n return result as ListToolsResult;\n },\n\n async callTool(name: string, args?: Record<string, unknown>): Promise<CallToolResult> {\n const result = await transport.request('tools/call', { name, arguments: args ?? {} });\n return result as CallToolResult;\n },\n\n close(): void {\n transport.close();\n },\n };\n\n return connection;\n}\n","/**\n * CDP connection to the browser that the upstream Playwright MCP controls.\n *\n * Strategy (in priority order):\n * 1. Explicit `--cdp-endpoint` flag (highest priority)\n * 2. Parse CDP URL from upstream stderr — Chrome prints `DevTools listening on ws://...`\n * 3. Try well-known debug port 9222 as fallback\n * 4. Auto-inject `--remote-debugging-port=9222` into upstream passthrough args\n */\n\nimport type { Browser, Page } from 'playwright-core';\nimport type { ChildProcess } from 'node:child_process';\n\n// CDP WebSocket URL regex — Chrome prints this to stderr when remote debugging is on\nconst CDP_WS_REGEX = /DevTools listening on (ws:\\/\\/[^\\s]+)/;\n\nlet cachedConnection: { browser: Browser; page: Page } | null = null;\n\n/**\n * Find the CDP WebSocket URL in the upstream process's stderr output.\n */\nexport function extractCdpUrlFromStderr(data: string): string | null {\n const match = CDP_WS_REGEX.exec(data);\n return match ? (match[1] ?? null) : null;\n}\n\n/**\n * Lazily connect to the upstream browser via CDP and return a shared Page handle.\n *\n * The connection is cached — subsequent calls return the same handle.\n */\nexport async function getUpstreamPage(\n upstreamProcess: ChildProcess | null,\n options: { cdpEndpoint?: string; debugPort?: number },\n): Promise<{ browser: Browser; page: Page }> {\n if (cachedConnection) {\n return cachedConnection;\n }\n\n let cdpEndpoint = options.cdpEndpoint;\n\n // 1. Explicit endpoint from flags\n if (!cdpEndpoint) {\n // 2. Parse from upstream stderr if we have the process\n if (upstreamProcess?.stderr) {\n const stderrLines: string[] = [];\n upstreamProcess.stderr.on('data', (chunk: Buffer) => {\n stderrLines.push(chunk.toString());\n });\n\n // Give Chrome a moment to emit the listening message\n await new Promise<void>((resolve) => setTimeout(resolve, 500));\n\n for (const line of stderrLines) {\n const url = extractCdpUrlFromStderr(line);\n if (url) {\n cdpEndpoint = url;\n break;\n }\n }\n }\n }\n\n // 3. Fall back to well-known debug port\n if (!cdpEndpoint) {\n const port = options.debugPort ?? 9222;\n cdpEndpoint = `http://localhost:${port}`;\n }\n\n // Connect via CDP\n const browser = await chromiumConnect(cdpEndpoint);\n const pages = await browser.contexts()[0]?.pages() ?? [];\n const page = pages[0] ?? (await browser.newPage());\n\n cachedConnection = { browser, page };\n return cachedConnection;\n}\n\n/* eslint-disable @typescript-eslint/no-require-imports */\nfunction getNodeModuleCreateRequire(): NodeJS.Require {\n const { createRequire } = require('node:module');\n return createRequire(import.meta.url);\n}\n/* eslint-enable @typescript-eslint/no-require-imports */\n\n/**\n * Thin wrapper around playwright-core's CDP connect to allow swapping\n * the implementation during tests.\n */\nexport async function chromiumConnect(endpoint: string): Promise<Browser> {\n const req = getNodeModuleCreateRequire();\n const pw = req('playwright-core') as unknown as { chromium: { connectOverCDP: (url: string) => Promise<Browser> } };\n return pw.chromium.connectOverCDP(endpoint);\n}\n\nexport function resetCachedConnection(): void {\n cachedConnection = null;\n}\n","/**\n * Checkpoint tool definitions and the handler that runs them locally.\n */\n\nimport type { Page } from 'playwright-core';\nimport type {\n CheckpointRecord,\n WebVitalsSnapshot,\n} from '../types';\nimport { captureCheckpoint, sanitizeSegment } from '../core';\nimport { runReporters } from '../report';\nimport type { CheckpointConfig } from '../types';\n\n// ---------------------------------------------------------------------------\n// Tool input schemas (JSON Schema for MCP tool registration)\n// ---------------------------------------------------------------------------\n\nconst browserCheckpointSchema = {\n type: 'object' as const,\n properties: {\n name: { type: 'string', description: 'Unique name for this checkpoint (e.g. \"homepage\", \"after-login\").' },\n description: {\n type: 'string',\n description: 'Long-form description of what this checkpoint captures.',\n },\n highlightSelector: {\n type: 'string',\n description: 'CSS selector for a region to highlight in the annotated screenshot.',\n },\n fullPage: {\n type: 'boolean',\n description: 'Capture the full page (default: true). Set false for viewport-only.',\n },\n collectors: {\n type: 'object',\n description: 'Per-collector overrides. Set to false to disable, or an options object to configure.',\n additionalProperties: true,\n },\n },\n required: ['name'],\n};\n\nconst browserCheckpointReportSchema = {\n type: 'object' as const,\n properties: {\n outputDir: {\n type: 'string',\n description: 'Directory to write report files (default: ./report).',\n },\n format: {\n type: 'string',\n enum: ['html', 'markdown', 'mdx'],\n description: 'Report format(s) to generate.',\n },\n },\n};\n\nconst browserCheckpointCompareSchema = {\n type: 'object' as const,\n properties: {\n baseline: { type: 'string', description: 'Path or name of the baseline checkpoint manifest.' },\n current: { type: 'string', description: 'Path or name of the current checkpoint manifest.' },\n },\n required: ['baseline', 'current'],\n};\n\nexport const CHECKPOINT_TOOL_NAME = 'browser_checkpoint';\nexport const REPORT_TOOL_NAME = 'browser_checkpoint_report';\nexport const COMPARE_TOOL_NAME = 'browser_checkpoint_compare';\n\nexport const CHECKPOINT_TOOLS = [\n {\n name: CHECKPOINT_TOOL_NAME,\n description:\n 'Capture a structured snapshot of the current browser page: screenshot, accessibility violations, Web Vitals, console errors, and network failures. Returns an LLM-friendly summary plus artifact paths.',\n inputSchema: browserCheckpointSchema,\n },\n {\n name: REPORT_TOOL_NAME,\n description: 'Generate an HTML, Markdown, or MDX report from all checkpoint manifests in a directory.',\n inputSchema: browserCheckpointReportSchema,\n },\n {\n name: COMPARE_TOOL_NAME,\n description: 'Compare two checkpoint runs to surface differences (not yet implemented).',\n inputSchema: browserCheckpointCompareSchema,\n },\n] as const;\n\n// ---------------------------------------------------------------------------\n// Tool implementations\n// ---------------------------------------------------------------------------\n\nexport type ToolContext = {\n page: Page;\n outputDir: string;\n};\n\nexport async function handleBrowserCheckpoint(\n args: {\n name: string;\n description?: string;\n highlightSelector?: string;\n fullPage?: boolean;\n collectors?: Record<string, unknown>;\n },\n ctx: ToolContext,\n): Promise<string> {\n const slug = sanitizeSegment(args.name);\n\n const record = await captureCheckpoint(ctx.page, args.name, {\n outputDir: ctx.outputDir,\n highlightSelector: args.highlightSelector,\n fullPage: args.fullPage ?? true,\n description: args.description,\n collectors: args.collectors as Record<string, boolean | Record<string, unknown>> | undefined,\n });\n\n void slug; // reserved for future path-building use\n return formatCheckpointSummary(record);\n}\n\nexport async function handleBrowserCheckpointReport(\n args: {\n outputDir?: string;\n format?: 'html' | 'markdown' | 'mdx';\n },\n testResultsDir = 'test-results',\n): Promise<string> {\n const outputDir = args.outputDir ?? 'report';\n const config: CheckpointConfig = {\n reporters: {\n html: args.format === undefined || args.format === 'html',\n markdown: args.format === 'markdown',\n mdx: args.format === 'mdx',\n },\n };\n\n const { resolve } = await import('node:path');\n\n const results = await runReporters(\n config,\n resolve(process.cwd(), testResultsDir),\n resolve(process.cwd(), outputDir),\n );\n\n const lines = Object.entries(results).map(([name, result]) => `- ${name}: ${result.summary}`);\n if (lines.length === 0) {\n return 'No reports generated.';\n }\n return `Report generation complete:\\n${lines.join('\\n')}`;\n}\n\nexport function handleBrowserCheckpointCompare(\n _args: { baseline: string; current: string },\n): string {\n return 'browser_checkpoint_compare is not yet implemented.';\n}\n\n// ---------------------------------------------------------------------------\n// Summary formatter\n// ---------------------------------------------------------------------------\n\n/**\n * Format a CheckpointRecord into a clean, LLM-friendly text summary.\n */\nexport function formatCheckpointSummary(record: CheckpointRecord): string {\n const lines: string[] = [];\n\n lines.push(`Checkpoint \"${record.name}\" captured.`);\n lines.push(`URL: ${record.url}`);\n lines.push(`Title: ${record.title}`);\n lines.push('');\n\n // ── Accessibility ──────────────────────────────────────────────────────\n const axeData = record.collectors['axe'];\n if (axeData?.data) {\n const axe = axeData.data as { skipped?: boolean; violations?: number; reason?: string | null };\n if (axe.skipped) {\n lines.push(`Accessibility: skipped (${axe.reason ?? 'unknown reason'})`);\n } else {\n const violations = axe.violations ?? 0;\n lines.push(`Accessibility: ${violations} violation${violations !== 1 ? 's' : ''}`);\n }\n }\n\n // ── Web Vitals ────────────────────────────────────────────────────────\n const wvData = record.collectors['web-vitals'];\n if (wvData?.data) {\n const wv = wvData.data as WebVitalsSnapshot;\n lines.push('Web Vitals:');\n\n const metricLines: string[] = [];\n for (const [key, metric] of Object.entries(wv)) {\n if (!metric || typeof metric !== 'object') continue;\n const m = metric as { value: number | null; rating: string };\n if (m.value === null) continue;\n\n const ratingIcon = m.rating === 'good' ? '✅' : m.rating === 'needs-improvement' ? '⚠️' : m.rating === 'poor' ? '❌' : '';\n const label = key.replace(/Ms$/, '').toUpperCase();\n const formatted =\n key.endsWith('Ms') ? `${Math.round(m.value)}ms` : m.value.toFixed ? m.value.toFixed(3) : String(m.value);\n metricLines.push(` ${label}: ${formatted} (${m.rating} ${ratingIcon})`.trim());\n }\n if (metricLines.length > 0) {\n lines.push(metricLines.join('\\n'));\n }\n }\n\n // ── Console ────────────────────────────────────────────────────────────\n const consoleData = record.collectors['console'];\n if (consoleData?.data) {\n const entries = consoleData.data as Array<{ type: string; text: string }>;\n const errors = entries.filter((e) => e.type === 'error' || e.type === 'pageerror');\n if (errors.length > 0) {\n lines.push(`Console: ${errors.length} error${errors.length !== 1 ? 's' : ''}`);\n for (const err of errors.slice(0, 3)) {\n lines.push(` ${err.text}`);\n }\n if (errors.length > 3) {\n lines.push(` ... and ${errors.length - 3} more`);\n }\n } else {\n lines.push('Console: no errors');\n }\n }\n\n // ── Network ────────────────────────────────────────────────────────────\n const netData = record.collectors['network'];\n if (netData?.data) {\n const requests = netData.data as Array<{\n url: string;\n status: number | null;\n failureText: string | null;\n }>;\n const failed = requests.filter((r) => r.status === null || r.status >= 400 || r.failureText);\n if (failed.length > 0) {\n lines.push(`Network: ${failed.length} failed request${failed.length !== 1 ? 's' : ''}`);\n for (const req of failed.slice(0, 3)) {\n const reason = req.failureText ?? `${req.status} ${req.url}`;\n lines.push(` ${req.url} → ${reason}`);\n }\n if (failed.length > 3) {\n lines.push(` ... and ${failed.length - 3} more`);\n }\n } else {\n lines.push('Network: 0 failed requests');\n }\n }\n\n // ── Screenshot ─────────────────────────────────────────────────────────\n const ssData = record.collectors['screenshot'];\n if (ssData?.summary) {\n const ss = ssData.summary as { screenshotPath?: string };\n if (ss.screenshotPath) {\n lines.push(`Screenshot: ${ss.screenshotPath}`);\n }\n }\n\n return lines.join('\\n');\n}\n","/**\n * playwright-checkpoint MCP proxy server.\n *\n * Proxies all tools from an upstream Playwright MCP server while adding\n * the browser_checkpoint, browser_checkpoint_report, and\n * browser_checkpoint_compare tools handled locally.\n *\n * The StdioServerTransport is inlined locally. The SDK is loaded at runtime\n * (not bundled) so this module works even when the SDK is not installed,\n * with a clear error message when startMcpProxy() is actually called.\n */\n\nimport { StdioServerTransport } from './transport';\n\nimport { resolveUpstream, spawnUpstream, injectDebugPort } from './upstream';\nimport { getUpstreamPage, resetCachedConnection } from './browser-connect';\nimport {\n CHECKPOINT_TOOLS,\n CHECKPOINT_TOOL_NAME,\n REPORT_TOOL_NAME,\n COMPARE_TOOL_NAME,\n handleBrowserCheckpoint,\n handleBrowserCheckpointReport,\n handleBrowserCheckpointCompare,\n} from './tools';\n\n// ---------------------------------------------------------------------------\n// SDK availability check\n// ---------------------------------------------------------------------------\n\nfunction ensureMcpSdk(): void {\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require('@modelcontextprotocol/sdk');\n } catch {\n throw new Error(\n 'playwright-checkpoint MCP mode requires @modelcontextprotocol/sdk.\\n' +\n 'Install it: npm install @modelcontextprotocol/sdk',\n );\n }\n}\n\n/* eslint-disable @typescript-eslint/no-require-imports */\nfunction getNodeModuleCreateRequire(): (specifier: string) => NodeJS.Require {\n const { createRequire } = require('node:module');\n return createRequire(import.meta.url);\n}\n/* eslint-enable @typescript-eslint/no-require-imports */\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\nexport type McpProxyOptions = {\n /** Explicit upstream package to proxy (e.g. \"@playwright/mcp\"). */\n upstream?: string;\n /** Run in standalone mode — no upstream proxying. */\n standalone?: boolean;\n /** CDP endpoint to connect to the browser directly (overrides auto-detection). */\n cdpEndpoint?: string;\n /** Directory for checkpoint output files (default: ./checkpoints). */\n outputDir?: string;\n /** Arguments to pass through to the upstream server (everything after --). */\n passthrough?: string[];\n};\n\n// ---------------------------------------------------------------------------\n// Server\n// ---------------------------------------------------------------------------\n\nexport async function startMcpProxy(options: McpProxyOptions = {}): Promise<void> {\n ensureMcpSdk();\n\n // Load SDK at runtime — it is a peer-optional dep so we do not bundle it.\n const req = getNodeModuleCreateRequire();\n const sdk: { ListToolsRequestSchema: unknown; CallToolRequestSchema: unknown } = req('@modelcontextprotocol/sdk') as unknown as {\n ListToolsRequestSchema: unknown;\n CallToolRequestSchema: unknown;\n };\n const sdkServer: { Server: unknown } = req('@modelcontextprotocol/sdk/server') as unknown as {\n Server: new (opts: unknown, capabilities?: unknown) => unknown\n };\n\n const ListToolsRequestSchema = sdk.ListToolsRequestSchema;\n const CallToolRequestSchema = sdk.CallToolRequestSchema;\n \n const Server = sdkServer.Server as new (opts: unknown, capabilities?: unknown) => any;\n\n const outputDir = options.outputDir ?? './checkpoints';\n\n // ── Resolve upstream ────────────────────────────────────────────────────\n let upstreamConnection: ReturnType<typeof spawnUpstream> | null = null;\n const upstreamTools: Array<{\n name: string;\n description?: string;\n inputSchema: Record<string, unknown>;\n }> = [];\n\n const upstreamPkg = options.standalone ? null : resolveUpstream({ upstream: options.upstream });\n\n if (upstreamPkg) {\n console.error(`[playwright-checkpoint MCP] Proxying upstream: ${upstreamPkg}`);\n\n let passthroughArgs = options.passthrough ?? [];\n\n // If no explicit CDP endpoint and no debug port in args, inject one\n // so we can auto-detect the browser\n if (!options.cdpEndpoint) {\n passthroughArgs = injectDebugPort(passthroughArgs);\n }\n\n upstreamConnection = spawnUpstream(upstreamPkg, passthroughArgs);\n\n // Wait briefly for the upstream to initialize, then fetch its tool list\n await new Promise<void>((resolve) => setTimeout(resolve, 500));\n try {\n const result = await upstreamConnection.listTools();\n for (const tool of result.tools) {\n upstreamTools.push({\n name: tool.name,\n description: tool.description,\n inputSchema: tool.inputSchema,\n });\n }\n } catch (err) {\n console.error('[playwright-checkpoint MCP] Warning: could not list upstream tools:', err);\n }\n } else {\n console.error('[playwright-checkpoint MCP] Running in standalone mode (no upstream).');\n }\n\n // ── Build merged tool list ──────────────────────────────────────────────\n const checkpointTools = CHECKPOINT_TOOLS.map((t) => ({\n name: t.name,\n description: t.description,\n inputSchema: t.inputSchema as Record<string, unknown>,\n }));\n\n const allTools = [...checkpointTools, ...upstreamTools];\n\n // ── Create MCP server ─────────────────────────────────────────────────\n \n const server = new Server({ name: 'playwright-checkpoint', version: '0.1.0' }, { capabilities: { tools: {} } });\n\n \n server.setRequestHandler(ListToolsRequestSchema as any, async () => {\n return { tools: allTools };\n });\n\n \n server.setRequestHandler(CallToolRequestSchema as any, async (request: {\n params: { name: string; arguments?: Record<string, unknown> };\n }) => {\n const { name, arguments: args = {} } = request.params;\n\n // ── Handle checkpoint tools locally ──────────────────────────────────\n if (name === CHECKPOINT_TOOL_NAME) {\n const { page } = await getUpstreamPage(upstreamConnection?.process ?? null, {\n cdpEndpoint: options.cdpEndpoint,\n debugPort: 9222,\n });\n\n const result = await handleBrowserCheckpoint(\n args as Parameters<typeof handleBrowserCheckpoint>[0],\n { page, outputDir },\n );\n\n return { content: [{ type: 'text', text: result }] };\n }\n\n if (name === REPORT_TOOL_NAME) {\n const result = await handleBrowserCheckpointReport(\n args as Parameters<typeof handleBrowserCheckpointReport>[0],\n );\n return { content: [{ type: 'text', text: result }] };\n }\n\n if (name === COMPARE_TOOL_NAME) {\n const result = handleBrowserCheckpointCompare(\n args as Parameters<typeof handleBrowserCheckpointCompare>[0],\n );\n return { content: [{ type: 'text', text: result }], isError: true };\n }\n\n // ── Forward to upstream ──────────────────────────────────────────────\n if (upstreamConnection) {\n try {\n const result = await upstreamConnection.callTool(name, args as Record<string, unknown>);\n return {\n content: result.content as Array<{ type: string; [key: string]: unknown }>,\n isError: result.isError,\n structuredContent: result.structuredContent,\n };\n } catch (err) {\n return {\n content: [\n {\n type: 'text',\n text: `Upstream tool \"${name}\" failed: ${err instanceof Error ? err.message : String(err)}`,\n },\n ],\n isError: true,\n };\n }\n }\n\n // No upstream and tool not found\n return { content: [{ type: 'text', text: `Tool \"${name}\" is not available.` }], isError: true };\n });\n\n // ── Connect transport and start ───────────────────────────────────────\n const transport = new StdioServerTransport();\n \n await (server as any).connect(transport);\n\n // Clean up on exit\n const cleanup = (): void => {\n resetCachedConnection();\n upstreamConnection?.close();\n };\n process.on('SIGINT', cleanup);\n process.on('SIGTERM', cleanup);\n}\n"],"mappings":"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","names":["getNodeModuleCreateRequire","getNodeModuleCreateRequire"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "playwright-checkpoint",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Structured page snapshots for Playwright — screenshots, accessibility, web vitals, and auto-generated documentation from your e2e tests",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"playwright",
|