@llui/mcp 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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,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"}
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;AAqCxF;;;;;;;;;;;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;IA8B5D;;;;;;;;;;;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,EAS3C,CAAA"}
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import { dirname, resolve } from 'node:path';
3
3
  import { fileURLToPath } from 'node:url';
4
4
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
5
5
  import { ToolRegistry } from './tool-registry.js';
6
- import { registerDebugApiTools, registerCdpTools, registerCompilerTools, registerSourceTools, registerSsrTools, } from './tools/index.js';
6
+ import { registerDebugApiTools, registerCdpTools, registerCompilerTools, registerStaticCompilerTools, registerSourceTools, registerSsrTools, } from './tools/index.js';
7
7
  import { WebSocketRelayTransport, RelayUnavailableError, CdpSessionManager, } from './transports/index.js';
8
8
  /**
9
9
  * Version advertised in the MCP `initialize` handshake. Read once from
@@ -106,6 +106,7 @@ export class LluiMcpServer {
106
106
  registerDebugApiTools(this.registry);
107
107
  registerCdpTools(this.registry);
108
108
  registerCompilerTools(this.registry);
109
+ registerStaticCompilerTools(this.registry);
109
110
  registerSourceTools(this.registry);
110
111
  registerSsrTools(this.registry);
111
112
  // SDK-managed MCP server — owns the JSON-RPC protocol, handshake,
@@ -261,6 +262,7 @@ export const mcpToolDefinitions = (() => {
261
262
  registerDebugApiTools(registry);
262
263
  registerCdpTools(registry);
263
264
  registerCompilerTools(registry);
265
+ registerStaticCompilerTools(registry);
264
266
  registerSourceTools(registry);
265
267
  registerSsrTools(registry);
266
268
  return registry.listDefinitions();
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["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
+ {"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 registerStaticCompilerTools,\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 registerStaticCompilerTools(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 registerStaticCompilerTools(registry)\n registerSourceTools(registry)\n registerSsrTools(registry)\n return registry.listDefinitions()\n})()\n"]}
@@ -1,6 +1,7 @@
1
1
  export { registerDebugApiTools } from './debug-api.js';
2
2
  export { registerCdpTools } from './cdp.js';
3
3
  export { registerCompilerTools } from './compiler.js';
4
+ export { registerStaticCompilerTools } from './static-compiler.js';
4
5
  export { registerSourceTools } from './source.js';
5
6
  export { registerSsrTools } from './ssr.js';
6
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAC3C,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AACrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAC3C,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AACrD,OAAO,EAAE,2BAA2B,EAAE,MAAM,sBAAsB,CAAA;AAClE,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA"}
@@ -1,6 +1,7 @@
1
1
  export { registerDebugApiTools } from './debug-api.js';
2
2
  export { registerCdpTools } from './cdp.js';
3
3
  export { registerCompilerTools } from './compiler.js';
4
+ export { registerStaticCompilerTools } from './static-compiler.js';
4
5
  export { registerSourceTools } from './source.js';
5
6
  export { registerSsrTools } from './ssr.js';
6
7
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAC3C,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AACrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA","sourcesContent":["export { registerDebugApiTools } from './debug-api.js'\nexport { registerCdpTools } from './cdp.js'\nexport { registerCompilerTools } from './compiler.js'\nexport { registerSourceTools } from './source.js'\nexport { registerSsrTools } from './ssr.js'\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAC3C,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AACrD,OAAO,EAAE,2BAA2B,EAAE,MAAM,sBAAsB,CAAA;AAClE,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA","sourcesContent":["export { registerDebugApiTools } from './debug-api.js'\nexport { registerCdpTools } from './cdp.js'\nexport { registerCompilerTools } from './compiler.js'\nexport { registerStaticCompilerTools } from './static-compiler.js'\nexport { registerSourceTools } from './source.js'\nexport { registerSsrTools } from './ssr.js'\n"]}
@@ -0,0 +1,22 @@
1
+ import type { ToolRegistry } from '../tool-registry.js';
2
+ /**
3
+ * v2c §4 — MCP static-mode tools.
4
+ *
5
+ * Adapter over the @llui/compiler engine. Live-mode tools (in
6
+ * `tools/compiler.ts`) call into the running runtime via `ctx.relay`;
7
+ * static-mode tools answer the same questions from source. Useful when
8
+ * no app is running (CI, code-review LLMs, offline analysis), and as a
9
+ * sanity check that live answers match what the compiler would produce.
10
+ *
11
+ * Naming: every static tool carries the `llui_static_` prefix to keep
12
+ * the live/static surface explicit. Tools that fundamentally need
13
+ * runtime state (binding indices, message history, DOM trees) stay
14
+ * live-only.
15
+ *
16
+ * The dispatch table in `index.ts` does NOT prefer-live-when-available
17
+ * yet — both variants ship as distinct tool names. Future v2c work
18
+ * unifies them under one tool that picks the right backend; this push
19
+ * keeps them separate so the contract is observable.
20
+ */
21
+ export declare function registerStaticCompilerTools(registry: ToolRegistry): void;
22
+ //# sourceMappingURL=static-compiler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"static-compiler.d.ts","sourceRoot":"","sources":["../../src/tools/static-compiler.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAQvD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI,CAkGxE"}
@@ -0,0 +1,116 @@
1
+ import { z } from 'zod';
2
+ import { readFileSync } from 'node:fs';
3
+ import { resolve } from 'node:path';
4
+ import { transformLlui, collectDeps, registerIntrospectionFactory, registerDevtoolsFactory, } from '@llui/compiler';
5
+ import { introspectionFactory } from '@llui/compiler-introspection';
6
+ import { devtoolsFactory } from '@llui/compiler-devtools';
7
+ // Register opt-in module factories at module-import time. @llui/compiler
8
+ // doesn't depend on its sibling packages; the MCP package wires them
9
+ // when its static-compiler tool is loaded.
10
+ registerIntrospectionFactory(introspectionFactory);
11
+ registerDevtoolsFactory(devtoolsFactory);
12
+ /**
13
+ * v2c §4 — MCP static-mode tools.
14
+ *
15
+ * Adapter over the @llui/compiler engine. Live-mode tools (in
16
+ * `tools/compiler.ts`) call into the running runtime via `ctx.relay`;
17
+ * static-mode tools answer the same questions from source. Useful when
18
+ * no app is running (CI, code-review LLMs, offline analysis), and as a
19
+ * sanity check that live answers match what the compiler would produce.
20
+ *
21
+ * Naming: every static tool carries the `llui_static_` prefix to keep
22
+ * the live/static surface explicit. Tools that fundamentally need
23
+ * runtime state (binding indices, message history, DOM trees) stay
24
+ * live-only.
25
+ *
26
+ * The dispatch table in `index.ts` does NOT prefer-live-when-available
27
+ * yet — both variants ship as distinct tool names. Future v2c work
28
+ * unifies them under one tool that picks the right backend; this push
29
+ * keeps them separate so the contract is observable.
30
+ */
31
+ export function registerStaticCompilerTools(registry) {
32
+ registry.register({
33
+ name: 'llui_static_show_compiled',
34
+ description: 'Static counterpart of `llui_show_compiled`. Read a source file from disk and return its pre-transform source plus the post-transform output @llui/compiler would produce. Works without a running app.',
35
+ schema: z.object({
36
+ file: z
37
+ .string()
38
+ .describe('Absolute or workspace-relative path to a .ts/.tsx file. Reads the file from disk and runs the compiler transform on it.'),
39
+ }),
40
+ }, 'compiler', async (args) => {
41
+ const absPath = resolve(args.file);
42
+ let source;
43
+ try {
44
+ source = readFileSync(absPath, 'utf8');
45
+ }
46
+ catch (err) {
47
+ return {
48
+ pre: null,
49
+ post: null,
50
+ error: `Could not read file: ${err.message}`,
51
+ };
52
+ }
53
+ const result = transformLlui(source, absPath, false, false);
54
+ if (!result) {
55
+ return {
56
+ pre: source,
57
+ post: null,
58
+ note: 'File contains no @llui/dom imports / no reactive content; nothing to transform.',
59
+ };
60
+ }
61
+ return { pre: source, post: result.output };
62
+ });
63
+ registry.register({
64
+ name: 'llui_static_collect_paths',
65
+ description: 'Return the reactive state-access paths the @llui/compiler would extract from a file, plus the per-bit assignments and a budget summary. Companion to `llui_explain_mask` (live) — works without a running app, useful for understanding why a binding does/does not pick up a state change.',
66
+ schema: z.object({
67
+ file: z.string().describe('Absolute or workspace-relative path to a .ts/.tsx file.'),
68
+ }),
69
+ }, 'compiler', async (args) => {
70
+ const absPath = resolve(args.file);
71
+ let source;
72
+ try {
73
+ source = readFileSync(absPath, 'utf8');
74
+ }
75
+ catch (err) {
76
+ return {
77
+ paths: [],
78
+ error: `Could not read file: ${err.message}`,
79
+ };
80
+ }
81
+ const { lo, hi } = collectDeps(source);
82
+ const lowEntries = [...lo.entries()].map(([path, bit]) => ({
83
+ path,
84
+ bit,
85
+ word: 'lo',
86
+ }));
87
+ const highEntries = [...hi.entries()].map(([path, bit]) => ({
88
+ path,
89
+ bit,
90
+ word: 'hi',
91
+ }));
92
+ const all = [...lowEntries, ...highEntries];
93
+ const total = all.length;
94
+ // Top-level field rollup so callers can see which slices dominate.
95
+ const byTopLevel = new Map();
96
+ for (const e of all) {
97
+ const top = e.path.split('.', 1)[0];
98
+ byTopLevel.set(top, (byTopLevel.get(top) ?? 0) + 1);
99
+ }
100
+ const breakdown = [...byTopLevel.entries()]
101
+ .sort((a, b) => b[1] - a[1])
102
+ .map(([field, count]) => ({ field, count }));
103
+ // Budget warning. 62 is the two-word ceiling; past it the
104
+ // bitmask-overflow rule fires and bindings fall back to FULL_MASK.
105
+ const overflow = total > 62;
106
+ const fullMaskBits = all.filter((e) => e.bit === -1).length;
107
+ return {
108
+ total,
109
+ overflow,
110
+ fullMaskBits,
111
+ breakdown,
112
+ paths: all,
113
+ };
114
+ });
115
+ }
116
+ //# sourceMappingURL=static-compiler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"static-compiler.js","sourceRoot":"","sources":["../../src/tools/static-compiler.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod'\nimport { readFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport {\n transformLlui,\n collectDeps,\n registerIntrospectionFactory,\n registerDevtoolsFactory,\n} from '@llui/compiler'\nimport { introspectionFactory } from '@llui/compiler-introspection'\nimport { devtoolsFactory } from '@llui/compiler-devtools'\nimport type { ToolRegistry } from '../tool-registry.js'\n\n// Register opt-in module factories at module-import time. @llui/compiler\n// doesn't depend on its sibling packages; the MCP package wires them\n// when its static-compiler tool is loaded.\nregisterIntrospectionFactory(introspectionFactory)\nregisterDevtoolsFactory(devtoolsFactory)\n\n/**\n * v2c §4 — MCP static-mode tools.\n *\n * Adapter over the @llui/compiler engine. Live-mode tools (in\n * `tools/compiler.ts`) call into the running runtime via `ctx.relay`;\n * static-mode tools answer the same questions from source. Useful when\n * no app is running (CI, code-review LLMs, offline analysis), and as a\n * sanity check that live answers match what the compiler would produce.\n *\n * Naming: every static tool carries the `llui_static_` prefix to keep\n * the live/static surface explicit. Tools that fundamentally need\n * runtime state (binding indices, message history, DOM trees) stay\n * live-only.\n *\n * The dispatch table in `index.ts` does NOT prefer-live-when-available\n * yet — both variants ship as distinct tool names. Future v2c work\n * unifies them under one tool that picks the right backend; this push\n * keeps them separate so the contract is observable.\n */\nexport function registerStaticCompilerTools(registry: ToolRegistry): void {\n registry.register(\n {\n name: 'llui_static_show_compiled',\n description:\n 'Static counterpart of `llui_show_compiled`. Read a source file from disk and return its pre-transform source plus the post-transform output @llui/compiler would produce. Works without a running app.',\n schema: z.object({\n file: z\n .string()\n .describe(\n 'Absolute or workspace-relative path to a .ts/.tsx file. Reads the file from disk and runs the compiler transform on it.',\n ),\n }),\n },\n 'compiler',\n async (args) => {\n const absPath = resolve(args.file)\n let source: string\n try {\n source = readFileSync(absPath, 'utf8')\n } catch (err) {\n return {\n pre: null,\n post: null,\n error: `Could not read file: ${(err as Error).message}`,\n }\n }\n const result = transformLlui(source, absPath, false, false)\n if (!result) {\n return {\n pre: source,\n post: null,\n note: 'File contains no @llui/dom imports / no reactive content; nothing to transform.',\n }\n }\n return { pre: source, post: result.output }\n },\n )\n\n registry.register(\n {\n name: 'llui_static_collect_paths',\n description:\n 'Return the reactive state-access paths the @llui/compiler would extract from a file, plus the per-bit assignments and a budget summary. Companion to `llui_explain_mask` (live) — works without a running app, useful for understanding why a binding does/does not pick up a state change.',\n schema: z.object({\n file: z.string().describe('Absolute or workspace-relative path to a .ts/.tsx file.'),\n }),\n },\n 'compiler',\n async (args) => {\n const absPath = resolve(args.file)\n let source: string\n try {\n source = readFileSync(absPath, 'utf8')\n } catch (err) {\n return {\n paths: [],\n error: `Could not read file: ${(err as Error).message}`,\n }\n }\n const { lo, hi } = collectDeps(source)\n const lowEntries = [...lo.entries()].map(([path, bit]) => ({\n path,\n bit,\n word: 'lo' as const,\n }))\n const highEntries = [...hi.entries()].map(([path, bit]) => ({\n path,\n bit,\n word: 'hi' as const,\n }))\n const all = [...lowEntries, ...highEntries]\n const total = all.length\n\n // Top-level field rollup so callers can see which slices dominate.\n const byTopLevel = new Map<string, number>()\n for (const e of all) {\n const top = e.path.split('.', 1)[0]!\n byTopLevel.set(top, (byTopLevel.get(top) ?? 0) + 1)\n }\n const breakdown = [...byTopLevel.entries()]\n .sort((a, b) => b[1] - a[1])\n .map(([field, count]) => ({ field, count }))\n\n // Budget warning. 62 is the two-word ceiling; past it the\n // bitmask-overflow rule fires and bindings fall back to FULL_MASK.\n const overflow = total > 62\n const fullMaskBits = all.filter((e) => e.bit === -1).length\n\n return {\n total,\n overflow,\n fullMaskBits,\n breakdown,\n paths: all,\n }\n },\n )\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llui/mcp",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "bin": {
@@ -19,10 +19,12 @@
19
19
  "@modelcontextprotocol/sdk": "^1.29.0",
20
20
  "ws": "^8.18.0",
21
21
  "zod": "^4.0.0",
22
- "@llui/eslint-plugin": "0.1.0"
22
+ "@llui/compiler": "0.3.0",
23
+ "@llui/compiler-devtools": "0.3.0",
24
+ "@llui/compiler-introspection": "0.3.0"
23
25
  },
24
26
  "peerDependencies": {
25
- "@llui/dom": "^0.1.0",
27
+ "@llui/dom": "^0.2.0",
26
28
  "playwright": ">=1.0.0"
27
29
  },
28
30
  "peerDependenciesMeta": {
@@ -34,7 +36,7 @@
34
36
  "@types/node": "^22.0.0",
35
37
  "@types/ws": "^8.5.13",
36
38
  "playwright": "*",
37
- "@llui/dom": "0.1.0"
39
+ "@llui/dom": "0.2.0"
38
40
  },
39
41
  "description": "LLui MCP server — LLM debug tools via Model Context Protocol",
40
42
  "keywords": [