@probebrowser/trace-mcp 1.0.3 → 1.1.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.
- package/dist/server.js +18 -12
- package/dist/tools.js +196 -4
- package/package.json +8 -3
- package/src/server.ts +18 -12
- package/src/tools.ts +199 -4
- package/tests/resources.test.ts +150 -0
- package/tests/server.test.ts +163 -0
- package/tests/tools.test.ts +288 -0
- package/vitest.config.ts +17 -0
package/dist/server.js
CHANGED
|
@@ -3,16 +3,21 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
|
3
3
|
import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
4
4
|
import { Trace, ALL_TOOLS } from '@probebrowser/sdk';
|
|
5
5
|
import { TOOLS } from './tools.js';
|
|
6
|
+
import { zodToJsonSchema as _zodToJsonSchema } from 'zod-to-json-schema';
|
|
6
7
|
// Schema conversion helper
|
|
7
8
|
function zodToJsonSchema(schema) {
|
|
9
|
+
const jsonSchema = _zodToJsonSchema(schema, { target: 'openApi3' });
|
|
10
|
+
// MCP expects { type: 'object', properties: {...}, required: [...] }
|
|
8
11
|
return {
|
|
9
12
|
type: 'object',
|
|
10
|
-
properties: {},
|
|
13
|
+
properties: jsonSchema.properties || {},
|
|
14
|
+
required: jsonSchema.required || [],
|
|
11
15
|
};
|
|
12
16
|
}
|
|
13
17
|
export class TraceMcpServer {
|
|
14
18
|
server;
|
|
15
19
|
trace;
|
|
20
|
+
connected = false;
|
|
16
21
|
constructor() {
|
|
17
22
|
this.server = new Server({
|
|
18
23
|
name: 'trace-mcp-server',
|
|
@@ -72,7 +77,7 @@ export class TraceMcpServer {
|
|
|
72
77
|
}
|
|
73
78
|
if (!method)
|
|
74
79
|
throw new Error(`Unknown tool: ${toolName}`);
|
|
75
|
-
const isConnected =
|
|
80
|
+
const isConnected = this.connected;
|
|
76
81
|
try {
|
|
77
82
|
let result;
|
|
78
83
|
// 1. Handle Connect Tool
|
|
@@ -80,6 +85,7 @@ export class TraceMcpServer {
|
|
|
80
85
|
const url = String(request.params.arguments?.url);
|
|
81
86
|
// Re-init trace if headless mode changes? For now just connect.
|
|
82
87
|
await this.trace.connect(url);
|
|
88
|
+
this.connected = true;
|
|
83
89
|
return { content: [{ type: 'text', text: `Connected to ${url}` }] };
|
|
84
90
|
}
|
|
85
91
|
if (!isConnected) {
|
|
@@ -92,17 +98,17 @@ export class TraceMcpServer {
|
|
|
92
98
|
return { content: [{ type: 'text', text: 'Error: TRACE_API_KEY is required for AI features.' }], isError: true };
|
|
93
99
|
}
|
|
94
100
|
const prompt = String(request.params.arguments?.prompt || request.params.arguments?.q || 'Analyze page');
|
|
95
|
-
// Delegate to SDK's
|
|
96
|
-
const
|
|
101
|
+
// Delegate to SDK's query which runs AI-powered deep debug
|
|
102
|
+
const debugResult = await this.trace.query(prompt);
|
|
97
103
|
// Return the conclusion and steps
|
|
98
104
|
return {
|
|
99
105
|
content: [
|
|
100
|
-
{ type: 'text', text: `###
|
|
106
|
+
{ type: 'text', text: `### Summary\n${debugResult.analysis.summary}\n\n### Steps Taken\n${debugResult.execution.results.map((r) => `- ${r.tool}: ${r.success ? 'Success' : 'Failed'}`).join('\n')}` }
|
|
101
107
|
]
|
|
102
108
|
};
|
|
103
109
|
}
|
|
104
110
|
// 3. Handle Standard SDK Tools
|
|
105
|
-
result = await this.trace.
|
|
111
|
+
result = await this.trace.tool(toolDef.method, (request.params.arguments || {}));
|
|
106
112
|
return {
|
|
107
113
|
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
108
114
|
};
|
|
@@ -161,12 +167,12 @@ export class TraceMcpServer {
|
|
|
161
167
|
});
|
|
162
168
|
this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
163
169
|
const uri = request.params.uri;
|
|
164
|
-
if (!this.
|
|
170
|
+
if (!this.connected) {
|
|
165
171
|
throw new Error('Not connected. Call trace_connect first.');
|
|
166
172
|
}
|
|
167
173
|
// --- Console ---
|
|
168
174
|
if (uri === 'trace://console/errors') {
|
|
169
|
-
const logs = await this.trace.
|
|
175
|
+
const logs = await this.trace.tool('get_console_errors', {});
|
|
170
176
|
return { contents: [{ uri, mimeType: 'application/json', text: JSON.stringify(logs, null, 2) }] };
|
|
171
177
|
}
|
|
172
178
|
if (uri === 'trace://console/logs') {
|
|
@@ -175,21 +181,21 @@ export class TraceMcpServer {
|
|
|
175
181
|
// For now, let's return the summary as it contains counts.
|
|
176
182
|
// Ideally SDK should expose 'get_all_logs'.
|
|
177
183
|
// Fallback: use get_console_errors
|
|
178
|
-
const logs = await this.trace.
|
|
184
|
+
const logs = await this.trace.tool('get_console_summary', {});
|
|
179
185
|
return { contents: [{ uri, mimeType: 'application/json', text: JSON.stringify(logs, null, 2) }] };
|
|
180
186
|
}
|
|
181
187
|
// --- Network ---
|
|
182
188
|
if (uri === 'trace://network/failed') {
|
|
183
|
-
const reqs = await this.trace.
|
|
189
|
+
const reqs = await this.trace.tool('get_network_failed', {});
|
|
184
190
|
return { contents: [{ uri, mimeType: 'application/json', text: JSON.stringify(reqs, null, 2) }] };
|
|
185
191
|
}
|
|
186
192
|
if (uri === 'trace://network/all') {
|
|
187
|
-
const reqs = await this.trace.
|
|
193
|
+
const reqs = await this.trace.tool('get_network_summary', {});
|
|
188
194
|
return { contents: [{ uri, mimeType: 'application/json', text: JSON.stringify(reqs, null, 2) }] };
|
|
189
195
|
}
|
|
190
196
|
// --- DOM ---
|
|
191
197
|
if (uri === 'trace://dom/tree') {
|
|
192
|
-
const tree = await this.trace.
|
|
198
|
+
const tree = await this.trace.tool('get_dom_tree', { depth: 2 });
|
|
193
199
|
return { contents: [{ uri, mimeType: 'application/json', text: JSON.stringify(tree, null, 2) }] };
|
|
194
200
|
}
|
|
195
201
|
// --- Screenshot ---
|
package/dist/tools.js
CHANGED
|
@@ -100,8 +100,37 @@ export const TOOLS = {
|
|
|
100
100
|
requestId: z.string().describe('The ID of the request to replay'),
|
|
101
101
|
}),
|
|
102
102
|
},
|
|
103
|
+
trace_set_network_throttling: {
|
|
104
|
+
method: 'set_network_throttling',
|
|
105
|
+
description: 'Set network throttling to a preset (slow3G, fast3G, offline).',
|
|
106
|
+
schema: z.object({
|
|
107
|
+
preset: z.string().describe('Preset: slow3G, fast3G, or offline'),
|
|
108
|
+
}),
|
|
109
|
+
},
|
|
110
|
+
trace_set_custom_network_throttling: {
|
|
111
|
+
method: 'set_custom_network_throttling',
|
|
112
|
+
description: 'Set custom network throttling parameters.',
|
|
113
|
+
schema: z.object({
|
|
114
|
+
downloadThroughput: z.number().describe('Download speed in bytes/sec'),
|
|
115
|
+
uploadThroughput: z.number().describe('Upload speed in bytes/sec'),
|
|
116
|
+
latency: z.number().describe('Latency in ms'),
|
|
117
|
+
}),
|
|
118
|
+
},
|
|
119
|
+
trace_disable_network_throttling: {
|
|
120
|
+
method: 'disable_network_throttling',
|
|
121
|
+
description: 'Disable network throttling.',
|
|
122
|
+
schema: z.object({}),
|
|
123
|
+
},
|
|
124
|
+
// ============================================
|
|
125
|
+
// DIAGNOSTIC (1)
|
|
103
126
|
// ============================================
|
|
104
|
-
|
|
127
|
+
trace_full_page_diagnostic: {
|
|
128
|
+
method: 'full_page_diagnostic',
|
|
129
|
+
description: 'COMPREHENSIVE page diagnostic that checks EVERYTHING: console errors, network failures (404s, 500s), slow requests, layout/overlap issues, hidden elements, CORS errors, mixed content. Use this FIRST when asked to find errors or debug a page.',
|
|
130
|
+
schema: z.object({}),
|
|
131
|
+
},
|
|
132
|
+
// ============================================
|
|
133
|
+
// DOM & UI TOOLS (22)
|
|
105
134
|
// ============================================
|
|
106
135
|
trace_inspect_element: {
|
|
107
136
|
method: 'inspect_element',
|
|
@@ -117,6 +146,13 @@ export const TOOLS = {
|
|
|
117
146
|
selector: z.string().describe('CSS selector'),
|
|
118
147
|
}),
|
|
119
148
|
},
|
|
149
|
+
trace_find_by_text: {
|
|
150
|
+
method: 'find_by_text',
|
|
151
|
+
description: 'Find elements containing specific visible text. Best for finding buttons, links, labels by their text content.',
|
|
152
|
+
schema: z.object({
|
|
153
|
+
text: z.string().describe('Text to search for (case-insensitive, partial match)'),
|
|
154
|
+
}),
|
|
155
|
+
},
|
|
120
156
|
trace_get_element_styles: {
|
|
121
157
|
method: 'get_element_styles',
|
|
122
158
|
description: 'Get full computed style object.',
|
|
@@ -210,8 +246,28 @@ export const TOOLS = {
|
|
|
210
246
|
selector: z.string().describe('CSS selector'),
|
|
211
247
|
}),
|
|
212
248
|
},
|
|
249
|
+
trace_check_layout_issues: {
|
|
250
|
+
method: 'check_layout_issues',
|
|
251
|
+
description: 'Find ALL layout issues: overlapping elements, hidden buttons/links, text overflow. Use when debugging visual bugs.',
|
|
252
|
+
schema: z.object({}),
|
|
253
|
+
},
|
|
254
|
+
trace_find_overlapping_elements: {
|
|
255
|
+
method: 'find_overlapping_elements',
|
|
256
|
+
description: 'Find elements that overlap and block each other (like invisible overlays covering buttons).',
|
|
257
|
+
schema: z.object({}),
|
|
258
|
+
},
|
|
259
|
+
trace_find_ghost_blockers: {
|
|
260
|
+
method: 'find_ghost_blockers',
|
|
261
|
+
description: 'Find INVISIBLE elements blocking user interaction! Detects: opacity:0 overlays, transparent full-screen blockers, modal backdrops left open, high z-index empty elements. Use when user says "why can\'t I click X?"',
|
|
262
|
+
schema: z.object({}),
|
|
263
|
+
},
|
|
264
|
+
trace_find_hidden_interactive_elements: {
|
|
265
|
+
method: 'find_hidden_interactive_elements',
|
|
266
|
+
description: 'Find buttons, links, inputs that are hidden but should be visible.',
|
|
267
|
+
schema: z.object({}),
|
|
268
|
+
},
|
|
213
269
|
// ============================================
|
|
214
|
-
// DEBUGGER TOOLS (
|
|
270
|
+
// DEBUGGER TOOLS (27)
|
|
215
271
|
// ============================================
|
|
216
272
|
trace_get_debugger_state: {
|
|
217
273
|
method: 'get_debugger_state',
|
|
@@ -260,6 +316,15 @@ export const TOOLS = {
|
|
|
260
316
|
expression: z.string().describe('JS code to evaluate'),
|
|
261
317
|
}),
|
|
262
318
|
},
|
|
319
|
+
trace_set_conditional_breakpoint: {
|
|
320
|
+
method: 'set_conditional_breakpoint',
|
|
321
|
+
description: 'Set a breakpoint that only triggers when a condition is met.',
|
|
322
|
+
schema: z.object({
|
|
323
|
+
url: z.string().describe('Script URL pattern'),
|
|
324
|
+
line: z.number().describe('Line number'),
|
|
325
|
+
condition: z.string().describe('JS condition expression'),
|
|
326
|
+
}),
|
|
327
|
+
},
|
|
263
328
|
trace_set_exception_breakpoint: {
|
|
264
329
|
method: 'set_exception_breakpoint',
|
|
265
330
|
description: 'Pause on All or Uncaught exceptions.',
|
|
@@ -339,7 +404,7 @@ export const TOOLS = {
|
|
|
339
404
|
description: 'Continue execution.',
|
|
340
405
|
schema: z.object({}),
|
|
341
406
|
},
|
|
342
|
-
|
|
407
|
+
trace_set_dom_breakpoint_by_selector: {
|
|
343
408
|
method: 'set_dom_breakpoint_by_selector',
|
|
344
409
|
description: 'Break on node modification.',
|
|
345
410
|
schema: z.object({
|
|
@@ -361,8 +426,21 @@ export const TOOLS = {
|
|
|
361
426
|
enable: z.boolean().describe('True to enable, false to disable'),
|
|
362
427
|
}),
|
|
363
428
|
},
|
|
429
|
+
trace_get_variable_preview: {
|
|
430
|
+
method: 'get_variable_preview',
|
|
431
|
+
description: 'Get current variable values while debugging.',
|
|
432
|
+
schema: z.object({}),
|
|
433
|
+
},
|
|
434
|
+
trace_debug_and_analyze: {
|
|
435
|
+
method: 'debug_and_analyze',
|
|
436
|
+
description: 'Combined tool: set breakpoint, capture state, and analyze.',
|
|
437
|
+
schema: z.object({
|
|
438
|
+
url: z.string().describe('Script URL'),
|
|
439
|
+
line: z.number().describe('Line number'),
|
|
440
|
+
}),
|
|
441
|
+
},
|
|
364
442
|
// ============================================
|
|
365
|
-
// SOURCE TOOLS (
|
|
443
|
+
// SOURCE TOOLS (11)
|
|
366
444
|
// ============================================
|
|
367
445
|
trace_list_scripts: {
|
|
368
446
|
method: 'list_scripts',
|
|
@@ -414,6 +492,23 @@ export const TOOLS = {
|
|
|
414
492
|
scriptId: z.string().describe('Script ID'),
|
|
415
493
|
}),
|
|
416
494
|
},
|
|
495
|
+
trace_map_call_stack: {
|
|
496
|
+
method: 'map_call_stack',
|
|
497
|
+
description: 'Map the current call stack to original source locations using source maps.',
|
|
498
|
+
schema: z.object({}),
|
|
499
|
+
},
|
|
500
|
+
trace_list_blackbox_patterns: {
|
|
501
|
+
method: 'list_blackbox_patterns',
|
|
502
|
+
description: 'List current blackboxed script patterns (ignored during debugging).',
|
|
503
|
+
schema: z.object({}),
|
|
504
|
+
},
|
|
505
|
+
trace_analyze_fetch_origin: {
|
|
506
|
+
method: 'analyze_fetch_origin',
|
|
507
|
+
description: 'Analyze where a fetch/XHR request originated in the code.',
|
|
508
|
+
schema: z.object({
|
|
509
|
+
url: z.string().describe('URL pattern to match'),
|
|
510
|
+
}),
|
|
511
|
+
},
|
|
417
512
|
// ============================================
|
|
418
513
|
// PERFORMANCE TOOLS (4)
|
|
419
514
|
// ============================================
|
|
@@ -583,6 +678,20 @@ export const TOOLS = {
|
|
|
583
678
|
cacheName: z.string(),
|
|
584
679
|
}),
|
|
585
680
|
},
|
|
681
|
+
trace_clear_indexeddb: {
|
|
682
|
+
method: 'clear_indexeddb',
|
|
683
|
+
description: 'Clear an IndexedDB database.',
|
|
684
|
+
schema: z.object({
|
|
685
|
+
database: z.string().describe('Database name'),
|
|
686
|
+
}),
|
|
687
|
+
},
|
|
688
|
+
trace_delete_cache: {
|
|
689
|
+
method: 'delete_cache',
|
|
690
|
+
description: 'Delete a cache from Cache Storage.',
|
|
691
|
+
schema: z.object({
|
|
692
|
+
cacheName: z.string().describe('Cache name'),
|
|
693
|
+
}),
|
|
694
|
+
},
|
|
586
695
|
// ============================================
|
|
587
696
|
// SECURITY TOOLS (3)
|
|
588
697
|
// ============================================
|
|
@@ -741,6 +850,89 @@ export const TOOLS = {
|
|
|
741
850
|
}),
|
|
742
851
|
},
|
|
743
852
|
// ============================================
|
|
853
|
+
// FULLSTACK DEBUGGING TOOLS (4)
|
|
854
|
+
// ============================================
|
|
855
|
+
trace_test_api_endpoint: {
|
|
856
|
+
method: 'test_api_endpoint',
|
|
857
|
+
description: 'Make a request to the configured backend API. WARNING: may bypass CORS/auth restrictions.',
|
|
858
|
+
schema: z.object({
|
|
859
|
+
path: z.string().describe('API path (e.g., /users, /products/123)'),
|
|
860
|
+
method: z.enum(['GET', 'POST', 'PUT', 'DELETE', 'PATCH']).describe('HTTP method'),
|
|
861
|
+
body: z.any().optional().describe('Request body for POST/PUT/PATCH'),
|
|
862
|
+
headers: z.record(z.string()).optional().describe('Additional headers'),
|
|
863
|
+
}),
|
|
864
|
+
},
|
|
865
|
+
trace_replay_failed_api: {
|
|
866
|
+
method: 'replay_failed_api',
|
|
867
|
+
description: 'Replay a failed API request from the network log with optional modifications.',
|
|
868
|
+
schema: z.object({
|
|
869
|
+
requestId: z.string().describe('ID of the failed request to replay'),
|
|
870
|
+
modifications: z.any().optional().describe('Optional modifications: { headers, body, queryParams }'),
|
|
871
|
+
}),
|
|
872
|
+
},
|
|
873
|
+
trace_trace_request_flow: {
|
|
874
|
+
method: 'trace_request_flow',
|
|
875
|
+
description: 'Trace a request from frontend -> backend -> database. Correlates network request with API logs and DB queries.',
|
|
876
|
+
schema: z.object({
|
|
877
|
+
url: z.string().describe('URL pattern of the request to trace'),
|
|
878
|
+
correlationId: z.string().optional().describe('Optional X-Request-ID or correlation header'),
|
|
879
|
+
}),
|
|
880
|
+
},
|
|
881
|
+
trace_get_fullstack_config: {
|
|
882
|
+
method: 'get_fullstack_config',
|
|
883
|
+
description: 'Get the current full-stack debugging configuration (backend URL, DB type).',
|
|
884
|
+
schema: z.object({}),
|
|
885
|
+
},
|
|
886
|
+
// ============================================
|
|
887
|
+
// NODE.JS SERVER DEBUGGING TOOLS (7)
|
|
888
|
+
// ============================================
|
|
889
|
+
trace_connect_node_debugger: {
|
|
890
|
+
method: 'connect_node_debugger',
|
|
891
|
+
description: 'Connect to Node.js inspector (requires server started with --inspect). Auto-discovers on port 9229.',
|
|
892
|
+
schema: z.object({
|
|
893
|
+
port: z.number().optional().describe('Inspector port (default: 9229)'),
|
|
894
|
+
}),
|
|
895
|
+
},
|
|
896
|
+
trace_node_list_scripts: {
|
|
897
|
+
method: 'node_list_scripts',
|
|
898
|
+
description: 'List all server-side scripts loaded by Node.js (excludes node_modules and builtins).',
|
|
899
|
+
schema: z.object({}),
|
|
900
|
+
},
|
|
901
|
+
trace_node_get_source: {
|
|
902
|
+
method: 'node_get_source',
|
|
903
|
+
description: 'Read the source code of a server-side Node.js script.',
|
|
904
|
+
schema: z.object({
|
|
905
|
+
scriptId: z.string().describe('Script ID from node_list_scripts'),
|
|
906
|
+
}),
|
|
907
|
+
},
|
|
908
|
+
trace_node_set_breakpoint: {
|
|
909
|
+
method: 'node_set_breakpoint',
|
|
910
|
+
description: 'Set a breakpoint in server-side Node.js code.',
|
|
911
|
+
schema: z.object({
|
|
912
|
+
url: z.string().describe('Script URL or filename pattern'),
|
|
913
|
+
line: z.number().describe('Line number'),
|
|
914
|
+
}),
|
|
915
|
+
},
|
|
916
|
+
trace_node_get_variables: {
|
|
917
|
+
method: 'node_get_variables',
|
|
918
|
+
description: 'Get server-side variable values when Node.js debugger is paused.',
|
|
919
|
+
schema: z.object({}),
|
|
920
|
+
},
|
|
921
|
+
trace_node_step_debugger: {
|
|
922
|
+
method: 'node_step_debugger',
|
|
923
|
+
description: 'Step through server-side Node.js code.',
|
|
924
|
+
schema: z.object({
|
|
925
|
+
action: z.enum(['stepOver', 'stepInto', 'stepOut', 'resume']).describe('Step action'),
|
|
926
|
+
}),
|
|
927
|
+
},
|
|
928
|
+
trace_node_evaluate: {
|
|
929
|
+
method: 'node_evaluate',
|
|
930
|
+
description: 'Evaluate an expression in the Node.js server context.',
|
|
931
|
+
schema: z.object({
|
|
932
|
+
expression: z.string().describe('JavaScript expression to evaluate in Node.js'),
|
|
933
|
+
}),
|
|
934
|
+
},
|
|
935
|
+
// ============================================
|
|
744
936
|
// CONNECTION & META (2)
|
|
745
937
|
// ============================================
|
|
746
938
|
trace_connect: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@probebrowser/trace-mcp",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Trace MCP - Bridge between AI Agents and Trace",
|
|
5
5
|
"homepage": "https://trace.probebrowser.com/",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
"scripts": {
|
|
12
12
|
"build": "tsc",
|
|
13
13
|
"start": "node dist/index.js",
|
|
14
|
+
"test": "vitest run",
|
|
15
|
+
"test:watch": "vitest",
|
|
16
|
+
"test:coverage": "vitest run --coverage",
|
|
14
17
|
"prepublishOnly": "npm run build"
|
|
15
18
|
},
|
|
16
19
|
"dependencies": {
|
|
@@ -20,11 +23,13 @@
|
|
|
20
23
|
"@types/express": "^5.0.6",
|
|
21
24
|
"cors": "^2.8.6",
|
|
22
25
|
"express": "^5.2.1",
|
|
23
|
-
"zod": "^3.23.0"
|
|
26
|
+
"zod": "^3.23.0",
|
|
27
|
+
"zod-to-json-schema": "^3.25.1"
|
|
24
28
|
},
|
|
25
29
|
"devDependencies": {
|
|
26
30
|
"@types/node": "^20.12.0",
|
|
27
|
-
"typescript": "^5.4.0"
|
|
31
|
+
"typescript": "^5.4.0",
|
|
32
|
+
"vitest": "^1.6.0"
|
|
28
33
|
},
|
|
29
34
|
"publishConfig": {
|
|
30
35
|
"access": "public"
|
package/src/server.ts
CHANGED
|
@@ -12,18 +12,23 @@ import {
|
|
|
12
12
|
import { Trace, ALL_TOOLS } from '@probebrowser/sdk';
|
|
13
13
|
import { TOOLS } from './tools.js';
|
|
14
14
|
import { z } from 'zod';
|
|
15
|
+
import { zodToJsonSchema as _zodToJsonSchema } from 'zod-to-json-schema';
|
|
15
16
|
|
|
16
17
|
// Schema conversion helper
|
|
17
18
|
function zodToJsonSchema(schema: z.ZodType<any>): any {
|
|
19
|
+
const jsonSchema = _zodToJsonSchema(schema, { target: 'openApi3' }) as any;
|
|
20
|
+
// MCP expects { type: 'object', properties: {...}, required: [...] }
|
|
18
21
|
return {
|
|
19
22
|
type: 'object',
|
|
20
|
-
properties: {},
|
|
23
|
+
properties: jsonSchema.properties || {},
|
|
24
|
+
required: jsonSchema.required || [],
|
|
21
25
|
};
|
|
22
26
|
}
|
|
23
27
|
|
|
24
28
|
export class TraceMcpServer {
|
|
25
29
|
private server: Server;
|
|
26
30
|
private trace: Trace;
|
|
31
|
+
private connected = false;
|
|
27
32
|
|
|
28
33
|
constructor() {
|
|
29
34
|
this.server = new Server(
|
|
@@ -97,7 +102,7 @@ export class TraceMcpServer {
|
|
|
97
102
|
|
|
98
103
|
if (!method) throw new Error(`Unknown tool: ${toolName}`);
|
|
99
104
|
|
|
100
|
-
const isConnected =
|
|
105
|
+
const isConnected = this.connected;
|
|
101
106
|
|
|
102
107
|
try {
|
|
103
108
|
let result;
|
|
@@ -107,6 +112,7 @@ export class TraceMcpServer {
|
|
|
107
112
|
const url = String(request.params.arguments?.url);
|
|
108
113
|
// Re-init trace if headless mode changes? For now just connect.
|
|
109
114
|
await this.trace.connect(url);
|
|
115
|
+
this.connected = true;
|
|
110
116
|
return { content: [{ type: 'text', text: `Connected to ${url}` }] };
|
|
111
117
|
}
|
|
112
118
|
|
|
@@ -123,19 +129,19 @@ export class TraceMcpServer {
|
|
|
123
129
|
|
|
124
130
|
const prompt = String(request.params.arguments?.prompt || request.params.arguments?.q || 'Analyze page');
|
|
125
131
|
|
|
126
|
-
// Delegate to SDK's
|
|
127
|
-
const
|
|
132
|
+
// Delegate to SDK's query which runs AI-powered deep debug
|
|
133
|
+
const debugResult = await this.trace.query(prompt);
|
|
128
134
|
|
|
129
135
|
// Return the conclusion and steps
|
|
130
136
|
return {
|
|
131
137
|
content: [
|
|
132
|
-
{ type: 'text', text: `###
|
|
138
|
+
{ type: 'text', text: `### Summary\n${debugResult.analysis.summary}\n\n### Steps Taken\n${debugResult.execution.results.map((r: any) => `- ${r.tool}: ${r.success ? 'Success' : 'Failed'}`).join('\n')}` }
|
|
133
139
|
]
|
|
134
140
|
};
|
|
135
141
|
}
|
|
136
142
|
|
|
137
143
|
// 3. Handle Standard SDK Tools
|
|
138
|
-
result = await this.trace.
|
|
144
|
+
result = await this.trace.tool(toolDef.method, (request.params.arguments || {}) as Record<string, unknown>);
|
|
139
145
|
|
|
140
146
|
return {
|
|
141
147
|
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
@@ -196,13 +202,13 @@ export class TraceMcpServer {
|
|
|
196
202
|
|
|
197
203
|
this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
198
204
|
const uri = request.params.uri;
|
|
199
|
-
if (!this.
|
|
205
|
+
if (!this.connected) {
|
|
200
206
|
throw new Error('Not connected. Call trace_connect first.');
|
|
201
207
|
}
|
|
202
208
|
|
|
203
209
|
// --- Console ---
|
|
204
210
|
if (uri === 'trace://console/errors') {
|
|
205
|
-
const logs = await this.trace.
|
|
211
|
+
const logs = await this.trace.tool('get_console_errors', {});
|
|
206
212
|
return { contents: [{ uri, mimeType: 'application/json', text: JSON.stringify(logs, null, 2) }] };
|
|
207
213
|
}
|
|
208
214
|
if (uri === 'trace://console/logs') {
|
|
@@ -211,23 +217,23 @@ export class TraceMcpServer {
|
|
|
211
217
|
// For now, let's return the summary as it contains counts.
|
|
212
218
|
// Ideally SDK should expose 'get_all_logs'.
|
|
213
219
|
// Fallback: use get_console_errors
|
|
214
|
-
const logs = await this.trace.
|
|
220
|
+
const logs = await this.trace.tool('get_console_summary', {});
|
|
215
221
|
return { contents: [{ uri, mimeType: 'application/json', text: JSON.stringify(logs, null, 2) }] };
|
|
216
222
|
}
|
|
217
223
|
|
|
218
224
|
// --- Network ---
|
|
219
225
|
if (uri === 'trace://network/failed') {
|
|
220
|
-
const reqs = await this.trace.
|
|
226
|
+
const reqs = await this.trace.tool('get_network_failed', {});
|
|
221
227
|
return { contents: [{ uri, mimeType: 'application/json', text: JSON.stringify(reqs, null, 2) }] };
|
|
222
228
|
}
|
|
223
229
|
if (uri === 'trace://network/all') {
|
|
224
|
-
const reqs = await this.trace.
|
|
230
|
+
const reqs = await this.trace.tool('get_network_summary', {});
|
|
225
231
|
return { contents: [{ uri, mimeType: 'application/json', text: JSON.stringify(reqs, null, 2) }] };
|
|
226
232
|
}
|
|
227
233
|
|
|
228
234
|
// --- DOM ---
|
|
229
235
|
if (uri === 'trace://dom/tree') {
|
|
230
|
-
const tree = await this.trace.
|
|
236
|
+
const tree = await this.trace.tool('get_dom_tree', { depth: 2 });
|
|
231
237
|
return { contents: [{ uri, mimeType: 'application/json', text: JSON.stringify(tree, null, 2) }] };
|
|
232
238
|
}
|
|
233
239
|
|