what-devtools-mcp 0.11.8 → 0.12.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 +1 -1
- package/src/index.js +5 -0
- package/src/tool-registry.js +69 -0
- package/src/tools.js +9 -20
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -9,6 +9,7 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
|
9
9
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
10
10
|
import { createBridge } from './bridge.js';
|
|
11
11
|
import { registerTools } from './tools.js';
|
|
12
|
+
import { instrumentServer } from './tool-registry.js';
|
|
12
13
|
|
|
13
14
|
const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf-8'));
|
|
14
15
|
|
|
@@ -52,6 +53,10 @@ const server = new McpServer({
|
|
|
52
53
|
version: pkg.version,
|
|
53
54
|
});
|
|
54
55
|
|
|
56
|
+
// Record every registration so what_connection_status can report the real
|
|
57
|
+
// catalogue instead of a hand-maintained literal. Must run before any
|
|
58
|
+
// register* call.
|
|
59
|
+
instrumentServer(server);
|
|
55
60
|
registerTools(server, bridge);
|
|
56
61
|
|
|
57
62
|
// --- MCP Resources: static docs for agent context ---
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The single source of truth for what tools this MCP server actually exposes.
|
|
3
|
+
*
|
|
4
|
+
* `what_connection_status` is the tool CLAUDE.md tells every agent to call FIRST
|
|
5
|
+
* to orient, and it used to answer with a hand-maintained literal array of 17
|
|
6
|
+
* entries while 29 tools were registered. The product under-reported its own
|
|
7
|
+
* differentiator by 41% at the moment of first contact, and the same catalogue
|
|
8
|
+
* was hand-copied into CLAUDE.md, AGENTS.md and the docs, each free to drift.
|
|
9
|
+
*
|
|
10
|
+
* A framework whose thesis is machine-readable truth cannot hand-maintain its own
|
|
11
|
+
* machine-readable index, so the catalogue is now derived from registration by
|
|
12
|
+
* wrapping `server.tool` once before the three register* functions run.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const registered = [];
|
|
16
|
+
|
|
17
|
+
// Tools that answer with no browser attached. Everything else needs a live page
|
|
18
|
+
// on the bridge, which is a stronger requirement than "the dev server is up" and
|
|
19
|
+
// is the single most common reason an agent's first call comes back empty.
|
|
20
|
+
const OFFLINE_TOOLS = new Set([
|
|
21
|
+
'what_connection_status',
|
|
22
|
+
'what_lint',
|
|
23
|
+
'what_validate',
|
|
24
|
+
'what_scaffold',
|
|
25
|
+
'what_fix',
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Wrap `server.tool` so every registration is recorded. Call once, before any
|
|
30
|
+
* register* function runs. Idempotent.
|
|
31
|
+
*/
|
|
32
|
+
export function instrumentServer(server) {
|
|
33
|
+
if (server.__whatToolRegistryInstalled) return server;
|
|
34
|
+
const original = server.tool.bind(server);
|
|
35
|
+
server.tool = (name, description, ...rest) => {
|
|
36
|
+
registered.push({ name, desc: description });
|
|
37
|
+
return original(name, description, ...rest);
|
|
38
|
+
};
|
|
39
|
+
server.__whatToolRegistryInstalled = true;
|
|
40
|
+
return server;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Every tool registered so far, in registration order. */
|
|
44
|
+
export function getRegisteredTools() {
|
|
45
|
+
return registered.map((t) => ({ ...t }));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Split the catalogue by what can actually answer right now.
|
|
50
|
+
*
|
|
51
|
+
* No competitor's MCP server distinguishes "this tool exists" from "this tool can
|
|
52
|
+
* answer in this session", which is the distinction that actually costs an agent
|
|
53
|
+
* turns: it discovers the difference by calling and failing.
|
|
54
|
+
*/
|
|
55
|
+
export function describeToolAvailability(connected) {
|
|
56
|
+
const all = getRegisteredTools();
|
|
57
|
+
const offline = all.filter((t) => OFFLINE_TOOLS.has(t.name));
|
|
58
|
+
const needsBrowser = all.filter((t) => !OFFLINE_TOOLS.has(t.name));
|
|
59
|
+
return {
|
|
60
|
+
total: all.length,
|
|
61
|
+
available: connected ? all : offline,
|
|
62
|
+
requiresBrowser: connected ? [] : needsBrowser,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Test hook: drop the recorded registrations. @internal */
|
|
67
|
+
export function __resetToolRegistry() {
|
|
68
|
+
registered.length = 0;
|
|
69
|
+
}
|
package/src/tools.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { z } from 'zod';
|
|
7
|
+
import { describeToolAvailability } from './tool-registry.js';
|
|
7
8
|
|
|
8
9
|
export function registerTools(server, bridge) {
|
|
9
10
|
// --- Helpers ---
|
|
@@ -112,26 +113,14 @@ export function registerTools(server, bridge) {
|
|
|
112
113
|
'Make sure your app is running with the what-devtools-mcp Vite plugin',
|
|
113
114
|
'Or manually call connectDevToolsMCP() in your browser console',
|
|
114
115
|
],
|
|
115
|
-
// Tool
|
|
116
|
-
tools
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
{
|
|
122
|
-
|
|
123
|
-
{ name: 'what_page_map', desc: 'Full page layout skeleton' },
|
|
124
|
-
{ name: 'what_diagnose', desc: 'One-call health check (errors + perf + reactivity)' },
|
|
125
|
-
{ name: 'what_errors', desc: 'Runtime errors with fix suggestions' },
|
|
126
|
-
{ name: 'what_signal_trace', desc: 'Why did a signal change? Causal chain.' },
|
|
127
|
-
{ name: 'what_dependency_graph', desc: 'Reactive dependency graph' },
|
|
128
|
-
{ name: 'what_watch', desc: 'Observe events over a time window' },
|
|
129
|
-
{ name: 'what_record_window', desc: 'Rank effects that re-ran during a recording window — what fired for this action?' },
|
|
130
|
-
{ name: 'what_set_signal', desc: 'Change a signal value in the live app' },
|
|
131
|
-
{ name: 'what_lint', desc: 'Static analysis for code (no browser needed)' },
|
|
132
|
-
{ name: 'what_scaffold', desc: 'Generate boilerplate (no browser needed)' },
|
|
133
|
-
{ name: 'what_fix', desc: 'Error diagnosis with code examples (no browser needed)' },
|
|
134
|
-
],
|
|
116
|
+
// Tool catalogue, DERIVED from registration rather than hand-maintained.
|
|
117
|
+
// `tools` is every registered tool; `available` is the subset that can
|
|
118
|
+
// answer right now, which is the distinction that actually costs an
|
|
119
|
+
// agent turns when no browser is attached.
|
|
120
|
+
...(() => {
|
|
121
|
+
const a = describeToolAvailability(connected);
|
|
122
|
+
return { toolCount: a.total, tools: a.available, requiresBrowser: a.requiresBrowser.map((t) => t.name) };
|
|
123
|
+
})(),
|
|
135
124
|
};
|
|
136
125
|
|
|
137
126
|
return {
|