browser-devtools-mcp 0.2.2 → 0.2.3

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.
Files changed (50) hide show
  1. package/README.md +200 -25
  2. package/dist/cli/index.d.ts +1 -0
  3. package/dist/cli/runner.js +158 -0
  4. package/dist/core-B3VLZZCP.js +1 -0
  5. package/dist/core-IV5QBQ2N.js +13 -0
  6. package/dist/core-NLBNZBEB.js +1124 -0
  7. package/dist/daemon-server.js +1 -1
  8. package/dist/index.js +2 -209
  9. package/dist/platform/browser/cli/runner.js +2 -0
  10. package/dist/platform/browser/index.d.ts +2 -0
  11. package/dist/platform/browser/tools/a11y/index.d.ts +2 -0
  12. package/dist/platform/browser/tools/content/index.d.ts +2 -0
  13. package/dist/platform/browser/tools/debug/index.d.ts +2 -0
  14. package/dist/platform/browser/tools/figma/index.d.ts +2 -0
  15. package/dist/platform/browser/tools/index.d.ts +5 -0
  16. package/dist/platform/browser/tools/interaction/index.d.ts +2 -0
  17. package/dist/platform/browser/tools/navigation/index.d.ts +2 -0
  18. package/dist/platform/browser/tools/o11y/index.d.ts +2 -0
  19. package/dist/platform/browser/tools/react/index.d.ts +2 -0
  20. package/dist/platform/browser/tools/run/index.d.ts +2 -0
  21. package/dist/platform/browser/tools/stub/index.d.ts +2 -0
  22. package/dist/platform/browser/tools/sync/index.d.ts +2 -0
  23. package/dist/platform/index.d.ts +3 -0
  24. package/dist/platform/node/cli/runner.js +2 -0
  25. package/dist/platform/node/entry.js +2 -0
  26. package/dist/platform/node/index.d.ts +11 -0
  27. package/dist/platform/node/tools/debug/index.d.ts +6 -0
  28. package/dist/platform/node/tools/index.d.ts +10 -0
  29. package/dist/platform/node/tools/run/index.d.ts +2 -0
  30. package/dist/platform/types.d.ts +15 -0
  31. package/dist/tools/index.d.ts +1 -4
  32. package/dist/tools/types.d.ts +8 -3
  33. package/package.json +6 -2
  34. package/dist/cli.js +0 -179
  35. package/dist/core.js +0 -764
  36. package/dist/tools/a11y/index.d.ts +0 -2
  37. package/dist/tools/content/index.d.ts +0 -2
  38. package/dist/tools/debug/index.d.ts +0 -2
  39. package/dist/tools/figma/index.d.ts +0 -2
  40. package/dist/tools/interaction/index.d.ts +0 -2
  41. package/dist/tools/navigation/index.d.ts +0 -2
  42. package/dist/tools/o11y/index.d.ts +0 -2
  43. package/dist/tools/react/index.d.ts +0 -2
  44. package/dist/tools/run/index.d.ts +0 -2
  45. package/dist/tools/stub/index.d.ts +0 -2
  46. package/dist/tools/sync/index.d.ts +0 -2
  47. /package/dist/{otel → platform/browser/otel}/otel-initializer.bundle.js +0 -0
  48. /package/dist/{tools → platform/browser/tools}/figma/compare/index.d.ts +0 -0
  49. /package/dist/{tools → platform/browser/tools}/figma/compare/types.d.ts +0 -0
  50. /package/dist/{types.d.ts → platform/browser/types.d.ts} +0 -0
@@ -1 +1 @@
1
- import{A as C,C as y,D as u,E as c,F as d,G as I,H as D,a as t,x as T,y as b,z as E}from"./core.js";import{createRequire as N}from"module";import{Command as x,Option as _,InvalidOptionArgumentError as q}from"commander";import{serve as M}from"@hono/node-server";import{Hono as $}from"hono";import{cors as F}from"hono/cors";import{z as k}from"zod";var H=N(import.meta.url),A=0,R=0,i=new $,a=new Map,P="#default",p={get sessionNotFound(){return f(404,"Session Not Found")},get toolNotFound(){return f(404,"Tool Not Found")},get internalServerError(){return f(500,"Internal Server Error")}};function f(n,r){return{error:{code:n,message:r}}}t(f,"_buildErrorResponse");async function S(n){if(n.closed=!0,n.context)try{await n.context.close(),u("Closed MCP session context")}catch(r){d("Error occurred while closing MCP session context",r)}a.delete(n.id)}t(S,"_closeSession");function G(n,r){let s=Date.now(),e={id:r,toolExecutor:new I(()=>r),closed:!1,createdAt:s,lastActiveAt:s};return u(`Created session with id ${r}`),e}t(G,"_createSession");function j(n){let r=Date.now();return{id:n.id,createdAt:n.createdAt,lastActiveAt:n.lastActiveAt,idleSeconds:Math.floor((r-n.lastActiveAt)/1e3)}}t(j,"_getSessionInfo");async function O(n){let r=n.req.header("session-id")||P;return a.get(r)}t(O,"_getSession");async function L(n){let r=n.req.header("session-id")||P,s=a.get(r);return s?u(`Reusing session with id ${r}`):(u(`No session could be found with id ${r}`),s=G(n,r),a.set(r,s)),s}t(L,"_getOrCreateSession");function U(){let n=!1;setInterval(t(()=>{let s=Date.now();n&&a.size===0&&(c("No active session found, so terminating daemon server"),process.exit(0));for(let[e,o]of a)u(`Checking whether session with id ${e} is idle or not ...`),s-o.lastActiveAt>b*1e3&&(u(`Session with id ${e} is idle, so it will be closing ...`),S(o).then(()=>{u(`Session with id ${e} was idle, so it has been closed`)}).catch(l=>{d(`Unable to delete idle session with id ${e}`,l)}));n=a.size===0},"sessionCheck"),E*1e3)}t(U,"_scheduleIdleSessionCheck");async function z(n){let r=n.req.raw.clone();u(`Got request: ${await r.json()}`)}t(z,"_logRequest");async function W(n){let r=Object.fromEntries(D.map(e=>[e.name(),e]));i.use("*",F({origin:"*",allowMethods:["GET","POST","DELETE","OPTIONS"],allowHeaders:["Content-Type","Authorization","session-id"]})),R=n,A=Date.now();let s=t(async e=>{c(`Received ${e}, initiating graceful shutdown...`);let o=[];for(let l of a.values())o.push(S(l));await Promise.allSettled(o),c("All sessions closed, exiting..."),process.exit(0)},"gracefulShutdown");process.on("SIGTERM",()=>s("SIGTERM")),process.on("SIGINT",()=>s("SIGINT")),process.on("uncaughtException",e=>{d("Uncaught exception",e)}),process.on("unhandledRejection",e=>{d("Unhandled rejection",e)}),i.get("/health",e=>e.json({status:"ok"})),i.get("/info",e=>{let o={version:H("../package.json").version,uptime:Math.floor((Date.now()-A)/1e3),sessionCount:a.size,port:R};return e.json(o)}),i.get("/sessions",e=>{let o=[];for(let l of a.values())o.push(j(l));return e.json({sessions:o})}),i.get("/session",async e=>{let o=await O(e);return o?e.json(j(o)):e.json(p.sessionNotFound,404)}),i.post("/shutdown",async e=>{c("Shutdown request received, closing all sessions...");let o=[];for(let l of a.values())o.push(S(l));return await Promise.allSettled(o),c("All sessions closed, shutting down daemon server..."),setTimeout(()=>{process.exit(0)},500),e.json({status:"shutting_down"},200)}),i.post("/call",async e=>{try{y()&&await z(e);let o=await L(e);o.lastActiveAt=Date.now();let l=await e.req.json(),v=r[l.toolName];if(!v)return e.json(p.toolNotFound,404);let h;try{h=k.object(v.inputSchema()).parse(l.toolInput)}catch(g){let m=g.errors&&Array.isArray(g.errors)?g.errors.map(w=>`${w.path?.join(".")||"input"}: ${w.message}`).join("; "):"Invalid tool input";return e.json(f(400,`Invalid Tool Request: ${m}`),400)}try{let m={toolOutput:await o.toolExecutor.executeTool(v,h)};return e.json(m,200)}catch(g){let m={toolError:{code:g.code,message:g.message}};return e.json(m,500)}}catch(o){return d("Error occurred while handling tool call request",o),e.json(p.internalServerError,500)}}),i.delete("/session",async e=>{try{let o=await O(e);return o?(await S(o),e.json({ok:!0},200)):e.json(p.sessionNotFound,404)}catch(o){return d("Error occurred while deleting session",o),e.json(p.internalServerError,500)}}),i.onError((e,o)=>(d("Unhandled error in request handler",e),o.json({error:{code:500,message:"Internal Server Error"}},500))),i.notFound(e=>e.json({error:"Not Found",status:404},404)),M({fetch:i.fetch,port:n},()=>c(`Listening on port ${n}`)),U()}t(W,"startDaemonHTTPServer");var Z=import.meta.url===`file://${process.argv[1]}`||import.meta.url===`file://${process.argv[1]}.mjs`||process.argv[1]?.endsWith("daemon-server.js")||process.argv[1]?.endsWith("daemon-server.mjs");if(Z){let n=function(e){let o=Number(e);if(!Number.isInteger(o)||o<1||o>65535)throw new q("port must be an integer between 1 and 65535");return o};K=n,t(n,"parsePort");let s=new x().addOption(new _("--port <number>","port for daemon HTTP server").argParser(n).default(T)).allowUnknownOption().parse(process.argv).opts();C(),c("Starting daemon HTTP server..."),W(s.port).then(()=>{c("Daemon HTTP server started")}).catch(e=>{d("Failed to start daemon HTTP server",e),process.exit(1)})}var K;export{W as startDaemonHTTPServer};
1
+ import{a as h}from"./core-NLBNZBEB.js";import{F as E,G as b,H as C,I as y,K as I,L as u,M as c,O as d,a as t}from"./core-IV5QBQ2N.js";import{createRequire as P}from"node:module";import{Command as N,Option as x,InvalidOptionArgumentError as _}from"commander";import{serve as q}from"@hono/node-server";import{Hono as M}from"hono";import{cors as $}from"hono/cors";import{z as F}from"zod";var k=P(import.meta.url),D=0,A=0,i=new M,a=new Map,O="#default",p={get sessionNotFound(){return f(404,"Session Not Found")},get toolNotFound(){return f(404,"Tool Not Found")},get internalServerError(){return f(500,"Internal Server Error")}};function f(n,r){return{error:{code:n,message:r}}}t(f,"_buildErrorResponse");async function S(n){if(n.closed=!0,n.context)try{await n.context.close(),u("Closed MCP session context")}catch(r){d("Error occurred while closing MCP session context",r)}a.delete(n.id)}t(S,"_closeSession");function H(n,r){let s=Date.now(),e={id:r,toolExecutor:h.toolsInfo.createToolExecutor(()=>r),closed:!1,createdAt:s,lastActiveAt:s};return u(`Created session with id ${r}`),e}t(H,"_createSession");function R(n){let r=Date.now();return{id:n.id,createdAt:n.createdAt,lastActiveAt:n.lastActiveAt,idleSeconds:Math.floor((r-n.lastActiveAt)/1e3)}}t(R,"_getSessionInfo");async function j(n){let r=n.req.header("session-id")||O;return a.get(r)}t(j,"_getSession");async function G(n){let r=n.req.header("session-id")||O,s=a.get(r);return s?u(`Reusing session with id ${r}`):(u(`No session could be found with id ${r}`),s=H(n,r),a.set(r,s)),s}t(G,"_getOrCreateSession");function L(){let n=!1;setInterval(t(()=>{let s=Date.now();n&&a.size===0&&(c("No active session found, so terminating daemon server"),process.exit(0));for(let[e,o]of a)u(`Checking whether session with id ${e} is idle or not ...`),s-o.lastActiveAt>b*1e3&&(u(`Session with id ${e} is idle, so it will be closing ...`),S(o).then(()=>{u(`Session with id ${e} was idle, so it has been closed`)}).catch(l=>{d(`Unable to delete idle session with id ${e}`,l)}));n=a.size===0},"sessionCheck"),C*1e3)}t(L,"_scheduleIdleSessionCheck");async function U(n){let r=n.req.raw.clone();u(`Got request: ${await r.json()}`)}t(U,"_logRequest");async function z(n){let r=Object.fromEntries(h.toolsInfo.tools.map(e=>[e.name(),e]));i.use("*",$({origin:"*",allowMethods:["GET","POST","DELETE","OPTIONS"],allowHeaders:["Content-Type","Authorization","session-id"]})),A=n,D=Date.now();let s=t(async e=>{c(`Received ${e}, initiating graceful shutdown...`);let o=[];for(let l of a.values())o.push(S(l));await Promise.allSettled(o),c("All sessions closed, exiting..."),process.exit(0)},"gracefulShutdown");process.on("SIGTERM",()=>s("SIGTERM")),process.on("SIGINT",()=>s("SIGINT")),process.on("uncaughtException",e=>{d("Uncaught exception",e)}),process.on("unhandledRejection",e=>{d("Unhandled rejection",e)}),i.get("/health",e=>e.json({status:"ok"})),i.get("/info",e=>{let o={version:k("../package.json").version,uptime:Math.floor((Date.now()-D)/1e3),sessionCount:a.size,port:A};return e.json(o)}),i.get("/sessions",e=>{let o=[];for(let l of a.values())o.push(R(l));return e.json({sessions:o})}),i.get("/session",async e=>{let o=await j(e);return o?e.json(R(o)):e.json(p.sessionNotFound,404)}),i.post("/shutdown",async e=>{c("Shutdown request received, closing all sessions...");let o=[];for(let l of a.values())o.push(S(l));return await Promise.allSettled(o),c("All sessions closed, shutting down daemon server..."),setTimeout(()=>{process.exit(0)},500),e.json({status:"shutting_down"},200)}),i.post("/call",async e=>{try{I()&&await U(e);let o=await G(e);o.lastActiveAt=Date.now();let l=await e.req.json(),v=r[l.toolName];if(!v)return e.json(p.toolNotFound,404);let T;try{T=F.object(v.inputSchema()).parse(l.toolInput)}catch(g){let m=g.errors&&Array.isArray(g.errors)?g.errors.map(w=>`${w.path?.join(".")||"input"}: ${w.message}`).join("; "):"Invalid tool input";return e.json(f(400,`Invalid Tool Request: ${m}`),400)}try{let m={toolOutput:await o.toolExecutor.executeTool(v,T)};return e.json(m,200)}catch(g){let m={toolError:{code:g.code,message:g.message}};return e.json(m,500)}}catch(o){return d("Error occurred while handling tool call request",o),e.json(p.internalServerError,500)}}),i.delete("/session",async e=>{try{let o=await j(e);return o?(await S(o),e.json({ok:!0},200)):e.json(p.sessionNotFound,404)}catch(o){return d("Error occurred while deleting session",o),e.json(p.internalServerError,500)}}),i.onError((e,o)=>(d("Unhandled error in request handler",e),o.json({error:{code:500,message:"Internal Server Error"}},500))),i.notFound(e=>e.json({error:"Not Found",status:404},404)),q({fetch:i.fetch,port:n},()=>c(`Listening on port ${n}`)),L()}t(z,"startDaemonHTTPServer");var W=import.meta.url===`file://${process.argv[1]}`||import.meta.url===`file://${process.argv[1]}.mjs`||process.argv[1]?.endsWith("daemon-server.js")||process.argv[1]?.endsWith("daemon-server.mjs");if(W){let n=function(e){let o=Number(e);if(!Number.isInteger(o)||o<1||o>65535)throw new _("port must be an integer between 1 and 65535");return o};Z=n,t(n,"parsePort");let s=new N().addOption(new x("--port <number>","port for daemon HTTP server").argParser(n).default(E)).allowUnknownOption().parse(process.argv).opts();y(),c("Starting daemon HTTP server..."),z(s.port).then(()=>{c("Daemon HTTP server started")}).catch(e=>{d("Failed to start daemon HTTP server",e),process.exit(1)})}var Z;export{z as startDaemonHTTPServer};
package/dist/index.js CHANGED
@@ -1,211 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import{A as w,B as y,C as S,D as r,E as u,F as c,G as T,H as C,a as s,b as f,c as h,d as b,e as v}from"./core.js";import H from"crypto";import{createRequire as A}from"module";var N=A(import.meta.url),P="browser-devtools-mcp",I=N("../package.json").version,D=`
3
- This MCP server exposes a Playwright-powered browser runtime to AI agents,
4
- enabling deep, bidirectional debugging and interaction with live web pages.
2
+ import{a as d}from"./core-NLBNZBEB.js";import{I as P,J as b,K as y,L as n,M as u,O as c,a as t,b as T,d as v,e as h,f as C}from"./core-IV5QBQ2N.js";import{createRequire as $}from"node:module";var A=$(import.meta.url),M="browser-devtools-mcp",w=A("../package.json").version;function E(){let r=[];return r.push(d.serverInfo.instructions),r.join(`
5
3
 
6
- It supports both visual understanding and code-level inspection of browser state,
7
- similar to existing Playwright and Chrome DevTools\u2013based MCP servers, with a focus on AI-driven exploration, diagnosis, and action.
8
-
9
- Core capabilities include:
10
-
11
- **Content & Visual Inspection:**
12
- - Screenshots (full page or specific elements)
13
- - HTML and text content extraction with filtering options
14
- - PDF generation with customizable formats
15
- - Design comparison: Compare live page UI against Figma designs with similarity scoring
16
- - Accessibility snapshots (ARIA and AX tree) with visual diagnostics
17
- - Viewport and window resizing for responsive testing
18
-
19
- **Browser Control & Interaction:**
20
- - Navigation (go to URL, back, forward)
21
- - Element interaction (click, fill, hover, select, drag)
22
- - Keyboard simulation (press-key)
23
- - Scrolling (viewport or container-based with multiple modes)
24
- - Viewport emulation and real window resizing
25
-
26
- **JavaScript Execution:**
27
- - Run JavaScript in browser page context (access to window, document, DOM, Web APIs)
28
- - Run JavaScript in Node.js VM sandbox on the server (with Playwright Page access and safe built-ins)
29
-
30
- **Observability & Monitoring:**
31
- - Console message capture with filtering
32
- - HTTP request/response monitoring with detailed filtering
33
- - Web Vitals performance metrics (LCP, INP, CLS, TTFB, FCP) with recommendations
34
- - OpenTelemetry trace ID management for distributed tracing correlation
35
-
36
- **Network Stubbing & Mocking:**
37
- - HTTP request interception and modification (headers, body, method) using glob patterns
38
- - HTTP response mocking (fulfill with custom status/headers/body or abort) with configurable delay, times limit, and probability
39
- - Stub management (list all installed stubs, clear specific or all stubs)
40
- - Supports A/B testing, security testing, offline testing, error scenarios, and flaky API testing
41
-
42
- **Synchronization:**
43
- - Network idle waiting for async operations
44
- - Configurable timeouts and polling intervals
45
-
46
- **Design Comparison:**
47
- - Figma design comparison: Compare live page UI against Figma design snapshots
48
- - Multi-signal similarity scoring (MSSIM, image embedding, text embedding)
49
- - Configurable weights and comparison modes (raw vs semantic)
50
-
51
- **React Component Inspection:**
52
- - Get component for element: Find React component(s) associated with a DOM element using React Fiber
53
- - Get element for component: Map a React component instance to the DOM elements it renders
54
- - Requires persistent browser context (BROWSER_PERSISTENT_ENABLE=true) for optimal operation
55
- - React DevTools extension must be manually installed in the browser profile (MCP server does NOT auto-install)
56
- - Without extension, tools fall back to best-effort DOM scanning for __reactFiber$ pointers (less reliable)
57
-
58
- **Non-Blocking Debugging:**
59
- - Tracepoints: Capture call stack, local variables, and watch expressions at specific code locations without pausing execution
60
- - Logpoints: Evaluate and log expressions at code locations (lightweight alternative to tracepoints)
61
- - Exceptionpoints: Automatically capture snapshots when uncaught or all exceptions occur
62
- - Netpoints: Monitor specific network requests/responses matching URL patterns
63
- - Dompoints: Monitor DOM mutations (attribute changes, subtree modifications, node removal) on specific elements
64
- - Watch expressions: Evaluate custom expressions at every tracepoint hit
65
- - Source map support: Automatically resolves bundled code locations to original source files
66
- - Snapshot retrieval: Query captured snapshots by probe ID or sequence number for incremental polling
67
-
68
- **Advanced Features:**
69
- - OpenTelemetry integration: Automatic UI trace collection and backend trace correlation
70
- - Session-based architecture with long-lived browser contexts
71
- - Persistent browser contexts for stateful sessions (required for React tools)
72
- - Headless and headful mode support
73
- - System-installed browser usage option
74
- - Streamable responses and server-initiated notifications
75
- - Clean lifecycle management and teardown
76
-
77
- UI debugging guidance for AI agents:
78
- - Prefer Accessibility (AX) and ARIA snapshots over raw DOM dumps when diagnosing UI problems.
79
- These snapshots provide higher-signal, semantically meaningful anchors (roles, names, states) that
80
- map more reliably to what users perceive and what assistive tech can interact with.
81
- - Use the AX Tree Snapshot tool to correlate interactive semantics with runtime visual truth:
82
- bounding boxes, visibility, viewport intersection, and (optionally) computed styles.
83
- - If a UI control appears present but interactions fail (e.g., clicks do nothing), suspect overlap/occlusion.
84
- In such cases, enable occlusion checking ("elementFromPoint") to identify which element is actually on top.
85
- - Use ARIA snapshots to reason about accessibility roles/states and to validate that the intended
86
- semantics (labels, roles, disabled state, focusability) match the visible UI.
87
- - Before taking screenshots or snapshots, wait for network idle to ensure page stability.
88
- - Use Web Vitals tool to assess performance and identify optimization opportunities.
89
- - For design validation, use "figma_compare-page-with-design" to compare live page UI against Figma designs:
90
- - Use "semantic" MSSIM mode for comparing real data vs design data (less sensitive to text/value differences)
91
- - Use "raw" MSSIM mode only when expecting near pixel-identical output
92
- - If layout mismatch is suspected, run with fullPage=true first, then retry with a selector for the problematic region
93
- - Notes explain which signals were used or skipped; skipped signals usually mean missing cloud configuration
94
- - For React component inspection, use "react_get-component-for-element" and "react_get-element-for-component":
95
- - These tools work best with persistent browser context enabled (BROWSER_PERSISTENT_ENABLE=true)
96
- - React DevTools extension must be manually installed in the browser profile (MCP server does NOT auto-install)
97
- - Chrome Web Store: https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi
98
- - Without extension, tools use best-effort DOM scanning (less reliable than using DevTools hook)
99
- - Component names and debug source info are best-effort and may vary by build (dev/prod)
100
- - For distributed tracing, set trace IDs before navigation to correlate frontend and backend traces.
101
- - For testing and debugging scenarios, use stub tools to intercept/modify requests or mock responses:
102
- - Use "stub_intercept-http-request" to modify outgoing requests (inject headers, change body/method)
103
- - Use "stub_mock-http-response" to mock responses for offline testing, error scenarios, or flaky API simulation
104
- - Use "stub_list" to check what stubs are active and "stub_clear" to remove them when done
105
- - For non-blocking JavaScript debugging:
106
- - Use "debug_put-tracepoint" to capture call stack, local variables, and watch expressions at specific code locations
107
- - Use "debug_put-logpoint" for lightweight logging at code locations (no call stack capture)
108
- - Use "debug_put-exceptionpoint" to capture snapshots when exceptions occur (state: "uncaught" or "all")
109
- - Use "debug_put-netpoint" to monitor specific network requests/responses by URL pattern
110
- - Use "debug_put-dompoint" to monitor DOM mutations on specific elements
111
- - Use "debug_add-watch" to add watch expressions evaluated at every tracepoint hit
112
- - Use "debug_get-tracepoint-snapshots", "debug_get-logpoint-snapshots", etc. to retrieve captured data
113
- - Use "fromSequence" parameter for incremental polling of new snapshots
114
- - Tracepoints/logpoints support source maps: set probes on original source file locations
115
-
116
- This server is designed for AI coding assistants, visual debugging agents, and automated analysis tools
117
- that need to reason about what a page looks like, how it is structured, and how it behaves \u2014 all through a single MCP interface.
118
-
119
- It treats the browser as a queryable, inspectable, and controllable execution environment rather than a static screenshot source.
120
- `,M=`
121
- <ui_debugging_policy>
122
- When asked to check for UI problems, layout issues, or visual bugs, ALWAYS follow this policy:
123
-
124
- 1. **Synchronization**: If the page loads content asynchronously, call "sync_wait-for-network-idle" first
125
- to ensure the page is stable before inspection.
126
-
127
- 2. **Visual Inspection**: Call "content_take-screenshot" for general aesthetics and layout overview.
128
-
129
- 3. **Accessibility Tree Analysis**: Call "a11y_take-ax-tree-snapshot" tool with "checkOcclusion:true"
130
- - Provides precise bounding boxes, runtime visual data, and occlusion detection
131
- - Best for detecting overlaps and measuring exact positions
132
- - Use "onlyVisible:true" or "onlyInViewport:true" to filter results
133
- - Set "includeStyles:true" to analyze computed CSS properties
134
-
135
- 4. **ARIA Snapshot**: Call "a11y_take-aria-snapshot" tool (full page or specific selector)
136
- - Provides semantic structure and accessibility roles
137
- - Best for understanding page hierarchy and accessibility issues
138
- - Use in combination with AX tree snapshot for comprehensive analysis
139
-
140
- 5. **Design Comparison** (if Figma design is available): Call "figma_compare-page-with-design" tool
141
- - Compares live page UI against Figma design snapshot
142
- - Returns combined similarity score using multiple signals (MSSIM, image embedding, text embedding)
143
- - Use "semantic" mode for real data vs design data comparisons
144
- - Use "raw" mode only when expecting pixel-identical output
145
- - Notes explain which signals were used or skipped
146
-
147
- 6. **React Component Inspection** (if page uses React): Use React tools to understand component structure
148
- - Call "react_get-component-for-element" with selector or (x,y) to find React component for a DOM element
149
- - Call "react_get-element-for-component" to find DOM elements rendered by a React component
150
- - **Important:** These tools require persistent browser context (BROWSER_PERSISTENT_ENABLE=true)
151
- - React DevTools extension must be manually installed in the browser profile for optimal reliability
152
- - Without extension, tools use best-effort DOM scanning (less reliable)
153
- - Component names and debug source info are best-effort and may vary by build (dev/prod)
154
-
155
- 7. **Performance Check** (optional but recommended): Call "o11y_get-web-vitals" to assess page performance
156
- - Identifies performance issues that may affect user experience
157
- - Provides actionable recommendations based on Google's thresholds
158
-
159
- 8. **Console & Network Inspection**: Check for errors and failed requests
160
- - Call "o11y_get-console-messages" with "type:ERROR" to find JavaScript errors
161
- - Call "o11y_get-http-requests" with "ok:false" to find failed network requests
162
- - If network issues are suspected or testing error scenarios, use stub tools:
163
- - Use "stub_mock-http-response" to simulate error responses (e.g., 500, 404, timeout) to test UI error handling
164
- - Use "stub_intercept-http-request" to modify requests (e.g., inject headers) to test different scenarios
165
- - Use "stub_list" to verify active stubs and "stub_clear" to remove them after testing
166
-
167
- 9. **Manual Verification**: Calculate bounding box overlaps:
168
- - Horizontal: (element1.x + element1.width) \u2264 element2.x
169
- - Vertical: (element1.y + element1.height) \u2264 element2.y
170
-
171
- 10. **Report ALL findings**: aesthetic issues, overlaps, spacing problems, alignment issues,
172
- accessibility problems, semantic structure issues, design parity issues (if compared with Figma),
173
- React component structure issues (if inspected), performance problems, console errors, failed requests
174
-
175
- 11. **JavaScript Execution** (when needed for advanced debugging):
176
- - Use "run_js-in-browser" to inspect or mutate DOM state, read client-side variables, or extract computed values directly from the page
177
- - Use "run_js-in-sandbox" for server-side automation logic that needs access to Playwright Page object or safe built-ins
178
-
179
- 12. **Non-Blocking JavaScript Debugging** (for deep code investigation):
180
- - Use "debug_put-tracepoint" to set breakpoints that capture call stack and local variables without pausing
181
- - Use "debug_put-logpoint" for lightweight logging at specific code locations
182
- - Use "debug_put-exceptionpoint" with state "uncaught" or "all" to capture exception snapshots
183
- - Use "debug_put-netpoint" to monitor specific network requests by URL pattern
184
- - Use "debug_put-dompoint" to monitor DOM mutations (attributes, subtree, removal) on elements
185
- - Use "debug_add-watch" to add expressions evaluated at every tracepoint hit
186
- - Retrieve snapshots with "debug_get-tracepoint-snapshots", "debug_get-logpoint-snapshots", etc.
187
- - Use "fromSequence" parameter to poll only new snapshots since last retrieval
188
- - Probes support source maps: specify original source file paths for bundled applications
189
- - Use "debug_status" to check current debugging state and probe counts
190
-
191
- **Tool Usage Notes:**
192
- - AX tree: Technical measurements, occlusion, precise positioning, visual diagnostics
193
- - ARIA snapshot: Semantic understanding, accessibility structure, role hierarchy
194
- - Screenshot: Quick visual reference, but not sufficient alone
195
- - Network idle: Essential for SPAs and async content
196
- - Web Vitals: Performance context for UI issues
197
- - Tracepoints: Deep code investigation with call stack and variables (non-blocking)
198
- - Logpoints: Lightweight logging at specific code locations
199
- - Exceptionpoints: Automatic exception capture without manual breakpoints
200
- - Netpoints/Dompoints: Monitor network and DOM changes without code modification
201
-
202
- **Important:**
203
- - Never assume "looks good visually" = "no problems". Overlaps and accessibility issues
204
- can be functionally broken while appearing visually correct.
205
- - Always check occlusion when interactions fail or elements appear misaligned.
206
- - Use scroll tool if elements are below the fold before inspection.
207
- - For responsive issues, use resize-viewport or resize-window tools to test different sizes.
208
- </ui_debugging_policy>
209
- `;function x(){let t=[];return t.push(D),t.join(`
210
-
211
- `).trim()}s(x,"getServerInstructions");import{StreamableHTTPTransport as L}from"@hono/mcp";import{serve as j}from"@hono/node-server";import{McpServer as $}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as V}from"@modelcontextprotocol/sdk/server/stdio.js";import{Hono as B}from"hono";import{cors as W}from"hono/cors";var z={jsonrpc:"2.0",error:{code:0,message:"N/A"},id:null},d={get sessionNotFound(){return m(-32001,"Session Not Found")},get unauthorized(){return m(-32001,"Unauthorized")},get internalServerError(){return m(-32603,"Internal Server Error")}},p=new Map;function m(t,e){let o={...z};return o.error.code=t,o.error.message=e,o}s(m,"_buildMCPErrorResponse");function G(t){if("image"in t&&t.image!==null&&typeof t.image=="object"&&"data"in t.image&&"mimeType"in t.image&&Buffer.isBuffer(t.image.data)&&typeof t.image.mimeType=="string"){let e=t.image;return delete t.image,e}}s(G,"_getImage");function J(t){let e=G(t),o=[];return o.push({type:"text",text:JSON.stringify(t,null,2)}),e&&(e.mimeType==="image/svg+xml"?o.push({type:"text",text:e.data.toString(),mimeType:e.mimeType}):o.push({type:"image",data:e.data.toString("base64"),mimeType:e.mimeType})),{content:o,structuredContent:t,isError:!1}}s(J,"_toResponse");function X(t){let e=new $({name:P,version:I},{capabilities:{resources:{},tools:{}},instructions:x()}),o=[];o.push({role:"user",content:{type:"text",text:M}}),e.registerPrompt("default_system",{title:"Default System Prompt",description:"General behavior for the AI assistant"},async()=>({description:"Defines the assistant's general reasoning and tool usage rules.",messages:o}));let n=new T(()=>t.sessionIdProvider?t.sessionIdProvider():""),i=s(a=>async U=>{try{let g=await n.executeTool(a,U);return J(g)}catch(g){return{content:[{type:"text",text:`Error: ${g.message}`}],isError:!0}}},"createToolCallback");return C.forEach(a=>{r(`Registering tool ${a.name()} ...`),e.registerTool(a.name(),{description:a.description(),inputSchema:a.inputSchema(),outputSchema:a.outputSchema()},i(a))}),e}s(X,"_createServer");async function _(t,e){let o=X({config:e.config,sessionIdProvider:s(()=>t.sessionId,"sessionIdProvider")});return await o.connect(t),o}s(_,"_createAndConnectServer");function k(){return{}}s(k,"_getConfig");function Y(t,e,o){let n={transport:e,server:o,closed:!1,lastActiveAt:Date.now()},i=t.env.incoming.socket;return i._mcpRegistered||(i._mcpRegistered=!0,i.on("close",async()=>{r(`Socket, which is for MCP session with id ${e.sessionId}, has been closed`),v&&await e.close()})),ee(e,n.server),r(`Created MCP server session with id ${e.sessionId}`),n}s(Y,"_createSession");async function K(t){let e=k(),o={},n=new L({enableJsonResponse:!0,sessionIdGenerator:s(()=>H.randomUUID(),"sessionIdGenerator"),onsessioninitialized:s(async i=>{let a=Y(t,n,o.server);p.set(i,a),r(`MCP session initialized with id ${i}`)},"onsessioninitialized"),onsessionclosed:s(async i=>{r(`Closing MCP session closed with id ${i} ...`),await n.close(),r(`MCP session closed with id ${i}`)},"onsessionclosed")});return o.server=await _(n,{config:e}),n}s(K,"_createTransport");async function Q(t){let e=t.req.header("mcp-session-id");if(e){let o=p.get(e);if(o)return r(`Reusing MCP session with id ${e}`),o.transport}}s(Q,"_getTransport");async function Z(t){let e=t.req.header("mcp-session-id");if(e){let o=p.get(e);if(o)return r(`Reusing MCP session with id ${e}`),o.transport;r(`No MCP session could be found with id ${e}`);return}return await K(t)}s(Z,"_getOrCreateTransport");function ee(t,e){let o=!1;t.onclose=async()=>{if(r(`Closing MCP session with id ${t.sessionId} ...`),o){r(`MCP session with id ${t.sessionId} has already been closed`);return}o=!0;try{await e.close(),r("Closed MCP server")}catch(n){c("Error occurred while closing MCP server",n)}if(t.sessionId){let n=p.get(t.sessionId);if(n&&(n.closed=!0,n.context))try{await n.context.close(),r("Closed MCP session context")}catch(i){c("Error occurred while closing MCP session context",i)}p.delete(t.sessionId)}r(`Closing MCP session with id ${t.sessionId} ...`)}}s(ee,"_registerMCPSessionClose");function te(){setInterval(s(()=>{let e=Date.now();for(let[o,n]of p)r(`Checking whether session with id ${o} is idle or not ...`),e-n.lastActiveAt>h*1e3&&(r(`Session with id ${o} is idle, so it will be closing ...`),n.transport.close().then(()=>{r(`Session with id ${o} was idle, so it has been closed`)}).catch(i=>{c(`Unable to delete idle session with id ${o}`,i)}))},"sessionCheck"),b*1e3)}s(te,"_scheduleIdleSessionCheck");async function oe(t){let e=t.req.raw.clone();r(`Got request: ${await e.json()}`)}s(oe,"_logRequest");function se(t){let e=t.req.header("mcp-session-id");if(e){let o=p.get(e);o&&(o.lastActiveAt=Date.now())}}s(se,"_markSessionAsActive");async function E(){let t=new V;await _(t,{config:k()})}s(E,"startStdioServer");var l=new B;async function R(t){l.use("*",W({origin:"*",allowMethods:["GET","POST","OPTIONS"],allowHeaders:["Content-Type","Authorization","MCP-Protocol-Version"]})),l.get("/health",e=>e.json({status:"ok"})),l.get("/ping",e=>e.json({status:"ok",message:"pong"})),l.get("/mcp",e=>e.json({status:"ok",protocol:"model-context-protocol",version:"1.0"})),l.post("/mcp",async e=>{try{S()&&await oe(e);let o=await Z(e);return o?(se(e),await o.handleRequest(e)):e.json(d.sessionNotFound,400)}catch(o){return c("Error occurred while handling MCP request",o),e.json(d.internalServerError,500)}}),l.delete("/mcp",async e=>{try{let o=await Q(e);return o?(await o.close(),e.json({ok:!0},200)):e.json(d.sessionNotFound,400)}catch(o){return c("Error occurred while deleting MCP session",o),e.json(d.internalServerError,500)}}),l.notFound(e=>e.json({error:"Not Found",status:404},404)),j({fetch:l.fetch,port:t},()=>u(`Listening on port ${t}`)),te()}s(R,"startStreamableHTTPServer");import{Command as ne,Option as O,InvalidOptionArgumentError as re}from"commander";function ie(t){let e=Number(t);if(!Number.isInteger(e)||e<1||e>65535)throw new re("port must be an integer between 1 and 65535");return e}s(ie,"_parsePort");function ae(){return new ne().addOption(new O("--transport <type>","transport type").choices(["stdio","streamable-http"]).default("stdio")).addOption(new O("--port <number>","port for Streamable HTTP transport").argParser(ie).default(f)).allowUnknownOption().parse(process.argv).opts()}s(ae,"_getOptions");async function ce(){let t=ae();t.transport==="stdio"?(y(),await E()):t.transport==="streamable-http"?(u("Starting MCP server..."),await R(t.port),u("Started MCP Server")):(c(`Invalid transport: ${t.transport}`),process.exit(1))}s(ce,"main");ce().catch(t=>{w(),c("MCP server error",t),process.exit(1)});
4
+ `).trim()}t(E,"getServerInstructions");function I(){return d.serverInfo.policies}t(I,"getServerPolicies");import q from"node:crypto";import{StreamableHTTPTransport as D}from"@hono/mcp";import{serve as V}from"@hono/node-server";import{McpServer as F}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as L}from"@modelcontextprotocol/sdk/server/stdio.js";import{Hono as z}from"hono";import{cors as U}from"hono/cors";var W={jsonrpc:"2.0",error:{code:0,message:"N/A"},id:null},p={get sessionNotFound(){return S(-32001,"Session Not Found")},get unauthorized(){return S(-32001,"Unauthorized")},get internalServerError(){return S(-32603,"Internal Server Error")}},g=new Map;function S(r,e){let o={...W};return o.error.code=r,o.error.message=e,o}t(S,"_buildMCPErrorResponse");function G(r){if("image"in r&&r.image!==null&&typeof r.image=="object"&&"data"in r.image&&"mimeType"in r.image&&Buffer.isBuffer(r.image.data)&&typeof r.image.mimeType=="string"){let e=r.image;return delete r.image,e}}t(G,"_getImage");function B(r){let e=G(r),o=[];return o.push({type:"text",text:JSON.stringify(r,null,2)}),e&&(e.mimeType==="image/svg+xml"?o.push({type:"text",text:e.data.toString(),mimeType:e.mimeType}):o.push({type:"image",data:e.data.toString("base64"),mimeType:e.mimeType})),{content:o,structuredContent:r,isError:!1}}t(B,"_toResponse");function J(r){let e=new F({name:M,version:w},{capabilities:{resources:{},tools:{}},instructions:E()}),o=[],s=I();if(s)for(let a of s)o.push({role:"user",content:{type:"text",text:a}});e.registerPrompt("default_system",{title:"Default System Prompt",description:"General behavior for the AI assistant"},async()=>({description:"Defines the assistant's general reasoning and tool usage rules.",messages:o}));let i=d.toolsInfo.createToolExecutor(()=>r.sessionIdProvider?r.sessionIdProvider():""),m=t(a=>async H=>{try{let f=await i.executeTool(a,H);return B(f)}catch(f){return{content:[{type:"text",text:`Error: ${f.message}`}],isError:!0}}},"createToolCallback");return d.toolsInfo.tools.forEach(a=>{n(`Registering tool ${a.name()} ...`),e.registerTool(a.name(),{description:a.description(),inputSchema:a.inputSchema(),outputSchema:a.outputSchema()},m(a))}),e}t(J,"_createServer");async function O(r,e){let o=J({config:e.config,sessionIdProvider:t(()=>r.sessionId,"sessionIdProvider")});return await o.connect(r),o}t(O,"_createAndConnectServer");function R(){return{}}t(R,"_getConfig");function K(r,e,o){let s={transport:e,server:o,closed:!1,lastActiveAt:Date.now()},i=r.env.incoming.socket;return i._mcpRegistered||(i._mcpRegistered=!0,i.on("close",async()=>{n(`Socket, which is for MCP session with id ${e.sessionId}, has been closed`),C&&await e.close()})),Z(e,s.server),n(`Created MCP server session with id ${e.sessionId}`),s}t(K,"_createSession");async function Y(r){let e=R(),o={},s=new D({enableJsonResponse:!0,sessionIdGenerator:t(()=>q.randomUUID(),"sessionIdGenerator"),onsessioninitialized:t(async i=>{let m=K(r,s,o.server);g.set(i,m),n(`MCP session initialized with id ${i}`)},"onsessioninitialized"),onsessionclosed:t(async i=>{n(`Closing MCP session closed with id ${i} ...`),await s.close(),n(`MCP session closed with id ${i}`)},"onsessionclosed")});return o.server=await O(s,{config:e}),s}t(Y,"_createTransport");async function Q(r){let e=r.req.header("mcp-session-id");if(e){let o=g.get(e);if(o)return n(`Reusing MCP session with id ${e}`),o.transport}}t(Q,"_getTransport");async function X(r){let e=r.req.header("mcp-session-id");if(e){let o=g.get(e);if(o)return n(`Reusing MCP session with id ${e}`),o.transport;n(`No MCP session could be found with id ${e}`);return}return await Y(r)}t(X,"_getOrCreateTransport");function Z(r,e){let o=!1;r.onclose=async()=>{if(n(`Closing MCP session with id ${r.sessionId} ...`),o){n(`MCP session with id ${r.sessionId} has already been closed`);return}o=!0;try{await e.close(),n("Closed MCP server")}catch(s){c("Error occurred while closing MCP server",s)}if(r.sessionId){let s=g.get(r.sessionId);if(s&&(s.closed=!0,s.context))try{await s.context.close(),n("Closed MCP session context")}catch(i){c("Error occurred while closing MCP session context",i)}g.delete(r.sessionId)}n(`Closing MCP session with id ${r.sessionId} ...`)}}t(Z,"_registerMCPSessionClose");function ee(){setInterval(t(()=>{let e=Date.now();for(let[o,s]of g)n(`Checking whether session with id ${o} is idle or not ...`),e-s.lastActiveAt>v*1e3&&(n(`Session with id ${o} is idle, so it will be closing ...`),s.transport.close().then(()=>{n(`Session with id ${o} was idle, so it has been closed`)}).catch(i=>{c(`Unable to delete idle session with id ${o}`,i)}))},"sessionCheck"),h*1e3)}t(ee,"_scheduleIdleSessionCheck");async function re(r){let e=r.req.raw.clone();n(`Got request: ${await e.json()}`)}t(re,"_logRequest");function oe(r){let e=r.req.header("mcp-session-id");if(e){let o=g.get(e);o&&(o.lastActiveAt=Date.now())}}t(oe,"_markSessionAsActive");async function _(){let r=new L;await O(r,{config:R()})}t(_,"startStdioServer");var l=new z;async function x(r){l.use("*",U({origin:"*",allowMethods:["GET","POST","OPTIONS"],allowHeaders:["Content-Type","Authorization","MCP-Protocol-Version"]})),l.get("/health",e=>e.json({status:"ok"})),l.get("/ping",e=>e.json({status:"ok",message:"pong"})),l.get("/mcp",e=>e.json({status:"ok",protocol:"model-context-protocol",version:"1.0"})),l.post("/mcp",async e=>{try{y()&&await re(e);let o=await X(e);return o?(oe(e),await o.handleRequest(e)):e.json(p.sessionNotFound,400)}catch(o){return c("Error occurred while handling MCP request",o),e.json(p.internalServerError,500)}}),l.delete("/mcp",async e=>{try{let o=await Q(e);return o?(await o.close(),e.json({ok:!0},200)):e.json(p.sessionNotFound,400)}catch(o){return c("Error occurred while deleting MCP session",o),e.json(p.internalServerError,500)}}),l.notFound(e=>e.json({error:"Not Found",status:404},404)),V({fetch:l.fetch,port:r},()=>u(`Listening on port ${r}`)),ee()}t(x,"startStreamableHTTPServer");import{Command as te,Option as N,InvalidOptionArgumentError as se}from"commander";function ne(r){let e=Number(r);if(!Number.isInteger(e)||e<1||e>65535)throw new se("port must be an integer between 1 and 65535");return e}t(ne,"_parsePort");function ie(){return new te().addOption(new N("--transport <type>","transport type").choices(["stdio","streamable-http"]).default("stdio")).addOption(new N("--port <number>","port for Streamable HTTP transport").argParser(ne).default(T)).allowUnknownOption().parse(process.argv).opts()}t(ie,"_getOptions");async function ae(){let r=ie();r.transport==="stdio"?(b(),await _()):r.transport==="streamable-http"?(u("Starting MCP server..."),await x(r.port),u("Started MCP Server")):(c(`Invalid transport: ${r.transport}`),process.exit(1))}t(ae,"main");ae().catch(r=>{P(),c("MCP server error",r),process.exit(1)});
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ process.env.PLATFORM="browser";await import("../../../cli/runner.js");
@@ -0,0 +1,2 @@
1
+ import { PlatformInfo } from '../types';
2
+ export declare const platformInfo: PlatformInfo;
@@ -0,0 +1,2 @@
1
+ import { Tool } from '../../../../tools/types';
2
+ export declare const tools: Tool[];
@@ -0,0 +1,2 @@
1
+ import { Tool } from '../../../../tools/types';
2
+ export declare const tools: Tool[];
@@ -0,0 +1,2 @@
1
+ import { Tool } from '../../../../tools/types';
2
+ export declare const tools: Tool[];
@@ -0,0 +1,2 @@
1
+ import { Tool } from '../../../../tools/types';
2
+ export declare const tools: Tool[];
@@ -0,0 +1,5 @@
1
+ import type { Tool } from '../../../tools';
2
+ import { BrowserToolSessionContext } from './context';
3
+ import { BrowserToolExecutor } from './tool-executor';
4
+ export declare const tools: Tool<BrowserToolSessionContext>[];
5
+ export declare function createToolExecutor(sessionIdProvider: () => string): BrowserToolExecutor;
@@ -0,0 +1,2 @@
1
+ import { Tool } from '../../../../tools/types';
2
+ export declare const tools: Tool[];
@@ -0,0 +1,2 @@
1
+ import { Tool } from '../../../../tools/types';
2
+ export declare const tools: Tool[];
@@ -0,0 +1,2 @@
1
+ import { Tool } from '../../../../tools/types';
2
+ export declare const tools: Tool[];
@@ -0,0 +1,2 @@
1
+ import { Tool } from '../../../../tools/types';
2
+ export declare const tools: Tool[];
@@ -0,0 +1,2 @@
1
+ import { Tool } from '../../../../tools/types';
2
+ export declare const tools: Tool[];
@@ -0,0 +1,2 @@
1
+ import { Tool } from '../../../../tools/types';
2
+ export declare const tools: Tool[];
@@ -0,0 +1,2 @@
1
+ import { Tool } from '../../../../tools/types';
2
+ export declare const tools: Tool[];
@@ -0,0 +1,3 @@
1
+ import { PlatformInfo } from './types';
2
+ export * from './types';
3
+ export declare const platformInfo: PlatformInfo;
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ process.env.PLATFORM="node";await import("../../../cli/runner.js");
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ process.env.PLATFORM="node";await import("../../index.js");
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Node.js Platform
3
+ *
4
+ * Exports Node.js platform info for debugging Node.js backend processes.
5
+ */
6
+ import { PlatformInfo } from '../types';
7
+ export { tools, createToolExecutor } from './tools';
8
+ export { SERVER_INSTRUCTIONS, NODE_DEBUGGING_POLICY } from './server-info';
9
+ export { NodeToolSessionContext } from './tools/context';
10
+ export { NodeToolExecutor } from './tools/tool-executor';
11
+ export declare const nodePlatformInfo: PlatformInfo;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Node.js Debug Tools
3
+ * Non-blocking V8 debugging for Node.js backend processes
4
+ */
5
+ import { Tool } from '../../../../tools';
6
+ export declare const tools: Tool[];
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Node.js Platform Tools
3
+ *
4
+ * Aggregates all Node.js debugging tools and provides the tool executor factory.
5
+ */
6
+ import type { Tool } from '../../../tools';
7
+ import { NodeToolSessionContext } from './context';
8
+ import { NodeToolExecutor } from './tool-executor';
9
+ export declare const tools: Tool<NodeToolSessionContext>[];
10
+ export declare function createToolExecutor(sessionIdProvider: () => string): NodeToolExecutor;
@@ -0,0 +1,2 @@
1
+ import type { Tool } from '../../../../tools';
2
+ export declare const tools: Tool[];
@@ -0,0 +1,15 @@
1
+ import { Provider } from '../cli';
2
+ import { Tool, ToolExecutor } from '../tools';
3
+ export type PlatformInfo = {
4
+ serverInfo: {
5
+ instructions: string;
6
+ policies?: string[] | undefined;
7
+ };
8
+ toolsInfo: {
9
+ tools: Tool[];
10
+ createToolExecutor: (sessionIdProvider: () => string) => ToolExecutor;
11
+ };
12
+ cliInfo: {
13
+ cliProvider: Provider;
14
+ };
15
+ };
@@ -1,4 +1 @@
1
- import type { Tool, ToolInput, ToolOutput, ToolOutputWithImage } from './types';
2
- export * from './tool-executor';
3
- export declare const tools: Tool[];
4
- export type { Tool, ToolInput, ToolOutput, ToolOutputWithImage };
1
+ export type { Tool, ToolExecutor, ToolInput, ToolInputSchema, ToolOutput, ToolOutputSchema, ToolOutputWithImage, ToolSessionContext, } from './types';
@@ -1,5 +1,4 @@
1
1
  import { ZodRawShape } from 'zod';
2
- import { ToolSessionContext } from '../context';
3
2
  export type ToolInputSchema = ZodRawShape;
4
3
  export type ToolOutputSchema = ZodRawShape;
5
4
  export interface ToolInput {
@@ -12,10 +11,16 @@ export interface ToolOutputWithImage extends ToolOutput {
12
11
  mimeType: string;
13
12
  };
14
13
  }
15
- export interface Tool {
14
+ export interface Tool<SC extends ToolSessionContext = ToolSessionContext> {
16
15
  name(): string;
17
16
  description(): string;
18
17
  inputSchema(): ToolInputSchema;
19
18
  outputSchema(): ToolOutputSchema;
20
- handle(context: ToolSessionContext, args: ToolInput): Promise<ToolOutput>;
19
+ handle(context: SC, args: ToolInput): Promise<ToolOutput>;
20
+ }
21
+ export interface ToolSessionContext {
22
+ close(): Promise<boolean>;
23
+ }
24
+ export interface ToolExecutor<SC extends ToolSessionContext = ToolSessionContext> {
25
+ executeTool(tool: Tool<SC>, args: ToolInput): Promise<ToolOutput>;
21
26
  }
package/package.json CHANGED
@@ -1,13 +1,15 @@
1
1
  {
2
2
  "name": "browser-devtools-mcp",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "MCP Server for Browser Dev Tools",
5
5
  "private": false,
6
6
  "type": "module",
7
7
  "main": "./dist/index.js",
8
8
  "bin": {
9
9
  "browser-devtools-mcp": "./dist/index.js",
10
- "browser-devtools-cli": "./dist/cli.js"
10
+ "node-devtools-mcp": "./dist/platform/node/entry.js",
11
+ "browser-devtools-cli": "./dist/platform/browser/cli/runner.js",
12
+ "node-devtools-cli": "./dist/platform/node/cli/runner.js"
11
13
  },
12
14
  "author": "Serkan Ozal <serkanozal86@gmail.com> (https://github.com/serkan-ozal)",
13
15
  "homepage": "https://github.com/serkan-ozal/browser-devtools-mcp",
@@ -63,6 +65,7 @@
63
65
  "@types/node": "^18.19.87",
64
66
  "@types/picomatch": "^4.0.2",
65
67
  "@types/pngjs": "^6.0.5",
68
+ "@types/ws": "^8.18.1",
66
69
  "@typescript-eslint/eslint-plugin": "^8.31.1",
67
70
  "@typescript-eslint/parser": "^8.31.1",
68
71
  "esbuild": "^0.27.2",
@@ -92,6 +95,7 @@
92
95
  "pngjs": "^7.0.0",
93
96
  "sharp": "^0.34.5",
94
97
  "ssim.js": "^3.5.0",
98
+ "ws": "^8.19.0",
95
99
  "zod": "^3.24.3"
96
100
  }
97
101
  }