framewatch-mcp-server 0.1.0 → 0.2.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/README.md +895 -15
- package/dist/constants.d.ts +274 -0
- package/dist/constants.js +279 -0
- package/dist/constants.js.map +1 -1
- package/dist/engine/browser.d.ts +20 -4
- package/dist/engine/browser.js +26 -9
- package/dist/engine/browser.js.map +1 -1
- package/dist/engine/clicks.d.ts +221 -0
- package/dist/engine/clicks.js +801 -0
- package/dist/engine/clicks.js.map +1 -0
- package/dist/engine/forms.d.ts +137 -0
- package/dist/engine/forms.js +474 -0
- package/dist/engine/forms.js.map +1 -0
- package/dist/engine/hmr.d.ts +41 -0
- package/dist/engine/hmr.js +91 -0
- package/dist/engine/hmr.js.map +1 -0
- package/dist/engine/inspect.d.ts +31 -0
- package/dist/engine/inspect.js +383 -0
- package/dist/engine/inspect.js.map +1 -0
- package/dist/engine/interaction.d.ts +12 -7
- package/dist/engine/interaction.js +110 -18
- package/dist/engine/interaction.js.map +1 -1
- package/dist/engine/links.d.ts +134 -0
- package/dist/engine/links.js +384 -0
- package/dist/engine/links.js.map +1 -0
- package/dist/engine/mocks.d.ts +53 -0
- package/dist/engine/mocks.js +148 -0
- package/dist/engine/mocks.js.map +1 -0
- package/dist/engine/rtl.d.ts +129 -0
- package/dist/engine/rtl.js +540 -0
- package/dist/engine/rtl.js.map +1 -0
- package/dist/engine/seo.d.ts +189 -0
- package/dist/engine/seo.js +398 -0
- package/dist/engine/seo.js.map +1 -0
- package/dist/engine/snapshot.d.ts +29 -0
- package/dist/engine/snapshot.js +10 -0
- package/dist/engine/snapshot.js.map +1 -0
- package/dist/engine/vue.d.ts +54 -0
- package/dist/engine/vue.js +419 -0
- package/dist/engine/vue.js.map +1 -0
- package/dist/index.js +45 -1
- package/dist/index.js.map +1 -1
- package/dist/tools/accessibility.d.ts +4 -0
- package/dist/tools/accessibility.js +9 -2
- package/dist/tools/accessibility.js.map +1 -1
- package/dist/tools/api-mock.d.ts +405 -0
- package/dist/tools/api-mock.js +186 -0
- package/dist/tools/api-mock.js.map +1 -0
- package/dist/tools/capture.d.ts +90 -26
- package/dist/tools/capture.js +109 -58
- package/dist/tools/capture.js.map +1 -1
- package/dist/tools/compare.d.ts +4 -0
- package/dist/tools/compare.js +16 -5
- package/dist/tools/compare.js.map +1 -1
- package/dist/tools/dead-clicks.d.ts +128 -0
- package/dist/tools/dead-clicks.js +570 -0
- package/dist/tools/dead-clicks.js.map +1 -0
- package/dist/tools/form-test.d.ts +112 -0
- package/dist/tools/form-test.js +477 -0
- package/dist/tools/form-test.js.map +1 -0
- package/dist/tools/index.d.ts +17 -1
- package/dist/tools/index.js +45 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/inspect.d.ts +78 -0
- package/dist/tools/inspect.js +136 -0
- package/dist/tools/inspect.js.map +1 -0
- package/dist/tools/interact.d.ts +37 -18
- package/dist/tools/interact.js +113 -13
- package/dist/tools/interact.js.map +1 -1
- package/dist/tools/links.d.ts +129 -0
- package/dist/tools/links.js +640 -0
- package/dist/tools/links.js.map +1 -0
- package/dist/tools/responsive.d.ts +10 -6
- package/dist/tools/responsive.js +21 -4
- package/dist/tools/responsive.js.map +1 -1
- package/dist/tools/rtl.d.ts +241 -0
- package/dist/tools/rtl.js +410 -0
- package/dist/tools/rtl.js.map +1 -0
- package/dist/tools/save-auth.d.ts +263 -0
- package/dist/tools/save-auth.js +253 -0
- package/dist/tools/save-auth.js.map +1 -0
- package/dist/tools/screenshot.d.ts +4 -0
- package/dist/tools/screenshot.js +15 -4
- package/dist/tools/screenshot.js.map +1 -1
- package/dist/tools/seo.d.ts +113 -0
- package/dist/tools/seo.js +281 -0
- package/dist/tools/seo.js.map +1 -0
- package/dist/tools/snapshot.d.ts +122 -0
- package/dist/tools/snapshot.js +183 -0
- package/dist/tools/snapshot.js.map +1 -0
- package/dist/tools/wait-for.d.ts +107 -0
- package/dist/tools/wait-for.js +167 -0
- package/dist/tools/wait-for.js.map +1 -0
- package/dist/utils/arabic-text.d.ts +14 -0
- package/dist/utils/arabic-text.js +193 -0
- package/dist/utils/arabic-text.js.map +1 -0
- package/dist/utils/budget.d.ts +41 -0
- package/dist/utils/budget.js +182 -0
- package/dist/utils/budget.js.map +1 -0
- package/dist/utils/format.d.ts +11 -1
- package/dist/utils/format.js +27 -4
- package/dist/utils/format.js.map +1 -1
- package/dist/utils/highlight.d.ts +69 -0
- package/dist/utils/highlight.js +181 -0
- package/dist/utils/highlight.js.map +1 -0
- package/dist/utils/link-rules.d.ts +100 -0
- package/dist/utils/link-rules.js +284 -0
- package/dist/utils/link-rules.js.map +1 -0
- package/dist/utils/mock-rules.d.ts +144 -0
- package/dist/utils/mock-rules.js +224 -0
- package/dist/utils/mock-rules.js.map +1 -0
- package/dist/utils/rtl-rules.d.ts +142 -0
- package/dist/utils/rtl-rules.js +296 -0
- package/dist/utils/rtl-rules.js.map +1 -0
- package/dist/utils/seo-rules.d.ts +129 -0
- package/dist/utils/seo-rules.js +726 -0
- package/dist/utils/seo-rules.js.map +1 -0
- package/dist/utils/snapshot-rules.d.ts +33 -0
- package/dist/utils/snapshot-rules.js +111 -0
- package/dist/utils/snapshot-rules.js.map +1 -0
- package/dist/utils/storage-state.d.ts +76 -0
- package/dist/utils/storage-state.js +195 -0
- package/dist/utils/storage-state.js.map +1 -0
- package/dist/utils/style-rules.d.ts +107 -0
- package/dist/utils/style-rules.js +223 -0
- package/dist/utils/style-rules.js.map +1 -0
- package/dist/utils/test-data.d.ts +75 -0
- package/dist/utils/test-data.js +294 -0
- package/dist/utils/test-data.js.map +1 -0
- package/dist/utils/vue-rules.d.ts +72 -0
- package/dist/utils/vue-rules.js +108 -0
- package/dist/utils/vue-rules.js.map +1 -0
- package/package.json +6 -4
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\nimport { readFileSync, realpathSync } from \"node:fs\";\nimport { pathToFileURL } from \"node:url\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { closeBrowser } from \"./engine/browser.js\";\nimport { registerAllTools } from \"./tools/index.js\";\nimport { shutdownDevServer } from \"./utils/server-process.js\";\n\nexport const SERVER_NAME = \"framewatch\";\nexport const SERVER_VERSION: string = readPackageVersion();\n\n/**\n * Build a fully configured FrameWatch McpServer (not yet connected to a\n * transport). Exported so tests and embedders can wire their own transport;\n * importing this module has no side effects — the stdio server only starts\n * when this file is executed directly (`node dist/index.js` / the npm bin).\n */\nexport function createServer(): McpServer {\n const server = new McpServer(\n { name: SERVER_NAME, version: SERVER_VERSION },\n {\n instructions:\n \"FrameWatch gives you visual eyes on running web apps. Use framewatch_screenshot to see the \" +\n \"current state of a page. Pass a `selector` to zoom in on one element, or `wait_for` to wait \" +\n \"for an element before capturing. Use framewatch_capture to record a page for a few seconds \" +\n \"and get back only the frames where something meaningful changed (animations, splash screens, \" +\n \"transitions, anything that changes over time), each with a crop of the changed region and its \" +\n \"position. Lower `sensitivity` to keep more frames, raise `max_frames` for longer sequences. \" +\n \"Pass `interactions` to replay a click/type/scroll/tap/swipe script while it records — that is how you \" +\n \"test a flow such as a login. Use framewatch_interact for one action at a time: it keeps the page open \" +\n \"between calls, so you can click, look at the result, type, and look again without replaying the flow. \" +\n \"Both capture and interact can attach the context behind a frame — console output and uncaught errors \" +\n \"(on by default), plus network requests, DOM mutations and paint timing via `include_network`, \" +\n \"`include_dom` and `include_performance` — which is how you find out why a frame looks wrong. \" +\n \"Use framewatch_responsive to see one page at mobile, tablet and desktop widths at once (it also \" +\n \"reports content that overflows its viewport), framewatch_accessibility to run an axe-core WCAG audit, \" +\n \"and framewatch_compare to diff two URLs — or the page interact has open against a URL — with an \" +\n \"overlay of every pixel that differs. If the app is not running yet, framewatch_start_server runs its \" +\n \"dev server (e.g. `npm run dev`) and waits for the port; framewatch_stop_server stops it again.\",\n },\n );\n registerAllTools(server);\n return server;\n}\n\n/** Run the server on stdio until the client disconnects or the process is signalled. */\nexport async function main(): Promise<void> {\n const server = createServer();\n const transport = new StdioServerTransport();\n\n // stderr may be closed by the host (e.g. parent died); never let a log write\n // turn into an uncaught 'error' event that re-enters the handlers below.\n process.stderr.on(\"error\", () => {});\n\n let shuttingDown = false;\n const shutdown = async (reason: string, exitCode = 0): Promise<void> => {\n if (shuttingDown) return;\n shuttingDown = true;\n log(`shutting down (${reason})`);\n try {\n // The dev server is a child process of this one; leaving it behind would\n // hold its port and outlive the client that asked for it.\n await shutdownDevServer();\n await closeBrowser();\n } finally {\n await server.close().catch(() => {});\n process.exit(exitCode);\n }\n };\n\n // Client went away. The SDK transport only fires onclose on protocol errors,\n // so watch stdin EOF directly — once Chromium is running it would otherwise\n // keep the event loop (and a headless browser) alive forever.\n process.stdin.on(\"end\", () => void shutdown(\"stdin closed\"));\n process.stdin.on(\"close\", () => void shutdown(\"stdin closed\"));\n transport.onclose = () => void shutdown(\"transport closed\");\n\n process.on(\"SIGINT\", () => void shutdown(\"SIGINT\"));\n process.on(\"SIGTERM\", () => void shutdown(\"SIGTERM\"));\n process.on(\"SIGHUP\", () => void shutdown(\"SIGHUP\"));\n process.on(\"uncaughtException\", (err) => {\n log(`uncaught exception: ${formatError(err)}`);\n void shutdown(\"uncaught exception\", 1);\n });\n process.on(\"unhandledRejection\", (err) => {\n log(`unhandled rejection: ${formatError(err)}`);\n });\n\n await server.connect(transport);\n log(`${SERVER_NAME} v${SERVER_VERSION} ready on stdio`);\n}\n\n/** stdout is the MCP channel — all diagnostics go to stderr, and never throw. */\nfunction log(message: string): void {\n try {\n process.stderr.write(`[framewatch] ${message}\\n`);\n } catch {\n // stderr is gone; nothing sensible to do.\n }\n}\n\nfunction formatError(err: unknown): string {\n return err instanceof Error ? (err.stack ?? err.message) : String(err);\n}\n\nfunction readPackageVersion(): string {\n try {\n const pkg = JSON.parse(readFileSync(new URL(\"../package.json\", import.meta.url), \"utf8\")) as { version?: string };\n return pkg.version ?? \"0.0.0\";\n } catch {\n return \"0.0.0\";\n }\n}\n\n/**\n * True when this file is the process entry point. npm's bin shim is a symlink,\n * so compare real paths: import.meta.url is already resolved, argv[1] is not.\n */\nfunction isRunDirectly(): boolean {\n const entry = process.argv[1];\n if (!entry) return false;\n try {\n return import.meta.url === pathToFileURL(realpathSync(entry)).href;\n } catch {\n return false;\n }\n}\n\nif (isRunDirectly()) {\n main().catch((err) => {\n log(`fatal: ${formatError(err)}`);\n process.exit(1);\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAE9D,MAAM,CAAC,MAAM,WAAW,GAAG,YAAY,CAAC;AACxC,MAAM,CAAC,MAAM,cAAc,GAAW,kBAAkB,EAAE,CAAC;AAE3D;;;;;GAKG;AACH,MAAM,UAAU,YAAY;IAC1B,MAAM,MAAM,GAAG,IAAI,SAAS,CAC1B,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,cAAc,EAAE,EAC9C;QACE,YAAY,EACV,6FAA6F;YAC7F,8FAA8F;YAC9F,6FAA6F;YAC7F,+FAA+F;YAC/F,gGAAgG;YAChG,8FAA8F;YAC9F,wGAAwG;YACxG,wGAAwG;YACxG,wGAAwG;YACxG,6GAA6G;YAC7G,2GAA2G;YAC3G,wGAAwG;YACxG,8GAA8G;YAC9G,4GAA4G;YAC5G,uGAAuG;YACvG,mGAAmG;YACnG,yGAAyG;YACzG,yGAAyG;YACzG,4GAA4G;YAC5G,sGAAsG;YACtG,2GAA2G;YAC3G,4GAA4G;YAC5G,qFAAqF;YACrF,oFAAoF;YACpF,uGAAuG;YACvG,gGAAgG;YAChG,+FAA+F;YAC/F,kGAAkG;YAClG,wGAAwG;YACxG,kGAAkG;YAClG,wGAAwG;YACxG,uGAAuG;YACvG,wGAAwG;YACxG,8GAA8G;YAC9G,wGAAwG;YACxG,6GAA6G;YAC7G,8GAA8G;YAC9G,uGAAuG;YACvG,2GAA2G;YAC3G,yGAAyG;YACzG,wGAAwG;YACxG,qFAAqF;YACrF,wGAAwG;YACxG,yGAAyG;YACzG,sGAAsG;YACtG,qGAAqG;YACrG,gGAAgG;YAChG,uGAAuG;YACvG,uGAAuG;YACvG,uGAAuG;YACvG,oGAAoG;YACpG,mGAAmG;YACnG,iEAAiE;YACjE,wGAAwG;YACxG,2GAA2G;YAC3G,4CAA4C;YAC5C,oDAAoD;YACpD,wGAAwG;YACxG,6EAA6E;YAC7E,kEAAkE;YAClE,gGAAgG;KACnG,CACF,CAAC;IACF,gBAAgB,CAAC,MAAM,CAAC,CAAC;IACzB,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,wFAAwF;AACxF,MAAM,CAAC,KAAK,UAAU,IAAI;IACxB,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;IAC9B,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAE7C,6EAA6E;IAC7E,yEAAyE;IACzE,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAErC,IAAI,YAAY,GAAG,KAAK,CAAC;IACzB,MAAM,QAAQ,GAAG,KAAK,EAAE,MAAc,EAAE,QAAQ,GAAG,CAAC,EAAiB,EAAE;QACrE,IAAI,YAAY;YAAE,OAAO;QACzB,YAAY,GAAG,IAAI,CAAC;QACpB,GAAG,CAAC,kBAAkB,MAAM,GAAG,CAAC,CAAC;QACjC,IAAI,CAAC;YACH,yEAAyE;YACzE,0DAA0D;YAC1D,MAAM,iBAAiB,EAAE,CAAC;YAC1B,MAAM,YAAY,EAAE,CAAC;QACvB,CAAC;gBAAS,CAAC;YACT,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YACrC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACzB,CAAC;IACH,CAAC,CAAC;IAEF,6EAA6E;IAC7E,4EAA4E;IAC5E,8DAA8D;IAC9D,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,KAAK,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC;IAC7D,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC;IAC/D,SAAS,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC,KAAK,QAAQ,CAAC,kBAAkB,CAAC,CAAC;IAE5D,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,KAAK,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;IACpD,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,KAAK,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;IACtD,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,KAAK,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;IACpD,OAAO,CAAC,EAAE,CAAC,mBAAmB,EAAE,CAAC,GAAG,EAAE,EAAE;QACtC,GAAG,CAAC,uBAAuB,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC/C,KAAK,QAAQ,CAAC,oBAAoB,EAAE,CAAC,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IACH,OAAO,CAAC,EAAE,CAAC,oBAAoB,EAAE,CAAC,GAAG,EAAE,EAAE;QACvC,GAAG,CAAC,wBAAwB,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,GAAG,CAAC,GAAG,WAAW,KAAK,cAAc,iBAAiB,CAAC,CAAC;AAC1D,CAAC;AAED,iFAAiF;AACjF,SAAS,GAAG,CAAC,OAAe;IAC1B,IAAI,CAAC;QACH,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,OAAO,IAAI,CAAC,CAAC;IACpD,CAAC;IAAC,MAAM,CAAC;QACP,0CAA0C;IAC5C,CAAC;AACH,CAAC;AAED,SAAS,WAAW,CAAC,GAAY;IAC/B,OAAO,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;AACzE,CAAC;AAED,SAAS,kBAAkB;IACzB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAyB,CAAC;QAClH,OAAO,GAAG,CAAC,OAAO,IAAI,OAAO,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,OAAO,CAAC;IACjB,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAS,aAAa;IACpB,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC9B,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IACzB,IAAI,CAAC;QACH,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,aAAa,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IACrE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,IAAI,aAAa,EAAE,EAAE,CAAC;IACpB,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;QACnB,GAAG,CAAC,UAAU,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAClC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["#!/usr/bin/env node\nimport { readFileSync, realpathSync } from \"node:fs\";\nimport { pathToFileURL } from \"node:url\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { closeBrowser } from \"./engine/browser.js\";\nimport { registerAllTools } from \"./tools/index.js\";\nimport { shutdownDevServer } from \"./utils/server-process.js\";\n\nexport const SERVER_NAME = \"framewatch\";\nexport const SERVER_VERSION: string = readPackageVersion();\n\n/**\n * Build a fully configured FrameWatch McpServer (not yet connected to a\n * transport). Exported so tests and embedders can wire their own transport;\n * importing this module has no side effects — the stdio server only starts\n * when this file is executed directly (`node dist/index.js` / the npm bin).\n */\nexport function createServer(): McpServer {\n const server = new McpServer(\n { name: SERVER_NAME, version: SERVER_VERSION },\n {\n instructions:\n \"FrameWatch gives you visual eyes on running web apps. Use framewatch_screenshot to see the \" +\n \"current state of a page. Pass a `selector` to zoom in on one element, or `wait_for` to wait \" +\n \"for an element before capturing. Use framewatch_capture to record a page for a few seconds \" +\n \"and get back only the frames where something meaningful changed (animations, splash screens, \" +\n \"transitions, anything that changes over time), each with a crop of the changed region and its \" +\n \"position. Lower `sensitivity` to keep more frames, raise `max_frames` for longer sequences. \" +\n \"Pass `interactions` to replay a click/type/scroll/tap/swipe script while it records — that is how you \" +\n \"test a flow such as a login. Use framewatch_interact for one action at a time: it keeps the page open \" +\n \"between calls, so you can click, look at the result, type, and look again without replaying the flow. \" +\n \"Before acting, use framewatch_snapshot to read that page as a tree of named elements — every field, button \" +\n \"and link with its accessible name and a ref like `e8` — and pass the ref as `ref` to framewatch_interact \" +\n \"instead of guessing a selector from a screenshot (mode `interactive` lists only what can be acted on; \" +\n \"`include_snapshot` on interact returns fresh refs after each action). When you are building or checking UI, \" +\n \"use framewatch_inspect on refs or selectors to measure how elements are really built: box in viewport px, \" +\n \"font, text and background colour with the WCAG contrast ratio, padding, margin, gap, border, radius, \" +\n \"clipping and overflow, and alignment to parent and previous sibling, with each target boxed on a \" +\n \"screenshot; call it with no targets for a design inventory of the page (fonts, sizes, colours, spacing \" +\n \"and radii in use, with counts) to spot the odd one out. On a Vue app, inspect also names the component \" +\n \"behind each element with its props and state, snapshot's `include_components` returns the component tree, \" +\n \"and every session tool reads the page the moment the app is mounted and its router ready instead of \" +\n \"sleeping. After you save a file, use framewatch_wait_for (`until: hot_update`) to return the moment Vite \" +\n \"has patched the open page — no reload, no replayed flow — and `navigate` goes through vue-router when the \" +\n \"app has one, keeping its state. Every tool that returns frames states the viewport \" +\n \"and image scale: coordinates and regions are always in viewport px, not image px. \" +\n \"Both capture and interact can attach the context behind a frame — console output and uncaught errors \" +\n \"(on by default), plus network requests, DOM mutations and paint timing via `include_network`, \" +\n \"`include_dom` and `include_performance` — which is how you find out why a frame looks wrong. \" +\n \"Use framewatch_responsive to see one page at mobile, tablet and desktop widths at once (it also \" +\n \"reports content that overflows its viewport), framewatch_accessibility to run an axe-core WCAG audit, \" +\n \"and framewatch_compare to diff two URLs — or the page interact has open against a URL — with an \" +\n \"overlay of every pixel that differs. Use framewatch_form_test on anything with fields in it: it fills \" +\n \"every form with valid data, with nothing, with special characters, with Arabic and with harmless XSS \" +\n \"payloads, one fresh page per strategy, and reports what each one produced — the validation shown, the \" +\n \"requests made, the console output, the values silently truncated. Use framewatch_seo to audit what a search \" +\n \"engine and a link preview make of a page: title and description with their lengths, canonical, robots \" +\n \"directives and robots.txt, Open Graph tags with the share image fetched and measured, the heading outline, \" +\n \"images with no alt text, and JSON-LD structured data — all read from the rendered DOM, so a client-rendered \" +\n \"app is measured properly. Use framewatch_dead_clicks to find the controls that look clickable and do \" +\n \"nothing: it clicks every link, button, click role and pointer-cursor element on the page and reports the \" +\n \"ones where absolutely nothing happened — no navigation, no DOM change, no request, no console output — \" +\n \"plus the ones whose handler threw, with a screenshot of them boxed in red. It really does press every \" +\n \"button, so pass `exclude` or `selector` to keep it away from anything destructive. \" +\n \"Use framewatch_links to check where every link on a page actually goes: 404s, server errors, redirect \" +\n \"chains, redirects that end on an error page, and `#fragment` links that point at no element. It reuses \" +\n \"the browser's own result for the images, scripts and stylesheets the page already loaded, retries a \" +\n \"refused HEAD as a GET before calling anything broken, and reports a 401/403/429 as a refused check \" +\n \"rather than a dead link. Raise `depth` to follow the site's internal links a level at a time. \" +\n \"Use framewatch_api_mock to answer the page's own API calls yourself and record what it does with the \" +\n \"answer: the empty list, the 500, the 401, the response that takes five seconds, the body that is not \" +\n \"valid JSON, the request that fails outright — one word each via `scenario`, or set the status, body, \" +\n \"headers and delay by hand. It returns the same diff cards as framewatch_capture, plus a report of \" +\n \"what each mock actually served, including any that matched nothing — which is how you find out a \" +\n \"pattern was wrong instead of trusting a page that looked fine. \" +\n \"Use framewatch_rtl to test a page in LTR and RTL and get back only what failed to mirror — a box that \" +\n \"did not move, text that stayed left-aligned, padding that did not swap, an icon that did not flip — with \" +\n \"both screenshots and every finding boxed. \" +\n \"For an app behind a login, run the flow once with \" +\n \"framewatch_save_auth and pass the file it writes as `storage_state` to any of these tools — they then \" +\n \"open the page already signed in instead of replaying the login every time. \" +\n \"If the app is not running yet, framewatch_start_server runs its \" +\n \"dev server (e.g. `npm run dev`) and waits for the port; framewatch_stop_server stops it again.\",\n },\n );\n registerAllTools(server);\n return server;\n}\n\n/** Run the server on stdio until the client disconnects or the process is signalled. */\nexport async function main(): Promise<void> {\n const server = createServer();\n const transport = new StdioServerTransport();\n\n // stderr may be closed by the host (e.g. parent died); never let a log write\n // turn into an uncaught 'error' event that re-enters the handlers below.\n process.stderr.on(\"error\", () => {});\n\n let shuttingDown = false;\n const shutdown = async (reason: string, exitCode = 0): Promise<void> => {\n if (shuttingDown) return;\n shuttingDown = true;\n log(`shutting down (${reason})`);\n try {\n // The dev server is a child process of this one; leaving it behind would\n // hold its port and outlive the client that asked for it.\n await shutdownDevServer();\n await closeBrowser();\n } finally {\n await server.close().catch(() => {});\n process.exit(exitCode);\n }\n };\n\n // Client went away. The SDK transport only fires onclose on protocol errors,\n // so watch stdin EOF directly — once Chromium is running it would otherwise\n // keep the event loop (and a headless browser) alive forever.\n process.stdin.on(\"end\", () => void shutdown(\"stdin closed\"));\n process.stdin.on(\"close\", () => void shutdown(\"stdin closed\"));\n transport.onclose = () => void shutdown(\"transport closed\");\n\n process.on(\"SIGINT\", () => void shutdown(\"SIGINT\"));\n process.on(\"SIGTERM\", () => void shutdown(\"SIGTERM\"));\n process.on(\"SIGHUP\", () => void shutdown(\"SIGHUP\"));\n process.on(\"uncaughtException\", (err) => {\n log(`uncaught exception: ${formatError(err)}`);\n void shutdown(\"uncaught exception\", 1);\n });\n process.on(\"unhandledRejection\", (err) => {\n log(`unhandled rejection: ${formatError(err)}`);\n });\n\n await server.connect(transport);\n log(`${SERVER_NAME} v${SERVER_VERSION} ready on stdio`);\n}\n\n/** stdout is the MCP channel — all diagnostics go to stderr, and never throw. */\nfunction log(message: string): void {\n try {\n process.stderr.write(`[framewatch] ${message}\\n`);\n } catch {\n // stderr is gone; nothing sensible to do.\n }\n}\n\nfunction formatError(err: unknown): string {\n return err instanceof Error ? (err.stack ?? err.message) : String(err);\n}\n\nfunction readPackageVersion(): string {\n try {\n const pkg = JSON.parse(readFileSync(new URL(\"../package.json\", import.meta.url), \"utf8\")) as { version?: string };\n return pkg.version ?? \"0.0.0\";\n } catch {\n return \"0.0.0\";\n }\n}\n\n/**\n * True when this file is the process entry point. npm's bin shim is a symlink,\n * so compare real paths: import.meta.url is already resolved, argv[1] is not.\n */\nfunction isRunDirectly(): boolean {\n const entry = process.argv[1];\n if (!entry) return false;\n try {\n return import.meta.url === pathToFileURL(realpathSync(entry)).href;\n } catch {\n return false;\n }\n}\n\nif (isRunDirectly()) {\n main().catch((err) => {\n log(`fatal: ${formatError(err)}`);\n process.exit(1);\n });\n}\n"]}
|
|
@@ -27,6 +27,7 @@ export declare const accessibilityInputShape: {
|
|
|
27
27
|
width?: number | undefined;
|
|
28
28
|
height?: number | undefined;
|
|
29
29
|
}>>;
|
|
30
|
+
storage_state: z.ZodOptional<z.ZodString>;
|
|
30
31
|
};
|
|
31
32
|
export declare const accessibilityInputSchema: z.ZodObject<{
|
|
32
33
|
url: z.ZodString;
|
|
@@ -46,6 +47,7 @@ export declare const accessibilityInputSchema: z.ZodObject<{
|
|
|
46
47
|
width?: number | undefined;
|
|
47
48
|
height?: number | undefined;
|
|
48
49
|
}>>;
|
|
50
|
+
storage_state: z.ZodOptional<z.ZodString>;
|
|
49
51
|
}, "strip", z.ZodTypeAny, {
|
|
50
52
|
url: string;
|
|
51
53
|
standard: "wcag2a" | "wcag2aa" | "wcag21aa";
|
|
@@ -58,6 +60,7 @@ export declare const accessibilityInputSchema: z.ZodObject<{
|
|
|
58
60
|
height: number;
|
|
59
61
|
} | undefined;
|
|
60
62
|
wait_for?: string | undefined;
|
|
63
|
+
storage_state?: string | undefined;
|
|
61
64
|
}, {
|
|
62
65
|
url: string;
|
|
63
66
|
viewport?: {
|
|
@@ -70,6 +73,7 @@ export declare const accessibilityInputSchema: z.ZodObject<{
|
|
|
70
73
|
wait_for_timeout_ms?: number | undefined;
|
|
71
74
|
max_violations?: number | undefined;
|
|
72
75
|
max_elements?: number | undefined;
|
|
76
|
+
storage_state?: string | undefined;
|
|
73
77
|
}>;
|
|
74
78
|
export type AccessibilityInput = z.input<typeof accessibilityInputSchema>;
|
|
75
79
|
/** The trimmed shape the in-page script sends back — never raw axe results. */
|
|
@@ -3,6 +3,7 @@ import { readFileSync } from "node:fs";
|
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { A11Y_FRAME_WAIT_MS, A11Y_RUN_TIMEOUT_MS, DEFAULT_A11Y_WAIT_MS, DEFAULT_VIEWPORT, MAX_A11Y_HTML_LENGTH, MAX_A11Y_NODES_PER_VIOLATION, MAX_A11Y_VIOLATIONS, MAX_VIEWPORT_HEIGHT, MAX_VIEWPORT_WIDTH, NAVIGATION_TIMEOUT_MS, SELECTOR_TIMEOUT_MS, } from "../constants.js";
|
|
5
5
|
import { withPage } from "../engine/browser.js";
|
|
6
|
+
import { resolveStorageState, storageStateField, withAuthNote } from "../utils/storage-state.js";
|
|
6
7
|
export const ACCESSIBILITY_TOOL_NAME = "framewatch_accessibility";
|
|
7
8
|
/** WCAG conformance levels, and the axe-core tags each one runs. */
|
|
8
9
|
const STANDARDS = {
|
|
@@ -52,6 +53,7 @@ export const accessibilityInputShape = {
|
|
|
52
53
|
})
|
|
53
54
|
.optional()
|
|
54
55
|
.describe("Viewport size (defaults to 1280x720). Some rules — reflow, target size — depend on it."),
|
|
56
|
+
storage_state: storageStateField,
|
|
55
57
|
};
|
|
56
58
|
export const accessibilityInputSchema = z.object(accessibilityInputShape);
|
|
57
59
|
/**
|
|
@@ -88,7 +90,12 @@ export async function auditAccessibility(rawInput) {
|
|
|
88
90
|
// inline script and eval. The page's own CSP has no bearing on what axe
|
|
89
91
|
// measures, and without this a strictly configured app could not be
|
|
90
92
|
// audited at all.
|
|
91
|
-
const
|
|
93
|
+
const auth = await resolveStorageState(input.storage_state);
|
|
94
|
+
const contextOptions = {
|
|
95
|
+
bypassCSP: true,
|
|
96
|
+
...(auth ? { storageState: auth.state } : {}),
|
|
97
|
+
};
|
|
98
|
+
const report = await withPage({ viewport, contextOptions }, async (page) => {
|
|
92
99
|
await page.goto(input.url, { waitUntil: "load", timeout: NAVIGATION_TIMEOUT_MS });
|
|
93
100
|
if (input.wait_for) {
|
|
94
101
|
await page.waitForSelector(input.wait_for, { state: "visible", timeout: input.wait_for_timeout_ms });
|
|
@@ -108,7 +115,7 @@ export async function auditAccessibility(rawInput) {
|
|
|
108
115
|
frame_wait_ms: A11Y_FRAME_WAIT_MS,
|
|
109
116
|
}), A11Y_RUN_TIMEOUT_MS, `axe-core did not finish within ${A11Y_RUN_TIMEOUT_MS}ms`);
|
|
110
117
|
});
|
|
111
|
-
return { content: [{ type: "text", text: formatReport(report, input.standard) }] };
|
|
118
|
+
return withAuthNote({ content: [{ type: "text", text: formatReport(report, input.standard) }] }, auth);
|
|
112
119
|
}
|
|
113
120
|
catch (error) {
|
|
114
121
|
return errorResult(describeAuditFailure(input, error));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"accessibility.js","sourceRoot":"","sources":["../../src/tools/accessibility.ts"],"names":[],"mappings":"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","sourcesContent":["import { createRequire } from \"node:module\";\nimport { readFileSync } from \"node:fs\";\nimport { z } from \"zod\";\nimport type { Frame, Page } from \"playwright\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { CallToolResult } from \"@modelcontextprotocol/sdk/types.js\";\nimport {\n A11Y_FRAME_WAIT_MS,\n A11Y_RUN_TIMEOUT_MS,\n DEFAULT_A11Y_WAIT_MS,\n DEFAULT_VIEWPORT,\n MAX_A11Y_HTML_LENGTH,\n MAX_A11Y_NODES_PER_VIOLATION,\n MAX_A11Y_VIOLATIONS,\n MAX_VIEWPORT_HEIGHT,\n MAX_VIEWPORT_WIDTH,\n NAVIGATION_TIMEOUT_MS,\n SELECTOR_TIMEOUT_MS,\n} from \"../constants.js\";\nimport { withPage } from \"../engine/browser.js\";\n\nexport const ACCESSIBILITY_TOOL_NAME = \"framewatch_accessibility\";\n\n/** WCAG conformance levels, and the axe-core tags each one runs. */\nconst STANDARDS = {\n wcag2a: [\"wcag2a\"],\n wcag2aa: [\"wcag2a\", \"wcag2aa\"],\n wcag21aa: [\"wcag2a\", \"wcag2aa\", \"wcag21a\", \"wcag21aa\"],\n} as const;\n\nexport type A11yStandard = keyof typeof STANDARDS;\n\n/** Worst first — this is the order violations are reported in. */\nconst IMPACT_ORDER = [\"critical\", \"serious\", \"moderate\", \"minor\"] as const;\n\nexport const accessibilityInputShape = {\n url: z.string().url().describe(\"URL to audit, e.g. http://localhost:3000 (http, https and file URLs are accepted)\"),\n standard: z\n .enum([\"wcag2a\", \"wcag2aa\", \"wcag21aa\"])\n .default(\"wcag2aa\")\n .describe(\"Conformance level to test against. wcag2aa is the usual legal baseline; wcag21aa adds WCAG 2.1.\"),\n wait_ms: z\n .number()\n .int()\n .min(0)\n .default(DEFAULT_A11Y_WAIT_MS)\n .describe(\"Wait time (ms) after page load before auditing, so the app can finish rendering\"),\n wait_for: z.string().optional().describe(\"CSS selector to wait for (visible) before auditing\"),\n wait_for_timeout_ms: z\n .number()\n .int()\n .min(1)\n .default(SELECTOR_TIMEOUT_MS)\n .describe(\"Max time (ms) to wait for `wait_for` to appear (must be > 0)\"),\n max_violations: z\n .number()\n .int()\n .min(1)\n .max(MAX_A11Y_VIOLATIONS)\n .default(MAX_A11Y_VIOLATIONS)\n .describe(\"Maximum violation types to report (they are reported worst-impact first)\"),\n max_elements: z\n .number()\n .int()\n .min(1)\n .max(20)\n .default(MAX_A11Y_NODES_PER_VIOLATION)\n .describe(\"Maximum offending elements listed under each violation\"),\n viewport: z\n .object({\n width: z.number().int().min(1).max(MAX_VIEWPORT_WIDTH).default(DEFAULT_VIEWPORT.width),\n height: z.number().int().min(1).max(MAX_VIEWPORT_HEIGHT).default(DEFAULT_VIEWPORT.height),\n })\n .optional()\n .describe(\"Viewport size (defaults to 1280x720). Some rules — reflow, target size — depend on it.\"),\n};\n\nexport const accessibilityInputSchema = z.object(accessibilityInputShape);\nexport type AccessibilityInput = z.input<typeof accessibilityInputSchema>;\n\n/** The trimmed shape the in-page script sends back — never raw axe results. */\ninterface AxeNode {\n target: string;\n html: string;\n summary: string;\n}\n\ninterface AxeViolation {\n id: string;\n impact: string;\n help: string;\n description: string;\n help_url: string;\n tags: string[];\n node_count: number;\n nodes: AxeNode[];\n}\n\ninterface AxeReport {\n violations: AxeViolation[];\n /** Rules that passed, that found nothing to test, and that need a human. */\n passes: number;\n inapplicable: number;\n incomplete: number;\n /** Total violation types found, before `max_violations` trimmed the list. */\n total_violations: number;\n /** Offending elements across every violation, including those not listed. */\n total_nodes: number;\n url: string;\n axe_version: string;\n}\n\n/**\n * Run an axe-core accessibility audit and report what it found.\n *\n * axe is injected into the page rather than reimplemented: it is the engine\n * behind most commercial accessibility tooling, so a violation reported here\n * is one an auditor would also raise. It is injected as a script through\n * `evaluate`, into a context created with CSP bypassed, so a page with a\n * strict Content-Security-Policy can still be audited.\n *\n * Only violations come back. Passing rules are counted, not listed — a list of\n * two hundred rules that did not fire is noise, but the count is what tells\n * you the audit actually ran.\n */\nexport async function auditAccessibility(rawInput: AccessibilityInput): Promise<CallToolResult> {\n const parsed = accessibilityInputSchema.safeParse(rawInput);\n if (!parsed.success) {\n const issues = parsed.error.issues.map((i) => `${i.path.join(\".\") || \"input\"}: ${i.message}`).join(\"; \");\n return errorResult(`Accessibility audit failed: invalid input — ${issues}`);\n }\n const input = parsed.data;\n const viewport = input.viewport ?? { ...DEFAULT_VIEWPORT };\n\n let source: string;\n try {\n source = axeSource();\n } catch (error) {\n return errorResult(\n \"Accessibility audit failed: the axe-core library could not be loaded from this installation \" +\n `(${error instanceof Error ? error.message : String(error)}). Reinstall dependencies and try again.`,\n );\n }\n\n try {\n // `bypassCSP` so the audit can be injected into an app that forbids\n // inline script and eval. The page's own CSP has no bearing on what axe\n // measures, and without this a strictly configured app could not be\n // audited at all.\n const report = await withPage({ viewport, contextOptions: { bypassCSP: true } }, async (page) => {\n await page.goto(input.url, { waitUntil: \"load\", timeout: NAVIGATION_TIMEOUT_MS });\n if (input.wait_for) {\n await page.waitForSelector(input.wait_for, { state: \"visible\", timeout: input.wait_for_timeout_ms });\n }\n if (input.wait_ms > 0) {\n await page.waitForTimeout(input.wait_ms);\n }\n await injectAxe(page, source);\n // axe walks the whole DOM; on a big enough app that takes a while, and\n // `page.evaluate` has no timeout of its own — a page that wedges\n // mid-audit would otherwise hang the tool call forever.\n return withTimeout(\n runAxe(page, {\n tags: [...STANDARDS[input.standard]],\n max_violations: input.max_violations,\n max_elements: input.max_elements,\n max_html: MAX_A11Y_HTML_LENGTH,\n frame_wait_ms: A11Y_FRAME_WAIT_MS,\n }),\n A11Y_RUN_TIMEOUT_MS,\n `axe-core did not finish within ${A11Y_RUN_TIMEOUT_MS}ms`,\n );\n });\n\n return { content: [{ type: \"text\", text: formatReport(report, input.standard) }] };\n } catch (error) {\n return errorResult(describeAuditFailure(input, error));\n }\n}\n\n/** Options handed to the in-page runner. Must stay JSON-serialisable. */\ninterface RunOptions {\n tags: string[];\n max_violations: number;\n max_elements: number;\n max_html: number;\n frame_wait_ms: number;\n}\n\n/**\n * Put axe in the main frame and in every child frame.\n *\n * axe audits iframes by talking to a copy of itself inside each one, so a page\n * that embeds anything (a preview pane, an embedded checkout) needs it\n * everywhere. Child frames are best-effort: one that refuses injection — it\n * may be mid-navigation, or gone by the time we reach it — must not stop the\n * audit of the page around it.\n */\nasync function injectAxe(page: Page, source: string): Promise<void> {\n await evaluateSource(page.mainFrame(), source);\n for (const frame of page.frames()) {\n if (frame === page.mainFrame()) continue;\n await evaluateSource(frame, source).catch(() => {});\n }\n}\n\n/**\n * Evaluate the axe bundle as a script in `frame`.\n *\n * The source goes in as a *string*, which Playwright evaluates as a script in\n * the frame — no script tag to be blocked, and the audit context is created\n * with CSP bypassed so injection works on strictly configured apps too.\n *\n * `allowedOrigins` is widened because axe defaults to same-origin frame\n * messaging only, and a cross-origin iframe would otherwise never answer.\n */\nasync function evaluateSource(frame: Frame, source: string): Promise<void> {\n await frame.evaluate(source as unknown as () => void);\n await frame.evaluate(() => {\n const axe = (globalThis as any).axe;\n if (axe?.configure) axe.configure({ allowedOrigins: [\"<unsafe_all_origins>\"] });\n });\n}\n\n/**\n * Run axe in the page and bring back only what gets printed.\n *\n * The trimming happens in the page on purpose: a full axe result carries every\n * passing rule with every element it checked, which on a real app is megabytes\n * of JSON to serialise across the protocol and then throw away.\n */\nasync function runAxe(page: Page, options: RunOptions): Promise<AxeReport> {\n return page.evaluate(async (opts: RunOptions): Promise<AxeReport> => {\n const g = globalThis as any;\n const axe = g.axe;\n if (!axe?.run) throw new Error(\"axe-core did not load in the page\");\n\n const results = await axe.run(g.document, {\n runOnly: { type: \"tag\", values: opts.tags },\n // Only violations are reported, so this is the only result type whose\n // element details need collecting.\n resultTypes: [\"violations\"],\n frameWaitTime: opts.frame_wait_ms,\n });\n\n const order = [\"critical\", \"serious\", \"moderate\", \"minor\"];\n const rank = (impact: string): number => {\n const at = order.indexOf(impact);\n return at === -1 ? order.length : at;\n };\n\n const all = (results.violations ?? []).slice().sort((a: any, b: any) => {\n const byImpact = rank(String(a.impact)) - rank(String(b.impact));\n if (byImpact !== 0) return byImpact;\n // Then by blast radius: the rule breaking twenty elements first.\n return (b.nodes?.length ?? 0) - (a.nodes?.length ?? 0);\n });\n\n const elide = (text: string): string => {\n const flat = String(text ?? \"\")\n .replace(/\\s+/g, \" \")\n .trim();\n return flat.length > opts.max_html ? `${flat.slice(0, opts.max_html)}…` : flat;\n };\n\n return {\n violations: all.slice(0, opts.max_violations).map((violation: any) => ({\n id: String(violation.id ?? \"unknown\"),\n impact: String(violation.impact ?? \"unknown\"),\n help: String(violation.help ?? \"\"),\n description: String(violation.description ?? \"\"),\n help_url: String(violation.helpUrl ?? \"\"),\n tags: (violation.tags ?? []).map((tag: unknown) => String(tag)),\n node_count: violation.nodes?.length ?? 0,\n nodes: (violation.nodes ?? []).slice(0, opts.max_elements).map((node: any) => ({\n target: (node.target ?? []).map((part: unknown) => String(part)).join(\" \"),\n html: elide(node.html),\n // The first line is the rule that failed for this element; the rest\n // repeats the violation's own help text.\n summary: elide(String(node.failureSummary ?? \"\").split(\"\\n\").slice(1).join(\" \")),\n })),\n })),\n passes: results.passes?.length ?? 0,\n inapplicable: results.inapplicable?.length ?? 0,\n incomplete: results.incomplete?.length ?? 0,\n total_violations: all.length,\n total_nodes: all.reduce((sum: number, violation: any) => sum + (violation.nodes?.length ?? 0), 0),\n url: String(results.url ?? \"\"),\n axe_version: String(results.testEngine?.version ?? \"\"),\n };\n }, options);\n}\n\n/**\n * Render the report as text.\n *\n * Each violation gets its impact, the rule that failed, what to do about it,\n * the elements at fault and the URL of Deque's write-up — everything needed to\n * fix it without a second lookup.\n */\nexport function formatReport(report: AxeReport, standard: A11yStandard): string {\n const scope = `${standard.toUpperCase()} (axe-core ${report.axe_version || \"?\"})`;\n const lines: string[] = [];\n\n if (report.total_violations === 0) {\n lines.push(`No ${scope} violations on ${report.url}.`);\n lines.push(auditFooter(report));\n return lines.join(\"\\n\");\n }\n\n lines.push(\n `${report.total_violations} ${scope} violation ${report.total_violations === 1 ? \"type\" : \"types\"} on ${report.url}, ` +\n `affecting ${report.total_nodes} ${report.total_nodes === 1 ? \"element\" : \"elements\"} — ${countByImpact(report)}.`,\n );\n lines.push(auditFooter(report));\n lines.push(\"\");\n\n report.violations.forEach((violation, index) => {\n lines.push(\n `${index + 1}. [${violation.impact}] ${violation.help} (${violation.id}) — ` +\n `${violation.node_count} ${violation.node_count === 1 ? \"element\" : \"elements\"}`,\n );\n lines.push(` ${violation.description}`);\n for (const node of violation.nodes) {\n lines.push(` • ${node.target || \"?\"}`);\n if (node.html) lines.push(` ${node.html}`);\n if (node.summary) lines.push(` ${node.summary}`);\n }\n if (violation.node_count > violation.nodes.length) {\n lines.push(` … and ${violation.node_count - violation.nodes.length} more elements`);\n }\n if (violation.help_url) lines.push(` ${violation.help_url}`);\n });\n\n if (report.total_violations > report.violations.length) {\n lines.push(\"\");\n lines.push(\n `… and ${report.total_violations - report.violations.length} more violation types not shown ` +\n \"(raise `max_violations` to see them).\",\n );\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * The counts that say how much of the page was actually judged. `incomplete`\n * is the important one: those are the checks axe could not decide on its own\n * (typically colour contrast over an image) and they are the rules a human\n * still has to look at.\n */\nfunction auditFooter(report: AxeReport): string {\n return `${report.passes} rules passed, ${report.incomplete} need a human to check, ${report.inapplicable} did not apply.`;\n}\n\nfunction countByImpact(report: AxeReport): string {\n const counts = new Map<string, number>();\n for (const violation of report.violations) {\n counts.set(violation.impact, (counts.get(violation.impact) ?? 0) + 1);\n }\n const known = IMPACT_ORDER.filter((impact) => counts.has(impact)).map((impact) => `${counts.get(impact)} ${impact}`);\n const other = [...counts.keys()]\n .filter((impact) => !IMPACT_ORDER.includes(impact as (typeof IMPACT_ORDER)[number]))\n .map((impact) => `${counts.get(impact)} ${impact}`);\n return [...known, ...other].join(\", \");\n}\n\n/**\n * The axe-core bundle as source text, read once and cached.\n *\n * The minified build is preferred purely for injection size; the readable one\n * is an equivalent fallback for installations that ship only it.\n */\nlet cachedSource: string | null = null;\n\nexport function axeSource(): string {\n if (cachedSource !== null) return cachedSource;\n const require = createRequire(import.meta.url);\n const candidates = [\"axe-core/axe.min.js\", \"axe-core/axe.js\", \"axe-core\"];\n const problems: string[] = [];\n for (const candidate of candidates) {\n try {\n cachedSource = readFileSync(require.resolve(candidate), \"utf8\");\n return cachedSource;\n } catch (error) {\n problems.push(`${candidate}: ${error instanceof Error ? error.message.split(\"\\n\")[0] : String(error)}`);\n }\n }\n throw new Error(problems.join(\"; \"));\n}\n\n/** Reject with `message` if `work` has not settled within `ms`. */\nasync function withTimeout<T>(work: Promise<T>, ms: number, message: string): Promise<T> {\n let timer: NodeJS.Timeout | undefined;\n const expire = new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(new Error(message)), ms);\n });\n try {\n return await Promise.race([work, expire]);\n } finally {\n clearTimeout(timer);\n // The losing side has to be settled too: the audit outlives a timeout and\n // would otherwise reject unobserved once the context is torn down.\n void work.catch(() => {});\n }\n}\n\nfunction errorResult(text: string): CallToolResult {\n return { isError: true, content: [{ type: \"text\", text }] };\n}\n\n/**\n * One actionable line for a failed audit. Mirrors `describeFailure` in\n * screenshot.ts: match on the failing Playwright call, never on substrings of\n * a user-supplied selector.\n */\nexport function describeAuditFailure(\n input: { url: string; wait_for?: string; wait_for_timeout_ms: number },\n error: unknown,\n): string {\n const message = error instanceof Error ? error.message : String(error);\n const firstLine = message.split(\"\\n\")[0];\n const prefix = `Accessibility audit of ${input.url} failed:`;\n\n if (/Executable doesn't exist|browserType\\.launch/i.test(message)) {\n return (\n `${prefix} Playwright's Chromium browser is not installed. ` +\n `Run \\`npx playwright install chromium\\` and try again. (${firstLine})`\n );\n }\n if (input.wait_for && /^page\\.waitForSelector:/.test(message)) {\n return `${prefix} selector \"${input.wait_for}\" did not become visible within ${input.wait_for_timeout_ms}ms.`;\n }\n if (/^page\\.goto:/.test(message)) {\n return `${prefix} the page could not be opened — ${firstLine}`;\n }\n return `${prefix} ${firstLine}`;\n}\n\nexport function registerAccessibilityTool(server: McpServer): void {\n server.registerTool(\n ACCESSIBILITY_TOOL_NAME,\n {\n title: \"Accessibility\",\n description:\n \"Run an axe-core accessibility audit on a page and report the violations: impact level, the rule that \" +\n \"failed, the elements at fault and a link to how to fix each one. Choose the conformance level with \" +\n \"`standard` (wcag2a, wcag2aa, wcag21aa). Rules that passed are counted rather than listed, and the \" +\n \"'needs review' count tells you what axe could not decide on its own.\",\n inputSchema: accessibilityInputShape,\n annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },\n },\n async (args) => auditAccessibility(args),\n );\n}\n\n/** Exported for tests: the axe tag list each standard maps to. */\nexport const A11Y_STANDARD_TAGS: Readonly<Record<A11yStandard, readonly string[]>> = STANDARDS;\n"]}
|
|
1
|
+
{"version":3,"file":"accessibility.js","sourceRoot":"","sources":["../../src/tools/accessibility.ts"],"names":[],"mappings":"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","sourcesContent":["import { createRequire } from \"node:module\";\nimport { readFileSync } from \"node:fs\";\nimport { z } from \"zod\";\nimport type { Frame, Page } from \"playwright\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { CallToolResult } from \"@modelcontextprotocol/sdk/types.js\";\nimport {\n A11Y_FRAME_WAIT_MS,\n A11Y_RUN_TIMEOUT_MS,\n DEFAULT_A11Y_WAIT_MS,\n DEFAULT_VIEWPORT,\n MAX_A11Y_HTML_LENGTH,\n MAX_A11Y_NODES_PER_VIOLATION,\n MAX_A11Y_VIOLATIONS,\n MAX_VIEWPORT_HEIGHT,\n MAX_VIEWPORT_WIDTH,\n NAVIGATION_TIMEOUT_MS,\n SELECTOR_TIMEOUT_MS,\n} from \"../constants.js\";\nimport { withPage } from \"../engine/browser.js\";\nimport { resolveStorageState, storageStateField, withAuthNote } from \"../utils/storage-state.js\";\n\nexport const ACCESSIBILITY_TOOL_NAME = \"framewatch_accessibility\";\n\n/** WCAG conformance levels, and the axe-core tags each one runs. */\nconst STANDARDS = {\n wcag2a: [\"wcag2a\"],\n wcag2aa: [\"wcag2a\", \"wcag2aa\"],\n wcag21aa: [\"wcag2a\", \"wcag2aa\", \"wcag21a\", \"wcag21aa\"],\n} as const;\n\nexport type A11yStandard = keyof typeof STANDARDS;\n\n/** Worst first — this is the order violations are reported in. */\nconst IMPACT_ORDER = [\"critical\", \"serious\", \"moderate\", \"minor\"] as const;\n\nexport const accessibilityInputShape = {\n url: z.string().url().describe(\"URL to audit, e.g. http://localhost:3000 (http, https and file URLs are accepted)\"),\n standard: z\n .enum([\"wcag2a\", \"wcag2aa\", \"wcag21aa\"])\n .default(\"wcag2aa\")\n .describe(\"Conformance level to test against. wcag2aa is the usual legal baseline; wcag21aa adds WCAG 2.1.\"),\n wait_ms: z\n .number()\n .int()\n .min(0)\n .default(DEFAULT_A11Y_WAIT_MS)\n .describe(\"Wait time (ms) after page load before auditing, so the app can finish rendering\"),\n wait_for: z.string().optional().describe(\"CSS selector to wait for (visible) before auditing\"),\n wait_for_timeout_ms: z\n .number()\n .int()\n .min(1)\n .default(SELECTOR_TIMEOUT_MS)\n .describe(\"Max time (ms) to wait for `wait_for` to appear (must be > 0)\"),\n max_violations: z\n .number()\n .int()\n .min(1)\n .max(MAX_A11Y_VIOLATIONS)\n .default(MAX_A11Y_VIOLATIONS)\n .describe(\"Maximum violation types to report (they are reported worst-impact first)\"),\n max_elements: z\n .number()\n .int()\n .min(1)\n .max(20)\n .default(MAX_A11Y_NODES_PER_VIOLATION)\n .describe(\"Maximum offending elements listed under each violation\"),\n viewport: z\n .object({\n width: z.number().int().min(1).max(MAX_VIEWPORT_WIDTH).default(DEFAULT_VIEWPORT.width),\n height: z.number().int().min(1).max(MAX_VIEWPORT_HEIGHT).default(DEFAULT_VIEWPORT.height),\n })\n .optional()\n .describe(\"Viewport size (defaults to 1280x720). Some rules — reflow, target size — depend on it.\"),\n storage_state: storageStateField,\n};\n\nexport const accessibilityInputSchema = z.object(accessibilityInputShape);\nexport type AccessibilityInput = z.input<typeof accessibilityInputSchema>;\n\n/** The trimmed shape the in-page script sends back — never raw axe results. */\ninterface AxeNode {\n target: string;\n html: string;\n summary: string;\n}\n\ninterface AxeViolation {\n id: string;\n impact: string;\n help: string;\n description: string;\n help_url: string;\n tags: string[];\n node_count: number;\n nodes: AxeNode[];\n}\n\ninterface AxeReport {\n violations: AxeViolation[];\n /** Rules that passed, that found nothing to test, and that need a human. */\n passes: number;\n inapplicable: number;\n incomplete: number;\n /** Total violation types found, before `max_violations` trimmed the list. */\n total_violations: number;\n /** Offending elements across every violation, including those not listed. */\n total_nodes: number;\n url: string;\n axe_version: string;\n}\n\n/**\n * Run an axe-core accessibility audit and report what it found.\n *\n * axe is injected into the page rather than reimplemented: it is the engine\n * behind most commercial accessibility tooling, so a violation reported here\n * is one an auditor would also raise. It is injected as a script through\n * `evaluate`, into a context created with CSP bypassed, so a page with a\n * strict Content-Security-Policy can still be audited.\n *\n * Only violations come back. Passing rules are counted, not listed — a list of\n * two hundred rules that did not fire is noise, but the count is what tells\n * you the audit actually ran.\n */\nexport async function auditAccessibility(rawInput: AccessibilityInput): Promise<CallToolResult> {\n const parsed = accessibilityInputSchema.safeParse(rawInput);\n if (!parsed.success) {\n const issues = parsed.error.issues.map((i) => `${i.path.join(\".\") || \"input\"}: ${i.message}`).join(\"; \");\n return errorResult(`Accessibility audit failed: invalid input — ${issues}`);\n }\n const input = parsed.data;\n const viewport = input.viewport ?? { ...DEFAULT_VIEWPORT };\n\n let source: string;\n try {\n source = axeSource();\n } catch (error) {\n return errorResult(\n \"Accessibility audit failed: the axe-core library could not be loaded from this installation \" +\n `(${error instanceof Error ? error.message : String(error)}). Reinstall dependencies and try again.`,\n );\n }\n\n try {\n // `bypassCSP` so the audit can be injected into an app that forbids\n // inline script and eval. The page's own CSP has no bearing on what axe\n // measures, and without this a strictly configured app could not be\n // audited at all.\n const auth = await resolveStorageState(input.storage_state);\n const contextOptions = {\n bypassCSP: true,\n ...(auth ? { storageState: auth.state } : {}),\n };\n const report = await withPage({ viewport, contextOptions }, async (page) => {\n await page.goto(input.url, { waitUntil: \"load\", timeout: NAVIGATION_TIMEOUT_MS });\n if (input.wait_for) {\n await page.waitForSelector(input.wait_for, { state: \"visible\", timeout: input.wait_for_timeout_ms });\n }\n if (input.wait_ms > 0) {\n await page.waitForTimeout(input.wait_ms);\n }\n await injectAxe(page, source);\n // axe walks the whole DOM; on a big enough app that takes a while, and\n // `page.evaluate` has no timeout of its own — a page that wedges\n // mid-audit would otherwise hang the tool call forever.\n return withTimeout(\n runAxe(page, {\n tags: [...STANDARDS[input.standard]],\n max_violations: input.max_violations,\n max_elements: input.max_elements,\n max_html: MAX_A11Y_HTML_LENGTH,\n frame_wait_ms: A11Y_FRAME_WAIT_MS,\n }),\n A11Y_RUN_TIMEOUT_MS,\n `axe-core did not finish within ${A11Y_RUN_TIMEOUT_MS}ms`,\n );\n });\n\n return withAuthNote({ content: [{ type: \"text\", text: formatReport(report, input.standard) }] }, auth);\n } catch (error) {\n return errorResult(describeAuditFailure(input, error));\n }\n}\n\n/** Options handed to the in-page runner. Must stay JSON-serialisable. */\ninterface RunOptions {\n tags: string[];\n max_violations: number;\n max_elements: number;\n max_html: number;\n frame_wait_ms: number;\n}\n\n/**\n * Put axe in the main frame and in every child frame.\n *\n * axe audits iframes by talking to a copy of itself inside each one, so a page\n * that embeds anything (a preview pane, an embedded checkout) needs it\n * everywhere. Child frames are best-effort: one that refuses injection — it\n * may be mid-navigation, or gone by the time we reach it — must not stop the\n * audit of the page around it.\n */\nasync function injectAxe(page: Page, source: string): Promise<void> {\n await evaluateSource(page.mainFrame(), source);\n for (const frame of page.frames()) {\n if (frame === page.mainFrame()) continue;\n await evaluateSource(frame, source).catch(() => {});\n }\n}\n\n/**\n * Evaluate the axe bundle as a script in `frame`.\n *\n * The source goes in as a *string*, which Playwright evaluates as a script in\n * the frame — no script tag to be blocked, and the audit context is created\n * with CSP bypassed so injection works on strictly configured apps too.\n *\n * `allowedOrigins` is widened because axe defaults to same-origin frame\n * messaging only, and a cross-origin iframe would otherwise never answer.\n */\nasync function evaluateSource(frame: Frame, source: string): Promise<void> {\n await frame.evaluate(source as unknown as () => void);\n await frame.evaluate(() => {\n const axe = (globalThis as any).axe;\n if (axe?.configure) axe.configure({ allowedOrigins: [\"<unsafe_all_origins>\"] });\n });\n}\n\n/**\n * Run axe in the page and bring back only what gets printed.\n *\n * The trimming happens in the page on purpose: a full axe result carries every\n * passing rule with every element it checked, which on a real app is megabytes\n * of JSON to serialise across the protocol and then throw away.\n */\nasync function runAxe(page: Page, options: RunOptions): Promise<AxeReport> {\n return page.evaluate(async (opts: RunOptions): Promise<AxeReport> => {\n const g = globalThis as any;\n const axe = g.axe;\n if (!axe?.run) throw new Error(\"axe-core did not load in the page\");\n\n const results = await axe.run(g.document, {\n runOnly: { type: \"tag\", values: opts.tags },\n // Only violations are reported, so this is the only result type whose\n // element details need collecting.\n resultTypes: [\"violations\"],\n frameWaitTime: opts.frame_wait_ms,\n });\n\n const order = [\"critical\", \"serious\", \"moderate\", \"minor\"];\n const rank = (impact: string): number => {\n const at = order.indexOf(impact);\n return at === -1 ? order.length : at;\n };\n\n const all = (results.violations ?? []).slice().sort((a: any, b: any) => {\n const byImpact = rank(String(a.impact)) - rank(String(b.impact));\n if (byImpact !== 0) return byImpact;\n // Then by blast radius: the rule breaking twenty elements first.\n return (b.nodes?.length ?? 0) - (a.nodes?.length ?? 0);\n });\n\n const elide = (text: string): string => {\n const flat = String(text ?? \"\")\n .replace(/\\s+/g, \" \")\n .trim();\n return flat.length > opts.max_html ? `${flat.slice(0, opts.max_html)}…` : flat;\n };\n\n return {\n violations: all.slice(0, opts.max_violations).map((violation: any) => ({\n id: String(violation.id ?? \"unknown\"),\n impact: String(violation.impact ?? \"unknown\"),\n help: String(violation.help ?? \"\"),\n description: String(violation.description ?? \"\"),\n help_url: String(violation.helpUrl ?? \"\"),\n tags: (violation.tags ?? []).map((tag: unknown) => String(tag)),\n node_count: violation.nodes?.length ?? 0,\n nodes: (violation.nodes ?? []).slice(0, opts.max_elements).map((node: any) => ({\n target: (node.target ?? []).map((part: unknown) => String(part)).join(\" \"),\n html: elide(node.html),\n // The first line is the rule that failed for this element; the rest\n // repeats the violation's own help text.\n summary: elide(String(node.failureSummary ?? \"\").split(\"\\n\").slice(1).join(\" \")),\n })),\n })),\n passes: results.passes?.length ?? 0,\n inapplicable: results.inapplicable?.length ?? 0,\n incomplete: results.incomplete?.length ?? 0,\n total_violations: all.length,\n total_nodes: all.reduce((sum: number, violation: any) => sum + (violation.nodes?.length ?? 0), 0),\n url: String(results.url ?? \"\"),\n axe_version: String(results.testEngine?.version ?? \"\"),\n };\n }, options);\n}\n\n/**\n * Render the report as text.\n *\n * Each violation gets its impact, the rule that failed, what to do about it,\n * the elements at fault and the URL of Deque's write-up — everything needed to\n * fix it without a second lookup.\n */\nexport function formatReport(report: AxeReport, standard: A11yStandard): string {\n const scope = `${standard.toUpperCase()} (axe-core ${report.axe_version || \"?\"})`;\n const lines: string[] = [];\n\n if (report.total_violations === 0) {\n lines.push(`No ${scope} violations on ${report.url}.`);\n lines.push(auditFooter(report));\n return lines.join(\"\\n\");\n }\n\n lines.push(\n `${report.total_violations} ${scope} violation ${report.total_violations === 1 ? \"type\" : \"types\"} on ${report.url}, ` +\n `affecting ${report.total_nodes} ${report.total_nodes === 1 ? \"element\" : \"elements\"} — ${countByImpact(report)}.`,\n );\n lines.push(auditFooter(report));\n lines.push(\"\");\n\n report.violations.forEach((violation, index) => {\n lines.push(\n `${index + 1}. [${violation.impact}] ${violation.help} (${violation.id}) — ` +\n `${violation.node_count} ${violation.node_count === 1 ? \"element\" : \"elements\"}`,\n );\n lines.push(` ${violation.description}`);\n for (const node of violation.nodes) {\n lines.push(` • ${node.target || \"?\"}`);\n if (node.html) lines.push(` ${node.html}`);\n if (node.summary) lines.push(` ${node.summary}`);\n }\n if (violation.node_count > violation.nodes.length) {\n lines.push(` … and ${violation.node_count - violation.nodes.length} more elements`);\n }\n if (violation.help_url) lines.push(` ${violation.help_url}`);\n });\n\n if (report.total_violations > report.violations.length) {\n lines.push(\"\");\n lines.push(\n `… and ${report.total_violations - report.violations.length} more violation types not shown ` +\n \"(raise `max_violations` to see them).\",\n );\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * The counts that say how much of the page was actually judged. `incomplete`\n * is the important one: those are the checks axe could not decide on its own\n * (typically colour contrast over an image) and they are the rules a human\n * still has to look at.\n */\nfunction auditFooter(report: AxeReport): string {\n return `${report.passes} rules passed, ${report.incomplete} need a human to check, ${report.inapplicable} did not apply.`;\n}\n\nfunction countByImpact(report: AxeReport): string {\n const counts = new Map<string, number>();\n for (const violation of report.violations) {\n counts.set(violation.impact, (counts.get(violation.impact) ?? 0) + 1);\n }\n const known = IMPACT_ORDER.filter((impact) => counts.has(impact)).map((impact) => `${counts.get(impact)} ${impact}`);\n const other = [...counts.keys()]\n .filter((impact) => !IMPACT_ORDER.includes(impact as (typeof IMPACT_ORDER)[number]))\n .map((impact) => `${counts.get(impact)} ${impact}`);\n return [...known, ...other].join(\", \");\n}\n\n/**\n * The axe-core bundle as source text, read once and cached.\n *\n * The minified build is preferred purely for injection size; the readable one\n * is an equivalent fallback for installations that ship only it.\n */\nlet cachedSource: string | null = null;\n\nexport function axeSource(): string {\n if (cachedSource !== null) return cachedSource;\n const require = createRequire(import.meta.url);\n const candidates = [\"axe-core/axe.min.js\", \"axe-core/axe.js\", \"axe-core\"];\n const problems: string[] = [];\n for (const candidate of candidates) {\n try {\n cachedSource = readFileSync(require.resolve(candidate), \"utf8\");\n return cachedSource;\n } catch (error) {\n problems.push(`${candidate}: ${error instanceof Error ? error.message.split(\"\\n\")[0] : String(error)}`);\n }\n }\n throw new Error(problems.join(\"; \"));\n}\n\n/** Reject with `message` if `work` has not settled within `ms`. */\nasync function withTimeout<T>(work: Promise<T>, ms: number, message: string): Promise<T> {\n let timer: NodeJS.Timeout | undefined;\n const expire = new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(new Error(message)), ms);\n });\n try {\n return await Promise.race([work, expire]);\n } finally {\n clearTimeout(timer);\n // The losing side has to be settled too: the audit outlives a timeout and\n // would otherwise reject unobserved once the context is torn down.\n void work.catch(() => {});\n }\n}\n\nfunction errorResult(text: string): CallToolResult {\n return { isError: true, content: [{ type: \"text\", text }] };\n}\n\n/**\n * One actionable line for a failed audit. Mirrors `describeFailure` in\n * screenshot.ts: match on the failing Playwright call, never on substrings of\n * a user-supplied selector.\n */\nexport function describeAuditFailure(\n input: { url: string; wait_for?: string; wait_for_timeout_ms: number },\n error: unknown,\n): string {\n const message = error instanceof Error ? error.message : String(error);\n const firstLine = message.split(\"\\n\")[0];\n const prefix = `Accessibility audit of ${input.url} failed:`;\n\n if (/Executable doesn't exist|browserType\\.launch/i.test(message)) {\n return (\n `${prefix} Playwright's Chromium browser is not installed. ` +\n `Run \\`npx playwright install chromium\\` and try again. (${firstLine})`\n );\n }\n if (input.wait_for && /^page\\.waitForSelector:/.test(message)) {\n return `${prefix} selector \"${input.wait_for}\" did not become visible within ${input.wait_for_timeout_ms}ms.`;\n }\n if (/^page\\.goto:/.test(message)) {\n return `${prefix} the page could not be opened — ${firstLine}`;\n }\n return `${prefix} ${firstLine}`;\n}\n\nexport function registerAccessibilityTool(server: McpServer): void {\n server.registerTool(\n ACCESSIBILITY_TOOL_NAME,\n {\n title: \"Accessibility\",\n description:\n \"Run an axe-core accessibility audit on a page and report the violations: impact level, the rule that \" +\n \"failed, the elements at fault and a link to how to fix each one. Choose the conformance level with \" +\n \"`standard` (wcag2a, wcag2aa, wcag21aa). Rules that passed are counted rather than listed, and the \" +\n \"'needs review' count tells you what axe could not decide on its own.\",\n inputSchema: accessibilityInputShape,\n annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },\n },\n async (args) => auditAccessibility(args),\n );\n}\n\n/** Exported for tests: the axe tag list each standard maps to. */\nexport const A11Y_STANDARD_TAGS: Readonly<Record<A11yStandard, readonly string[]>> = STANDARDS;\n"]}
|