@probebrowser/trace-mcp 1.0.2 → 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 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: {}, // Simplified
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 = !!this.trace.getCurrentUrl();
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 queryDeep which calls Azure /v1/agent-step
96
- const analysis = await this.trace.queryDeep(prompt);
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: `### Conclusion\n${analysis.conclusion}\n\n### Steps Taken\n${analysis.steps.map(s => `- ${s.tool}: ${s.success ? 'Success' : 'Failed'}`).join('\n')}` }
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.executeTool(toolDef.method, request.params.arguments);
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.trace.getCurrentUrl()) {
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.executeTool('get_console_errors', {});
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.executeTool('get_console_summary', {});
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.executeTool('get_network_failed', {});
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.executeTool('get_network_summary', {});
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.executeTool('get_dom_tree', { depth: 2 });
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
- // 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.2",
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,20 +11,25 @@
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": {
17
- "@modelcontextprotocol/sdk": "^0.6.0",
18
- "@probebrowser/sdk": "^1.3.0",
20
+ "@modelcontextprotocol/sdk": "^1.0.0",
21
+ "@probebrowser/sdk": "^2.0.1",
19
22
  "@types/cors": "^2.8.19",
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: {}, // Simplified
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 = !!this.trace.getCurrentUrl();
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 queryDeep which calls Azure /v1/agent-step
127
- const analysis = await this.trace.queryDeep(prompt);
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: `### Conclusion\n${analysis.conclusion}\n\n### Steps Taken\n${analysis.steps.map(s => `- ${s.tool}: ${s.success ? 'Success' : 'Failed'}`).join('\n')}` }
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.executeTool(toolDef.method, request.params.arguments as any);
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.trace.getCurrentUrl()) {
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.executeTool('get_console_errors', {});
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.executeTool('get_console_summary', {});
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.executeTool('get_network_failed', {});
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.executeTool('get_network_summary', {});
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.executeTool('get_dom_tree', { depth: 2 });
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