@llui/mcp 0.0.25 → 0.0.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { LluiDebugAPI } from '@llui/dom';
2
2
  import type { Server as HttpServer } from 'node:http';
3
- import { Server as McpServer } from '@modelcontextprotocol/sdk/server/index.js';
3
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
4
4
  import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
5
5
  import { type ToolDefinition } from './tool-registry.js';
6
6
  /**
@@ -79,6 +79,10 @@ export declare class LluiMcpServer {
79
79
  * deployments where every session needs its own SDK Server — each
80
80
  * routes tool calls through the shared relay, so the single
81
81
  * bridgeHost owns all the browser-facing state.
82
+ *
83
+ * Uses the high-level `McpServer.registerTool` API: each tool's
84
+ * Zod schema (declared once in the registry) drives both runtime
85
+ * input validation and the JSON Schema published to `tools/list`.
82
86
  */
83
87
  private buildMcpServer;
84
88
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAI7C,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,WAAW,CAAA;AACrD,OAAO,EAAE,MAAM,IAAI,SAAS,EAAE,MAAM,2CAA2C,CAAA;AAC/E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+CAA+C,CAAA;AAE9E,OAAO,EAAkC,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAoCxF;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,GAAE,MAAsB,GAAG,MAAM,CAgBvE;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,GAAE,MAAsB,GAAG,MAAM,CAErE;AAID,MAAM,WAAW,oBAAoB;IACnC;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,UAAU,CAAA;IACrB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAc;IACvC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAyB;IAC/C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAQ;IACnC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAW;IAC/B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAmB;IACvC,OAAO,CAAC,MAAM,CAAsB;IAEpC;;;;;;;;;OASG;gBACS,UAAU,GAAE,oBAAoB,GAAG,MAAa;IA6B5D;;;;;;;OAOG;IACH,OAAO,CAAC,cAAc;IAwCtB;;;;;;;;;;OAUG;IACH,gBAAgB,IAAI,SAAS;IAI7B;;;;OAIG;IACG,OAAO,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlD,uEAAuE;IACvE,aAAa,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI;IAItC;;;;;OAKG;IACH,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAM5B;;;;OAIG;IACH,WAAW,IAAI,IAAI;IAQnB,UAAU,IAAI,IAAI;IAKlB,OAAO,CAAC,eAAe;IAevB,OAAO,CAAC,gBAAgB;IASxB,6CAA6C;IAC7C,QAAQ,IAAI,cAAc,EAAE;IAI5B,8BAA8B;IACxB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;CAIpF;AAED;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,EAAE,cAAc,EAQ3C,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAI7C,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,WAAW,CAAA;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AACnE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+CAA+C,CAAA;AAE9E,OAAO,EAAkC,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAoCxF;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,GAAE,MAAsB,GAAG,MAAM,CAgBvE;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,GAAE,MAAsB,GAAG,MAAM,CAErE;AAID,MAAM,WAAW,oBAAoB;IACnC;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,UAAU,CAAA;IACrB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAc;IACvC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAyB;IAC/C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAQ;IACnC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAW;IAC/B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAmB;IACvC,OAAO,CAAC,MAAM,CAAsB;IAEpC;;;;;;;;;OASG;gBACS,UAAU,GAAE,oBAAoB,GAAG,MAAa;IA6B5D;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,cAAc;IAiDtB;;;;;;;;;;OAUG;IACH,gBAAgB,IAAI,SAAS;IAI7B;;;;OAIG;IACG,OAAO,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlD,uEAAuE;IACvE,aAAa,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI;IAItC;;;;;OAKG;IACH,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAM5B;;;;OAIG;IACH,WAAW,IAAI,IAAI;IAQnB,UAAU,IAAI,IAAI;IAKlB,OAAO,CAAC,eAAe;IAevB,OAAO,CAAC,gBAAgB;IASxB,6CAA6C;IAC7C,QAAQ,IAAI,cAAc,EAAE;IAI5B,8BAA8B;IACxB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;CAIpF;AAED;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,EAAE,cAAc,EAQ3C,CAAA"}
package/dist/index.js CHANGED
@@ -1,8 +1,7 @@
1
1
  import { mkdirSync, readFileSync, writeFileSync, unlinkSync, existsSync } from 'node:fs';
2
2
  import { dirname, resolve } from 'node:path';
3
3
  import { fileURLToPath } from 'node:url';
4
- import { Server as McpServer } from '@modelcontextprotocol/sdk/server/index.js';
5
- import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';
4
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
6
5
  import { ToolRegistry } from './tool-registry.js';
7
6
  import { registerDebugApiTools, registerCdpTools, registerCompilerTools, registerSourceTools, registerSsrTools, } from './tools/index.js';
8
7
  import { WebSocketRelayTransport, RelayUnavailableError, CdpSessionManager, } from './transports/index.js';
@@ -120,42 +119,47 @@ export class LluiMcpServer {
120
119
  * deployments where every session needs its own SDK Server — each
121
120
  * routes tool calls through the shared relay, so the single
122
121
  * bridgeHost owns all the browser-facing state.
122
+ *
123
+ * Uses the high-level `McpServer.registerTool` API: each tool's
124
+ * Zod schema (declared once in the registry) drives both runtime
125
+ * input validation and the JSON Schema published to `tools/list`.
123
126
  */
124
127
  buildMcpServer() {
125
128
  const mcp = new McpServer({ name: '@llui/mcp', version: PACKAGE_VERSION }, { capabilities: { tools: {} } });
126
- mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
127
- tools: this.getTools().map((t) => ({
128
- name: t.name,
129
- description: t.description,
130
- inputSchema: t.inputSchema,
131
- })),
132
- }));
133
- mcp.setRequestHandler(CallToolRequestSchema, async (request) => {
134
- const { name, arguments: args } = request.params;
135
- try {
136
- const result = await this.handleToolCall(name, args ?? {});
137
- return {
138
- content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
139
- };
140
- }
141
- catch (err) {
142
- // Bridge-unavailable errors carry a structured diagnostic — surface
143
- // it as an isError tool result so the caller (typically Claude) sees
144
- // WHY the browser isn't reachable, not just that it failed.
145
- if (err instanceof RelayUnavailableError) {
129
+ for (const { spec, handler } of this.registry.listEntries()) {
130
+ mcp.registerTool(spec.name, { description: spec.description, inputSchema: spec.schema.shape }, async (args) => {
131
+ const ctx = { relay: this.relay, cdp: this.cdp };
132
+ try {
133
+ const result = await handler(args, ctx);
134
+ // structuredContent is what current Claude clients
135
+ // (Desktop + CC) consume preferentially when present —
136
+ // typed JSON instead of a stringified blob. The text
137
+ // content stays as a fallback for older clients.
146
138
  return {
147
- isError: true,
148
- content: [
149
- {
150
- type: 'text',
151
- text: JSON.stringify({ error: 'bridge-unavailable', ...err.diagnostic }, null, 2),
152
- },
153
- ],
139
+ structuredContent: result,
140
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
154
141
  };
155
142
  }
156
- throw err;
157
- }
158
- });
143
+ catch (err) {
144
+ // Bridge-unavailable errors carry a structured diagnostic —
145
+ // surface it as an isError tool result so the caller
146
+ // (typically Claude) sees WHY the browser isn't reachable,
147
+ // not just that it failed.
148
+ if (err instanceof RelayUnavailableError) {
149
+ return {
150
+ isError: true,
151
+ content: [
152
+ {
153
+ type: 'text',
154
+ text: JSON.stringify({ error: 'bridge-unavailable', ...err.diagnostic }, null, 2),
155
+ },
156
+ ],
157
+ };
158
+ }
159
+ throw err;
160
+ }
161
+ });
162
+ }
159
163
  return mcp;
160
164
  }
161
165
  /**
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACxF,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAExC,OAAO,EAAE,MAAM,IAAI,SAAS,EAAE,MAAM,2CAA2C,CAAA;AAE/E,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAA;AAClG,OAAO,EAAE,YAAY,EAAyC,MAAM,oBAAoB,CAAA;AACxF,OAAO,EACL,qBAAqB,EACrB,gBAAgB,EAChB,qBAAqB,EACrB,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACL,uBAAuB,EACvB,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,uBAAuB,CAAA;AAE9B;;;;;;;GAOG;AACH,MAAM,eAAe,GAAW,CAAC,GAAG,EAAE;IACpC,IAAI,CAAC;QACH,iEAAiE;QACjE,mCAAmC;QACnC,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;QACpD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,CAAC,IAAI,EAAE,iBAAiB,CAAC,EAAE,MAAM,CAAC,CAE5E,CAAA;QACD,OAAO,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAA;IAClE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC,CAAC,EAAE,CAAA;AAEJ;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAAgB,OAAO,CAAC,GAAG,EAAE;IAC7D,IAAI,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,CAAA;IACxB,IAAI,eAAe,GAAkB,IAAI,CAAA;IACzC,OAAO,IAAI,EAAE,CAAC;QACZ,sCAAsC;QACtC,IAAI,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,qBAAqB,CAAC,CAAC;YAAE,OAAO,GAAG,CAAA;QAC/D,IAAI,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YAAE,OAAO,GAAG,CAAA;QAChD,+CAA+C;QAC/C,IAAI,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;YAAE,eAAe,GAAG,GAAG,CAAA;QACnE,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAA;QAC3B,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;YACnB,mEAAmE;YACnE,OAAO,eAAe,IAAI,KAAK,CAAA;QACjC,CAAC;QACD,GAAG,GAAG,MAAM,CAAA;IACd,CAAC;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAc,OAAO,CAAC,GAAG,EAAE;IAC3D,OAAO,OAAO,CAAC,iBAAiB,CAAC,GAAG,CAAC,EAAE,0CAA0C,CAAC,CAAA;AACpF,CAAC;AAiCD,MAAM,OAAO,aAAa;IACP,QAAQ,CAAc;IACtB,KAAK,CAAyB;IAC9B,UAAU,CAAQ;IAClB,GAAG,CAAW;IACd,GAAG,CAAmB;IAC/B,MAAM,GAAkB,IAAI,CAAA;IAEpC;;;;;;;;;OASG;IACH,YAAY,aAA4C,IAAI;QAC1D,MAAM,IAAI,GACR,OAAO,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,UAAU,CAAA;QAC1E,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,IAAI,CAAA;QACzC,IAAI,CAAC,QAAQ,GAAG,IAAI,YAAY,EAAE,CAAA;QAClC,iEAAiE;QACjE,iEAAiE;QACjE,6DAA6D;QAC7D,4BAA4B;QAC5B,IAAI,CAAC,KAAK,GAAG,IAAI,uBAAuB,CAAC;YACvC,IAAI,EAAE,IAAI,CAAC,UAAU;YACrB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,UAAU,EAAE,iBAAiB,EAAE;SAChC,CAAC,CAAA;QACF,IAAI,CAAC,GAAG,GAAG,IAAI,iBAAiB,CAAC;YAC/B,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,IAAI;YAC3B,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,KAAK;SAC7B,CAAC,CAAA;QACF,qBAAqB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QACpC,gBAAgB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QAC/B,qBAAqB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QACpC,mBAAmB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QAClC,gBAAgB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QAE/B,kEAAkE;QAClE,oEAAoE;QACpE,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,cAAc,EAAE,CAAA;IAClC,CAAC;IAED;;;;;;;OAOG;IACK,cAAc;QACpB,MAAM,GAAG,GAAG,IAAI,SAAS,CACvB,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,eAAe,EAAE,EAC/C,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,CAChC,CAAA;QACD,GAAG,CAAC,iBAAiB,CAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;YACzD,KAAK,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBACjC,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,WAAW,EAAE,CAAC,CAAC,WAAW;gBAC1B,WAAW,EAAE,CAAC,CAAC,WAAW;aAC3B,CAAC,CAAC;SACJ,CAAC,CAAC,CAAA;QACH,GAAG,CAAC,iBAAiB,CAAC,qBAAqB,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;YAC7D,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,MAAM,CAAA;YAChD,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,IAAI,IAAI,EAAE,CAAC,CAAA;gBAC1D,OAAO;oBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;iBAC5E,CAAA;YACH,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,oEAAoE;gBACpE,qEAAqE;gBACrE,4DAA4D;gBAC5D,IAAI,GAAG,YAAY,qBAAqB,EAAE,CAAC;oBACzC,OAAO;wBACL,OAAO,EAAE,IAAI;wBACb,OAAO,EAAE;4BACP;gCACE,IAAI,EAAE,MAAe;gCACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,oBAAoB,EAAE,GAAG,GAAG,CAAC,UAAU,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;6BAClF;yBACF;qBACF,CAAA;gBACH,CAAC;gBACD,MAAM,GAAG,CAAA;YACX,CAAC;QACH,CAAC,CAAC,CAAA;QACF,OAAO,GAAG,CAAA;IACZ,CAAC;IAED;;;;;;;;;;OAUG;IACH,gBAAgB;QACd,OAAO,IAAI,CAAC,cAAc,EAAE,CAAA;IAC9B,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,OAAO,CAAC,SAAoB;QAChC,MAAM,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;IACnC,CAAC;IAED,uEAAuE;IACvE,aAAa,CAAC,GAAiB;QAC7B,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAAA;IAC/B,CAAC;IAED;;;;;OAKG;IACH,SAAS,CAAC,GAAW;QACnB,IAAI,CAAC,MAAM,GAAG,GAAG,CAAA;QACjB,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAA;QACvB,IAAI,IAAI,CAAC,KAAK,CAAC,eAAe,EAAE;YAAE,IAAI,CAAC,eAAe,EAAE,CAAA;IAC1D,CAAC;IAED;;;;OAIG;IACH,WAAW;QACT,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAA;QAElB,+DAA+D;QAC/D,iEAAiE;QACjE,IAAI,CAAC,eAAe,EAAE,CAAA;IACxB,CAAC;IAED,UAAU;QACR,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAA;QACjB,IAAI,CAAC,gBAAgB,EAAE,CAAA;IACzB,CAAC;IAEO,eAAe;QACrB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,iBAAiB,EAAE,CAAA;YAChC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;YAC7C,MAAM,OAAO,GAAmD;gBAC9D,IAAI,EAAE,IAAI,CAAC,UAAU;gBACrB,GAAG,EAAE,OAAO,CAAC,GAAG;aACjB,CAAA;YACD,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI;gBAAE,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAA;YACtD,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAA;QAC9C,CAAC;QAAC,MAAM,CAAC;YACP,wEAAwE;QAC1E,CAAC;IACH,CAAC;IAEO,gBAAgB;QACtB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,iBAAiB,EAAE,CAAA;YAChC,IAAI,UAAU,CAAC,IAAI,CAAC;gBAAE,UAAU,CAAC,IAAI,CAAC,CAAA;QACxC,CAAC;QAAC,MAAM,CAAC;YACP,oCAAoC;QACtC,CAAC;IACH,CAAC;IAED,6CAA6C;IAC7C,QAAQ;QACN,OAAO,IAAI,CAAC,QAAQ,CAAC,eAAe,EAAE,CAAA;IACxC,CAAC;IAED,8BAA8B;IAC9B,KAAK,CAAC,cAAc,CAAC,IAAY,EAAE,IAA6B;QAC9D,MAAM,GAAG,GAAgB,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,CAAA;QAC7D,OAAO,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,CAAA;IAChD,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAqB,CAAC,GAAG,EAAE;IACxD,MAAM,QAAQ,GAAG,IAAI,YAAY,EAAE,CAAA;IACnC,qBAAqB,CAAC,QAAQ,CAAC,CAAA;IAC/B,gBAAgB,CAAC,QAAQ,CAAC,CAAA;IAC1B,qBAAqB,CAAC,QAAQ,CAAC,CAAA;IAC/B,mBAAmB,CAAC,QAAQ,CAAC,CAAA;IAC7B,gBAAgB,CAAC,QAAQ,CAAC,CAAA;IAC1B,OAAO,QAAQ,CAAC,eAAe,EAAE,CAAA;AACnC,CAAC,CAAC,EAAE,CAAA","sourcesContent":["import type { LluiDebugAPI } from '@llui/dom'\nimport { mkdirSync, readFileSync, writeFileSync, unlinkSync, existsSync } from 'node:fs'\nimport { dirname, resolve } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport type { Server as HttpServer } from 'node:http'\nimport { Server as McpServer } from '@modelcontextprotocol/sdk/server/index.js'\nimport type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js'\nimport { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js'\nimport { ToolRegistry, type ToolContext, type ToolDefinition } from './tool-registry.js'\nimport {\n registerDebugApiTools,\n registerCdpTools,\n registerCompilerTools,\n registerSourceTools,\n registerSsrTools,\n} from './tools/index.js'\nimport {\n WebSocketRelayTransport,\n RelayUnavailableError,\n CdpSessionManager,\n} from './transports/index.js'\n\n/**\n * Version advertised in the MCP `initialize` handshake. Read once from\n * our own `package.json` so it stays in sync with the publish bump,\n * instead of a hardcoded literal that silently drifts each release.\n *\n * Falls back to `'unknown'` on read failure — SDK initialization still\n * succeeds; only the cosmetic serverInfo.version is affected.\n */\nconst PACKAGE_VERSION: string = (() => {\n try {\n // dist layout: `dist/index.js` → `package.json` is two levels up\n // from the module file at runtime.\n const here = dirname(fileURLToPath(import.meta.url))\n const pkg = JSON.parse(readFileSync(resolve(here, '../package.json'), 'utf8')) as {\n version?: string\n }\n return typeof pkg.version === 'string' ? pkg.version : 'unknown'\n } catch {\n return 'unknown'\n }\n})()\n\n/**\n * Walk up from `start` until we find a workspace root marker. Used by\n * both the MCP server (writing the active marker) and the Vite plugin\n * (watching it) so they agree on a single shared location regardless of\n * which subdirectory each process happens to be running in.\n *\n * Strong markers (workspace root): pnpm-workspace.yaml, .git directory.\n * If neither is found anywhere up the chain, falls back to the highest\n * package.json above `start`. For pnpm monorepos this finds the workspace\n * root from any subpackage; for single-package projects it finds the\n * package root.\n */\nexport function findWorkspaceRoot(start: string = process.cwd()): string {\n let dir = resolve(start)\n let lastPackageJson: string | null = null\n while (true) {\n // Strong markers — return immediately\n if (existsSync(resolve(dir, 'pnpm-workspace.yaml'))) return dir\n if (existsSync(resolve(dir, '.git'))) return dir\n // Track the highest package.json as a fallback\n if (existsSync(resolve(dir, 'package.json'))) lastPackageJson = dir\n const parent = dirname(dir)\n if (parent === dir) {\n // Reached filesystem root — return the highest package.json we saw\n return lastPackageJson ?? start\n }\n dir = parent\n }\n}\n\n/**\n * Path where the MCP server writes its active port marker. Vite plugins\n * watch this file to auto-trigger browser-side `__lluiConnect()` whenever\n * the MCP server starts, regardless of whether Vite or MCP started first.\n *\n * Resolved relative to the workspace root (not the immediate cwd) so the\n * MCP server and the Vite plugin always agree on a single location even\n * when one runs from the repo root and the other from a subpackage.\n */\nexport function mcpActiveFilePath(cwd: string = process.cwd()): string {\n return resolve(findWorkspaceRoot(cwd), 'node_modules/.cache/llui-mcp/active.json')\n}\n\n// ── MCP Server ──────────────────────────────────────────────────\n\nexport interface LluiMcpServerOptions {\n /**\n * Port for the browser-relay WebSocket bridge. When the MCP transport\n * is stdio (the CLI default), the relay stands up its own server on\n * this port. When the MCP transport is HTTP, the relay attaches to\n * that HTTP server and the MCP protocol + bridge share a single port.\n */\n bridgePort?: number\n /**\n * Optional pre-existing `http.Server` to share with the bridge. When\n * provided, the bridge attaches to it via upgrade routing on\n * `/bridge`; `bridgePort` is ignored for server-creation purposes\n * (but still written into the marker file so consumers know where to\n * connect).\n */\n attachTo?: HttpServer\n /**\n * Optional dev-server URL for CDP fallback navigation. When provided,\n * the CDP session manager will use this URL as the target for Playwright\n * browser instances.\n */\n devUrl?: string\n /**\n * Whether to run the Playwright browser in headed mode (visible window).\n * Defaults to false (headless).\n */\n headed?: boolean\n}\n\nexport class LluiMcpServer {\n private readonly registry: ToolRegistry\n private readonly relay: WebSocketRelayTransport\n private readonly bridgePort: number\n private readonly mcp: McpServer\n private readonly cdp: CdpSessionManager\n private devUrl: string | null = null\n\n /**\n * @param optsOrPort options object (preferred) or bridge port (legacy).\n * The numeric-port form is kept for one release cycle of back-compat;\n * new code should always pass an options object. The options form\n * supports `attachTo` for HTTP-transport deployments that share a\n * single port between MCP and the browser bridge — the numeric form\n * can't express that.\n * @deprecated numeric `optsOrPort` — pass `{ bridgePort }` instead.\n * This overload will be removed in a future breaking release.\n */\n constructor(optsOrPort: LluiMcpServerOptions | number = 5200) {\n const opts: LluiMcpServerOptions =\n typeof optsOrPort === 'number' ? { bridgePort: optsOrPort } : optsOrPort\n this.bridgePort = opts.bridgePort ?? 5200\n this.registry = new ToolRegistry()\n // Pass bridgePort even in attachTo mode — the relay's diagnose()\n // needs it for the port field of BridgeDiagnostic. The `start()`\n // path is gated on `attachTo` first so a standalone listener\n // never gets created twice.\n this.relay = new WebSocketRelayTransport({\n port: this.bridgePort,\n attachTo: opts.attachTo,\n markerPath: mcpActiveFilePath(),\n })\n this.cdp = new CdpSessionManager({\n devUrl: opts.devUrl ?? null,\n headed: opts.headed ?? false,\n })\n registerDebugApiTools(this.registry)\n registerCdpTools(this.registry)\n registerCompilerTools(this.registry)\n registerSourceTools(this.registry)\n registerSsrTools(this.registry)\n\n // SDK-managed MCP server — owns the JSON-RPC protocol, handshake,\n // session lifecycle. Transport is plugged in later via `connect()`.\n this.mcp = this.buildMcpServer()\n }\n\n /**\n * Build a fresh SDK `McpServer` wired to THIS instance's tool\n * registry and browser relay. The primary `this.mcp` uses one.\n * `createSessionMcp()` returns additional ones for HTTP-transport\n * deployments where every session needs its own SDK Server — each\n * routes tool calls through the shared relay, so the single\n * bridgeHost owns all the browser-facing state.\n */\n private buildMcpServer(): McpServer {\n const mcp = new McpServer(\n { name: '@llui/mcp', version: PACKAGE_VERSION },\n { capabilities: { tools: {} } },\n )\n mcp.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: this.getTools().map((t) => ({\n name: t.name,\n description: t.description,\n inputSchema: t.inputSchema,\n })),\n }))\n mcp.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args } = request.params\n try {\n const result = await this.handleToolCall(name, args ?? {})\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }],\n }\n } catch (err) {\n // Bridge-unavailable errors carry a structured diagnostic — surface\n // it as an isError tool result so the caller (typically Claude) sees\n // WHY the browser isn't reachable, not just that it failed.\n if (err instanceof RelayUnavailableError) {\n return {\n isError: true,\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify({ error: 'bridge-unavailable', ...err.diagnostic }, null, 2),\n },\n ],\n }\n }\n throw err\n }\n })\n return mcp\n }\n\n /**\n * Build a new SDK MCP server sharing this instance's registry + relay,\n * for HTTP-transport deployments where each session needs its own\n * `Server` (SDK requirement). Call-site pattern:\n *\n * const bridgeHost = new LluiMcpServer({ bridgePort, attachTo: httpServer })\n * bridgeHost.startBridge()\n * // Per session:\n * const sessionMcp = bridgeHost.createSessionMcp()\n * await sessionMcp.connect(transport)\n */\n createSessionMcp(): McpServer {\n return this.buildMcpServer()\n }\n\n /**\n * Connect the SDK MCP server to a transport (stdio, HTTP, etc).\n * The CLI builds the transport based on command-line flags and\n * hands it in here.\n */\n async connect(transport: Transport): Promise<void> {\n await this.mcp.connect(transport)\n }\n\n /** Connect to a debug API instance directly (for in-process usage). */\n connectDirect(api: LluiDebugAPI): void {\n this.relay.connectDirect(api)\n }\n\n /**\n * Set the dev-server URL that Phase 2's CDP fallback can navigate a\n * Playwright browser to. Persisted into the active marker file so the\n * Vite plugin (or other consumers) can rebroadcast it. If the bridge is\n * already running, rewrites the marker so consumers see the update.\n */\n setDevUrl(url: string): void {\n this.devUrl = url\n this.cdp.setDevUrl(url)\n if (this.relay.isServerRunning()) this.writeActiveFile()\n }\n\n /**\n * Start a WebSocket server on the configured bridge port. The browser-side\n * relay (injected by the Vite plugin in dev mode) connects here and forwards\n * debug-API calls.\n */\n startBridge(): void {\n this.relay.start()\n\n // Write the active marker file so Vite plugins watching it can\n // dispatch an HMR custom event to auto-trigger browser connects.\n this.writeActiveFile()\n }\n\n stopBridge(): void {\n this.relay.stop()\n this.removeActiveFile()\n }\n\n private writeActiveFile(): void {\n try {\n const path = mcpActiveFilePath()\n mkdirSync(dirname(path), { recursive: true })\n const payload: { port: number; pid: number; devUrl?: string } = {\n port: this.bridgePort,\n pid: process.pid,\n }\n if (this.devUrl !== null) payload.devUrl = this.devUrl\n writeFileSync(path, JSON.stringify(payload))\n } catch {\n // Best-effort — failure to write the marker should not crash the server\n }\n }\n\n private removeActiveFile(): void {\n try {\n const path = mcpActiveFilePath()\n if (existsSync(path)) unlinkSync(path)\n } catch {\n // Ignore — file may already be gone\n }\n }\n\n /** Get tool definitions for MCP handshake */\n getTools(): ToolDefinition[] {\n return this.registry.listDefinitions()\n }\n\n /** Handle an MCP tool call */\n async handleToolCall(name: string, args: Record<string, unknown>): Promise<unknown> {\n const ctx: ToolContext = { relay: this.relay, cdp: this.cdp }\n return this.registry.dispatch(name, args, ctx)\n }\n}\n\n/**\n * Snapshot of all registered tool definitions. Kept as a named export for\n * backward compatibility with downstream consumers that used to import the\n * `TOOLS` array re-export under this alias.\n */\nexport const mcpToolDefinitions: ToolDefinition[] = (() => {\n const registry = new ToolRegistry()\n registerDebugApiTools(registry)\n registerCdpTools(registry)\n registerCompilerTools(registry)\n registerSourceTools(registry)\n registerSsrTools(registry)\n return registry.listDefinitions()\n})()\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { LluiDebugAPI } from '@llui/dom'\nimport { mkdirSync, readFileSync, writeFileSync, unlinkSync, existsSync } from 'node:fs'\nimport { dirname, resolve } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport type { Server as HttpServer } from 'node:http'\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js'\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js'\nimport { ToolRegistry, type ToolContext, type ToolDefinition } from './tool-registry.js'\nimport {\n registerDebugApiTools,\n registerCdpTools,\n registerCompilerTools,\n registerSourceTools,\n registerSsrTools,\n} from './tools/index.js'\nimport {\n WebSocketRelayTransport,\n RelayUnavailableError,\n CdpSessionManager,\n} from './transports/index.js'\n\n/**\n * Version advertised in the MCP `initialize` handshake. Read once from\n * our own `package.json` so it stays in sync with the publish bump,\n * instead of a hardcoded literal that silently drifts each release.\n *\n * Falls back to `'unknown'` on read failure — SDK initialization still\n * succeeds; only the cosmetic serverInfo.version is affected.\n */\nconst PACKAGE_VERSION: string = (() => {\n try {\n // dist layout: `dist/index.js` → `package.json` is two levels up\n // from the module file at runtime.\n const here = dirname(fileURLToPath(import.meta.url))\n const pkg = JSON.parse(readFileSync(resolve(here, '../package.json'), 'utf8')) as {\n version?: string\n }\n return typeof pkg.version === 'string' ? pkg.version : 'unknown'\n } catch {\n return 'unknown'\n }\n})()\n\n/**\n * Walk up from `start` until we find a workspace root marker. Used by\n * both the MCP server (writing the active marker) and the Vite plugin\n * (watching it) so they agree on a single shared location regardless of\n * which subdirectory each process happens to be running in.\n *\n * Strong markers (workspace root): pnpm-workspace.yaml, .git directory.\n * If neither is found anywhere up the chain, falls back to the highest\n * package.json above `start`. For pnpm monorepos this finds the workspace\n * root from any subpackage; for single-package projects it finds the\n * package root.\n */\nexport function findWorkspaceRoot(start: string = process.cwd()): string {\n let dir = resolve(start)\n let lastPackageJson: string | null = null\n while (true) {\n // Strong markers — return immediately\n if (existsSync(resolve(dir, 'pnpm-workspace.yaml'))) return dir\n if (existsSync(resolve(dir, '.git'))) return dir\n // Track the highest package.json as a fallback\n if (existsSync(resolve(dir, 'package.json'))) lastPackageJson = dir\n const parent = dirname(dir)\n if (parent === dir) {\n // Reached filesystem root — return the highest package.json we saw\n return lastPackageJson ?? start\n }\n dir = parent\n }\n}\n\n/**\n * Path where the MCP server writes its active port marker. Vite plugins\n * watch this file to auto-trigger browser-side `__lluiConnect()` whenever\n * the MCP server starts, regardless of whether Vite or MCP started first.\n *\n * Resolved relative to the workspace root (not the immediate cwd) so the\n * MCP server and the Vite plugin always agree on a single location even\n * when one runs from the repo root and the other from a subpackage.\n */\nexport function mcpActiveFilePath(cwd: string = process.cwd()): string {\n return resolve(findWorkspaceRoot(cwd), 'node_modules/.cache/llui-mcp/active.json')\n}\n\n// ── MCP Server ──────────────────────────────────────────────────\n\nexport interface LluiMcpServerOptions {\n /**\n * Port for the browser-relay WebSocket bridge. When the MCP transport\n * is stdio (the CLI default), the relay stands up its own server on\n * this port. When the MCP transport is HTTP, the relay attaches to\n * that HTTP server and the MCP protocol + bridge share a single port.\n */\n bridgePort?: number\n /**\n * Optional pre-existing `http.Server` to share with the bridge. When\n * provided, the bridge attaches to it via upgrade routing on\n * `/bridge`; `bridgePort` is ignored for server-creation purposes\n * (but still written into the marker file so consumers know where to\n * connect).\n */\n attachTo?: HttpServer\n /**\n * Optional dev-server URL for CDP fallback navigation. When provided,\n * the CDP session manager will use this URL as the target for Playwright\n * browser instances.\n */\n devUrl?: string\n /**\n * Whether to run the Playwright browser in headed mode (visible window).\n * Defaults to false (headless).\n */\n headed?: boolean\n}\n\nexport class LluiMcpServer {\n private readonly registry: ToolRegistry\n private readonly relay: WebSocketRelayTransport\n private readonly bridgePort: number\n private readonly mcp: McpServer\n private readonly cdp: CdpSessionManager\n private devUrl: string | null = null\n\n /**\n * @param optsOrPort options object (preferred) or bridge port (legacy).\n * The numeric-port form is kept for one release cycle of back-compat;\n * new code should always pass an options object. The options form\n * supports `attachTo` for HTTP-transport deployments that share a\n * single port between MCP and the browser bridge — the numeric form\n * can't express that.\n * @deprecated numeric `optsOrPort` — pass `{ bridgePort }` instead.\n * This overload will be removed in a future breaking release.\n */\n constructor(optsOrPort: LluiMcpServerOptions | number = 5200) {\n const opts: LluiMcpServerOptions =\n typeof optsOrPort === 'number' ? { bridgePort: optsOrPort } : optsOrPort\n this.bridgePort = opts.bridgePort ?? 5200\n this.registry = new ToolRegistry()\n // Pass bridgePort even in attachTo mode — the relay's diagnose()\n // needs it for the port field of BridgeDiagnostic. The `start()`\n // path is gated on `attachTo` first so a standalone listener\n // never gets created twice.\n this.relay = new WebSocketRelayTransport({\n port: this.bridgePort,\n attachTo: opts.attachTo,\n markerPath: mcpActiveFilePath(),\n })\n this.cdp = new CdpSessionManager({\n devUrl: opts.devUrl ?? null,\n headed: opts.headed ?? false,\n })\n registerDebugApiTools(this.registry)\n registerCdpTools(this.registry)\n registerCompilerTools(this.registry)\n registerSourceTools(this.registry)\n registerSsrTools(this.registry)\n\n // SDK-managed MCP server — owns the JSON-RPC protocol, handshake,\n // session lifecycle. Transport is plugged in later via `connect()`.\n this.mcp = this.buildMcpServer()\n }\n\n /**\n * Build a fresh SDK `McpServer` wired to THIS instance's tool\n * registry and browser relay. The primary `this.mcp` uses one.\n * `createSessionMcp()` returns additional ones for HTTP-transport\n * deployments where every session needs its own SDK Server — each\n * routes tool calls through the shared relay, so the single\n * bridgeHost owns all the browser-facing state.\n *\n * Uses the high-level `McpServer.registerTool` API: each tool's\n * Zod schema (declared once in the registry) drives both runtime\n * input validation and the JSON Schema published to `tools/list`.\n */\n private buildMcpServer(): McpServer {\n const mcp = new McpServer(\n { name: '@llui/mcp', version: PACKAGE_VERSION },\n { capabilities: { tools: {} } },\n )\n for (const { spec, handler } of this.registry.listEntries()) {\n mcp.registerTool(\n spec.name,\n { description: spec.description, inputSchema: spec.schema.shape },\n async (args) => {\n const ctx: ToolContext = { relay: this.relay, cdp: this.cdp }\n try {\n const result = await handler(args as Record<string, unknown>, ctx)\n // structuredContent is what current Claude clients\n // (Desktop + CC) consume preferentially when present —\n // typed JSON instead of a stringified blob. The text\n // content stays as a fallback for older clients.\n return {\n structuredContent: result as Record<string, unknown>,\n content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],\n } satisfies CallToolResult\n } catch (err) {\n // Bridge-unavailable errors carry a structured diagnostic —\n // surface it as an isError tool result so the caller\n // (typically Claude) sees WHY the browser isn't reachable,\n // not just that it failed.\n if (err instanceof RelayUnavailableError) {\n return {\n isError: true,\n content: [\n {\n type: 'text',\n text: JSON.stringify(\n { error: 'bridge-unavailable', ...err.diagnostic },\n null,\n 2,\n ),\n },\n ],\n } satisfies CallToolResult\n }\n throw err\n }\n },\n )\n }\n return mcp\n }\n\n /**\n * Build a new SDK MCP server sharing this instance's registry + relay,\n * for HTTP-transport deployments where each session needs its own\n * `Server` (SDK requirement). Call-site pattern:\n *\n * const bridgeHost = new LluiMcpServer({ bridgePort, attachTo: httpServer })\n * bridgeHost.startBridge()\n * // Per session:\n * const sessionMcp = bridgeHost.createSessionMcp()\n * await sessionMcp.connect(transport)\n */\n createSessionMcp(): McpServer {\n return this.buildMcpServer()\n }\n\n /**\n * Connect the SDK MCP server to a transport (stdio, HTTP, etc).\n * The CLI builds the transport based on command-line flags and\n * hands it in here.\n */\n async connect(transport: Transport): Promise<void> {\n await this.mcp.connect(transport)\n }\n\n /** Connect to a debug API instance directly (for in-process usage). */\n connectDirect(api: LluiDebugAPI): void {\n this.relay.connectDirect(api)\n }\n\n /**\n * Set the dev-server URL that Phase 2's CDP fallback can navigate a\n * Playwright browser to. Persisted into the active marker file so the\n * Vite plugin (or other consumers) can rebroadcast it. If the bridge is\n * already running, rewrites the marker so consumers see the update.\n */\n setDevUrl(url: string): void {\n this.devUrl = url\n this.cdp.setDevUrl(url)\n if (this.relay.isServerRunning()) this.writeActiveFile()\n }\n\n /**\n * Start a WebSocket server on the configured bridge port. The browser-side\n * relay (injected by the Vite plugin in dev mode) connects here and forwards\n * debug-API calls.\n */\n startBridge(): void {\n this.relay.start()\n\n // Write the active marker file so Vite plugins watching it can\n // dispatch an HMR custom event to auto-trigger browser connects.\n this.writeActiveFile()\n }\n\n stopBridge(): void {\n this.relay.stop()\n this.removeActiveFile()\n }\n\n private writeActiveFile(): void {\n try {\n const path = mcpActiveFilePath()\n mkdirSync(dirname(path), { recursive: true })\n const payload: { port: number; pid: number; devUrl?: string } = {\n port: this.bridgePort,\n pid: process.pid,\n }\n if (this.devUrl !== null) payload.devUrl = this.devUrl\n writeFileSync(path, JSON.stringify(payload))\n } catch {\n // Best-effort — failure to write the marker should not crash the server\n }\n }\n\n private removeActiveFile(): void {\n try {\n const path = mcpActiveFilePath()\n if (existsSync(path)) unlinkSync(path)\n } catch {\n // Ignore — file may already be gone\n }\n }\n\n /** Get tool definitions for MCP handshake */\n getTools(): ToolDefinition[] {\n return this.registry.listDefinitions()\n }\n\n /** Handle an MCP tool call */\n async handleToolCall(name: string, args: Record<string, unknown>): Promise<unknown> {\n const ctx: ToolContext = { relay: this.relay, cdp: this.cdp }\n return this.registry.dispatch(name, args, ctx)\n }\n}\n\n/**\n * Snapshot of all registered tool definitions. Kept as a named export for\n * backward compatibility with downstream consumers that used to import the\n * `TOOLS` array re-export under this alias.\n */\nexport const mcpToolDefinitions: ToolDefinition[] = (() => {\n const registry = new ToolRegistry()\n registerDebugApiTools(registry)\n registerCdpTools(registry)\n registerCompilerTools(registry)\n registerSourceTools(registry)\n registerSsrTools(registry)\n return registry.listDefinitions()\n})()\n"]}
@@ -1,3 +1,11 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * External-facing tool definition. Kept JSON-Schema-shaped on the
4
+ * outside so back-compat consumers (`getTools()`, the
5
+ * `mcpToolDefinitions` snapshot, tests asserting `inputSchema.properties`)
6
+ * keep working unchanged. The JSON Schema is derived from the Zod
7
+ * schema via `z.toJSONSchema` at registration time.
8
+ */
1
9
  export interface ToolDefinition {
2
10
  name: string;
3
11
  description: string;
@@ -68,12 +76,33 @@ export interface CdpTransport {
68
76
  reason?: string;
69
77
  }>;
70
78
  }
71
- export type ToolHandler = (args: Record<string, unknown>, ctx: ToolContext) => Promise<unknown>;
79
+ /**
80
+ * Author-facing tool spec. The Zod schema is the single source of
81
+ * truth for both runtime input validation and the JSON Schema
82
+ * published in `tools/list`. Handlers receive the parsed (and thus
83
+ * typed) arguments — no more `args.foo as string` ceremony at every
84
+ * call site.
85
+ */
86
+ export interface ToolSpec<S extends z.ZodObject<z.ZodRawShape> = z.ZodObject<z.ZodRawShape>> {
87
+ name: string;
88
+ description: string;
89
+ schema: S;
90
+ }
91
+ export type ToolHandler<S extends z.ZodObject<z.ZodRawShape> = z.ZodObject<z.ZodRawShape>> = (args: z.infer<S>, ctx: ToolContext) => Promise<unknown>;
72
92
  export declare class ToolRegistry {
73
93
  private entries;
74
- register(definition: ToolDefinition, layer: ToolLayer, handler: ToolHandler): void;
94
+ register<S extends z.ZodObject<z.ZodRawShape>>(spec: ToolSpec<S>, layer: ToolLayer, handler: ToolHandler<S>): void;
75
95
  dispatch(name: string, args: Record<string, unknown>, ctx: ToolContext): Promise<unknown>;
76
96
  listDefinitions(): ToolDefinition[];
97
+ /**
98
+ * Iterator over the registered (spec, handler) pairs. Used by
99
+ * `index.ts` to register each tool with the SDK's high-level
100
+ * `McpServer.registerTool` API.
101
+ */
102
+ listEntries(): {
103
+ spec: ToolSpec;
104
+ handler: ToolHandler;
105
+ }[];
77
106
  getLayer(name: string): ToolLayer | null;
78
107
  }
79
108
  //# sourceMappingURL=tool-registry.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tool-registry.d.ts","sourceRoot":"","sources":["../src/tool-registry.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ,CAAA;QACd,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;QACnC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;KACpB,CAAA;CACF;AAED,MAAM,MAAM,SAAS,GAAG,WAAW,GAAG,KAAK,GAAG,QAAQ,GAAG,UAAU,CAAA;AAEnE,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,cAAc,GAAG,IAAI,CAAA;IAC5B,GAAG,EAAE,YAAY,GAAG,IAAI,CAAA;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IACvD,WAAW,IAAI,OAAO,CAAA;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAA;IAClD,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAA;IACjB,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,MAAM,EAAE,OAAO,CAAA;IACf,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IACxF,WAAW,IAAI,OAAO,CAAA;IACtB,UAAU,CAAC,IAAI,EAAE;QACf,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,QAAQ,CAAC,EAAE,OAAO,CAAA;QAClB,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAA;KACxB,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC/D,qBAAqB,CAAC,IAAI,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,eAAe,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC/F,gBAAgB,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,YAAY,EAAE,CAAA;IAChE,gBAAgB,CACd,KAAK,CAAC,EAAE,MAAM,EACd,MAAM,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAChD,YAAY,EAAE,CAAA;IACjB,cAAc,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,UAAU,EAAE,CAAA;IAC5C,YAAY,IAAI,OAAO,CAAC;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAC9D;AAED,MAAM,MAAM,WAAW,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,WAAW,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;AAQ/F,qBAAa,YAAY;IACvB,OAAO,CAAC,OAAO,CAA2B;IAE1C,QAAQ,CAAC,UAAU,EAAE,cAAc,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,GAAG,IAAI;IAO5E,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC;IAM/F,eAAe,IAAI,cAAc,EAAE;IAInC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;CAGzC"}
1
+ {"version":3,"file":"tool-registry.d.ts","sourceRoot":"","sources":["../src/tool-registry.ts"],"names":[],"mappings":"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"}
@@ -1,19 +1,61 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Convert a Zod object schema to the JSON Schema shape this registry
4
+ * has historically exposed (`{type:'object', properties, required?}`).
5
+ * Filters out the `additionalProperties` and `$schema` keys Zod's
6
+ * `toJSONSchema` adds — they don't affect MCP clients but bloat the
7
+ * snapshot and break tests asserting an exact shape.
8
+ */
9
+ function toLegacyJsonSchema(schema) {
10
+ const raw = z.toJSONSchema(schema);
11
+ const out = {
12
+ type: 'object',
13
+ properties: raw.properties ?? {},
14
+ };
15
+ if (raw.required && raw.required.length > 0)
16
+ out.required = raw.required;
17
+ return out;
18
+ }
1
19
  export class ToolRegistry {
2
20
  entries = new Map();
3
- register(definition, layer, handler) {
4
- if (this.entries.has(definition.name)) {
5
- throw new Error(`Duplicate tool registration: ${definition.name}`);
21
+ register(spec, layer, handler) {
22
+ if (this.entries.has(spec.name)) {
23
+ throw new Error(`Duplicate tool registration: ${spec.name}`);
6
24
  }
7
- this.entries.set(definition.name, { definition, layer, handler });
25
+ this.entries.set(spec.name, {
26
+ spec: spec,
27
+ layer,
28
+ handler: handler,
29
+ jsonSchema: toLegacyJsonSchema(spec.schema),
30
+ });
8
31
  }
9
32
  async dispatch(name, args, ctx) {
10
33
  const entry = this.entries.get(name);
11
34
  if (!entry)
12
35
  throw new Error(`Unknown tool: ${name}`);
13
- return entry.handler(args, ctx);
36
+ // Validate input against the Zod schema before dispatching. Any
37
+ // mismatched / extra-required fields surface as a structured
38
+ // error here rather than a downstream `undefined.foo` crash.
39
+ const parsed = entry.spec.schema.safeParse(args);
40
+ if (!parsed.success) {
41
+ throw new Error(`Invalid args for ${name}: ${parsed.error.message}`);
42
+ }
43
+ return entry.handler(parsed.data, ctx);
14
44
  }
15
45
  listDefinitions() {
16
- return Array.from(this.entries.values()).map((e) => e.definition);
46
+ return Array.from(this.entries.values()).map((e) => ({
47
+ name: e.spec.name,
48
+ description: e.spec.description,
49
+ inputSchema: e.jsonSchema,
50
+ }));
51
+ }
52
+ /**
53
+ * Iterator over the registered (spec, handler) pairs. Used by
54
+ * `index.ts` to register each tool with the SDK's high-level
55
+ * `McpServer.registerTool` API.
56
+ */
57
+ listEntries() {
58
+ return Array.from(this.entries.values()).map((e) => ({ spec: e.spec, handler: e.handler }));
17
59
  }
18
60
  getLayer(name) {
19
61
  return this.entries.get(name)?.layer ?? null;
@@ -1 +1 @@
1
- {"version":3,"file":"tool-registry.js","sourceRoot":"","sources":["../src/tool-registry.ts"],"names":[],"mappings":"AA4EA,MAAM,OAAO,YAAY;IACf,OAAO,GAAG,IAAI,GAAG,EAAiB,CAAA;IAE1C,QAAQ,CAAC,UAA0B,EAAE,KAAgB,EAAE,OAAoB;QACzE,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACtC,MAAM,IAAI,KAAK,CAAC,gCAAgC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAA;QACpE,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAA;IACnE,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,IAAY,EAAE,IAA6B,EAAE,GAAgB;QAC1E,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QACpC,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAA;QACpD,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;IACjC,CAAC;IAED,eAAe;QACb,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAA;IACnE,CAAC;IAED,QAAQ,CAAC,IAAY;QACnB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,IAAI,IAAI,CAAA;IAC9C,CAAC;CACF","sourcesContent":["export interface ToolDefinition {\n name: string\n description: string\n inputSchema: {\n type: 'object'\n properties: Record<string, unknown>\n required?: string[]\n }\n}\n\nexport type ToolLayer = 'debug-api' | 'cdp' | 'source' | 'compiler'\n\nexport interface ToolContext {\n relay: RelayTransport | null\n cdp: CdpTransport | null\n}\n\nexport interface RelayTransport {\n call(method: string, args: unknown[]): Promise<unknown>\n isAvailable(): boolean\n}\n\nexport interface ConsoleEntry {\n level: 'log' | 'info' | 'warn' | 'error' | 'debug'\n text: string\n timestamp: number\n stackTrace?: string\n}\n\nexport interface NetworkEntry {\n requestId: string\n url: string\n method: string\n status: number | null\n startTime: number\n endTime: number | null\n durationMs: number | null\n failed: boolean\n failureReason?: string\n}\n\nexport interface ErrorEntry {\n text: string\n stack: string\n timestamp: number\n url?: string\n line?: number\n column?: number\n}\n\nexport interface CdpTransport {\n call(domain: string, method: string, params?: Record<string, unknown>): Promise<unknown>\n isAvailable(): boolean\n screenshot(opts: {\n selector?: string\n fullPage?: boolean\n format?: 'png' | 'jpeg'\n }): Promise<{ data: string; format: string; mimeType: string }>\n accessibilitySnapshot(opts: { selector?: string; interestingOnly?: boolean }): Promise<unknown>\n getConsoleBuffer(limit?: number, level?: string): ConsoleEntry[]\n getNetworkBuffer(\n limit?: number,\n filter?: { urlPattern?: string; status?: number },\n ): NetworkEntry[]\n getErrorBuffer(limit?: number): ErrorEntry[]\n closeBrowser(): Promise<{ closed: boolean; reason?: string }>\n}\n\nexport type ToolHandler = (args: Record<string, unknown>, ctx: ToolContext) => Promise<unknown>\n\ninterface Entry {\n definition: ToolDefinition\n layer: ToolLayer\n handler: ToolHandler\n}\n\nexport class ToolRegistry {\n private entries = new Map<string, Entry>()\n\n register(definition: ToolDefinition, layer: ToolLayer, handler: ToolHandler): void {\n if (this.entries.has(definition.name)) {\n throw new Error(`Duplicate tool registration: ${definition.name}`)\n }\n this.entries.set(definition.name, { definition, layer, handler })\n }\n\n async dispatch(name: string, args: Record<string, unknown>, ctx: ToolContext): Promise<unknown> {\n const entry = this.entries.get(name)\n if (!entry) throw new Error(`Unknown tool: ${name}`)\n return entry.handler(args, ctx)\n }\n\n listDefinitions(): ToolDefinition[] {\n return Array.from(this.entries.values()).map((e) => e.definition)\n }\n\n getLayer(name: string): ToolLayer | null {\n return this.entries.get(name)?.layer ?? null\n }\n}\n"]}
1
+ {"version":3,"file":"tool-registry.js","sourceRoot":"","sources":["../src/tool-registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAuGvB;;;;;;GAMG;AACH,SAAS,kBAAkB,CAAC,MAAkC;IAC5D,MAAM,GAAG,GAAG,CAAC,CAAC,YAAY,CAAC,MAAM,CAIhC,CAAA;IACD,MAAM,GAAG,GAAkC;QACzC,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE,GAAG,CAAC,UAAU,IAAI,EAAE;KACjC,CAAA;IACD,IAAI,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC;QAAE,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAA;IACxE,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,MAAM,OAAO,YAAY;IACf,OAAO,GAAG,IAAI,GAAG,EAAiB,CAAA;IAE1C,QAAQ,CACN,IAAiB,EACjB,KAAgB,EAChB,OAAuB;QAEvB,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CAAC,gCAAgC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA;QAC9D,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE;YAC1B,IAAI,EAAE,IAAgB;YACtB,KAAK;YACL,OAAO,EAAE,OAAsB;YAC/B,UAAU,EAAE,kBAAkB,CAAC,IAAI,CAAC,MAAM,CAAC;SAC5C,CAAC,CAAA;IACJ,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,IAAY,EAAE,IAA6B,EAAE,GAAgB;QAC1E,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QACpC,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAA;QACpD,gEAAgE;QAChE,6DAA6D;QAC7D,6DAA6D;QAC7D,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;QAChD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CAAC,oBAAoB,IAAI,KAAK,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAA;QACtE,CAAC;QACD,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;IACxC,CAAC;IAED,eAAe;QACb,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACnD,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI;YACjB,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,WAAW;YAC/B,WAAW,EAAE,CAAC,CAAC,UAAU;SAC1B,CAAC,CAAC,CAAA;IACL,CAAC;IAED;;;;OAIG;IACH,WAAW;QACT,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAA;IAC7F,CAAC;IAED,QAAQ,CAAC,IAAY;QACnB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,IAAI,IAAI,CAAA;IAC9C,CAAC;CACF","sourcesContent":["import { z } from 'zod'\n\n/**\n * External-facing tool definition. Kept JSON-Schema-shaped on the\n * outside so back-compat consumers (`getTools()`, the\n * `mcpToolDefinitions` snapshot, tests asserting `inputSchema.properties`)\n * keep working unchanged. The JSON Schema is derived from the Zod\n * schema via `z.toJSONSchema` at registration time.\n */\nexport interface ToolDefinition {\n name: string\n description: string\n inputSchema: {\n type: 'object'\n properties: Record<string, unknown>\n required?: string[]\n }\n}\n\nexport type ToolLayer = 'debug-api' | 'cdp' | 'source' | 'compiler'\n\nexport interface ToolContext {\n relay: RelayTransport | null\n cdp: CdpTransport | null\n}\n\nexport interface RelayTransport {\n call(method: string, args: unknown[]): Promise<unknown>\n isAvailable(): boolean\n}\n\nexport interface ConsoleEntry {\n level: 'log' | 'info' | 'warn' | 'error' | 'debug'\n text: string\n timestamp: number\n stackTrace?: string\n}\n\nexport interface NetworkEntry {\n requestId: string\n url: string\n method: string\n status: number | null\n startTime: number\n endTime: number | null\n durationMs: number | null\n failed: boolean\n failureReason?: string\n}\n\nexport interface ErrorEntry {\n text: string\n stack: string\n timestamp: number\n url?: string\n line?: number\n column?: number\n}\n\nexport interface CdpTransport {\n call(domain: string, method: string, params?: Record<string, unknown>): Promise<unknown>\n isAvailable(): boolean\n screenshot(opts: {\n selector?: string\n fullPage?: boolean\n format?: 'png' | 'jpeg'\n }): Promise<{ data: string; format: string; mimeType: string }>\n accessibilitySnapshot(opts: { selector?: string; interestingOnly?: boolean }): Promise<unknown>\n getConsoleBuffer(limit?: number, level?: string): ConsoleEntry[]\n getNetworkBuffer(\n limit?: number,\n filter?: { urlPattern?: string; status?: number },\n ): NetworkEntry[]\n getErrorBuffer(limit?: number): ErrorEntry[]\n closeBrowser(): Promise<{ closed: boolean; reason?: string }>\n}\n\n/**\n * Author-facing tool spec. The Zod schema is the single source of\n * truth for both runtime input validation and the JSON Schema\n * published in `tools/list`. Handlers receive the parsed (and thus\n * typed) arguments — no more `args.foo as string` ceremony at every\n * call site.\n */\nexport interface ToolSpec<S extends z.ZodObject<z.ZodRawShape> = z.ZodObject<z.ZodRawShape>> {\n name: string\n description: string\n schema: S\n}\n\nexport type ToolHandler<S extends z.ZodObject<z.ZodRawShape> = z.ZodObject<z.ZodRawShape>> = (\n args: z.infer<S>,\n ctx: ToolContext,\n) => Promise<unknown>\n\ninterface Entry {\n spec: ToolSpec\n layer: ToolLayer\n handler: ToolHandler\n /** Memoized JSON Schema for back-compat `ToolDefinition.inputSchema`. */\n jsonSchema: ToolDefinition['inputSchema']\n}\n\n/**\n * Convert a Zod object schema to the JSON Schema shape this registry\n * has historically exposed (`{type:'object', properties, required?}`).\n * Filters out the `additionalProperties` and `$schema` keys Zod's\n * `toJSONSchema` adds — they don't affect MCP clients but bloat the\n * snapshot and break tests asserting an exact shape.\n */\nfunction toLegacyJsonSchema(schema: z.ZodObject<z.ZodRawShape>): ToolDefinition['inputSchema'] {\n const raw = z.toJSONSchema(schema) as {\n type?: string\n properties?: Record<string, unknown>\n required?: string[]\n }\n const out: ToolDefinition['inputSchema'] = {\n type: 'object',\n properties: raw.properties ?? {},\n }\n if (raw.required && raw.required.length > 0) out.required = raw.required\n return out\n}\n\nexport class ToolRegistry {\n private entries = new Map<string, Entry>()\n\n register<S extends z.ZodObject<z.ZodRawShape>>(\n spec: ToolSpec<S>,\n layer: ToolLayer,\n handler: ToolHandler<S>,\n ): void {\n if (this.entries.has(spec.name)) {\n throw new Error(`Duplicate tool registration: ${spec.name}`)\n }\n this.entries.set(spec.name, {\n spec: spec as ToolSpec,\n layer,\n handler: handler as ToolHandler,\n jsonSchema: toLegacyJsonSchema(spec.schema),\n })\n }\n\n async dispatch(name: string, args: Record<string, unknown>, ctx: ToolContext): Promise<unknown> {\n const entry = this.entries.get(name)\n if (!entry) throw new Error(`Unknown tool: ${name}`)\n // Validate input against the Zod schema before dispatching. Any\n // mismatched / extra-required fields surface as a structured\n // error here rather than a downstream `undefined.foo` crash.\n const parsed = entry.spec.schema.safeParse(args)\n if (!parsed.success) {\n throw new Error(`Invalid args for ${name}: ${parsed.error.message}`)\n }\n return entry.handler(parsed.data, ctx)\n }\n\n listDefinitions(): ToolDefinition[] {\n return Array.from(this.entries.values()).map((e) => ({\n name: e.spec.name,\n description: e.spec.description,\n inputSchema: e.jsonSchema,\n }))\n }\n\n /**\n * Iterator over the registered (spec, handler) pairs. Used by\n * `index.ts` to register each tool with the SDK's high-level\n * `McpServer.registerTool` API.\n */\n listEntries(): { spec: ToolSpec; handler: ToolHandler }[] {\n return Array.from(this.entries.values()).map((e) => ({ spec: e.spec, handler: e.handler }))\n }\n\n getLayer(name: string): ToolLayer | null {\n return this.entries.get(name)?.layer ?? null\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"cdp.d.ts","sourceRoot":"","sources":["../../src/tools/cdp.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAEvD,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI,CAyJ7D"}
1
+ {"version":3,"file":"cdp.d.ts","sourceRoot":"","sources":["../../src/tools/cdp.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAEvD,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI,CAwH7D"}
package/dist/tools/cdp.js CHANGED
@@ -1,22 +1,13 @@
1
+ import { z } from 'zod';
1
2
  export function registerCdpTools(registry) {
2
3
  registry.register({
3
4
  name: 'llui_screenshot',
4
5
  description: 'Capture a screenshot of the browser page or a specific element. Returns base64-encoded PNG or JPEG. Requires CDP transport (browser attached via :9222 or Playwright fallback).',
5
- inputSchema: {
6
- type: 'object',
7
- properties: {
8
- selector: { type: 'string', description: 'CSS selector screenshot only this element' },
9
- fullPage: {
10
- type: 'boolean',
11
- description: 'Capture full scrollable page (default false)',
12
- },
13
- format: {
14
- type: 'string',
15
- enum: ['png', 'jpeg'],
16
- description: 'Image format (default png)',
17
- },
18
- },
19
- },
6
+ schema: z.object({
7
+ selector: z.string().optional().describe('CSS selector — screenshot only this element'),
8
+ fullPage: z.boolean().optional().describe('Capture full scrollable page (default false)'),
9
+ format: z.enum(['png', 'jpeg']).optional().describe('Image format (default png)'),
10
+ }),
20
11
  }, 'cdp', async (args, ctx) => {
21
12
  if (!ctx.cdp)
22
13
  return cdpUnavailable();
@@ -29,19 +20,13 @@ export function registerCdpTools(registry) {
29
20
  registry.register({
30
21
  name: 'llui_a11y_tree',
31
22
  description: 'Return the accessibility tree for the page or a specific element. Useful for verifying ARIA roles, labels, and keyboard navigation structure.',
32
- inputSchema: {
33
- type: 'object',
34
- properties: {
35
- selector: {
36
- type: 'string',
37
- description: 'Root element CSS selector (default: full page)',
38
- },
39
- interestingOnly: {
40
- type: 'boolean',
41
- description: 'Omit nodes with no accessibility-relevant attributes (default true)',
42
- },
43
- },
44
- },
23
+ schema: z.object({
24
+ selector: z.string().optional().describe('Root element CSS selector (default: full page)'),
25
+ interestingOnly: z
26
+ .boolean()
27
+ .optional()
28
+ .describe('Omit nodes with no accessibility-relevant attributes (default true)'),
29
+ }),
45
30
  }, 'cdp', async (args, ctx) => {
46
31
  if (!ctx.cdp)
47
32
  return cdpUnavailable();
@@ -53,58 +38,44 @@ export function registerCdpTools(registry) {
53
38
  registry.register({
54
39
  name: 'llui_network_tail',
55
40
  description: 'Return recent network requests captured since the CDP session started. Includes URL, method, status, timing, and failure info.',
56
- inputSchema: {
57
- type: 'object',
58
- properties: {
59
- limit: {
60
- type: 'number',
61
- description: 'Max entries to return (default: all buffered, max 500)',
62
- },
63
- filter: {
64
- type: 'object',
65
- properties: {
66
- urlPattern: { type: 'string', description: 'Regex pattern to match URLs' },
67
- status: { type: 'number', description: 'HTTP status code to filter on' },
68
- },
69
- },
70
- },
71
- },
41
+ schema: z.object({
42
+ limit: z
43
+ .number()
44
+ .optional()
45
+ .describe('Max entries to return (default: all buffered, max 500)'),
46
+ filter: z
47
+ .object({
48
+ urlPattern: z.string().optional().describe('Regex pattern to match URLs'),
49
+ status: z.number().optional().describe('HTTP status code to filter on'),
50
+ })
51
+ .optional(),
52
+ }),
72
53
  }, 'cdp', async (args, ctx) => {
73
54
  if (!ctx.cdp)
74
55
  return cdpUnavailable();
75
- const filter = args.filter;
76
- return { entries: ctx.cdp.getNetworkBuffer(args.limit, filter) };
56
+ return { entries: ctx.cdp.getNetworkBuffer(args.limit, args.filter) };
77
57
  });
78
58
  registry.register({
79
59
  name: 'llui_console_tail',
80
60
  description: 'Return recent browser console entries (log, info, warn, error, debug) captured since the CDP session started.',
81
- inputSchema: {
82
- type: 'object',
83
- properties: {
84
- limit: { type: 'number', description: 'Max entries to return' },
85
- level: {
86
- type: 'string',
87
- enum: ['log', 'info', 'warn', 'error', 'debug'],
88
- description: 'Filter to this level only',
89
- },
90
- },
91
- },
61
+ schema: z.object({
62
+ limit: z.number().optional().describe('Max entries to return'),
63
+ level: z
64
+ .enum(['log', 'info', 'warn', 'error', 'debug'])
65
+ .optional()
66
+ .describe('Filter to this level only'),
67
+ }),
92
68
  }, 'cdp', async (args, ctx) => {
93
69
  if (!ctx.cdp)
94
70
  return cdpUnavailable();
95
- return {
96
- entries: ctx.cdp.getConsoleBuffer(args.limit, args.level),
97
- };
71
+ return { entries: ctx.cdp.getConsoleBuffer(args.limit, args.level) };
98
72
  });
99
73
  registry.register({
100
74
  name: 'llui_uncaught_errors',
101
75
  description: 'Return recent uncaught JavaScript exceptions captured since the CDP session started.',
102
- inputSchema: {
103
- type: 'object',
104
- properties: {
105
- limit: { type: 'number', description: 'Max entries to return' },
106
- },
107
- },
76
+ schema: z.object({
77
+ limit: z.number().optional().describe('Max entries to return'),
78
+ }),
108
79
  }, 'cdp', async (args, ctx) => {
109
80
  if (!ctx.cdp)
110
81
  return cdpUnavailable();
@@ -113,7 +84,7 @@ export function registerCdpTools(registry) {
113
84
  registry.register({
114
85
  name: 'llui_browser_close',
115
86
  description: 'Close the Playwright-owned fallback browser and clear the CDP session buffers. No-op if the browser is user-owned (attached via :9222).',
116
- inputSchema: { type: 'object', properties: {} },
87
+ schema: z.object({}),
117
88
  }, 'cdp', async (_args, ctx) => {
118
89
  if (!ctx.cdp)
119
90
  return { closed: false, reason: 'no_cdp_transport' };
@@ -1 +1 @@
1
- {"version":3,"file":"cdp.js","sourceRoot":"","sources":["../../src/tools/cdp.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,gBAAgB,CAAC,QAAsB;IACrD,QAAQ,CAAC,QAAQ,CACf;QACE,IAAI,EAAE,iBAAiB;QACvB,WAAW,EACT,iLAAiL;QACnL,WAAW,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACV,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,6CAA6C,EAAE;gBACxF,QAAQ,EAAE;oBACR,IAAI,EAAE,SAAS;oBACf,WAAW,EAAE,8CAA8C;iBAC5D;gBACD,MAAM,EAAE;oBACN,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC;oBACrB,WAAW,EAAE,4BAA4B;iBAC1C;aACF;SACF;KACF,EACD,KAAK,EACL,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE;QAClB,IAAI,CAAC,GAAG,CAAC,GAAG;YAAE,OAAO,cAAc,EAAE,CAAA;QACrC,OAAO,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC;YACxB,QAAQ,EAAE,IAAI,CAAC,QAA8B;YAC7C,QAAQ,EAAE,IAAI,CAAC,QAA+B;YAC9C,MAAM,EAAE,IAAI,CAAC,MAAoC;SAClD,CAAC,CAAA;IACJ,CAAC,CACF,CAAA;IAED,QAAQ,CAAC,QAAQ,CACf;QACE,IAAI,EAAE,gBAAgB;QACtB,WAAW,EACT,+IAA+I;QACjJ,WAAW,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACV,QAAQ,EAAE;oBACR,IAAI,EAAE,QAAQ;oBACd,WAAW,EAAE,gDAAgD;iBAC9D;gBACD,eAAe,EAAE;oBACf,IAAI,EAAE,SAAS;oBACf,WAAW,EAAE,qEAAqE;iBACnF;aACF;SACF;KACF,EACD,KAAK,EACL,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE;QAClB,IAAI,CAAC,GAAG,CAAC,GAAG;YAAE,OAAO,cAAc,EAAE,CAAA;QACrC,OAAO,GAAG,CAAC,GAAG,CAAC,qBAAqB,CAAC;YACnC,QAAQ,EAAE,IAAI,CAAC,QAA8B;YAC7C,eAAe,EAAG,IAAI,CAAC,eAAuC,IAAI,IAAI;SACvE,CAAC,CAAA;IACJ,CAAC,CACF,CAAA;IAED,QAAQ,CAAC,QAAQ,CACf;QACE,IAAI,EAAE,mBAAmB;QACzB,WAAW,EACT,gIAAgI;QAClI,WAAW,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACV,KAAK,EAAE;oBACL,IAAI,EAAE,QAAQ;oBACd,WAAW,EAAE,wDAAwD;iBACtE;gBACD,MAAM,EAAE;oBACN,IAAI,EAAE,QAAQ;oBACd,UAAU,EAAE;wBACV,UAAU,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,6BAA6B,EAAE;wBAC1E,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,+BAA+B,EAAE;qBACzE;iBACF;aACF;SACF;KACF,EACD,KAAK,EACL,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE;QAClB,IAAI,CAAC,GAAG,CAAC,GAAG;YAAE,OAAO,cAAc,EAAE,CAAA;QACrC,MAAM,MAAM,GAAG,IAAI,CAAC,MAA8D,CAAA;QAClF,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,GAAG,CAAC,gBAAgB,CAAC,IAAI,CAAC,KAA2B,EAAE,MAAM,CAAC,EAAE,CAAA;IACxF,CAAC,CACF,CAAA;IAED,QAAQ,CAAC,QAAQ,CACf;QACE,IAAI,EAAE,mBAAmB;QACzB,WAAW,EACT,+GAA+G;QACjH,WAAW,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACV,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,uBAAuB,EAAE;gBAC/D,KAAK,EAAE;oBACL,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC;oBAC/C,WAAW,EAAE,2BAA2B;iBACzC;aACF;SACF;KACF,EACD,KAAK,EACL,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE;QAClB,IAAI,CAAC,GAAG,CAAC,GAAG;YAAE,OAAO,cAAc,EAAE,CAAA;QACrC,OAAO;YACL,OAAO,EAAE,GAAG,CAAC,GAAG,CAAC,gBAAgB,CAC/B,IAAI,CAAC,KAA2B,EAChC,IAAI,CAAC,KAA2B,CACjC;SACF,CAAA;IACH,CAAC,CACF,CAAA;IAED,QAAQ,CAAC,QAAQ,CACf;QACE,IAAI,EAAE,sBAAsB;QAC5B,WAAW,EACT,sFAAsF;QACxF,WAAW,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACV,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,uBAAuB,EAAE;aAChE;SACF;KACF,EACD,KAAK,EACL,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE;QAClB,IAAI,CAAC,GAAG,CAAC,GAAG;YAAE,OAAO,cAAc,EAAE,CAAA;QACrC,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,CAAC,KAA2B,CAAC,EAAE,CAAA;IAC7E,CAAC,CACF,CAAA;IAED,QAAQ,CAAC,QAAQ,CACf;QACE,IAAI,EAAE,oBAAoB;QAC1B,WAAW,EACT,yIAAyI;QAC3I,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,EAAE;KAChD,EACD,KAAK,EACL,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QACnB,IAAI,CAAC,GAAG,CAAC,GAAG;YAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAA;QAClE,OAAO,GAAG,CAAC,GAAG,CAAC,YAAY,EAAE,CAAA;IAC/B,CAAC,CACF,CAAA;AACH,CAAC;AAED,SAAS,cAAc;IACrB,OAAO;QACL,EAAE,EAAE,KAAK;QACT,KAAK,EAAE,iBAAiB;QACxB,IAAI,EAAE,mEAAmE;KAC1E,CAAA;AACH,CAAC","sourcesContent":["import type { ToolRegistry } from '../tool-registry.js'\n\nexport function registerCdpTools(registry: ToolRegistry): void {\n registry.register(\n {\n name: 'llui_screenshot',\n description:\n 'Capture a screenshot of the browser page or a specific element. Returns base64-encoded PNG or JPEG. Requires CDP transport (browser attached via :9222 or Playwright fallback).',\n inputSchema: {\n type: 'object',\n properties: {\n selector: { type: 'string', description: 'CSS selector — screenshot only this element' },\n fullPage: {\n type: 'boolean',\n description: 'Capture full scrollable page (default false)',\n },\n format: {\n type: 'string',\n enum: ['png', 'jpeg'],\n description: 'Image format (default png)',\n },\n },\n },\n },\n 'cdp',\n async (args, ctx) => {\n if (!ctx.cdp) return cdpUnavailable()\n return ctx.cdp.screenshot({\n selector: args.selector as string | undefined,\n fullPage: args.fullPage as boolean | undefined,\n format: args.format as 'png' | 'jpeg' | undefined,\n })\n },\n )\n\n registry.register(\n {\n name: 'llui_a11y_tree',\n description:\n 'Return the accessibility tree for the page or a specific element. Useful for verifying ARIA roles, labels, and keyboard navigation structure.',\n inputSchema: {\n type: 'object',\n properties: {\n selector: {\n type: 'string',\n description: 'Root element CSS selector (default: full page)',\n },\n interestingOnly: {\n type: 'boolean',\n description: 'Omit nodes with no accessibility-relevant attributes (default true)',\n },\n },\n },\n },\n 'cdp',\n async (args, ctx) => {\n if (!ctx.cdp) return cdpUnavailable()\n return ctx.cdp.accessibilitySnapshot({\n selector: args.selector as string | undefined,\n interestingOnly: (args.interestingOnly as boolean | undefined) ?? true,\n })\n },\n )\n\n registry.register(\n {\n name: 'llui_network_tail',\n description:\n 'Return recent network requests captured since the CDP session started. Includes URL, method, status, timing, and failure info.',\n inputSchema: {\n type: 'object',\n properties: {\n limit: {\n type: 'number',\n description: 'Max entries to return (default: all buffered, max 500)',\n },\n filter: {\n type: 'object',\n properties: {\n urlPattern: { type: 'string', description: 'Regex pattern to match URLs' },\n status: { type: 'number', description: 'HTTP status code to filter on' },\n },\n },\n },\n },\n },\n 'cdp',\n async (args, ctx) => {\n if (!ctx.cdp) return cdpUnavailable()\n const filter = args.filter as { urlPattern?: string; status?: number } | undefined\n return { entries: ctx.cdp.getNetworkBuffer(args.limit as number | undefined, filter) }\n },\n )\n\n registry.register(\n {\n name: 'llui_console_tail',\n description:\n 'Return recent browser console entries (log, info, warn, error, debug) captured since the CDP session started.',\n inputSchema: {\n type: 'object',\n properties: {\n limit: { type: 'number', description: 'Max entries to return' },\n level: {\n type: 'string',\n enum: ['log', 'info', 'warn', 'error', 'debug'],\n description: 'Filter to this level only',\n },\n },\n },\n },\n 'cdp',\n async (args, ctx) => {\n if (!ctx.cdp) return cdpUnavailable()\n return {\n entries: ctx.cdp.getConsoleBuffer(\n args.limit as number | undefined,\n args.level as string | undefined,\n ),\n }\n },\n )\n\n registry.register(\n {\n name: 'llui_uncaught_errors',\n description:\n 'Return recent uncaught JavaScript exceptions captured since the CDP session started.',\n inputSchema: {\n type: 'object',\n properties: {\n limit: { type: 'number', description: 'Max entries to return' },\n },\n },\n },\n 'cdp',\n async (args, ctx) => {\n if (!ctx.cdp) return cdpUnavailable()\n return { errors: ctx.cdp.getErrorBuffer(args.limit as number | undefined) }\n },\n )\n\n registry.register(\n {\n name: 'llui_browser_close',\n description:\n 'Close the Playwright-owned fallback browser and clear the CDP session buffers. No-op if the browser is user-owned (attached via :9222).',\n inputSchema: { type: 'object', properties: {} },\n },\n 'cdp',\n async (_args, ctx) => {\n if (!ctx.cdp) return { closed: false, reason: 'no_cdp_transport' }\n return ctx.cdp.closeBrowser()\n },\n )\n}\n\nfunction cdpUnavailable() {\n return {\n ok: false,\n error: 'cdp_unavailable',\n hint: 'CDP transport is not configured. Pass --url <devUrl> to llui-mcp.',\n }\n}\n"]}
1
+ {"version":3,"file":"cdp.js","sourceRoot":"","sources":["../../src/tools/cdp.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod'\nimport type { ToolRegistry } from '../tool-registry.js'\n\nexport function registerCdpTools(registry: ToolRegistry): void {\n registry.register(\n {\n name: 'llui_screenshot',\n description:\n 'Capture a screenshot of the browser page or a specific element. Returns base64-encoded PNG or JPEG. Requires CDP transport (browser attached via :9222 or Playwright fallback).',\n schema: z.object({\n selector: z.string().optional().describe('CSS selector — screenshot only this element'),\n fullPage: z.boolean().optional().describe('Capture full scrollable page (default false)'),\n format: z.enum(['png', 'jpeg']).optional().describe('Image format (default png)'),\n }),\n },\n 'cdp',\n async (args, ctx) => {\n if (!ctx.cdp) return cdpUnavailable()\n return ctx.cdp.screenshot({\n selector: args.selector,\n fullPage: args.fullPage,\n format: args.format,\n })\n },\n )\n\n registry.register(\n {\n name: 'llui_a11y_tree',\n description:\n 'Return the accessibility tree for the page or a specific element. Useful for verifying ARIA roles, labels, and keyboard navigation structure.',\n schema: z.object({\n selector: z.string().optional().describe('Root element CSS selector (default: full page)'),\n interestingOnly: z\n .boolean()\n .optional()\n .describe('Omit nodes with no accessibility-relevant attributes (default true)'),\n }),\n },\n 'cdp',\n async (args, ctx) => {\n if (!ctx.cdp) return cdpUnavailable()\n return ctx.cdp.accessibilitySnapshot({\n selector: args.selector,\n interestingOnly: args.interestingOnly ?? true,\n })\n },\n )\n\n registry.register(\n {\n name: 'llui_network_tail',\n description:\n 'Return recent network requests captured since the CDP session started. Includes URL, method, status, timing, and failure info.',\n schema: z.object({\n limit: z\n .number()\n .optional()\n .describe('Max entries to return (default: all buffered, max 500)'),\n filter: z\n .object({\n urlPattern: z.string().optional().describe('Regex pattern to match URLs'),\n status: z.number().optional().describe('HTTP status code to filter on'),\n })\n .optional(),\n }),\n },\n 'cdp',\n async (args, ctx) => {\n if (!ctx.cdp) return cdpUnavailable()\n return { entries: ctx.cdp.getNetworkBuffer(args.limit, args.filter) }\n },\n )\n\n registry.register(\n {\n name: 'llui_console_tail',\n description:\n 'Return recent browser console entries (log, info, warn, error, debug) captured since the CDP session started.',\n schema: z.object({\n limit: z.number().optional().describe('Max entries to return'),\n level: z\n .enum(['log', 'info', 'warn', 'error', 'debug'])\n .optional()\n .describe('Filter to this level only'),\n }),\n },\n 'cdp',\n async (args, ctx) => {\n if (!ctx.cdp) return cdpUnavailable()\n return { entries: ctx.cdp.getConsoleBuffer(args.limit, args.level) }\n },\n )\n\n registry.register(\n {\n name: 'llui_uncaught_errors',\n description:\n 'Return recent uncaught JavaScript exceptions captured since the CDP session started.',\n schema: z.object({\n limit: z.number().optional().describe('Max entries to return'),\n }),\n },\n 'cdp',\n async (args, ctx) => {\n if (!ctx.cdp) return cdpUnavailable()\n return { errors: ctx.cdp.getErrorBuffer(args.limit) }\n },\n )\n\n registry.register(\n {\n name: 'llui_browser_close',\n description:\n 'Close the Playwright-owned fallback browser and clear the CDP session buffers. No-op if the browser is user-owned (attached via :9222).',\n schema: z.object({}),\n },\n 'cdp',\n async (_args, ctx) => {\n if (!ctx.cdp) return { closed: false, reason: 'no_cdp_transport' }\n return ctx.cdp.closeBrowser()\n },\n )\n}\n\nfunction cdpUnavailable() {\n return {\n ok: false,\n error: 'cdp_unavailable',\n hint: 'CDP transport is not configured. Pass --url <devUrl> to llui-mcp.',\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"compiler.d.ts","sourceRoot":"","sources":["../../src/tools/compiler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAEvD,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI,CA8ElE"}
1
+ {"version":3,"file":"compiler.d.ts","sourceRoot":"","sources":["../../src/tools/compiler.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAEvD,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI,CA6DlE"}