@probebrowser/mcp-server 1.2.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/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@probebrowser/mcp-server",
3
+ "version": "1.2.0",
4
+ "description": "Trace MCP Server - Bridge between AI Agents and Trace",
5
+ "main": "dist/index.js",
6
+ "type": "module",
7
+ "bin": {
8
+ "trace-mcp": "./dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "start": "node dist/index.js",
13
+ "prepublishOnly": "npm run build"
14
+ },
15
+ "dependencies": {
16
+ "@modelcontextprotocol/sdk": "^0.6.0",
17
+ "@probebrowser/sdk": "^1.3.0",
18
+ "@types/cors": "^2.8.19",
19
+ "@types/express": "^5.0.6",
20
+ "cors": "^2.8.6",
21
+ "express": "^5.2.1",
22
+ "zod": "^3.23.0"
23
+ },
24
+ "devDependencies": {
25
+ "@types/node": "^20.12.0",
26
+ "typescript": "^5.4.0"
27
+ },
28
+ "publishConfig": {
29
+ "access": "public"
30
+ },
31
+ "keywords": [
32
+ "mcp",
33
+ "model-context-protocol",
34
+ "trace",
35
+ "debugging",
36
+ "ai",
37
+ "browser",
38
+ "devtools"
39
+ ],
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "https://github.com/AhmadBilalAmjad/dev-intelli.git"
43
+ },
44
+ "author": "Probe",
45
+ "license": "Apache-2.0"
46
+ }
package/src/index.ts ADDED
@@ -0,0 +1,34 @@
1
+ import express from 'express';
2
+ import cors from 'cors';
3
+ import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
4
+ import { TraceMcpServer } from './server.js';
5
+
6
+ async function main() {
7
+ const transportType = process.env.TRANSPORT || 'stdio';
8
+
9
+ if (transportType === 'sse') {
10
+ const app = express();
11
+ app.use(cors());
12
+
13
+ // Create MCP Server
14
+ const traceServer = new TraceMcpServer();
15
+ const transport = new SSEServerTransport('/messages', app as any); // Type cast might be needed if SDK types mismatch express
16
+
17
+ await traceServer.connect(transport);
18
+
19
+ const port = process.env.PORT || 3000;
20
+ app.listen(port, () => {
21
+ console.error(`Trace MCP Server (SSE) running on port ${port}`);
22
+ console.error(`Endpoint: http://localhost:${port}/sse`);
23
+ });
24
+ } else {
25
+ // Stdio Mode
26
+ const traceServer = new TraceMcpServer();
27
+ await traceServer.start();
28
+ }
29
+ }
30
+
31
+ main().catch((error) => {
32
+ console.error('Fatal error:', error);
33
+ process.exit(1);
34
+ });
File without changes
package/src/server.ts ADDED
@@ -0,0 +1,330 @@
1
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import {
4
+ CallToolRequestSchema,
5
+ ListToolsRequestSchema,
6
+ ListResourcesRequestSchema,
7
+ ReadResourceRequestSchema,
8
+ ListPromptsRequestSchema,
9
+ GetPromptRequestSchema,
10
+ Tool,
11
+ } from '@modelcontextprotocol/sdk/types.js';
12
+ import { Trace, ALL_TOOLS } from '@probebrowser/sdk';
13
+ import { TOOLS } from './tools.js';
14
+ import { z } from 'zod';
15
+
16
+ // Schema conversion helper
17
+ function zodToJsonSchema(schema: z.ZodType<any>): any {
18
+ return {
19
+ type: 'object',
20
+ properties: {}, // Simplified
21
+ };
22
+ }
23
+
24
+ export class TraceMcpServer {
25
+ private server: Server;
26
+ private trace: Trace;
27
+
28
+ constructor() {
29
+ this.server = new Server(
30
+ {
31
+ name: 'trace-mcp-server',
32
+ version: '1.2.0',
33
+ },
34
+ {
35
+ capabilities: {
36
+ tools: {},
37
+ resources: {},
38
+ prompts: {},
39
+ },
40
+ }
41
+ );
42
+
43
+ // Initialize Trace SDK with Env Config
44
+ const headless = process.env.TRACE_HEADLESS !== 'false'; // Default to true
45
+ const verbose = process.env.TRACE_VERBOSE === 'true';
46
+
47
+ this.trace = new Trace({
48
+ headless,
49
+ verbose,
50
+ });
51
+
52
+ this.setupHandlers();
53
+ }
54
+
55
+ private setupHandlers() {
56
+ // ============================================
57
+ // 1. TOOLS
58
+ // ============================================
59
+ 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
+ }));
65
+
66
+ const definedNames = new Set(Object.keys(TOOLS));
67
+ const distinctAllTools = new Set(ALL_TOOLS);
68
+
69
+ // Add dynamic tools
70
+ for (const tool of distinctAllTools) {
71
+ const mcpName = `trace_${tool}`;
72
+ if (!definedNames.has(mcpName)) {
73
+ definedTools.push({
74
+ name: mcpName,
75
+ description: `Execute ${tool} (Dynamic Tool)`,
76
+ inputSchema: { type: 'object', properties: {} },
77
+ });
78
+ }
79
+ }
80
+
81
+ return { tools: definedTools };
82
+ });
83
+
84
+ this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
85
+ const toolName = request.params.name;
86
+ let toolDef = (TOOLS as any)[toolName];
87
+ let method = toolDef?.method;
88
+
89
+ // Dynamic Tool Logic
90
+ if (!toolDef && toolName.startsWith('trace_')) {
91
+ const rawName = toolName.replace('trace_', '');
92
+ if (ALL_TOOLS.includes(rawName)) {
93
+ method = rawName;
94
+ toolDef = { method: rawName };
95
+ }
96
+ }
97
+
98
+ if (!method) throw new Error(`Unknown tool: ${toolName}`);
99
+
100
+ const isConnected = !!this.trace.getCurrentUrl();
101
+
102
+ try {
103
+ let result;
104
+
105
+ // 1. Handle Connect Tool
106
+ if (toolDef.method === 'connect') {
107
+ const url = String(request.params.arguments?.url);
108
+ // Re-init trace if headless mode changes? For now just connect.
109
+ await this.trace.connect(url);
110
+ return { content: [{ type: 'text', text: `Connected to ${url}` }] };
111
+ }
112
+
113
+ if (!isConnected) {
114
+ return { content: [{ type: 'text', text: 'Error: No active page. Call trace_connect first.' }], isError: true };
115
+ }
116
+
117
+ // 2. Handle Azure AI Agent (Unified Deep Debug)
118
+ if (toolDef.method === 'deep_debug' || toolDef.method === 'trace_p') {
119
+ // Check for API Key
120
+ if (!process.env.TRACE_API_KEY) {
121
+ return { content: [{ type: 'text', text: 'Error: TRACE_API_KEY is required for AI features.' }], isError: true };
122
+ }
123
+
124
+ const prompt = String(request.params.arguments?.prompt || request.params.arguments?.q || 'Analyze page');
125
+
126
+ // Delegate to SDK's queryDeep which calls Azure /v1/agent-step
127
+ const analysis = await this.trace.queryDeep(prompt);
128
+
129
+ // Return the conclusion and steps
130
+ return {
131
+ 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')}` }
133
+ ]
134
+ };
135
+ }
136
+
137
+ // 3. Handle Standard SDK Tools
138
+ result = await this.trace.executeTool(toolDef.method, request.params.arguments as any);
139
+
140
+ return {
141
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
142
+ };
143
+ } catch (error: any) {
144
+ return {
145
+ content: [{ type: 'text', text: `Error: ${error.message}` }],
146
+ isError: true,
147
+ };
148
+ }
149
+ });
150
+
151
+ // ============================================
152
+ // 2. RESOURCES
153
+ // ============================================
154
+ this.server.setRequestHandler(ListResourcesRequestSchema, async () => {
155
+ return {
156
+ resources: [
157
+ {
158
+ uri: 'trace://console/errors',
159
+ name: 'Console Errors',
160
+ mimeType: 'application/json',
161
+ description: 'List of all console errors and exceptions',
162
+ },
163
+ {
164
+ uri: 'trace://console/logs',
165
+ name: 'All Console Logs',
166
+ mimeType: 'application/json',
167
+ description: 'Full console history (info, warn, error)',
168
+ },
169
+ {
170
+ uri: 'trace://network/failed',
171
+ name: 'Failed Network Requests',
172
+ mimeType: 'application/json',
173
+ description: 'List of failed network requests (4xx, 5xx)',
174
+ },
175
+ {
176
+ uri: 'trace://network/all',
177
+ name: 'All Network Traffic',
178
+ mimeType: 'application/json',
179
+ description: 'Recent network requests summary',
180
+ },
181
+ {
182
+ uri: 'trace://dom/tree',
183
+ name: 'DOM Tree',
184
+ mimeType: 'application/json',
185
+ description: 'Simplified DOM structure',
186
+ },
187
+ {
188
+ uri: 'trace://screenshot',
189
+ name: 'Page Screenshot',
190
+ mimeType: 'image/png',
191
+ description: 'Current page screenshot',
192
+ },
193
+ ],
194
+ };
195
+ });
196
+
197
+ this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
198
+ const uri = request.params.uri;
199
+ if (!this.trace.getCurrentUrl()) {
200
+ throw new Error('Not connected. Call trace_connect first.');
201
+ }
202
+
203
+ // --- Console ---
204
+ if (uri === 'trace://console/errors') {
205
+ const logs = await this.trace.executeTool('get_console_errors', {});
206
+ return { contents: [{ uri, mimeType: 'application/json', text: JSON.stringify(logs, null, 2) }] };
207
+ }
208
+ if (uri === 'trace://console/logs') {
209
+ // We don't have get_console_logs explicitly in ALL_TOOLS list in previous step,
210
+ // but we can use get_console_summary or implement a getter.
211
+ // For now, let's return the summary as it contains counts.
212
+ // Ideally SDK should expose 'get_all_logs'.
213
+ // Fallback: use get_console_errors
214
+ const logs = await this.trace.executeTool('get_console_summary', {});
215
+ return { contents: [{ uri, mimeType: 'application/json', text: JSON.stringify(logs, null, 2) }] };
216
+ }
217
+
218
+ // --- Network ---
219
+ if (uri === 'trace://network/failed') {
220
+ const reqs = await this.trace.executeTool('get_network_failed', {});
221
+ return { contents: [{ uri, mimeType: 'application/json', text: JSON.stringify(reqs, null, 2) }] };
222
+ }
223
+ if (uri === 'trace://network/all') {
224
+ const reqs = await this.trace.executeTool('get_network_summary', {});
225
+ return { contents: [{ uri, mimeType: 'application/json', text: JSON.stringify(reqs, null, 2) }] };
226
+ }
227
+
228
+ // --- DOM ---
229
+ if (uri === 'trace://dom/tree') {
230
+ const tree = await this.trace.executeTool('get_dom_tree', { depth: 2 });
231
+ return { contents: [{ uri, mimeType: 'application/json', text: JSON.stringify(tree, null, 2) }] };
232
+ }
233
+
234
+ // --- Screenshot ---
235
+ if (uri === 'trace://screenshot') {
236
+ // Try to capture snapshot via capture_execution_state which might have screenshot
237
+ // Or just return a placeholder if SDK doesn't support raw image output via tool interface easily
238
+ // For now, returning text explainer
239
+ return { contents: [{ uri, mimeType: 'text/plain', text: "Screenshot resource requires Trace SDK v1.4+" }] };
240
+ }
241
+
242
+ throw new Error(`Resource not found: ${uri}`);
243
+ });
244
+
245
+ // ============================================
246
+ // 3. PROMPTS
247
+ // ============================================
248
+ this.server.setRequestHandler(ListPromptsRequestSchema, async () => {
249
+ return {
250
+ prompts: [
251
+ {
252
+ name: 'debug_error',
253
+ description: 'Analyze the most recent error and suggest a fix',
254
+ arguments: [],
255
+ },
256
+ {
257
+ name: 'audit_performance',
258
+ description: 'Check metrics and run a quick profile',
259
+ arguments: [],
260
+ },
261
+ {
262
+ name: 'verify_security',
263
+ description: 'Check security headers and SSL status',
264
+ arguments: [],
265
+ },
266
+ ],
267
+ };
268
+ });
269
+
270
+ this.server.setRequestHandler(GetPromptRequestSchema, async (request) => {
271
+ const promptName = request.params.name;
272
+
273
+ if (promptName === 'debug_error') {
274
+ return {
275
+ messages: [
276
+ {
277
+ role: 'user',
278
+ content: {
279
+ type: 'text',
280
+ text: 'Please check the console for errors, analyze the most critical one, and suggest a code fix.',
281
+ },
282
+ },
283
+ ],
284
+ };
285
+ }
286
+
287
+ if (promptName === 'audit_performance') {
288
+ return {
289
+ messages: [
290
+ {
291
+ role: 'user',
292
+ content: {
293
+ type: 'text',
294
+ text: 'Please capture performance metrics, take a heap snapshot, and tell me if there are any obvious bottlenecks.',
295
+ },
296
+ },
297
+ ],
298
+ };
299
+ }
300
+
301
+ if (promptName === 'verify_security') {
302
+ return {
303
+ messages: [
304
+ {
305
+ role: 'user',
306
+ content: {
307
+ type: 'text',
308
+ text: 'Please check the security info, verify HTTPS, and look for mixed content warnings.',
309
+ },
310
+ },
311
+ ],
312
+ };
313
+ }
314
+
315
+ throw new Error('Prompt not found');
316
+ });
317
+ }
318
+
319
+ async connect(transport: any) {
320
+ await this.server.connect(transport);
321
+ console.error('Trace MCP Server connected');
322
+ }
323
+
324
+ async start() {
325
+ // Default to Stdio for backward compatibility
326
+ const transport = new StdioServerTransport();
327
+ await this.server.connect(transport);
328
+ console.error('Trace MCP Server running on stdio');
329
+ }
330
+ }