@probelabs/probe 0.6.0-rc100

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.
Files changed (115) hide show
  1. package/README.md +583 -0
  2. package/bin/.gitkeep +0 -0
  3. package/bin/probe +158 -0
  4. package/bin/probe-binary +0 -0
  5. package/build/agent/ProbeAgent.d.ts +199 -0
  6. package/build/agent/ProbeAgent.js +1486 -0
  7. package/build/agent/acp/README.md +347 -0
  8. package/build/agent/acp/connection.js +237 -0
  9. package/build/agent/acp/connection.test.js +311 -0
  10. package/build/agent/acp/examples/simple-client.js +212 -0
  11. package/build/agent/acp/examples/tool-lifecycle.js +230 -0
  12. package/build/agent/acp/final-test.js +173 -0
  13. package/build/agent/acp/index.js +5 -0
  14. package/build/agent/acp/integration.test.js +385 -0
  15. package/build/agent/acp/manual-test.js +410 -0
  16. package/build/agent/acp/protocol-test.js +190 -0
  17. package/build/agent/acp/server.js +448 -0
  18. package/build/agent/acp/server.test.js +371 -0
  19. package/build/agent/acp/test-runner.js +216 -0
  20. package/build/agent/acp/test-utils/README.md +315 -0
  21. package/build/agent/acp/test-utils/acp-tester.js +484 -0
  22. package/build/agent/acp/test-utils/mock-acp-client.js +434 -0
  23. package/build/agent/acp/tools.js +368 -0
  24. package/build/agent/acp/tools.test.js +334 -0
  25. package/build/agent/acp/types.js +218 -0
  26. package/build/agent/acp/types.test.js +327 -0
  27. package/build/agent/appTracer.js +360 -0
  28. package/build/agent/fileSpanExporter.js +169 -0
  29. package/build/agent/index.js +7426 -0
  30. package/build/agent/mcp/client.js +338 -0
  31. package/build/agent/mcp/config.js +313 -0
  32. package/build/agent/mcp/index.js +64 -0
  33. package/build/agent/mcp/xmlBridge.js +371 -0
  34. package/build/agent/mockProvider.js +53 -0
  35. package/build/agent/probeTool.js +257 -0
  36. package/build/agent/schemaUtils.js +1726 -0
  37. package/build/agent/simpleTelemetry.js +267 -0
  38. package/build/agent/telemetry.js +225 -0
  39. package/build/agent/tokenCounter.js +395 -0
  40. package/build/agent/tools.js +163 -0
  41. package/build/cli.js +49 -0
  42. package/build/delegate.js +267 -0
  43. package/build/directory-resolver.js +237 -0
  44. package/build/downloader.js +750 -0
  45. package/build/extract.js +149 -0
  46. package/build/index.js +70 -0
  47. package/build/mcp/index.js +514 -0
  48. package/build/mcp/index.ts +608 -0
  49. package/build/query.js +116 -0
  50. package/build/search.js +247 -0
  51. package/build/tools/common.js +410 -0
  52. package/build/tools/index.js +40 -0
  53. package/build/tools/langchain.js +88 -0
  54. package/build/tools/system-message.js +121 -0
  55. package/build/tools/vercel.js +271 -0
  56. package/build/utils/file-lister.js +193 -0
  57. package/build/utils.js +128 -0
  58. package/cjs/agent/ProbeAgent.cjs +5829 -0
  59. package/cjs/index.cjs +6217 -0
  60. package/cjs/package.json +3 -0
  61. package/index.d.ts +401 -0
  62. package/package.json +114 -0
  63. package/scripts/postinstall.js +172 -0
  64. package/src/agent/ProbeAgent.d.ts +199 -0
  65. package/src/agent/ProbeAgent.js +1486 -0
  66. package/src/agent/acp/README.md +347 -0
  67. package/src/agent/acp/connection.js +237 -0
  68. package/src/agent/acp/connection.test.js +311 -0
  69. package/src/agent/acp/examples/simple-client.js +212 -0
  70. package/src/agent/acp/examples/tool-lifecycle.js +230 -0
  71. package/src/agent/acp/final-test.js +173 -0
  72. package/src/agent/acp/index.js +5 -0
  73. package/src/agent/acp/integration.test.js +385 -0
  74. package/src/agent/acp/manual-test.js +410 -0
  75. package/src/agent/acp/protocol-test.js +190 -0
  76. package/src/agent/acp/server.js +448 -0
  77. package/src/agent/acp/server.test.js +371 -0
  78. package/src/agent/acp/test-runner.js +216 -0
  79. package/src/agent/acp/test-utils/README.md +315 -0
  80. package/src/agent/acp/test-utils/acp-tester.js +484 -0
  81. package/src/agent/acp/test-utils/mock-acp-client.js +434 -0
  82. package/src/agent/acp/tools.js +368 -0
  83. package/src/agent/acp/tools.test.js +334 -0
  84. package/src/agent/acp/types.js +218 -0
  85. package/src/agent/acp/types.test.js +327 -0
  86. package/src/agent/appTracer.js +360 -0
  87. package/src/agent/fileSpanExporter.js +169 -0
  88. package/src/agent/index.js +813 -0
  89. package/src/agent/mcp/client.js +338 -0
  90. package/src/agent/mcp/config.js +313 -0
  91. package/src/agent/mcp/index.js +64 -0
  92. package/src/agent/mcp/xmlBridge.js +371 -0
  93. package/src/agent/mockProvider.js +53 -0
  94. package/src/agent/probeTool.js +257 -0
  95. package/src/agent/schemaUtils.js +1726 -0
  96. package/src/agent/simpleTelemetry.js +267 -0
  97. package/src/agent/telemetry.js +225 -0
  98. package/src/agent/tokenCounter.js +395 -0
  99. package/src/agent/tools.js +163 -0
  100. package/src/cli.js +49 -0
  101. package/src/delegate.js +267 -0
  102. package/src/directory-resolver.js +237 -0
  103. package/src/downloader.js +750 -0
  104. package/src/extract.js +149 -0
  105. package/src/index.js +70 -0
  106. package/src/mcp/index.ts +608 -0
  107. package/src/query.js +116 -0
  108. package/src/search.js +247 -0
  109. package/src/tools/common.js +410 -0
  110. package/src/tools/index.js +40 -0
  111. package/src/tools/langchain.js +88 -0
  112. package/src/tools/system-message.js +121 -0
  113. package/src/tools/vercel.js +271 -0
  114. package/src/utils/file-lister.js +193 -0
  115. package/src/utils.js +128 -0
@@ -0,0 +1,338 @@
1
+ /**
2
+ * Enhanced MCP Client with support for all transport types
3
+ * Compatible with Claude's MCP configuration format
4
+ */
5
+
6
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
7
+ import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
8
+ import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
9
+ import { WebSocketClientTransport } from '@modelcontextprotocol/sdk/client/websocket.js';
10
+ import { loadMCPConfiguration, parseEnabledServers } from './config.js';
11
+
12
+ /**
13
+ * Create transport based on configuration
14
+ * @param {Object} serverConfig - Server configuration
15
+ * @returns {Object} Transport instance
16
+ */
17
+ export function createTransport(serverConfig) {
18
+ const { transport, command, args, url, env } = serverConfig;
19
+
20
+ switch (transport) {
21
+ case 'stdio':
22
+ return new StdioClientTransport({
23
+ command,
24
+ args: args || [],
25
+ env: env ? { ...process.env, ...env } : undefined
26
+ });
27
+
28
+ case 'sse':
29
+ if (!url) {
30
+ throw new Error('SSE transport requires a URL');
31
+ }
32
+ return new SSEClientTransport(new URL(url));
33
+
34
+ case 'websocket':
35
+ case 'ws':
36
+ if (!url) {
37
+ throw new Error('WebSocket transport requires a URL');
38
+ }
39
+ try {
40
+ return new WebSocketClientTransport(new URL(url));
41
+ } catch (error) {
42
+ throw new Error(`Invalid WebSocket URL: ${url}`);
43
+ }
44
+
45
+ case 'http':
46
+ case 'streamable':
47
+ // For HTTP, we'll use a custom implementation since the SDK
48
+ // doesn't provide a direct HTTP transport yet
49
+ if (!url) {
50
+ throw new Error('HTTP transport requires a URL');
51
+ }
52
+ // Return a custom HTTP transport wrapper
53
+ return createHttpTransport(url);
54
+
55
+ default:
56
+ throw new Error(`Unknown transport type: ${transport}`);
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Create a custom HTTP transport wrapper
62
+ * This simulates MCP over HTTP REST endpoints
63
+ */
64
+ function createHttpTransport(url) {
65
+ // This is a simplified HTTP transport
66
+ // In practice, you'd implement the full MCP protocol over HTTP
67
+ return {
68
+ async start() {
69
+ // Initialize HTTP connection
70
+ const response = await fetch(`${url}/initialize`, {
71
+ method: 'POST',
72
+ headers: { 'Content-Type': 'application/json' },
73
+ body: JSON.stringify({
74
+ protocolVersion: '2024-11-05',
75
+ capabilities: {}
76
+ })
77
+ });
78
+
79
+ if (!response.ok) {
80
+ throw new Error(`HTTP initialization failed: ${response.statusText}`);
81
+ }
82
+
83
+ return response.json();
84
+ },
85
+
86
+ async send(message) {
87
+ const response = await fetch(`${url}/message`, {
88
+ method: 'POST',
89
+ headers: { 'Content-Type': 'application/json' },
90
+ body: JSON.stringify(message)
91
+ });
92
+
93
+ if (!response.ok) {
94
+ throw new Error(`HTTP request failed: ${response.statusText}`);
95
+ }
96
+
97
+ return response.json();
98
+ },
99
+
100
+ async close() {
101
+ // Close HTTP connection
102
+ await fetch(`${url}/close`, {
103
+ method: 'POST'
104
+ }).catch(() => {
105
+ // Ignore close errors
106
+ });
107
+ }
108
+ };
109
+ }
110
+
111
+ /**
112
+ * MCP Client Manager - manages multiple MCP server connections
113
+ */
114
+ export class MCPClientManager {
115
+ constructor(options = {}) {
116
+ this.clients = new Map();
117
+ this.tools = new Map();
118
+ this.debug = options.debug || process.env.DEBUG_MCP === '1';
119
+ this.config = null;
120
+ }
121
+
122
+ /**
123
+ * Initialize MCP clients from configuration
124
+ * @param {Object} config - Optional configuration override
125
+ */
126
+ async initialize(config = null) {
127
+ // Load configuration
128
+ this.config = config || loadMCPConfiguration();
129
+ const servers = parseEnabledServers(this.config);
130
+
131
+ if (this.debug) {
132
+ console.error(`[MCP] Found ${servers.length} enabled servers`);
133
+ }
134
+
135
+ // Connect to each enabled server
136
+ const connectionPromises = servers.map(server =>
137
+ this.connectToServer(server).catch(error => {
138
+ console.error(`[MCP] Failed to connect to ${server.name}:`, error.message);
139
+ return null;
140
+ })
141
+ );
142
+
143
+ const results = await Promise.all(connectionPromises);
144
+ const connectedCount = results.filter(Boolean).length;
145
+
146
+ if (this.debug) {
147
+ console.error(`[MCP] Successfully connected to ${connectedCount}/${servers.length} servers`);
148
+ console.error(`[MCP] Total tools available: ${this.tools.size}`);
149
+ }
150
+
151
+ return {
152
+ connected: connectedCount,
153
+ total: servers.length,
154
+ tools: Array.from(this.tools.keys())
155
+ };
156
+ }
157
+
158
+ /**
159
+ * Connect to a single MCP server
160
+ * @param {Object} serverConfig - Server configuration
161
+ */
162
+ async connectToServer(serverConfig) {
163
+ const { name } = serverConfig;
164
+
165
+ try {
166
+ if (this.debug) {
167
+ console.error(`[MCP] Connecting to ${name} via ${serverConfig.transport}...`);
168
+ }
169
+
170
+ // Create transport
171
+ const transport = createTransport(serverConfig);
172
+
173
+ // Create client
174
+ const client = new Client(
175
+ {
176
+ name: `probe-client-${name}`,
177
+ version: '1.0.0'
178
+ },
179
+ {
180
+ capabilities: {}
181
+ }
182
+ );
183
+
184
+ // Connect
185
+ await client.connect(transport);
186
+
187
+ // Store client
188
+ this.clients.set(name, {
189
+ client,
190
+ transport,
191
+ config: serverConfig
192
+ });
193
+
194
+ // Fetch and register tools
195
+ const toolsResponse = await client.listTools();
196
+
197
+ if (toolsResponse && toolsResponse.tools) {
198
+ for (const tool of toolsResponse.tools) {
199
+ // Add server prefix to avoid conflicts
200
+ const qualifiedName = `${name}_${tool.name}`;
201
+ this.tools.set(qualifiedName, {
202
+ ...tool,
203
+ serverName: name,
204
+ originalName: tool.name
205
+ });
206
+
207
+ if (this.debug) {
208
+ console.error(`[MCP] Registered tool: ${qualifiedName}`);
209
+ }
210
+ }
211
+ }
212
+
213
+ if (this.debug) {
214
+ console.error(`[MCP] Connected to ${name} with ${toolsResponse?.tools?.length || 0} tools`);
215
+ }
216
+
217
+ return true;
218
+ } catch (error) {
219
+ console.error(`[MCP] Error connecting to ${name}:`, error.message);
220
+ return false;
221
+ }
222
+ }
223
+
224
+ /**
225
+ * Call a tool on its respective server
226
+ * @param {string} toolName - Qualified tool name (server_tool)
227
+ * @param {Object} args - Tool arguments
228
+ */
229
+ async callTool(toolName, args) {
230
+ const tool = this.tools.get(toolName);
231
+ if (!tool) {
232
+ throw new Error(`Unknown tool: ${toolName}`);
233
+ }
234
+
235
+ const clientInfo = this.clients.get(tool.serverName);
236
+ if (!clientInfo) {
237
+ throw new Error(`Server ${tool.serverName} not connected`);
238
+ }
239
+
240
+ try {
241
+ if (this.debug) {
242
+ console.error(`[MCP] Calling ${toolName} with args:`, args);
243
+ }
244
+
245
+ const result = await clientInfo.client.callTool({
246
+ name: tool.originalName,
247
+ arguments: args
248
+ });
249
+
250
+ return result;
251
+ } catch (error) {
252
+ console.error(`[MCP] Error calling tool ${toolName}:`, error);
253
+ throw error;
254
+ }
255
+ }
256
+
257
+ /**
258
+ * Get all available tools with their schemas
259
+ * @returns {Object} Map of tool name to tool definition
260
+ */
261
+ getTools() {
262
+ const tools = {};
263
+ for (const [name, tool] of this.tools.entries()) {
264
+ tools[name] = {
265
+ description: tool.description,
266
+ inputSchema: tool.inputSchema,
267
+ serverName: tool.serverName
268
+ };
269
+ }
270
+ return tools;
271
+ }
272
+
273
+ /**
274
+ * Get tools formatted for Vercel AI SDK
275
+ * @returns {Object} Tools in Vercel AI SDK format
276
+ */
277
+ getVercelTools() {
278
+ const tools = {};
279
+
280
+ for (const [name, tool] of this.tools.entries()) {
281
+ // Create a wrapper that calls the MCP tool
282
+ tools[name] = {
283
+ description: tool.description,
284
+ inputSchema: tool.inputSchema,
285
+ execute: async (args) => {
286
+ const result = await this.callTool(name, args);
287
+ // Extract text content from MCP response
288
+ if (result.content && result.content[0]) {
289
+ return result.content[0].text;
290
+ }
291
+ return JSON.stringify(result);
292
+ }
293
+ };
294
+ }
295
+
296
+ return tools;
297
+ }
298
+
299
+ /**
300
+ * Disconnect all clients
301
+ */
302
+ async disconnect() {
303
+ const disconnectPromises = [];
304
+
305
+ for (const [name, clientInfo] of this.clients.entries()) {
306
+ disconnectPromises.push(
307
+ clientInfo.client.close()
308
+ .then(() => {
309
+ if (this.debug) {
310
+ console.error(`[MCP] Disconnected from ${name}`);
311
+ }
312
+ })
313
+ .catch(error => {
314
+ console.error(`[MCP] Error disconnecting from ${name}:`, error);
315
+ })
316
+ );
317
+ }
318
+
319
+ await Promise.all(disconnectPromises);
320
+ this.clients.clear();
321
+ this.tools.clear();
322
+ }
323
+ }
324
+
325
+ /**
326
+ * Create and initialize MCP client manager with default configuration
327
+ */
328
+ export async function createMCPManager(options = {}) {
329
+ const manager = new MCPClientManager(options);
330
+ await manager.initialize();
331
+ return manager;
332
+ }
333
+
334
+ export default {
335
+ MCPClientManager,
336
+ createMCPManager,
337
+ createTransport
338
+ };
@@ -0,0 +1,313 @@
1
+ /**
2
+ * MCP Configuration Manager
3
+ * Handles loading and parsing MCP server configurations similar to Claude
4
+ */
5
+
6
+ import { readFileSync, existsSync, mkdirSync, writeFileSync } from 'fs';
7
+ import { join, dirname } from 'path';
8
+ import { homedir } from 'os';
9
+ import { fileURLToPath } from 'url';
10
+
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = dirname(__filename);
13
+
14
+ /**
15
+ * Default MCP configuration structure
16
+ */
17
+ const DEFAULT_CONFIG = {
18
+ mcpServers: {
19
+ // Example probe server configuration
20
+ 'probe-local': {
21
+ command: 'node',
22
+ args: [join(__dirname, '../../../examples/chat/mcpServer.js')],
23
+ transport: 'stdio',
24
+ enabled: false
25
+ },
26
+ 'probe-npm': {
27
+ command: 'npx',
28
+ args: ['-y', '@probelabs/probe@latest', 'mcp'],
29
+ transport: 'stdio',
30
+ enabled: false
31
+ }
32
+ }
33
+ };
34
+
35
+ /**
36
+ * Load MCP configuration from a specific file path
37
+ * @param {string} configPath - Path to MCP configuration file
38
+ * @returns {Object} Configuration object
39
+ * @throws {Error} If file doesn't exist or is invalid
40
+ */
41
+ export function loadMCPConfigurationFromPath(configPath) {
42
+ if (!configPath) {
43
+ throw new Error('Config path is required');
44
+ }
45
+
46
+ if (!existsSync(configPath)) {
47
+ throw new Error(`MCP configuration file not found: ${configPath}`);
48
+ }
49
+
50
+ try {
51
+ const content = readFileSync(configPath, 'utf8');
52
+ const config = JSON.parse(content);
53
+
54
+ if (process.env.DEBUG === '1') {
55
+ console.error(`[MCP] Loaded configuration from: ${configPath}`);
56
+ }
57
+
58
+ // Merge with environment variable overrides
59
+ return mergeWithEnvironment(config);
60
+ } catch (error) {
61
+ throw new Error(`Failed to parse MCP config from ${configPath}: ${error.message}`);
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Load MCP configuration from various sources (DEPRECATED - use loadMCPConfigurationFromPath for explicit paths)
67
+ * Priority order:
68
+ * 1. Environment variable MCP_CONFIG_PATH
69
+ * 2. Local project .mcp/config.json
70
+ * 3. Home directory ~/.config/probe/mcp.json
71
+ * 4. Home directory ~/.mcp/config.json (Claude compatible)
72
+ * 5. Default configuration
73
+ * @deprecated Use loadMCPConfigurationFromPath for explicit path loading or pass config directly
74
+ */
75
+ export function loadMCPConfiguration() {
76
+ const configPaths = [
77
+ // Environment variable path
78
+ process.env.MCP_CONFIG_PATH,
79
+ // Local project paths
80
+ join(process.cwd(), '.mcp', 'config.json'),
81
+ join(process.cwd(), 'mcp.config.json'),
82
+ // Home directory paths
83
+ join(homedir(), '.config', 'probe', 'mcp.json'),
84
+ join(homedir(), '.mcp', 'config.json'),
85
+ // Claude-style config location
86
+ join(homedir(), 'Library', 'Application Support', 'Claude', 'mcp_config.json'),
87
+ ].filter(Boolean);
88
+
89
+ let config = null;
90
+
91
+ // Try to load configuration from paths
92
+ for (const configPath of configPaths) {
93
+ if (existsSync(configPath)) {
94
+ try {
95
+ const content = readFileSync(configPath, 'utf8');
96
+ config = JSON.parse(content);
97
+ if (process.env.DEBUG === '1') {
98
+ console.error(`[MCP] Loaded configuration from: ${configPath}`);
99
+ }
100
+ break;
101
+ } catch (error) {
102
+ console.error(`[MCP] Failed to parse config from ${configPath}:`, error.message);
103
+ }
104
+ }
105
+ }
106
+
107
+ // Merge with environment variable overrides
108
+ config = mergeWithEnvironment(config || DEFAULT_CONFIG);
109
+
110
+ return config;
111
+ }
112
+
113
+ /**
114
+ * Merge configuration with environment variables
115
+ * Supports:
116
+ * - MCP_SERVERS_<NAME>_COMMAND: Command for server
117
+ * - MCP_SERVERS_<NAME>_ARGS: Comma-separated args
118
+ * - MCP_SERVERS_<NAME>_TRANSPORT: Transport type
119
+ * - MCP_SERVERS_<NAME>_URL: URL for HTTP/WebSocket transports
120
+ * - MCP_SERVERS_<NAME>_ENABLED: Enable/disable server
121
+ */
122
+ function mergeWithEnvironment(config) {
123
+ const serverPattern = /^MCP_SERVERS_([A-Z0-9_]+)_(.+)$/;
124
+
125
+ for (const [key, value] of Object.entries(process.env)) {
126
+ const match = key.match(serverPattern);
127
+ if (match) {
128
+ const [, serverName, property] = match;
129
+ const normalizedName = serverName.toLowerCase().replace(/_/g, '-');
130
+
131
+ if (!config.mcpServers) {
132
+ config.mcpServers = {};
133
+ }
134
+
135
+ if (!config.mcpServers[normalizedName]) {
136
+ config.mcpServers[normalizedName] = {};
137
+ }
138
+
139
+ switch (property) {
140
+ case 'COMMAND':
141
+ config.mcpServers[normalizedName].command = value;
142
+ break;
143
+ case 'ARGS':
144
+ config.mcpServers[normalizedName].args = value.split(',').map(arg => arg.trim());
145
+ break;
146
+ case 'TRANSPORT':
147
+ config.mcpServers[normalizedName].transport = value.toLowerCase();
148
+ break;
149
+ case 'URL':
150
+ config.mcpServers[normalizedName].url = value;
151
+ break;
152
+ case 'ENABLED':
153
+ config.mcpServers[normalizedName].enabled = value === 'true' || value === '1';
154
+ break;
155
+ case 'ENV':
156
+ // Support custom environment variables for the server
157
+ try {
158
+ config.mcpServers[normalizedName].env = JSON.parse(value);
159
+ } catch {
160
+ config.mcpServers[normalizedName].env = { [property]: value };
161
+ }
162
+ break;
163
+ }
164
+ }
165
+ }
166
+
167
+ return config;
168
+ }
169
+
170
+ /**
171
+ * Parse MCP server configuration to extract enabled servers
172
+ * @param {Object} config - Full MCP configuration
173
+ * @returns {Array} Array of server configurations ready for connection
174
+ */
175
+ export function parseEnabledServers(config) {
176
+ const servers = [];
177
+
178
+ if (!config || !config.mcpServers) {
179
+ return servers;
180
+ }
181
+
182
+ for (const [name, serverConfig] of Object.entries(config.mcpServers)) {
183
+ // Skip disabled servers
184
+ if (serverConfig.enabled === false) {
185
+ continue;
186
+ }
187
+
188
+ const server = {
189
+ name,
190
+ ...serverConfig
191
+ };
192
+
193
+ // Set default transport if not specified
194
+ if (!server.transport) {
195
+ if (server.url) {
196
+ // Infer transport from URL
197
+ if (server.url.startsWith('ws://') || server.url.startsWith('wss://')) {
198
+ server.transport = 'websocket';
199
+ } else if (server.url.includes('/sse')) {
200
+ server.transport = 'sse';
201
+ } else {
202
+ server.transport = 'http';
203
+ }
204
+ } else {
205
+ server.transport = 'stdio';
206
+ }
207
+ }
208
+
209
+ // Validate required fields based on transport
210
+ if (server.transport === 'stdio') {
211
+ if (!server.command) {
212
+ console.error(`[MCP] Server ${name} missing required 'command' for stdio transport`);
213
+ continue;
214
+ }
215
+ } else if (['websocket', 'sse', 'http'].includes(server.transport)) {
216
+ if (!server.url) {
217
+ console.error(`[MCP] Server ${name} missing required 'url' for ${server.transport} transport`);
218
+ continue;
219
+ }
220
+ }
221
+
222
+ servers.push(server);
223
+ }
224
+
225
+ return servers;
226
+ }
227
+
228
+ /**
229
+ * Create a sample MCP configuration file
230
+ */
231
+ export function createSampleConfig() {
232
+ return {
233
+ mcpServers: {
234
+ 'probe': {
235
+ command: 'npx',
236
+ args: ['-y', '@probelabs/probe@latest', 'mcp'],
237
+ transport: 'stdio',
238
+ enabled: true,
239
+ description: 'Probe code search MCP server'
240
+ },
241
+ 'filesystem': {
242
+ command: 'npx',
243
+ args: ['-y', '@modelcontextprotocol/server-filesystem', process.cwd()],
244
+ transport: 'stdio',
245
+ enabled: false,
246
+ description: 'Filesystem operations MCP server'
247
+ },
248
+ 'github': {
249
+ command: 'npx',
250
+ args: ['-y', '@modelcontextprotocol/server-github'],
251
+ transport: 'stdio',
252
+ enabled: false,
253
+ description: 'GitHub API MCP server',
254
+ env: {
255
+ GITHUB_TOKEN: 'your-github-token'
256
+ }
257
+ },
258
+ 'postgres': {
259
+ command: 'npx',
260
+ args: ['-y', '@modelcontextprotocol/server-postgres'],
261
+ transport: 'stdio',
262
+ enabled: false,
263
+ description: 'PostgreSQL database MCP server',
264
+ env: {
265
+ DATABASE_URL: 'postgresql://user:pass@localhost/db'
266
+ }
267
+ },
268
+ 'custom-http': {
269
+ url: 'http://localhost:3000/mcp',
270
+ transport: 'http',
271
+ enabled: false,
272
+ description: 'Custom HTTP MCP server'
273
+ },
274
+ 'custom-websocket': {
275
+ url: 'ws://localhost:8080',
276
+ transport: 'websocket',
277
+ enabled: false,
278
+ description: 'Custom WebSocket MCP server'
279
+ }
280
+ },
281
+ // Global settings
282
+ settings: {
283
+ timeout: 30000,
284
+ retryCount: 3,
285
+ debug: false
286
+ }
287
+ };
288
+ }
289
+
290
+ /**
291
+ * Save configuration to file
292
+ * @param {Object} config - Configuration to save
293
+ * @param {string} path - Path to save to
294
+ */
295
+ export function saveConfig(config, path) {
296
+ const dir = dirname(path);
297
+
298
+ // Create directory if it doesn't exist
299
+ if (!existsSync(dir)) {
300
+ mkdirSync(dir, { recursive: true });
301
+ }
302
+
303
+ writeFileSync(path, JSON.stringify(config, null, 2), 'utf8');
304
+ console.log(`[MCP] Configuration saved to: ${path}`);
305
+ }
306
+
307
+ export default {
308
+ loadMCPConfiguration,
309
+ loadMCPConfigurationFromPath,
310
+ parseEnabledServers,
311
+ createSampleConfig,
312
+ saveConfig
313
+ };
@@ -0,0 +1,64 @@
1
+ /**
2
+ * MCP (Model Context Protocol) integration for ProbeAgent
3
+ *
4
+ * This module provides:
5
+ * - MCP client management for connecting to MCP servers
6
+ * - XML/JSON hybrid tool interface
7
+ * - Configuration management
8
+ */
9
+
10
+ // Re-export main classes and functions
11
+ export { MCPClientManager, createMCPManager, createTransport } from './client.js';
12
+ export {
13
+ loadMCPConfiguration,
14
+ loadMCPConfigurationFromPath,
15
+ parseEnabledServers,
16
+ createSampleConfig,
17
+ saveConfig
18
+ } from './config.js';
19
+ export {
20
+ MCPXmlBridge,
21
+ mcpToolToXmlDefinition,
22
+ parseXmlMcpToolCall,
23
+ parseHybridXmlToolCall,
24
+ createHybridSystemMessage
25
+ } from './xmlBridge.js';
26
+
27
+ // Import for default export
28
+ import { MCPClientManager, createMCPManager, createTransport } from './client.js';
29
+ import {
30
+ loadMCPConfiguration,
31
+ loadMCPConfigurationFromPath,
32
+ parseEnabledServers,
33
+ createSampleConfig,
34
+ saveConfig
35
+ } from './config.js';
36
+ import {
37
+ MCPXmlBridge,
38
+ mcpToolToXmlDefinition,
39
+ parseXmlMcpToolCall,
40
+ parseHybridXmlToolCall,
41
+ createHybridSystemMessage
42
+ } from './xmlBridge.js';
43
+
44
+ // Default export for convenience
45
+ export default {
46
+ // Client
47
+ MCPClientManager,
48
+ createMCPManager,
49
+ createTransport,
50
+
51
+ // Config
52
+ loadMCPConfiguration,
53
+ loadMCPConfigurationFromPath,
54
+ parseEnabledServers,
55
+ createSampleConfig,
56
+ saveConfig,
57
+
58
+ // XML Bridge
59
+ MCPXmlBridge,
60
+ mcpToolToXmlDefinition,
61
+ parseXmlMcpToolCall,
62
+ parseHybridXmlToolCall,
63
+ createHybridSystemMessage
64
+ };