@probebrowser/trace-mcp 1.0.3 → 1.1.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/dist/server.js CHANGED
@@ -3,16 +3,48 @@ 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
+ // Tools hidden by default to stay within IDE 100-tool limits.
7
+ // Set TRACE_ALL_TOOLS=true to expose everything.
8
+ const NON_CORE_TOOLS = new Set([
9
+ // Node.js debugger (7) - browser-focused IDEs rarely need these
10
+ 'trace_connect_node_debugger', 'trace_node_list_scripts', 'trace_node_get_source',
11
+ 'trace_node_set_breakpoint', 'trace_node_get_variables', 'trace_node_step_debugger',
12
+ 'trace_node_evaluate',
13
+ // Cache Storage (3) - niche
14
+ 'trace_get_caches', 'trace_get_cache_contents', 'trace_delete_cache',
15
+ // Code/filesystem (11) - IDE already provides these
16
+ 'trace_read_file', 'trace_search_code', 'trace_get_file_tree', 'trace_get_project_info',
17
+ 'trace_get_error_context', 'trace_git_blame', 'trace_git_recent_changes',
18
+ 'trace_get_imports', 'trace_find_usages', 'trace_get_related_files', 'trace_get_env_vars',
19
+ // Advanced debugger (6) - power-user only
20
+ 'trace_capture_execution_state', 'trace_get_execution_history',
21
+ 'trace_watch_for_changes', 'trace_check_changes', 'trace_trace_variable_origin',
22
+ 'trace_debug_and_analyze',
23
+ // Advanced source (5) - power-user only
24
+ 'trace_blackbox_script', 'trace_list_blackbox_patterns', 'trace_list_original_sources',
25
+ 'trace_get_original_location', 'trace_map_call_stack',
26
+ // Advanced DOM (3)
27
+ 'trace_find_ghost_blockers', 'trace_find_hidden_interactive_elements', 'trace_inspect_mode',
28
+ // Tracing (3) - niche
29
+ 'trace_start_trace', 'trace_get_trace_spans', 'trace_get_error_rate',
30
+ // Timeline deep-dive (2) - niche
31
+ 'trace_diff_from_snapshot', 'trace_get_events_in_window',
32
+ ]);
33
+ import { zodToJsonSchema as _zodToJsonSchema } from 'zod-to-json-schema';
6
34
  // Schema conversion helper
7
35
  function zodToJsonSchema(schema) {
36
+ const jsonSchema = _zodToJsonSchema(schema, { target: 'openApi3' });
37
+ // MCP expects { type: 'object', properties: {...}, required: [...] }
8
38
  return {
9
39
  type: 'object',
10
- properties: {}, // Simplified
40
+ properties: jsonSchema.properties || {},
41
+ required: jsonSchema.required || [],
11
42
  };
12
43
  }
13
44
  export class TraceMcpServer {
14
45
  server;
15
46
  trace;
47
+ connected = false;
16
48
  constructor() {
17
49
  this.server = new Server({
18
50
  name: 'trace-mcp-server',
@@ -38,17 +70,20 @@ export class TraceMcpServer {
38
70
  // 1. TOOLS
39
71
  // ============================================
40
72
  this.server.setRequestHandler(ListToolsRequestSchema, async () => {
41
- const definedTools = Object.entries(TOOLS).map(([name, def]) => ({
73
+ const allTools = process.env.TRACE_ALL_TOOLS === 'true';
74
+ const definedTools = Object.entries(TOOLS)
75
+ .filter(([name]) => allTools || !NON_CORE_TOOLS.has(name))
76
+ .map(([name, def]) => ({
42
77
  name,
43
78
  description: def.description,
44
79
  inputSchema: zodToJsonSchema(def.schema),
45
80
  }));
46
81
  const definedNames = new Set(Object.keys(TOOLS));
47
82
  const distinctAllTools = new Set(ALL_TOOLS);
48
- // Add dynamic tools
83
+ // Add dynamic tools (fallback for any SDK tools not in TOOLS)
49
84
  for (const tool of distinctAllTools) {
50
85
  const mcpName = `trace_${tool}`;
51
- if (!definedNames.has(mcpName)) {
86
+ if (!definedNames.has(mcpName) && (allTools || !NON_CORE_TOOLS.has(mcpName))) {
52
87
  definedTools.push({
53
88
  name: mcpName,
54
89
  description: `Execute ${tool} (Dynamic Tool)`,
@@ -72,7 +107,7 @@ export class TraceMcpServer {
72
107
  }
73
108
  if (!method)
74
109
  throw new Error(`Unknown tool: ${toolName}`);
75
- const isConnected = !!this.trace.getCurrentUrl();
110
+ const isConnected = this.connected;
76
111
  try {
77
112
  let result;
78
113
  // 1. Handle Connect Tool
@@ -80,6 +115,7 @@ export class TraceMcpServer {
80
115
  const url = String(request.params.arguments?.url);
81
116
  // Re-init trace if headless mode changes? For now just connect.
82
117
  await this.trace.connect(url);
118
+ this.connected = true;
83
119
  return { content: [{ type: 'text', text: `Connected to ${url}` }] };
84
120
  }
85
121
  if (!isConnected) {
@@ -92,17 +128,17 @@ export class TraceMcpServer {
92
128
  return { content: [{ type: 'text', text: 'Error: TRACE_API_KEY is required for AI features.' }], isError: true };
93
129
  }
94
130
  const prompt = String(request.params.arguments?.prompt || request.params.arguments?.q || 'Analyze page');
95
- // Delegate to SDK's queryDeep which calls Azure /v1/agent-step
96
- const analysis = await this.trace.queryDeep(prompt);
131
+ // Delegate to SDK's query which runs AI-powered deep debug
132
+ const debugResult = await this.trace.query(prompt);
97
133
  // Return the conclusion and steps
98
134
  return {
99
135
  content: [
100
- { type: 'text', text: `### Conclusion\n${analysis.conclusion}\n\n### Steps Taken\n${analysis.steps.map(s => `- ${s.tool}: ${s.success ? 'Success' : 'Failed'}`).join('\n')}` }
136
+ { 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
137
  ]
102
138
  };
103
139
  }
104
140
  // 3. Handle Standard SDK Tools
105
- result = await this.trace.executeTool(toolDef.method, request.params.arguments);
141
+ result = await this.trace.tool(toolDef.method, (request.params.arguments || {}));
106
142
  return {
107
143
  content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
108
144
  };
@@ -161,12 +197,12 @@ export class TraceMcpServer {
161
197
  });
162
198
  this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
163
199
  const uri = request.params.uri;
164
- if (!this.trace.getCurrentUrl()) {
200
+ if (!this.connected) {
165
201
  throw new Error('Not connected. Call trace_connect first.');
166
202
  }
167
203
  // --- Console ---
168
204
  if (uri === 'trace://console/errors') {
169
- const logs = await this.trace.executeTool('get_console_errors', {});
205
+ const logs = await this.trace.tool('get_console_errors', {});
170
206
  return { contents: [{ uri, mimeType: 'application/json', text: JSON.stringify(logs, null, 2) }] };
171
207
  }
172
208
  if (uri === 'trace://console/logs') {
@@ -175,21 +211,21 @@ export class TraceMcpServer {
175
211
  // For now, let's return the summary as it contains counts.
176
212
  // Ideally SDK should expose 'get_all_logs'.
177
213
  // Fallback: use get_console_errors
178
- const logs = await this.trace.executeTool('get_console_summary', {});
214
+ const logs = await this.trace.tool('get_console_summary', {});
179
215
  return { contents: [{ uri, mimeType: 'application/json', text: JSON.stringify(logs, null, 2) }] };
180
216
  }
181
217
  // --- Network ---
182
218
  if (uri === 'trace://network/failed') {
183
- const reqs = await this.trace.executeTool('get_network_failed', {});
219
+ const reqs = await this.trace.tool('get_network_failed', {});
184
220
  return { contents: [{ uri, mimeType: 'application/json', text: JSON.stringify(reqs, null, 2) }] };
185
221
  }
186
222
  if (uri === 'trace://network/all') {
187
- const reqs = await this.trace.executeTool('get_network_summary', {});
223
+ const reqs = await this.trace.tool('get_network_summary', {});
188
224
  return { contents: [{ uri, mimeType: 'application/json', text: JSON.stringify(reqs, null, 2) }] };
189
225
  }
190
226
  // --- DOM ---
191
227
  if (uri === 'trace://dom/tree') {
192
- const tree = await this.trace.executeTool('get_dom_tree', { depth: 2 });
228
+ const tree = await this.trace.tool('get_dom_tree', { depth: 2 });
193
229
  return { contents: [{ uri, mimeType: 'application/json', text: JSON.stringify(tree, null, 2) }] };
194
230
  }
195
231
  // --- 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
- // DOM & UI TOOLS (16)
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 (24)
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
- trace_set_dom_breakpoint: {
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 (8)
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",
3
+ "version": "1.1.1",
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
@@ -11,19 +11,52 @@ import {
11
11
  } from '@modelcontextprotocol/sdk/types.js';
12
12
  import { Trace, ALL_TOOLS } from '@probebrowser/sdk';
13
13
  import { TOOLS } from './tools.js';
14
+
15
+ // Tools hidden by default to stay within IDE 100-tool limits.
16
+ // Set TRACE_ALL_TOOLS=true to expose everything.
17
+ const NON_CORE_TOOLS = new Set([
18
+ // Node.js debugger (7) - browser-focused IDEs rarely need these
19
+ 'trace_connect_node_debugger', 'trace_node_list_scripts', 'trace_node_get_source',
20
+ 'trace_node_set_breakpoint', 'trace_node_get_variables', 'trace_node_step_debugger',
21
+ 'trace_node_evaluate',
22
+ // Cache Storage (3) - niche
23
+ 'trace_get_caches', 'trace_get_cache_contents', 'trace_delete_cache',
24
+ // Code/filesystem (11) - IDE already provides these
25
+ 'trace_read_file', 'trace_search_code', 'trace_get_file_tree', 'trace_get_project_info',
26
+ 'trace_get_error_context', 'trace_git_blame', 'trace_git_recent_changes',
27
+ 'trace_get_imports', 'trace_find_usages', 'trace_get_related_files', 'trace_get_env_vars',
28
+ // Advanced debugger (6) - power-user only
29
+ 'trace_capture_execution_state', 'trace_get_execution_history',
30
+ 'trace_watch_for_changes', 'trace_check_changes', 'trace_trace_variable_origin',
31
+ 'trace_debug_and_analyze',
32
+ // Advanced source (5) - power-user only
33
+ 'trace_blackbox_script', 'trace_list_blackbox_patterns', 'trace_list_original_sources',
34
+ 'trace_get_original_location', 'trace_map_call_stack',
35
+ // Advanced DOM (3)
36
+ 'trace_find_ghost_blockers', 'trace_find_hidden_interactive_elements', 'trace_inspect_mode',
37
+ // Tracing (3) - niche
38
+ 'trace_start_trace', 'trace_get_trace_spans', 'trace_get_error_rate',
39
+ // Timeline deep-dive (2) - niche
40
+ 'trace_diff_from_snapshot', 'trace_get_events_in_window',
41
+ ]);
14
42
  import { z } from 'zod';
43
+ import { zodToJsonSchema as _zodToJsonSchema } from 'zod-to-json-schema';
15
44
 
16
45
  // Schema conversion helper
17
46
  function zodToJsonSchema(schema: z.ZodType<any>): any {
47
+ const jsonSchema = _zodToJsonSchema(schema, { target: 'openApi3' }) as any;
48
+ // MCP expects { type: 'object', properties: {...}, required: [...] }
18
49
  return {
19
50
  type: 'object',
20
- properties: {}, // Simplified
51
+ properties: jsonSchema.properties || {},
52
+ required: jsonSchema.required || [],
21
53
  };
22
54
  }
23
55
 
24
56
  export class TraceMcpServer {
25
57
  private server: Server;
26
58
  private trace: Trace;
59
+ private connected = false;
27
60
 
28
61
  constructor() {
29
62
  this.server = new Server(
@@ -57,19 +90,23 @@ export class TraceMcpServer {
57
90
  // 1. TOOLS
58
91
  // ============================================
59
92
  this.server.setRequestHandler(ListToolsRequestSchema, async () => {
60
- const definedTools: Tool[] = Object.entries(TOOLS).map(([name, def]) => ({
61
- name,
62
- description: def.description,
63
- inputSchema: zodToJsonSchema(def.schema),
64
- }));
93
+ const allTools = process.env.TRACE_ALL_TOOLS === 'true';
94
+
95
+ const definedTools: Tool[] = Object.entries(TOOLS)
96
+ .filter(([name]) => allTools || !NON_CORE_TOOLS.has(name))
97
+ .map(([name, def]) => ({
98
+ name,
99
+ description: def.description,
100
+ inputSchema: zodToJsonSchema(def.schema),
101
+ }));
65
102
 
66
103
  const definedNames = new Set(Object.keys(TOOLS));
67
104
  const distinctAllTools = new Set(ALL_TOOLS);
68
105
 
69
- // Add dynamic tools
106
+ // Add dynamic tools (fallback for any SDK tools not in TOOLS)
70
107
  for (const tool of distinctAllTools) {
71
108
  const mcpName = `trace_${tool}`;
72
- if (!definedNames.has(mcpName)) {
109
+ if (!definedNames.has(mcpName) && (allTools || !NON_CORE_TOOLS.has(mcpName))) {
73
110
  definedTools.push({
74
111
  name: mcpName,
75
112
  description: `Execute ${tool} (Dynamic Tool)`,
@@ -97,7 +134,7 @@ export class TraceMcpServer {
97
134
 
98
135
  if (!method) throw new Error(`Unknown tool: ${toolName}`);
99
136
 
100
- const isConnected = !!this.trace.getCurrentUrl();
137
+ const isConnected = this.connected;
101
138
 
102
139
  try {
103
140
  let result;
@@ -107,6 +144,7 @@ export class TraceMcpServer {
107
144
  const url = String(request.params.arguments?.url);
108
145
  // Re-init trace if headless mode changes? For now just connect.
109
146
  await this.trace.connect(url);
147
+ this.connected = true;
110
148
  return { content: [{ type: 'text', text: `Connected to ${url}` }] };
111
149
  }
112
150
 
@@ -123,19 +161,19 @@ export class TraceMcpServer {
123
161
 
124
162
  const prompt = String(request.params.arguments?.prompt || request.params.arguments?.q || 'Analyze page');
125
163
 
126
- // Delegate to SDK's queryDeep which calls Azure /v1/agent-step
127
- const analysis = await this.trace.queryDeep(prompt);
164
+ // Delegate to SDK's query which runs AI-powered deep debug
165
+ const debugResult = await this.trace.query(prompt);
128
166
 
129
167
  // Return the conclusion and steps
130
168
  return {
131
169
  content: [
132
- { type: 'text', text: `### Conclusion\n${analysis.conclusion}\n\n### Steps Taken\n${analysis.steps.map(s => `- ${s.tool}: ${s.success ? 'Success' : 'Failed'}`).join('\n')}` }
170
+ { 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
171
  ]
134
172
  };
135
173
  }
136
174
 
137
175
  // 3. Handle Standard SDK Tools
138
- result = await this.trace.executeTool(toolDef.method, request.params.arguments as any);
176
+ result = await this.trace.tool(toolDef.method, (request.params.arguments || {}) as Record<string, unknown>);
139
177
 
140
178
  return {
141
179
  content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
@@ -196,13 +234,13 @@ export class TraceMcpServer {
196
234
 
197
235
  this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
198
236
  const uri = request.params.uri;
199
- if (!this.trace.getCurrentUrl()) {
237
+ if (!this.connected) {
200
238
  throw new Error('Not connected. Call trace_connect first.');
201
239
  }
202
240
 
203
241
  // --- Console ---
204
242
  if (uri === 'trace://console/errors') {
205
- const logs = await this.trace.executeTool('get_console_errors', {});
243
+ const logs = await this.trace.tool('get_console_errors', {});
206
244
  return { contents: [{ uri, mimeType: 'application/json', text: JSON.stringify(logs, null, 2) }] };
207
245
  }
208
246
  if (uri === 'trace://console/logs') {
@@ -211,23 +249,23 @@ export class TraceMcpServer {
211
249
  // For now, let's return the summary as it contains counts.
212
250
  // Ideally SDK should expose 'get_all_logs'.
213
251
  // Fallback: use get_console_errors
214
- const logs = await this.trace.executeTool('get_console_summary', {});
252
+ const logs = await this.trace.tool('get_console_summary', {});
215
253
  return { contents: [{ uri, mimeType: 'application/json', text: JSON.stringify(logs, null, 2) }] };
216
254
  }
217
255
 
218
256
  // --- Network ---
219
257
  if (uri === 'trace://network/failed') {
220
- const reqs = await this.trace.executeTool('get_network_failed', {});
258
+ const reqs = await this.trace.tool('get_network_failed', {});
221
259
  return { contents: [{ uri, mimeType: 'application/json', text: JSON.stringify(reqs, null, 2) }] };
222
260
  }
223
261
  if (uri === 'trace://network/all') {
224
- const reqs = await this.trace.executeTool('get_network_summary', {});
262
+ const reqs = await this.trace.tool('get_network_summary', {});
225
263
  return { contents: [{ uri, mimeType: 'application/json', text: JSON.stringify(reqs, null, 2) }] };
226
264
  }
227
265
 
228
266
  // --- DOM ---
229
267
  if (uri === 'trace://dom/tree') {
230
- const tree = await this.trace.executeTool('get_dom_tree', { depth: 2 });
268
+ const tree = await this.trace.tool('get_dom_tree', { depth: 2 });
231
269
  return { contents: [{ uri, mimeType: 'application/json', text: JSON.stringify(tree, null, 2) }] };
232
270
  }
233
271