converse-mcp-server 1.0.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/.env.example +177 -0
- package/README.md +425 -0
- package/bin/converse.js +45 -0
- package/docs/API.md +897 -0
- package/docs/ARCHITECTURE.md +552 -0
- package/docs/EXAMPLES.md +736 -0
- package/package.json +101 -0
- package/src/config.js +521 -0
- package/src/continuationStore.js +340 -0
- package/src/index.js +216 -0
- package/src/providers/google.js +441 -0
- package/src/providers/index.js +87 -0
- package/src/providers/openai.js +348 -0
- package/src/providers/xai.js +305 -0
- package/src/router.js +497 -0
- package/src/systemPrompts.js +90 -0
- package/src/tools/chat.js +336 -0
- package/src/tools/consensus.js +478 -0
- package/src/tools/index.js +156 -0
- package/src/transport/httpTransport.js +548 -0
- package/src/utils/console.js +64 -0
- package/src/utils/contextProcessor.js +475 -0
- package/src/utils/errorHandler.js +555 -0
- package/src/utils/logger.js +450 -0
- package/src/utils/tokenLimiter.js +217 -0
package/src/router.js
ADDED
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Central Request Router
|
|
3
|
+
*
|
|
4
|
+
* Single orchestration point that dispatches MCP requests to tools with dependency injection.
|
|
5
|
+
* Handles tool lookup, error management, and consistent response formatting.
|
|
6
|
+
* Follows functional architecture with comprehensive error handling.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
10
|
+
import { getContinuationStore } from './continuationStore.js';
|
|
11
|
+
import { getTools } from './tools/index.js';
|
|
12
|
+
import { getProviders } from './providers/index.js';
|
|
13
|
+
import { processUnifiedContext } from './utils/contextProcessor.js';
|
|
14
|
+
import { createLogger, startTimer } from './utils/logger.js';
|
|
15
|
+
import { debugError } from './utils/console.js';
|
|
16
|
+
import {
|
|
17
|
+
ConverseMCPError,
|
|
18
|
+
ToolError,
|
|
19
|
+
ValidationError,
|
|
20
|
+
createMCPErrorResponse,
|
|
21
|
+
withErrorHandler,
|
|
22
|
+
ERROR_CODES
|
|
23
|
+
} from './utils/errorHandler.js';
|
|
24
|
+
|
|
25
|
+
const logger = createLogger('router');
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Router-specific error class
|
|
29
|
+
*/
|
|
30
|
+
export class RouterError extends ConverseMCPError {
|
|
31
|
+
constructor(message, code = ERROR_CODES.ROUTER_ERROR, details = {}) {
|
|
32
|
+
super(message, code, details, 500);
|
|
33
|
+
this.name = 'RouterError';
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Standard error response format for consistent error handling
|
|
39
|
+
* @param {Error} error - The error that occurred
|
|
40
|
+
* @param {string} toolName - Name of the tool that failed
|
|
41
|
+
* @param {object} context - Additional context information
|
|
42
|
+
* @returns {object} Standardized error response
|
|
43
|
+
*/
|
|
44
|
+
export function createErrorResponse(error, toolName = 'unknown', context = {}) {
|
|
45
|
+
return createMCPErrorResponse(error, toolName, context);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Validate tool exists and is callable
|
|
50
|
+
* @param {string} toolName - Name of the tool to validate
|
|
51
|
+
* @param {object} tools - Available tools registry
|
|
52
|
+
* @returns {object} Validation result
|
|
53
|
+
*/
|
|
54
|
+
function validateTool(toolName, tools) {
|
|
55
|
+
if (!toolName || typeof toolName !== 'string') {
|
|
56
|
+
throw new RouterError(
|
|
57
|
+
'Tool name must be a non-empty string',
|
|
58
|
+
'INVALID_TOOL_NAME'
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!tools[toolName]) {
|
|
63
|
+
const availableTools = Object.keys(tools);
|
|
64
|
+
throw new RouterError(
|
|
65
|
+
`Tool error: Unknown tool '${toolName}'. Available tools: ${availableTools.join(', ')}`,
|
|
66
|
+
'UNKNOWN_TOOL',
|
|
67
|
+
{ requestedTool: toolName, availableTools }
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (typeof tools[toolName] !== 'function') {
|
|
72
|
+
throw new RouterError(
|
|
73
|
+
`Tool ${toolName} is not callable`,
|
|
74
|
+
'INVALID_TOOL_HANDLER',
|
|
75
|
+
{ toolName, toolType: typeof tools[toolName] }
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
isValid: true,
|
|
81
|
+
tool: tools[toolName]
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Enhanced dependency injection with error handling
|
|
87
|
+
* @param {object} config - Configuration object
|
|
88
|
+
* @returns {object} Dependencies object for tool injection
|
|
89
|
+
*/
|
|
90
|
+
async function createDependencies(config) {
|
|
91
|
+
try {
|
|
92
|
+
const continuationStore = getContinuationStore();
|
|
93
|
+
const tools = getTools();
|
|
94
|
+
const providers = getProviders();
|
|
95
|
+
|
|
96
|
+
// Validate that we have the necessary dependencies
|
|
97
|
+
if (!continuationStore) {
|
|
98
|
+
throw new RouterError(
|
|
99
|
+
'Failed to initialize continuation store',
|
|
100
|
+
'DEPENDENCY_ERROR'
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (!tools || Object.keys(tools).length === 0) {
|
|
105
|
+
throw new RouterError(
|
|
106
|
+
'No tools available - tools registry is empty',
|
|
107
|
+
'NO_TOOLS_AVAILABLE'
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (!providers || Object.keys(providers).length === 0) {
|
|
112
|
+
throw new RouterError(
|
|
113
|
+
'No providers available - providers registry is empty',
|
|
114
|
+
'NO_PROVIDERS_AVAILABLE'
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
config,
|
|
120
|
+
continuationStore,
|
|
121
|
+
providers,
|
|
122
|
+
contextProcessor: { processUnifiedContext },
|
|
123
|
+
router: {
|
|
124
|
+
createErrorResponse,
|
|
125
|
+
validateToolArguments,
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
} catch (error) {
|
|
130
|
+
debugError('Failed to create dependencies:', error);
|
|
131
|
+
throw error;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Creates and configures the central router for handling MCP requests
|
|
137
|
+
* @param {object} server - MCP Server instance
|
|
138
|
+
* @param {object} config - Configuration object with provider settings
|
|
139
|
+
* @returns {Promise<void>}
|
|
140
|
+
*/
|
|
141
|
+
export async function createRouter(server, config) {
|
|
142
|
+
const createRouterLogger = logger.operation('createRouter');
|
|
143
|
+
const timer = startTimer('router-initialization', 'router');
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
createRouterLogger.info('Initializing router');
|
|
147
|
+
|
|
148
|
+
// Initialize dependencies with validation
|
|
149
|
+
const dependencies = await createDependencies(config);
|
|
150
|
+
const tools = getTools();
|
|
151
|
+
|
|
152
|
+
createRouterLogger.info(`Router initialized with ${Object.keys(tools).length} tools`);
|
|
153
|
+
|
|
154
|
+
// Register unified tool call handler with enhanced error handling
|
|
155
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
156
|
+
const toolTimer = startTimer('tool-execution', 'router');
|
|
157
|
+
const toolName = request.params?.name;
|
|
158
|
+
const toolArgs = request.params?.arguments || {};
|
|
159
|
+
const requestLogger = logger.operation(`tool-call:${toolName}`);
|
|
160
|
+
|
|
161
|
+
try {
|
|
162
|
+
requestLogger.info('Tool execution started', {
|
|
163
|
+
data: { toolName, argCount: Object.keys(toolArgs).length }
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// Validate tool existence and callability
|
|
167
|
+
const { tool } = validateTool(toolName, tools);
|
|
168
|
+
|
|
169
|
+
// Validate tool arguments if schema is provided
|
|
170
|
+
if (tool.inputSchema) {
|
|
171
|
+
const isValidArgs = validateToolArguments(toolArgs, tool.inputSchema);
|
|
172
|
+
if (!isValidArgs) {
|
|
173
|
+
throw new ValidationError(
|
|
174
|
+
`Invalid arguments for tool ${toolName}`,
|
|
175
|
+
ERROR_CODES.INVALID_TOOL_ARGS,
|
|
176
|
+
{
|
|
177
|
+
providedArgs: Object.keys(toolArgs),
|
|
178
|
+
expectedSchema: tool.inputSchema
|
|
179
|
+
}
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Execute the tool with dependency injection
|
|
185
|
+
const result = await tool(toolArgs, dependencies);
|
|
186
|
+
|
|
187
|
+
const executionTime = toolTimer('completed');
|
|
188
|
+
requestLogger.info('Tool execution completed', {
|
|
189
|
+
data: { executionTime: `${executionTime}ms` }
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// Ensure result has proper format
|
|
193
|
+
if (!result || !result.content) {
|
|
194
|
+
throw new ToolError(
|
|
195
|
+
`Tool ${toolName} returned invalid result format`,
|
|
196
|
+
ERROR_CODES.TOOL_EXECUTION_FAILED,
|
|
197
|
+
{ result },
|
|
198
|
+
toolName
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return result;
|
|
203
|
+
|
|
204
|
+
} catch (error) {
|
|
205
|
+
const executionTime = toolTimer('failed');
|
|
206
|
+
requestLogger.error('Tool execution failed', {
|
|
207
|
+
error,
|
|
208
|
+
data: { executionTime: `${executionTime}ms`, argCount: Object.keys(toolArgs).length }
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
return createErrorResponse(error, toolName, {
|
|
212
|
+
executionTime,
|
|
213
|
+
arguments: Object.keys(toolArgs),
|
|
214
|
+
requestId: request.id || 'unknown'
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// Register enhanced list_tools handler
|
|
220
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
221
|
+
try {
|
|
222
|
+
const toolList = Object.entries(tools).map(([name, handler]) => {
|
|
223
|
+
const toolInfo = {
|
|
224
|
+
name,
|
|
225
|
+
description: handler.description || `${name} tool - no description provided`,
|
|
226
|
+
inputSchema: handler.inputSchema || {
|
|
227
|
+
type: 'object',
|
|
228
|
+
properties: {},
|
|
229
|
+
description: 'No input schema defined'
|
|
230
|
+
},
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
// Add additional metadata if available
|
|
234
|
+
if (handler.version) {
|
|
235
|
+
toolInfo.version = handler.version;
|
|
236
|
+
}
|
|
237
|
+
if (handler.category) {
|
|
238
|
+
toolInfo.category = handler.category;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return toolInfo;
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
return {
|
|
245
|
+
tools: toolList,
|
|
246
|
+
metadata: {
|
|
247
|
+
totalTools: toolList.length,
|
|
248
|
+
timestamp: new Date().toISOString(),
|
|
249
|
+
routerVersion: '1.0.0'
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
} catch (error) {
|
|
254
|
+
debugError('Error listing tools:', error);
|
|
255
|
+
throw new RouterError(
|
|
256
|
+
'Failed to list available tools',
|
|
257
|
+
'TOOLS_LIST_ERROR',
|
|
258
|
+
{ error: error.message }
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
// Note: Custom health endpoint removed - MCP uses standard protocol methods only
|
|
264
|
+
|
|
265
|
+
timer('completed');
|
|
266
|
+
createRouterLogger.info('Router configured successfully', {
|
|
267
|
+
data: {
|
|
268
|
+
tools: Object.keys(tools).length,
|
|
269
|
+
providers: Object.keys(dependencies.providers).length,
|
|
270
|
+
continuationStore: dependencies.continuationStore.constructor.name,
|
|
271
|
+
environment: config.environment.nodeEnv
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
// Return router interface for testing purposes
|
|
276
|
+
return {
|
|
277
|
+
listTools: async () => {
|
|
278
|
+
const tools = getTools();
|
|
279
|
+
return {
|
|
280
|
+
tools: Object.entries(tools).map(([name, tool]) => {
|
|
281
|
+
const toolSchema = {
|
|
282
|
+
name,
|
|
283
|
+
description: tool.description || 'No description available'
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
if (tool.inputSchema) {
|
|
287
|
+
toolSchema.inputSchema = tool.inputSchema;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return toolSchema;
|
|
291
|
+
})
|
|
292
|
+
};
|
|
293
|
+
},
|
|
294
|
+
|
|
295
|
+
callTool: async (toolCall) => {
|
|
296
|
+
const toolName = toolCall.name;
|
|
297
|
+
const toolArgs = toolCall.arguments || {};
|
|
298
|
+
|
|
299
|
+
try {
|
|
300
|
+
// Validate tool existence and callability
|
|
301
|
+
const { tool } = validateTool(toolName, tools);
|
|
302
|
+
|
|
303
|
+
// Validate tool arguments if schema is provided
|
|
304
|
+
if (tool.inputSchema) {
|
|
305
|
+
const isValidArgs = validateToolArguments(toolArgs, tool.inputSchema);
|
|
306
|
+
if (!isValidArgs) {
|
|
307
|
+
throw new ValidationError(
|
|
308
|
+
`Invalid arguments for tool ${toolName}`,
|
|
309
|
+
ERROR_CODES.INVALID_TOOL_ARGS,
|
|
310
|
+
{
|
|
311
|
+
providedArgs: Object.keys(toolArgs),
|
|
312
|
+
expectedSchema: tool.inputSchema
|
|
313
|
+
}
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Execute the tool with dependency injection
|
|
319
|
+
return await tool(toolArgs, dependencies);
|
|
320
|
+
} catch (error) {
|
|
321
|
+
return createErrorResponse(error, toolName, {
|
|
322
|
+
arguments: toolArgs,
|
|
323
|
+
executionTime: 0,
|
|
324
|
+
requestId: 'test'
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
} catch (error) {
|
|
331
|
+
timer('failed');
|
|
332
|
+
createRouterLogger.error('Router initialization failed', { error });
|
|
333
|
+
throw new RouterError(
|
|
334
|
+
'Router initialization failed',
|
|
335
|
+
ERROR_CODES.ROUTER_ERROR,
|
|
336
|
+
{ originalError: error.message }
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Get JSON Schema type for a JavaScript value
|
|
343
|
+
* @param {any} value - The value to get type for
|
|
344
|
+
* @returns {string} JSON Schema type
|
|
345
|
+
*/
|
|
346
|
+
function getJsonSchemaType(value) {
|
|
347
|
+
if (value === null) return 'null';
|
|
348
|
+
if (Array.isArray(value)) return 'array';
|
|
349
|
+
if (typeof value === 'number') {
|
|
350
|
+
return Number.isInteger(value) ? 'integer' : 'number';
|
|
351
|
+
}
|
|
352
|
+
return typeof value;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Check if a JavaScript value matches a JSON Schema type
|
|
357
|
+
* @param {any} value - The value to check
|
|
358
|
+
* @param {string} schemaType - The JSON Schema type to check against
|
|
359
|
+
* @returns {boolean} True if value matches the schema type
|
|
360
|
+
*/
|
|
361
|
+
function isValidJsonSchemaType(value, schemaType) {
|
|
362
|
+
if (schemaType === 'null') return value === null;
|
|
363
|
+
if (schemaType === 'array') return Array.isArray(value);
|
|
364
|
+
if (schemaType === 'object') return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
365
|
+
if (schemaType === 'boolean') return typeof value === 'boolean';
|
|
366
|
+
if (schemaType === 'string') return typeof value === 'string';
|
|
367
|
+
if (schemaType === 'number') return typeof value === 'number';
|
|
368
|
+
if (schemaType === 'integer') return typeof value === 'number' && Number.isInteger(value);
|
|
369
|
+
|
|
370
|
+
// For unknown types, fall back to JavaScript typeof
|
|
371
|
+
return typeof value === schemaType;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Enhanced tool argument validation against schema
|
|
376
|
+
* @param {object} args - Tool arguments to validate
|
|
377
|
+
* @param {object} schema - JSON schema for validation
|
|
378
|
+
* @returns {boolean} True if arguments are valid
|
|
379
|
+
* @throws {RouterError} If validation fails with details
|
|
380
|
+
*/
|
|
381
|
+
export function validateToolArguments(args, schema) {
|
|
382
|
+
try {
|
|
383
|
+
// If no schema provided, assume valid
|
|
384
|
+
if (!schema) {
|
|
385
|
+
return true;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// Basic type checking
|
|
389
|
+
if (schema.type === 'object' && (typeof args !== 'object' || args === null)) {
|
|
390
|
+
throw new RouterError(
|
|
391
|
+
'Arguments must be an object',
|
|
392
|
+
'INVALID_ARGUMENT_TYPE',
|
|
393
|
+
{ expected: 'object', received: typeof args }
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// Check required properties
|
|
398
|
+
if (schema.required && Array.isArray(schema.required)) {
|
|
399
|
+
const missing = schema.required.filter(key => !(key in args));
|
|
400
|
+
if (missing.length > 0) {
|
|
401
|
+
throw new RouterError(
|
|
402
|
+
`Validation error: Missing required arguments: ${missing.join(', ')}`,
|
|
403
|
+
'MISSING_REQUIRED_ARGS',
|
|
404
|
+
{ missing, provided: Object.keys(args) }
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Validate individual properties
|
|
410
|
+
if (schema.properties) {
|
|
411
|
+
for (const [key, propSchema] of Object.entries(schema.properties)) {
|
|
412
|
+
if (key in args) {
|
|
413
|
+
const value = args[key];
|
|
414
|
+
|
|
415
|
+
// Basic type validation using JSON Schema type semantics
|
|
416
|
+
if (propSchema.type && !isValidJsonSchemaType(value, propSchema.type)) {
|
|
417
|
+
const actualType = getJsonSchemaType(value);
|
|
418
|
+
throw new RouterError(
|
|
419
|
+
`Argument '${key}' must be of type ${propSchema.type}`,
|
|
420
|
+
'INVALID_ARGUMENT_TYPE',
|
|
421
|
+
{
|
|
422
|
+
argument: key,
|
|
423
|
+
expected: propSchema.type,
|
|
424
|
+
received: actualType
|
|
425
|
+
}
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// String length validation
|
|
430
|
+
if (propSchema.type === 'string') {
|
|
431
|
+
if (propSchema.minLength && value.length < propSchema.minLength) {
|
|
432
|
+
throw new RouterError(
|
|
433
|
+
`Argument '${key}' must be at least ${propSchema.minLength} characters`,
|
|
434
|
+
'ARGUMENT_TOO_SHORT',
|
|
435
|
+
{ argument: key, minLength: propSchema.minLength, actual: value.length }
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
if (propSchema.maxLength && value.length > propSchema.maxLength) {
|
|
439
|
+
throw new RouterError(
|
|
440
|
+
`Argument '${key}' must be at most ${propSchema.maxLength} characters`,
|
|
441
|
+
'ARGUMENT_TOO_LONG',
|
|
442
|
+
{ argument: key, maxLength: propSchema.maxLength, actual: value.length }
|
|
443
|
+
);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
return true;
|
|
451
|
+
|
|
452
|
+
} catch (error) {
|
|
453
|
+
if (error instanceof RouterError) {
|
|
454
|
+
throw error;
|
|
455
|
+
}
|
|
456
|
+
throw new RouterError(
|
|
457
|
+
`Argument validation failed: ${error.message}`,
|
|
458
|
+
'VALIDATION_ERROR',
|
|
459
|
+
{ originalError: error.message }
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Get router statistics and health information
|
|
466
|
+
* @param {object} dependencies - Router dependencies
|
|
467
|
+
* @returns {Promise<object>} Router statistics
|
|
468
|
+
*/
|
|
469
|
+
export async function getRouterStats(dependencies) {
|
|
470
|
+
try {
|
|
471
|
+
const tools = getTools();
|
|
472
|
+
const storeStats = await dependencies.continuationStore.getStats();
|
|
473
|
+
|
|
474
|
+
return {
|
|
475
|
+
timestamp: new Date().toISOString(),
|
|
476
|
+
uptime: process.uptime(),
|
|
477
|
+
tools: {
|
|
478
|
+
count: Object.keys(tools).length,
|
|
479
|
+
available: Object.keys(tools)
|
|
480
|
+
},
|
|
481
|
+
providers: {
|
|
482
|
+
count: Object.keys(dependencies.providers).length,
|
|
483
|
+
available: Object.keys(dependencies.providers)
|
|
484
|
+
},
|
|
485
|
+
continuationStore: storeStats,
|
|
486
|
+
memory: process.memoryUsage(),
|
|
487
|
+
environment: dependencies.config.environment.nodeEnv
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
} catch (error) {
|
|
491
|
+
throw new RouterError(
|
|
492
|
+
'Failed to get router statistics',
|
|
493
|
+
'STATS_ERROR',
|
|
494
|
+
{ error: error.message }
|
|
495
|
+
);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* System Prompts for Converse MCP Server Tools
|
|
3
|
+
*
|
|
4
|
+
* Matches the Python implementation system prompts exactly for feature parity.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Chat tool system prompt - matches Python systemprompts/chat_prompt.py
|
|
9
|
+
*/
|
|
10
|
+
export const CHAT_PROMPT = `
|
|
11
|
+
You are a senior engineering thought-partner collaborating with another AI agent. Your mission is to brainstorm, validate ideas,
|
|
12
|
+
and offer well-reasoned second opinions on technical decisions when they are justified and practical.
|
|
13
|
+
|
|
14
|
+
CRITICAL LINE NUMBER INSTRUCTIONS
|
|
15
|
+
Code is presented with line number markers "LINE│ code". These markers are for reference ONLY and MUST NOT be
|
|
16
|
+
included in any code you generate. Always reference specific line numbers in your replies in order to locate
|
|
17
|
+
exact positions if needed to point to exact locations. Include a very short code excerpt alongside for clarity.
|
|
18
|
+
Include context_start_text and context_end_text as backup references. Never include "LINE│" markers in generated code
|
|
19
|
+
snippets.
|
|
20
|
+
|
|
21
|
+
IF MORE INFORMATION IS NEEDED
|
|
22
|
+
If the agent is discussing specific code, functions, or project components that was not given as part of the context,
|
|
23
|
+
and you need additional context (e.g., related files, configuration, dependencies, test files) to provide meaningful
|
|
24
|
+
collaboration, you MUST respond ONLY with this JSON format (and nothing else). Do NOT ask for the same file you've been
|
|
25
|
+
provided unless for some reason its content is missing or incomplete:
|
|
26
|
+
{
|
|
27
|
+
"status": "files_required_to_continue",
|
|
28
|
+
"mandatory_instructions": "<your critical instructions for the agent>",
|
|
29
|
+
"files_needed": ["[file name here]", "[or some folder/]"]
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
CORE PRINCIPLES
|
|
33
|
+
• Work within the existing tech stack and architecture
|
|
34
|
+
• Avoid overengineering - prefer simple, practical solutions
|
|
35
|
+
• Focus on current scope, not speculative future needs
|
|
36
|
+
• Provide concrete, actionable recommendations with clear trade-offs
|
|
37
|
+
• Surface potential issues early and challenge assumptions constructively
|
|
38
|
+
`.trim();
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Consensus tool system prompt - matches Python systemprompts/consensus_prompt.py
|
|
42
|
+
*/
|
|
43
|
+
export const CONSENSUS_PROMPT = `
|
|
44
|
+
You're analyzing a technical problem alongside other AI models. Each model will propose solutions independently, then potentially see others' approaches.
|
|
45
|
+
|
|
46
|
+
Your goal: Find the best solution, whether it's yours or another model's. The key is often a single insight that makes everything click.
|
|
47
|
+
|
|
48
|
+
CRITICAL LINE NUMBER INSTRUCTIONS
|
|
49
|
+
Code is presented with line number markers "LINE│ code". These markers are for reference ONLY and MUST NOT be
|
|
50
|
+
included in any code you generate. Always reference specific line numbers in your replies in order to locate
|
|
51
|
+
exact positions if needed to point to exact locations. Include a very short code excerpt alongside for clarity.
|
|
52
|
+
Never include "LINE│" markers in generated code snippets.
|
|
53
|
+
|
|
54
|
+
IF MORE INFORMATION IS NEEDED
|
|
55
|
+
If you need to see specific code, files, or technical context to properly analyze the problem, respond with this exact JSON:
|
|
56
|
+
{
|
|
57
|
+
"status": "files_required_to_continue",
|
|
58
|
+
"mandatory_instructions": "<your critical instructions for the agent>",
|
|
59
|
+
"files_needed": ["[file name here]", "[or some folder/]"]
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
MANDATORY RESPONSE FORMAT
|
|
63
|
+
You MUST respond in exactly this Markdown structure:
|
|
64
|
+
|
|
65
|
+
## Approach
|
|
66
|
+
Present your solution and the key insight behind it. Be direct and clear about what makes your approach work.
|
|
67
|
+
If you're reviewing others' solutions, you'll do that in a later phase.
|
|
68
|
+
|
|
69
|
+
## Why This Works
|
|
70
|
+
Explain the technical reasoning. Be specific about why this approach solves the problem effectively.
|
|
71
|
+
What's the core mechanism or principle that makes it succeed?
|
|
72
|
+
|
|
73
|
+
## Implementation
|
|
74
|
+
Provide concrete code or steps if relevant. Show exactly how to implement your approach.
|
|
75
|
+
Focus on clarity and correctness.
|
|
76
|
+
|
|
77
|
+
## Trade-offs
|
|
78
|
+
What are the limitations or considerations? Be honest about where this approach might struggle
|
|
79
|
+
or what alternatives might be better in certain contexts.
|
|
80
|
+
|
|
81
|
+
QUALITY STANDARDS
|
|
82
|
+
- Focus on finding the most elegant solution
|
|
83
|
+
- Look for the key insight that simplifies the problem
|
|
84
|
+
- Be direct - don't hedge unnecessarily
|
|
85
|
+
- Value clarity and simplicity
|
|
86
|
+
- Consider edge cases and robustness
|
|
87
|
+
- Stay technical and grounded
|
|
88
|
+
|
|
89
|
+
Remember: The best solution often has one breakthrough insight that makes the complexity fall away.
|
|
90
|
+
`.trim();
|