what-devtools-mcp 0.12.4 → 0.13.1
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/package.json +13 -4
- package/src/bridge.js +6 -0
- package/src/client-commands.js +3 -3
- package/src/client.d.ts +31 -0
- package/src/client.js +0 -1
- package/src/tools-agent.js +4 -7
- package/src/tools-extended.js +3 -2
- package/src/tools.js +2 -0
- package/src/vite-plugin.d.ts +31 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "what-devtools-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.1",
|
|
4
4
|
"description": "MCP server bridging AI agents to live What Framework app state via WebSocket",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -8,9 +8,18 @@
|
|
|
8
8
|
},
|
|
9
9
|
"exports": {
|
|
10
10
|
".": "./src/index.js",
|
|
11
|
-
"./client":
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
"./client": {
|
|
12
|
+
"types": "./src/client.d.ts",
|
|
13
|
+
"default": "./src/client.js"
|
|
14
|
+
},
|
|
15
|
+
"./vite": {
|
|
16
|
+
"types": "./src/vite-plugin.d.ts",
|
|
17
|
+
"default": "./src/vite-plugin.js"
|
|
18
|
+
},
|
|
19
|
+
"./vite-plugin": {
|
|
20
|
+
"types": "./src/vite-plugin.d.ts",
|
|
21
|
+
"default": "./src/vite-plugin.js"
|
|
22
|
+
}
|
|
14
23
|
},
|
|
15
24
|
"files": [
|
|
16
25
|
"src"
|
package/src/bridge.js
CHANGED
|
@@ -13,6 +13,12 @@ import { join } from 'path';
|
|
|
13
13
|
const MAX_EVENT_LOG = 1000;
|
|
14
14
|
const MAX_ERROR_LOG = 100;
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* @param {object} [options]
|
|
18
|
+
* @param {number} [options.port]
|
|
19
|
+
* @param {string} [options.host]
|
|
20
|
+
* @param {(err: any) => void} [options.onError]
|
|
21
|
+
*/
|
|
16
22
|
export function createBridge({ port = 9229, host = '127.0.0.1', onError } = {}) {
|
|
17
23
|
let latestSnapshot = null;
|
|
18
24
|
const eventLog = [];
|
package/src/client-commands.js
CHANGED
|
@@ -601,7 +601,7 @@ export async function handleExtendedCommand(command, args, devtools) {
|
|
|
601
601
|
tag: el.tagName.toLowerCase(),
|
|
602
602
|
type: el.getAttribute('type') || undefined,
|
|
603
603
|
label: label || '(unlabeled)',
|
|
604
|
-
disabled: el.disabled || undefined,
|
|
604
|
+
disabled: /** @type {any} */ (el).disabled || undefined,
|
|
605
605
|
rect: rectOf(el),
|
|
606
606
|
});
|
|
607
607
|
}
|
|
@@ -768,7 +768,7 @@ export async function handleExtendedCommand(command, args, devtools) {
|
|
|
768
768
|
const canvas = document.createElement('canvas');
|
|
769
769
|
canvas.width = canvasWidth;
|
|
770
770
|
canvas.height = canvasHeight;
|
|
771
|
-
const ctx = canvas.getContext('2d');
|
|
771
|
+
const ctx = /** @type {CanvasRenderingContext2D} */ (canvas.getContext('2d'));
|
|
772
772
|
ctx.scale(scale * dpr, scale * dpr);
|
|
773
773
|
|
|
774
774
|
// Load SVG blob as image
|
|
@@ -801,7 +801,7 @@ export async function handleExtendedCommand(command, args, devtools) {
|
|
|
801
801
|
const smallCanvas = document.createElement('canvas');
|
|
802
802
|
smallCanvas.width = Math.ceil(canvasWidth / 2);
|
|
803
803
|
smallCanvas.height = Math.ceil(canvasHeight / 2);
|
|
804
|
-
const smallCtx = smallCanvas.getContext('2d');
|
|
804
|
+
const smallCtx = /** @type {CanvasRenderingContext2D} */ (smallCanvas.getContext('2d'));
|
|
805
805
|
smallCtx.drawImage(canvas, 0, 0, smallCanvas.width, smallCanvas.height);
|
|
806
806
|
dataUrl = smallCanvas.toDataURL('image/jpeg', 0.3);
|
|
807
807
|
base64 = dataUrl.split(',')[1];
|
package/src/client.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Types for `what-devtools-mcp/client`.
|
|
2
|
+
|
|
3
|
+
export interface DevToolsMCPConnection {
|
|
4
|
+
/** Stop probing and close the socket. Safe to call more than once. */
|
|
5
|
+
disconnect(): void;
|
|
6
|
+
/** Force an immediate reconnect attempt, resetting the probe back-off. */
|
|
7
|
+
reconnect(): void;
|
|
8
|
+
readonly isConnected: boolean;
|
|
9
|
+
/** Events forwarded to the bridge since the connection opened. */
|
|
10
|
+
readonly eventCount: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface ConnectDevToolsMCPOptions {
|
|
14
|
+
/** Bridge port. Default 9229. */
|
|
15
|
+
port?: number;
|
|
16
|
+
/** Shared secret the bridge requires. */
|
|
17
|
+
token?: string;
|
|
18
|
+
/** Override the discovery endpoint instead of deriving it from `port`. */
|
|
19
|
+
discoveryUrl?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Connect the running app to the devtools MCP bridge.
|
|
24
|
+
*
|
|
25
|
+
* Returns an inert connection in production (`NODE_ENV === 'production'` or
|
|
26
|
+
* `import.meta.env.PROD`) rather than throwing, so it is safe to call
|
|
27
|
+
* unconditionally.
|
|
28
|
+
*/
|
|
29
|
+
export function connectDevToolsMCP(
|
|
30
|
+
options?: ConnectDevToolsMCPOptions,
|
|
31
|
+
): DevToolsMCPConnection;
|
package/src/client.js
CHANGED
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
// Branded console logger
|
|
8
8
|
const BADGE = 'background:#6366f1;color:#fff;padding:2px 6px;border-radius:3px;font-weight:bold';
|
|
9
9
|
const BADGE_CMD = 'background:#22c55e;color:#fff;padding:2px 6px;border-radius:3px;font-weight:bold';
|
|
10
|
-
const BADGE_EVENT = 'background:#f97316;color:#fff;padding:2px 6px;border-radius:3px;font-weight:bold';
|
|
11
10
|
const BADGE_WARN = 'background:#eab308;color:#000;padding:2px 6px;border-radius:3px;font-weight:bold';
|
|
12
11
|
const DIM = 'color:#888';
|
|
13
12
|
|
package/src/tools-agent.js
CHANGED
|
@@ -617,7 +617,7 @@ export default ${name};
|
|
|
617
617
|
`;
|
|
618
618
|
},
|
|
619
619
|
|
|
620
|
-
page: ({ name,
|
|
620
|
+
page: ({ name, signals = [] }) => {
|
|
621
621
|
const signalDecls = signals.map(s => ` const ${s} = signal(null, '${s}');`).join('\n');
|
|
622
622
|
|
|
623
623
|
return `import { signal, effect, onMount } from 'what-framework';
|
|
@@ -646,7 +646,7 @@ export default ${name};
|
|
|
646
646
|
`;
|
|
647
647
|
},
|
|
648
648
|
|
|
649
|
-
form: ({ name,
|
|
649
|
+
form: ({ name, signals = [] }) => {
|
|
650
650
|
const fields = signals.length > 0 ? signals : ['email', 'password'];
|
|
651
651
|
const fieldDecls = fields.map(f => ` ${f}: { initial: '', rules: [rules.required('${f} is required')] },`).join('\n');
|
|
652
652
|
|
|
@@ -818,7 +818,7 @@ export function registerAgentTools(server, bridge) {
|
|
|
818
818
|
try {
|
|
819
819
|
const ruleIssues = rule.test(code);
|
|
820
820
|
issues.push(...ruleIssues);
|
|
821
|
-
} catch
|
|
821
|
+
} catch {
|
|
822
822
|
// Rule crashed — skip silently
|
|
823
823
|
}
|
|
824
824
|
}
|
|
@@ -930,7 +930,7 @@ export function registerAgentTools(server, bridge) {
|
|
|
930
930
|
componentCount: (result.output || '').match(/function\s+[A-Z]\w*\s*\(/g)?.length || 0,
|
|
931
931
|
},
|
|
932
932
|
});
|
|
933
|
-
} catch
|
|
933
|
+
} catch {
|
|
934
934
|
// Fallback: do basic validation without browser
|
|
935
935
|
const issues = [];
|
|
936
936
|
|
|
@@ -1230,9 +1230,6 @@ export function registerAgentTools(server, bridge) {
|
|
|
1230
1230
|
depCount: (e.depSignalIds || e.deps || []).length,
|
|
1231
1231
|
});
|
|
1232
1232
|
}
|
|
1233
|
-
const baselineEventCount = bridge.getEvents
|
|
1234
|
-
? bridge.getEvents(Date.now() - 1).length
|
|
1235
|
-
: 0;
|
|
1236
1233
|
const startTs = Date.now();
|
|
1237
1234
|
|
|
1238
1235
|
// ---- Wait for the window ----
|
package/src/tools-extended.js
CHANGED
|
@@ -33,7 +33,7 @@ export function registerExtendedTools(server, bridge) {
|
|
|
33
33
|
};
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
function noSnapshot(
|
|
36
|
+
function noSnapshot(_tool) {
|
|
37
37
|
return {
|
|
38
38
|
content: [{
|
|
39
39
|
type: 'text',
|
|
@@ -1000,6 +1000,7 @@ export function registerExtendedTools(server, bridge) {
|
|
|
1000
1000
|
}
|
|
1001
1001
|
|
|
1002
1002
|
// Get writer info from browser
|
|
1003
|
+
/** @type {Record<string, any>} */
|
|
1003
1004
|
let writers = { recentWrites: [], totalWrites: 0 };
|
|
1004
1005
|
try {
|
|
1005
1006
|
writers = await bridge.sendCommand('get-signal-writers', { signalId }, 5000);
|
|
@@ -1048,6 +1049,7 @@ export function registerExtendedTools(server, bridge) {
|
|
|
1048
1049
|
|
|
1049
1050
|
// Trace from recent writes
|
|
1050
1051
|
for (const write of (writers.recentWrites || []).slice(-5)) {
|
|
1052
|
+
/** @type {Record<string, any>} */
|
|
1051
1053
|
const entry = {
|
|
1052
1054
|
timestamp: write.timestamp,
|
|
1053
1055
|
previousValue: write.previousValue,
|
|
@@ -1114,7 +1116,6 @@ export function registerExtendedTools(server, bridge) {
|
|
|
1114
1116
|
|
|
1115
1117
|
const { componentName, boundingRect, styles, textContent, childElements, totalChildren, layout, viewport, accessibility } = result;
|
|
1116
1118
|
|
|
1117
|
-
const styleDesc = Object.entries(styles || {}).map(([k, v]) => `${k}: ${v}`).join(', ');
|
|
1118
1119
|
const childDesc = Object.entries(childElements || {}).map(([k, v]) => `${v} ${k}${v > 1 ? 's' : ''}`).join(', ');
|
|
1119
1120
|
|
|
1120
1121
|
const summary = `${componentName}: ${boundingRect.width}×${boundingRect.height}px at (${boundingRect.x},${boundingRect.y}). ` +
|
package/src/tools.js
CHANGED
|
@@ -477,6 +477,7 @@ export function registerTools(server, bridge) {
|
|
|
477
477
|
const classified = errors.map((err, idx) => {
|
|
478
478
|
const msg = err.message || err.error || '';
|
|
479
479
|
const parsed = parseStack(err.stack, knownComponents);
|
|
480
|
+
/** @type {Record<string, any>} */
|
|
480
481
|
let classification = {
|
|
481
482
|
id: `err_${idx}`,
|
|
482
483
|
severity: 'error',
|
|
@@ -773,6 +774,7 @@ function error(message) {
|
|
|
773
774
|
* what-core, what-devtools, node_modules, vite/, /@id/, internal anonymous).
|
|
774
775
|
*/
|
|
775
776
|
function parseStack(stack, knownComponents = []) {
|
|
777
|
+
/** @type {{ file: string|null, line: number|null, column: number|null, component: string|null }} */
|
|
776
778
|
const out = { file: null, line: null, column: null, component: null };
|
|
777
779
|
if (!stack || typeof stack !== 'string') return out;
|
|
778
780
|
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Types for `what-devtools-mcp/vite`. This is the export TypeScript users hit
|
|
2
|
+
// first, because it goes in `vite.config.ts`.
|
|
3
|
+
|
|
4
|
+
/** Path the dev server answers bridge-discovery probes on. */
|
|
5
|
+
export const DISCOVERY_PATH: string;
|
|
6
|
+
|
|
7
|
+
export interface WhatDevToolsMCPOptions {
|
|
8
|
+
/** Bridge port. Default 9229. */
|
|
9
|
+
port?: number;
|
|
10
|
+
/** Shared secret required by the bridge. */
|
|
11
|
+
token?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Structural Vite plugin shape — assignable to Vite's `PluginOption` without a
|
|
15
|
+
// hard type dependency on `vite`. Same convention as `what-compiler/vite`.
|
|
16
|
+
export interface WhatDevToolsMCPPlugin {
|
|
17
|
+
name: string;
|
|
18
|
+
apply?: 'serve' | 'build';
|
|
19
|
+
[hook: string]: unknown;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Vite plugin that injects the devtools MCP client into the dev server.
|
|
24
|
+
*
|
|
25
|
+
* `apply: 'serve'` only: it resolves, loads and injects nothing under
|
|
26
|
+
* `vite build`. `what-devtools` is an optional peer; if it is not installed the
|
|
27
|
+
* plugin degrades to a single log line instead of failing the dev transform.
|
|
28
|
+
*/
|
|
29
|
+
export default function whatDevToolsMCP(
|
|
30
|
+
options?: WhatDevToolsMCPOptions,
|
|
31
|
+
): WhatDevToolsMCPPlugin;
|