converse-mcp-server 2.22.4 โ 2.22.5
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/LICENSE +18 -18
- package/docs/ALTERNATIVE_PROVIDERS.md +449 -449
- package/docs/ARCHITECTURE.md +551 -551
- package/package.json +4 -4
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -1,552 +1,552 @@
|
|
|
1
|
-
# Converse MCP Server - Architecture Overview
|
|
2
|
-
|
|
3
|
-
## ๐๏ธ System Architecture
|
|
4
|
-
|
|
5
|
-
The Converse MCP Server follows a **functional, modular architecture** designed for simplicity, maintainability, and performance. It implements the Model Context Protocol (MCP) to provide AI capabilities through multiple providers.
|
|
6
|
-
|
|
7
|
-
## ๐ Project Structure
|
|
8
|
-
|
|
9
|
-
```
|
|
10
|
-
src/
|
|
11
|
-
โโโ index.js # Main entry point & MCP server setup
|
|
12
|
-
โโโ config.js # Configuration and environment management
|
|
13
|
-
โโโ systemPrompts.js # System prompts for tools
|
|
14
|
-
โโโ providers/ # AI provider implementations
|
|
15
|
-
โ โโโ registry.js # Provider registry and management
|
|
16
|
-
โ โโโ openai.js # OpenAI provider implementation
|
|
17
|
-
โ โโโ google.js # Google/Gemini provider implementation
|
|
18
|
-
โ โโโ xai.js # X.AI/Grok provider implementation
|
|
19
|
-
โโโ tools/ # MCP tool implementations
|
|
20
|
-
โ โโโ chat.js # Single-provider chat tool
|
|
21
|
-
โ โโโ consensus.js # Multi-provider consensus tool
|
|
22
|
-
โโโ utils/ # Utility functions
|
|
23
|
-
โ โโโ logger.js # Structured logging
|
|
24
|
-
โ โโโ context.js # File and image processing
|
|
25
|
-
โ โโโ continuation.js # Conversation persistence
|
|
26
|
-
โ โโโ validators.js # Input validation
|
|
27
|
-
bin/
|
|
28
|
-
โโโ converse.js # CLI entry point for npx execution
|
|
29
|
-
docs/ # Documentation
|
|
30
|
-
tests/ # Test suites
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
## ๐ Core Design Principles
|
|
34
|
-
|
|
35
|
-
### 1. Functional Programming
|
|
36
|
-
- **No Classes**: Pure functions and modules only
|
|
37
|
-
- **Immutable Data**: Avoid state mutations where possible
|
|
38
|
-
- **Composable Functions**: Small, focused, reusable functions
|
|
39
|
-
- **Error Boundaries**: Explicit error handling at module boundaries
|
|
40
|
-
|
|
41
|
-
### 2. Provider Abstraction
|
|
42
|
-
- **Unified Interface**: All providers implement consistent API
|
|
43
|
-
- **Auto-Discovery**: Providers register themselves dynamically
|
|
44
|
-
- **Graceful Degradation**: System works with any subset of providers
|
|
45
|
-
- **Parallel Execution**: Multiple providers can run simultaneously
|
|
46
|
-
|
|
47
|
-
### 3. Tool Architecture
|
|
48
|
-
- **Minimal Interface**: Tools expose simple, focused functionality
|
|
49
|
-
- **Context Processing**: Standardized file and image handling
|
|
50
|
-
- **Continuation Support**: Persistent conversation management
|
|
51
|
-
- **Parameter Validation**: Comprehensive input validation
|
|
52
|
-
|
|
53
|
-
## ๐ MCP Integration Layer
|
|
54
|
-
|
|
55
|
-
### Server Setup
|
|
56
|
-
|
|
57
|
-
**HTTP Transport (Default):**
|
|
58
|
-
```javascript
|
|
59
|
-
// index.js - HTTP transport setup
|
|
60
|
-
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
61
|
-
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
62
|
-
|
|
63
|
-
const server = new Server(
|
|
64
|
-
{ name: 'converse-mcp-server', version: '1.0.0' },
|
|
65
|
-
{ capabilities: { tools: {} } }
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
// HTTP transport on port 3157 (default)
|
|
69
|
-
const httpTransport = new StreamableHTTPServerTransport({
|
|
70
|
-
host: 'localhost',
|
|
71
|
-
port: 3157
|
|
72
|
-
});
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
**Stdio Transport (Legacy):**
|
|
76
|
-
```javascript
|
|
77
|
-
// Alternative stdio transport
|
|
78
|
-
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
79
|
-
|
|
80
|
-
const transport = new StdioServerTransport();
|
|
81
|
-
await server.connect(transport);
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
**Transport Selection:**
|
|
85
|
-
- **HTTP**: Default, better for development and debugging
|
|
86
|
-
- **Stdio**: Use `--transport=stdio` or `MCP_TRANSPORT=stdio`
|
|
87
|
-
|
|
88
|
-
### Tool Registration
|
|
89
|
-
```javascript
|
|
90
|
-
// Dynamic tool registration
|
|
91
|
-
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
92
|
-
const { name, arguments: args } = request.params;
|
|
93
|
-
|
|
94
|
-
switch (name) {
|
|
95
|
-
case 'chat':
|
|
96
|
-
return await chatTool(args);
|
|
97
|
-
case 'consensus':
|
|
98
|
-
return await consensusTool(args);
|
|
99
|
-
default:
|
|
100
|
-
throw new McpError(ErrorCode.MethodNotFound, `Tool not found: ${name}`);
|
|
101
|
-
}
|
|
102
|
-
});
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
## ๐ค Provider System
|
|
106
|
-
|
|
107
|
-
### Provider Registry Pattern
|
|
108
|
-
```javascript
|
|
109
|
-
// providers/registry.js
|
|
110
|
-
const providers = new Map();
|
|
111
|
-
|
|
112
|
-
export function registerProvider(name, implementation) {
|
|
113
|
-
if (implementation.isAvailable()) {
|
|
114
|
-
providers.set(name, implementation);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
export function getProvider(name) {
|
|
119
|
-
return providers.get(name);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
export function getAvailableProviders() {
|
|
123
|
-
return Array.from(providers.keys());
|
|
124
|
-
}
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
### Provider Implementation Contract
|
|
128
|
-
```javascript
|
|
129
|
-
// Each provider must implement:
|
|
130
|
-
export const providerImplementation = {
|
|
131
|
-
// Check if provider is configured and available
|
|
132
|
-
isAvailable: () => Boolean(process.env.API_KEY),
|
|
133
|
-
|
|
134
|
-
// Get supported models
|
|
135
|
-
getSupportedModels: () => ['model1', 'model2'],
|
|
136
|
-
|
|
137
|
-
// Main chat completion method
|
|
138
|
-
chatCompletion: async (messages, options) => {
|
|
139
|
-
// Implementation details...
|
|
140
|
-
return {
|
|
141
|
-
content: 'Response text',
|
|
142
|
-
usage: { input_tokens: 100, output_tokens: 50 }
|
|
143
|
-
};
|
|
144
|
-
},
|
|
145
|
-
|
|
146
|
-
// Provider name for logging/tracking
|
|
147
|
-
name: 'provider-name'
|
|
148
|
-
};
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
### Model Resolution
|
|
152
|
-
```javascript
|
|
153
|
-
// Automatic model resolution across providers
|
|
154
|
-
export function resolveModel(modelName) {
|
|
155
|
-
// Handle aliases (flash -> gemini-2.5-flash)
|
|
156
|
-
const resolvedName = MODEL_ALIASES[modelName] || modelName;
|
|
157
|
-
|
|
158
|
-
// Find provider that supports this model
|
|
159
|
-
const provider = findProviderForModel(resolvedName);
|
|
160
|
-
|
|
161
|
-
return { provider, model: resolvedName };
|
|
162
|
-
}
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
## ๐ ๏ธ Tool Architecture
|
|
166
|
-
|
|
167
|
-
### Tool Implementation Pattern
|
|
168
|
-
```javascript
|
|
169
|
-
// tools/example.js
|
|
170
|
-
export const exampleTool = {
|
|
171
|
-
definition: {
|
|
172
|
-
name: 'example',
|
|
173
|
-
description: 'Example tool description',
|
|
174
|
-
inputSchema: {
|
|
175
|
-
type: 'object',
|
|
176
|
-
properties: {
|
|
177
|
-
prompt: { type: 'string', description: 'User prompt' }
|
|
178
|
-
},
|
|
179
|
-
required: ['prompt']
|
|
180
|
-
}
|
|
181
|
-
},
|
|
182
|
-
|
|
183
|
-
handler: async (args) => {
|
|
184
|
-
// 1. Validate input
|
|
185
|
-
const validation = validateInput(args);
|
|
186
|
-
if (!validation.valid) {
|
|
187
|
-
throw new McpError(ErrorCode.InvalidParams, validation.error);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// 2. Process context (files, images)
|
|
191
|
-
const context = await processContext(args.files, args.images);
|
|
192
|
-
|
|
193
|
-
// 3. Execute main logic
|
|
194
|
-
const result = await executeLogic(args, context);
|
|
195
|
-
|
|
196
|
-
// 4. Return standardized response
|
|
197
|
-
return formatResponse(result);
|
|
198
|
-
}
|
|
199
|
-
};
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
### Context Processing Pipeline
|
|
203
|
-
```javascript
|
|
204
|
-
// utils/context.js
|
|
205
|
-
export async function processContext(files = [], images = []) {
|
|
206
|
-
return {
|
|
207
|
-
fileContext: await processFiles(files),
|
|
208
|
-
imageContext: await processImages(images)
|
|
209
|
-
};
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
async function processFiles(filePaths) {
|
|
213
|
-
return await Promise.all(
|
|
214
|
-
filePaths.map(async (path) => {
|
|
215
|
-
const content = await readFile(path);
|
|
216
|
-
return {
|
|
217
|
-
path,
|
|
218
|
-
content: addLineNumbers(content),
|
|
219
|
-
metadata: { size: content.length, type: getFileType(path) }
|
|
220
|
-
};
|
|
221
|
-
})
|
|
222
|
-
);
|
|
223
|
-
}
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
## ๐ Data Flow
|
|
227
|
-
|
|
228
|
-
### Single Tool Execution (Chat)
|
|
229
|
-
```
|
|
230
|
-
User Request
|
|
231
|
-
โ
|
|
232
|
-
Input Validation
|
|
233
|
-
โ
|
|
234
|
-
Context Processing (files/images)
|
|
235
|
-
โ
|
|
236
|
-
Provider Selection (auto/manual)
|
|
237
|
-
โ
|
|
238
|
-
Model Resolution
|
|
239
|
-
โ
|
|
240
|
-
API Call to Provider
|
|
241
|
-
โ
|
|
242
|
-
Response Processing
|
|
243
|
-
โ
|
|
244
|
-
Continuation Management
|
|
245
|
-
โ
|
|
246
|
-
Response to Client
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
### Multi-Provider Execution (Consensus)
|
|
250
|
-
```
|
|
251
|
-
User Request
|
|
252
|
-
โ
|
|
253
|
-
Input Validation
|
|
254
|
-
โ
|
|
255
|
-
Context Processing
|
|
256
|
-
โ
|
|
257
|
-
Provider Selection (multiple)
|
|
258
|
-
โ
|
|
259
|
-
Parallel Execution โโโโโฌโโโ Provider A
|
|
260
|
-
โโโโ Provider B
|
|
261
|
-
โโโโ Provider C
|
|
262
|
-
โ
|
|
263
|
-
Initial Response Collection
|
|
264
|
-
โ
|
|
265
|
-
Cross-Feedback Phase (optional)
|
|
266
|
-
โ
|
|
267
|
-
Parallel Refinement โโโโฌโโโ Provider A (sees B,C)
|
|
268
|
-
โโโโ Provider B (sees A,C)
|
|
269
|
-
โโโโ Provider C (sees A,B)
|
|
270
|
-
โ
|
|
271
|
-
Final Response Aggregation
|
|
272
|
-
โ
|
|
273
|
-
Response to Client
|
|
274
|
-
```
|
|
275
|
-
|
|
276
|
-
## ๐ง Configuration System
|
|
277
|
-
|
|
278
|
-
### Environment-Driven Configuration
|
|
279
|
-
```javascript
|
|
280
|
-
// config.js
|
|
281
|
-
export const config = {
|
|
282
|
-
// Provider API keys
|
|
283
|
-
providers: {
|
|
284
|
-
openai: { apiKey: process.env.OPENAI_API_KEY },
|
|
285
|
-
google: { apiKey: process.env.GOOGLE_API_KEY },
|
|
286
|
-
xai: { apiKey: process.env.XAI_API_KEY }
|
|
287
|
-
},
|
|
288
|
-
|
|
289
|
-
// Server settings
|
|
290
|
-
server: {
|
|
291
|
-
port: parseInt(process.env.PORT) || 3157,
|
|
292
|
-
logLevel: process.env.LOG_LEVEL || 'info',
|
|
293
|
-
maxOutputTokens: parseInt(process.env.MAX_MCP_OUTPUT_TOKENS) || 25000
|
|
294
|
-
},
|
|
295
|
-
|
|
296
|
-
// Model mappings and aliases
|
|
297
|
-
models: {
|
|
298
|
-
aliases: {
|
|
299
|
-
'flash': 'gemini-2.5-flash',
|
|
300
|
-
'pro': 'gemini-2.5-pro',
|
|
301
|
-
'grok': 'grok-4-0709'
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
};
|
|
305
|
-
```
|
|
306
|
-
|
|
307
|
-
### Dynamic Provider Registration
|
|
308
|
-
```javascript
|
|
309
|
-
// index.js - Provider initialization
|
|
310
|
-
async function initializeProviders() {
|
|
311
|
-
const providerModules = [
|
|
312
|
-
await import('./providers/openai.js'),
|
|
313
|
-
await import('./providers/google.js'),
|
|
314
|
-
await import('./providers/xai.js')
|
|
315
|
-
];
|
|
316
|
-
|
|
317
|
-
for (const module of providerModules) {
|
|
318
|
-
const provider = module.default;
|
|
319
|
-
if (provider.isAvailable()) {
|
|
320
|
-
registerProvider(provider.name, provider);
|
|
321
|
-
logger.info(`Registered provider: ${provider.name}`);
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
```
|
|
326
|
-
|
|
327
|
-
## ๐ State Management
|
|
328
|
-
|
|
329
|
-
### Stateless Design
|
|
330
|
-
- **No Global State**: Each request is independent
|
|
331
|
-
- **Continuation Storage**: Conversations stored as isolated state
|
|
332
|
-
- **Provider Independence**: Providers don't share state
|
|
333
|
-
- **Immutable Responses**: Responses are constructed, not modified
|
|
334
|
-
|
|
335
|
-
### Continuation System
|
|
336
|
-
```javascript
|
|
337
|
-
// utils/continuation.js
|
|
338
|
-
const continuations = new Map();
|
|
339
|
-
|
|
340
|
-
export function storeContinuation(id, data) {
|
|
341
|
-
continuations.set(id, {
|
|
342
|
-
...data,
|
|
343
|
-
lastAccessed: Date.now(),
|
|
344
|
-
messageCount: (data.messageCount || 0) + 1
|
|
345
|
-
});
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
export function getContinuation(id) {
|
|
349
|
-
const continuation = continuations.get(id);
|
|
350
|
-
if (continuation) {
|
|
351
|
-
continuation.lastAccessed = Date.now();
|
|
352
|
-
}
|
|
353
|
-
return continuation;
|
|
354
|
-
}
|
|
355
|
-
```
|
|
356
|
-
|
|
357
|
-
## ๐ Performance Characteristics
|
|
358
|
-
|
|
359
|
-
### Parallel Execution
|
|
360
|
-
- **Consensus Tool**: Executes all providers simultaneously
|
|
361
|
-
- **Non-Blocking I/O**: All async operations use Promise.all()
|
|
362
|
-
- **Provider Isolation**: One provider failure doesn't affect others
|
|
363
|
-
- **Request Batching**: Multiple requests handled concurrently
|
|
364
|
-
|
|
365
|
-
### Memory Management
|
|
366
|
-
- **Streaming Responses**: Large responses handled efficiently
|
|
367
|
-
- **Context Cleanup**: Old continuations automatically expire
|
|
368
|
-
- **File Processing**: Files read on-demand, not cached
|
|
369
|
-
- **Provider Pooling**: Connection reuse where possible
|
|
370
|
-
|
|
371
|
-
### Error Resilience
|
|
372
|
-
```javascript
|
|
373
|
-
// Graceful error handling pattern
|
|
374
|
-
async function executeWithFallback(primaryFn, fallbackFn) {
|
|
375
|
-
try {
|
|
376
|
-
return await primaryFn();
|
|
377
|
-
} catch (primaryError) {
|
|
378
|
-
logger.warn('Primary execution failed, attempting fallback', {
|
|
379
|
-
error: primaryError.message
|
|
380
|
-
});
|
|
381
|
-
|
|
382
|
-
try {
|
|
383
|
-
return await fallbackFn();
|
|
384
|
-
} catch (fallbackError) {
|
|
385
|
-
logger.error('Both primary and fallback failed', {
|
|
386
|
-
primaryError: primaryError.message,
|
|
387
|
-
fallbackError: fallbackError.message
|
|
388
|
-
});
|
|
389
|
-
throw new McpError(ErrorCode.InternalError, 'All execution attempts failed');
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
```
|
|
394
|
-
|
|
395
|
-
## ๐ Security Architecture
|
|
396
|
-
|
|
397
|
-
### Input Validation
|
|
398
|
-
```javascript
|
|
399
|
-
// utils/validators.js
|
|
400
|
-
export function validateChatInput(args) {
|
|
401
|
-
const errors = [];
|
|
402
|
-
|
|
403
|
-
if (!args.prompt || typeof args.prompt !== 'string') {
|
|
404
|
-
errors.push('prompt must be a non-empty string');
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
if (args.files && !Array.isArray(args.files)) {
|
|
408
|
-
errors.push('files must be an array');
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
return { valid: errors.length === 0, errors };
|
|
412
|
-
}
|
|
413
|
-
```
|
|
414
|
-
|
|
415
|
-
### Path Security
|
|
416
|
-
```javascript
|
|
417
|
-
// Prevent path traversal attacks
|
|
418
|
-
function validateFilePath(filePath) {
|
|
419
|
-
const normalized = path.resolve(filePath);
|
|
420
|
-
const allowed = path.resolve(process.cwd());
|
|
421
|
-
|
|
422
|
-
if (!normalized.startsWith(allowed)) {
|
|
423
|
-
throw new McpError(ErrorCode.InvalidParams, 'Access denied: Path outside allowed directory');
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
return normalized;
|
|
427
|
-
}
|
|
428
|
-
```
|
|
429
|
-
|
|
430
|
-
### API Key Protection
|
|
431
|
-
- **Environment Variables**: Keys never hardcoded
|
|
432
|
-
- **No Logging**: API keys excluded from all logs
|
|
433
|
-
- **Provider Isolation**: Keys scoped to specific providers
|
|
434
|
-
- **Error Sanitization**: Error messages don't expose keys
|
|
435
|
-
|
|
436
|
-
## ๐ Observability
|
|
437
|
-
|
|
438
|
-
### Structured Logging
|
|
439
|
-
```javascript
|
|
440
|
-
// utils/logger.js
|
|
441
|
-
export const logger = {
|
|
442
|
-
info: (message, meta = {}) => {
|
|
443
|
-
console.log(JSON.stringify({
|
|
444
|
-
level: 'info',
|
|
445
|
-
timestamp: new Date().toISOString(),
|
|
446
|
-
message,
|
|
447
|
-
...meta
|
|
448
|
-
}));
|
|
449
|
-
},
|
|
450
|
-
|
|
451
|
-
error: (message, error = {}) => {
|
|
452
|
-
console.error(JSON.stringify({
|
|
453
|
-
level: 'error',
|
|
454
|
-
timestamp: new Date().toISOString(),
|
|
455
|
-
message,
|
|
456
|
-
error: {
|
|
457
|
-
name: error.name,
|
|
458
|
-
message: error.message,
|
|
459
|
-
stack: error.stack
|
|
460
|
-
}
|
|
461
|
-
}));
|
|
462
|
-
}
|
|
463
|
-
};
|
|
464
|
-
```
|
|
465
|
-
|
|
466
|
-
### Request Tracing
|
|
467
|
-
```javascript
|
|
468
|
-
// Request correlation IDs
|
|
469
|
-
function generateRequestId() {
|
|
470
|
-
return `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
// All operations tagged with request ID
|
|
474
|
-
logger.info('Processing chat request', {
|
|
475
|
-
requestId,
|
|
476
|
-
provider: 'openai',
|
|
477
|
-
model: 'gpt-5'
|
|
478
|
-
});
|
|
479
|
-
```
|
|
480
|
-
|
|
481
|
-
## ๐ Extensibility
|
|
482
|
-
|
|
483
|
-
### Adding New Providers
|
|
484
|
-
1. Create provider module in `src/providers/`
|
|
485
|
-
2. Implement the provider contract
|
|
486
|
-
3. Export as default
|
|
487
|
-
4. Provider auto-registers if API key is available
|
|
488
|
-
|
|
489
|
-
### Adding New Tools
|
|
490
|
-
1. Create tool module in `src/tools/`
|
|
491
|
-
2. Define tool schema and handler
|
|
492
|
-
3. Register in main server setup
|
|
493
|
-
4. Add to MCP tool list
|
|
494
|
-
|
|
495
|
-
### Configuration Extensions
|
|
496
|
-
```javascript
|
|
497
|
-
// Adding new configuration options
|
|
498
|
-
export const config = {
|
|
499
|
-
// ... existing config
|
|
500
|
-
|
|
501
|
-
// New feature config
|
|
502
|
-
experimental: {
|
|
503
|
-
enableFeatureX: process.env.ENABLE_FEATURE_X === 'true',
|
|
504
|
-
featureXTimeout: parseInt(process.env.FEATURE_X_TIMEOUT) || 30000
|
|
505
|
-
}
|
|
506
|
-
};
|
|
507
|
-
```
|
|
508
|
-
|
|
509
|
-
## ๐งช Testing Architecture
|
|
510
|
-
|
|
511
|
-
### Test Strategy
|
|
512
|
-
- **Unit Tests**: Individual functions and modules
|
|
513
|
-
- **Integration Tests**: Provider interactions (mocked APIs)
|
|
514
|
-
- **E2E Tests**: Full request/response cycles
|
|
515
|
-
- **Contract Tests**: Provider interface compliance
|
|
516
|
-
|
|
517
|
-
### Test Organization
|
|
518
|
-
```
|
|
519
|
-
tests/
|
|
520
|
-
โโโ unit/ # Unit tests for individual functions
|
|
521
|
-
โโโ integration/ # Integration tests with mocked dependencies
|
|
522
|
-
โโโ e2e/ # End-to-end tests
|
|
523
|
-
โโโ fixtures/ # Test data and mocks
|
|
524
|
-
โโโ helpers/ # Test utilities
|
|
525
|
-
```
|
|
526
|
-
|
|
527
|
-
## ๐ง Development Workflow
|
|
528
|
-
|
|
529
|
-
### Hot Reload Development
|
|
530
|
-
```bash
|
|
531
|
-
# Development with auto-restart
|
|
532
|
-
npm run dev
|
|
533
|
-
|
|
534
|
-
# Debug mode with inspection
|
|
535
|
-
npm run debug
|
|
536
|
-
```
|
|
537
|
-
|
|
538
|
-
### Code Quality Pipeline
|
|
539
|
-
```bash
|
|
540
|
-
# Full validation pipeline
|
|
541
|
-
npm run validate
|
|
542
|
-
|
|
543
|
-
# Individual checks
|
|
544
|
-
npm run lint
|
|
545
|
-
npm run typecheck
|
|
546
|
-
npm run test
|
|
547
|
-
npm run format:check
|
|
548
|
-
```
|
|
549
|
-
|
|
550
|
-
---
|
|
551
|
-
|
|
1
|
+
# Converse MCP Server - Architecture Overview
|
|
2
|
+
|
|
3
|
+
## ๐๏ธ System Architecture
|
|
4
|
+
|
|
5
|
+
The Converse MCP Server follows a **functional, modular architecture** designed for simplicity, maintainability, and performance. It implements the Model Context Protocol (MCP) to provide AI capabilities through multiple providers.
|
|
6
|
+
|
|
7
|
+
## ๐ Project Structure
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
src/
|
|
11
|
+
โโโ index.js # Main entry point & MCP server setup
|
|
12
|
+
โโโ config.js # Configuration and environment management
|
|
13
|
+
โโโ systemPrompts.js # System prompts for tools
|
|
14
|
+
โโโ providers/ # AI provider implementations
|
|
15
|
+
โ โโโ registry.js # Provider registry and management
|
|
16
|
+
โ โโโ openai.js # OpenAI provider implementation
|
|
17
|
+
โ โโโ google.js # Google/Gemini provider implementation
|
|
18
|
+
โ โโโ xai.js # X.AI/Grok provider implementation
|
|
19
|
+
โโโ tools/ # MCP tool implementations
|
|
20
|
+
โ โโโ chat.js # Single-provider chat tool
|
|
21
|
+
โ โโโ consensus.js # Multi-provider consensus tool
|
|
22
|
+
โโโ utils/ # Utility functions
|
|
23
|
+
โ โโโ logger.js # Structured logging
|
|
24
|
+
โ โโโ context.js # File and image processing
|
|
25
|
+
โ โโโ continuation.js # Conversation persistence
|
|
26
|
+
โ โโโ validators.js # Input validation
|
|
27
|
+
bin/
|
|
28
|
+
โโโ converse.js # CLI entry point for npx execution
|
|
29
|
+
docs/ # Documentation
|
|
30
|
+
tests/ # Test suites
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## ๐ Core Design Principles
|
|
34
|
+
|
|
35
|
+
### 1. Functional Programming
|
|
36
|
+
- **No Classes**: Pure functions and modules only
|
|
37
|
+
- **Immutable Data**: Avoid state mutations where possible
|
|
38
|
+
- **Composable Functions**: Small, focused, reusable functions
|
|
39
|
+
- **Error Boundaries**: Explicit error handling at module boundaries
|
|
40
|
+
|
|
41
|
+
### 2. Provider Abstraction
|
|
42
|
+
- **Unified Interface**: All providers implement consistent API
|
|
43
|
+
- **Auto-Discovery**: Providers register themselves dynamically
|
|
44
|
+
- **Graceful Degradation**: System works with any subset of providers
|
|
45
|
+
- **Parallel Execution**: Multiple providers can run simultaneously
|
|
46
|
+
|
|
47
|
+
### 3. Tool Architecture
|
|
48
|
+
- **Minimal Interface**: Tools expose simple, focused functionality
|
|
49
|
+
- **Context Processing**: Standardized file and image handling
|
|
50
|
+
- **Continuation Support**: Persistent conversation management
|
|
51
|
+
- **Parameter Validation**: Comprehensive input validation
|
|
52
|
+
|
|
53
|
+
## ๐ MCP Integration Layer
|
|
54
|
+
|
|
55
|
+
### Server Setup
|
|
56
|
+
|
|
57
|
+
**HTTP Transport (Default):**
|
|
58
|
+
```javascript
|
|
59
|
+
// index.js - HTTP transport setup
|
|
60
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
61
|
+
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
62
|
+
|
|
63
|
+
const server = new Server(
|
|
64
|
+
{ name: 'converse-mcp-server', version: '1.0.0' },
|
|
65
|
+
{ capabilities: { tools: {} } }
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
// HTTP transport on port 3157 (default)
|
|
69
|
+
const httpTransport = new StreamableHTTPServerTransport({
|
|
70
|
+
host: 'localhost',
|
|
71
|
+
port: 3157
|
|
72
|
+
});
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Stdio Transport (Legacy):**
|
|
76
|
+
```javascript
|
|
77
|
+
// Alternative stdio transport
|
|
78
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
79
|
+
|
|
80
|
+
const transport = new StdioServerTransport();
|
|
81
|
+
await server.connect(transport);
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**Transport Selection:**
|
|
85
|
+
- **HTTP**: Default, better for development and debugging
|
|
86
|
+
- **Stdio**: Use `--transport=stdio` or `MCP_TRANSPORT=stdio`
|
|
87
|
+
|
|
88
|
+
### Tool Registration
|
|
89
|
+
```javascript
|
|
90
|
+
// Dynamic tool registration
|
|
91
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
92
|
+
const { name, arguments: args } = request.params;
|
|
93
|
+
|
|
94
|
+
switch (name) {
|
|
95
|
+
case 'chat':
|
|
96
|
+
return await chatTool(args);
|
|
97
|
+
case 'consensus':
|
|
98
|
+
return await consensusTool(args);
|
|
99
|
+
default:
|
|
100
|
+
throw new McpError(ErrorCode.MethodNotFound, `Tool not found: ${name}`);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## ๐ค Provider System
|
|
106
|
+
|
|
107
|
+
### Provider Registry Pattern
|
|
108
|
+
```javascript
|
|
109
|
+
// providers/registry.js
|
|
110
|
+
const providers = new Map();
|
|
111
|
+
|
|
112
|
+
export function registerProvider(name, implementation) {
|
|
113
|
+
if (implementation.isAvailable()) {
|
|
114
|
+
providers.set(name, implementation);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function getProvider(name) {
|
|
119
|
+
return providers.get(name);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function getAvailableProviders() {
|
|
123
|
+
return Array.from(providers.keys());
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Provider Implementation Contract
|
|
128
|
+
```javascript
|
|
129
|
+
// Each provider must implement:
|
|
130
|
+
export const providerImplementation = {
|
|
131
|
+
// Check if provider is configured and available
|
|
132
|
+
isAvailable: () => Boolean(process.env.API_KEY),
|
|
133
|
+
|
|
134
|
+
// Get supported models
|
|
135
|
+
getSupportedModels: () => ['model1', 'model2'],
|
|
136
|
+
|
|
137
|
+
// Main chat completion method
|
|
138
|
+
chatCompletion: async (messages, options) => {
|
|
139
|
+
// Implementation details...
|
|
140
|
+
return {
|
|
141
|
+
content: 'Response text',
|
|
142
|
+
usage: { input_tokens: 100, output_tokens: 50 }
|
|
143
|
+
};
|
|
144
|
+
},
|
|
145
|
+
|
|
146
|
+
// Provider name for logging/tracking
|
|
147
|
+
name: 'provider-name'
|
|
148
|
+
};
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Model Resolution
|
|
152
|
+
```javascript
|
|
153
|
+
// Automatic model resolution across providers
|
|
154
|
+
export function resolveModel(modelName) {
|
|
155
|
+
// Handle aliases (flash -> gemini-2.5-flash)
|
|
156
|
+
const resolvedName = MODEL_ALIASES[modelName] || modelName;
|
|
157
|
+
|
|
158
|
+
// Find provider that supports this model
|
|
159
|
+
const provider = findProviderForModel(resolvedName);
|
|
160
|
+
|
|
161
|
+
return { provider, model: resolvedName };
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## ๐ ๏ธ Tool Architecture
|
|
166
|
+
|
|
167
|
+
### Tool Implementation Pattern
|
|
168
|
+
```javascript
|
|
169
|
+
// tools/example.js
|
|
170
|
+
export const exampleTool = {
|
|
171
|
+
definition: {
|
|
172
|
+
name: 'example',
|
|
173
|
+
description: 'Example tool description',
|
|
174
|
+
inputSchema: {
|
|
175
|
+
type: 'object',
|
|
176
|
+
properties: {
|
|
177
|
+
prompt: { type: 'string', description: 'User prompt' }
|
|
178
|
+
},
|
|
179
|
+
required: ['prompt']
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
handler: async (args) => {
|
|
184
|
+
// 1. Validate input
|
|
185
|
+
const validation = validateInput(args);
|
|
186
|
+
if (!validation.valid) {
|
|
187
|
+
throw new McpError(ErrorCode.InvalidParams, validation.error);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// 2. Process context (files, images)
|
|
191
|
+
const context = await processContext(args.files, args.images);
|
|
192
|
+
|
|
193
|
+
// 3. Execute main logic
|
|
194
|
+
const result = await executeLogic(args, context);
|
|
195
|
+
|
|
196
|
+
// 4. Return standardized response
|
|
197
|
+
return formatResponse(result);
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Context Processing Pipeline
|
|
203
|
+
```javascript
|
|
204
|
+
// utils/context.js
|
|
205
|
+
export async function processContext(files = [], images = []) {
|
|
206
|
+
return {
|
|
207
|
+
fileContext: await processFiles(files),
|
|
208
|
+
imageContext: await processImages(images)
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async function processFiles(filePaths) {
|
|
213
|
+
return await Promise.all(
|
|
214
|
+
filePaths.map(async (path) => {
|
|
215
|
+
const content = await readFile(path);
|
|
216
|
+
return {
|
|
217
|
+
path,
|
|
218
|
+
content: addLineNumbers(content),
|
|
219
|
+
metadata: { size: content.length, type: getFileType(path) }
|
|
220
|
+
};
|
|
221
|
+
})
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## ๐ Data Flow
|
|
227
|
+
|
|
228
|
+
### Single Tool Execution (Chat)
|
|
229
|
+
```
|
|
230
|
+
User Request
|
|
231
|
+
โ
|
|
232
|
+
Input Validation
|
|
233
|
+
โ
|
|
234
|
+
Context Processing (files/images)
|
|
235
|
+
โ
|
|
236
|
+
Provider Selection (auto/manual)
|
|
237
|
+
โ
|
|
238
|
+
Model Resolution
|
|
239
|
+
โ
|
|
240
|
+
API Call to Provider
|
|
241
|
+
โ
|
|
242
|
+
Response Processing
|
|
243
|
+
โ
|
|
244
|
+
Continuation Management
|
|
245
|
+
โ
|
|
246
|
+
Response to Client
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Multi-Provider Execution (Consensus)
|
|
250
|
+
```
|
|
251
|
+
User Request
|
|
252
|
+
โ
|
|
253
|
+
Input Validation
|
|
254
|
+
โ
|
|
255
|
+
Context Processing
|
|
256
|
+
โ
|
|
257
|
+
Provider Selection (multiple)
|
|
258
|
+
โ
|
|
259
|
+
Parallel Execution โโโโโฌโโโ Provider A
|
|
260
|
+
โโโโ Provider B
|
|
261
|
+
โโโโ Provider C
|
|
262
|
+
โ
|
|
263
|
+
Initial Response Collection
|
|
264
|
+
โ
|
|
265
|
+
Cross-Feedback Phase (optional)
|
|
266
|
+
โ
|
|
267
|
+
Parallel Refinement โโโโฌโโโ Provider A (sees B,C)
|
|
268
|
+
โโโโ Provider B (sees A,C)
|
|
269
|
+
โโโโ Provider C (sees A,B)
|
|
270
|
+
โ
|
|
271
|
+
Final Response Aggregation
|
|
272
|
+
โ
|
|
273
|
+
Response to Client
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
## ๐ง Configuration System
|
|
277
|
+
|
|
278
|
+
### Environment-Driven Configuration
|
|
279
|
+
```javascript
|
|
280
|
+
// config.js
|
|
281
|
+
export const config = {
|
|
282
|
+
// Provider API keys
|
|
283
|
+
providers: {
|
|
284
|
+
openai: { apiKey: process.env.OPENAI_API_KEY },
|
|
285
|
+
google: { apiKey: process.env.GOOGLE_API_KEY },
|
|
286
|
+
xai: { apiKey: process.env.XAI_API_KEY }
|
|
287
|
+
},
|
|
288
|
+
|
|
289
|
+
// Server settings
|
|
290
|
+
server: {
|
|
291
|
+
port: parseInt(process.env.PORT) || 3157,
|
|
292
|
+
logLevel: process.env.LOG_LEVEL || 'info',
|
|
293
|
+
maxOutputTokens: parseInt(process.env.MAX_MCP_OUTPUT_TOKENS) || 25000
|
|
294
|
+
},
|
|
295
|
+
|
|
296
|
+
// Model mappings and aliases
|
|
297
|
+
models: {
|
|
298
|
+
aliases: {
|
|
299
|
+
'flash': 'gemini-2.5-flash',
|
|
300
|
+
'pro': 'gemini-2.5-pro',
|
|
301
|
+
'grok': 'grok-4-0709'
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Dynamic Provider Registration
|
|
308
|
+
```javascript
|
|
309
|
+
// index.js - Provider initialization
|
|
310
|
+
async function initializeProviders() {
|
|
311
|
+
const providerModules = [
|
|
312
|
+
await import('./providers/openai.js'),
|
|
313
|
+
await import('./providers/google.js'),
|
|
314
|
+
await import('./providers/xai.js')
|
|
315
|
+
];
|
|
316
|
+
|
|
317
|
+
for (const module of providerModules) {
|
|
318
|
+
const provider = module.default;
|
|
319
|
+
if (provider.isAvailable()) {
|
|
320
|
+
registerProvider(provider.name, provider);
|
|
321
|
+
logger.info(`Registered provider: ${provider.name}`);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
## ๐ State Management
|
|
328
|
+
|
|
329
|
+
### Stateless Design
|
|
330
|
+
- **No Global State**: Each request is independent
|
|
331
|
+
- **Continuation Storage**: Conversations stored as isolated state
|
|
332
|
+
- **Provider Independence**: Providers don't share state
|
|
333
|
+
- **Immutable Responses**: Responses are constructed, not modified
|
|
334
|
+
|
|
335
|
+
### Continuation System
|
|
336
|
+
```javascript
|
|
337
|
+
// utils/continuation.js
|
|
338
|
+
const continuations = new Map();
|
|
339
|
+
|
|
340
|
+
export function storeContinuation(id, data) {
|
|
341
|
+
continuations.set(id, {
|
|
342
|
+
...data,
|
|
343
|
+
lastAccessed: Date.now(),
|
|
344
|
+
messageCount: (data.messageCount || 0) + 1
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
export function getContinuation(id) {
|
|
349
|
+
const continuation = continuations.get(id);
|
|
350
|
+
if (continuation) {
|
|
351
|
+
continuation.lastAccessed = Date.now();
|
|
352
|
+
}
|
|
353
|
+
return continuation;
|
|
354
|
+
}
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
## ๐ Performance Characteristics
|
|
358
|
+
|
|
359
|
+
### Parallel Execution
|
|
360
|
+
- **Consensus Tool**: Executes all providers simultaneously
|
|
361
|
+
- **Non-Blocking I/O**: All async operations use Promise.all()
|
|
362
|
+
- **Provider Isolation**: One provider failure doesn't affect others
|
|
363
|
+
- **Request Batching**: Multiple requests handled concurrently
|
|
364
|
+
|
|
365
|
+
### Memory Management
|
|
366
|
+
- **Streaming Responses**: Large responses handled efficiently
|
|
367
|
+
- **Context Cleanup**: Old continuations automatically expire
|
|
368
|
+
- **File Processing**: Files read on-demand, not cached
|
|
369
|
+
- **Provider Pooling**: Connection reuse where possible
|
|
370
|
+
|
|
371
|
+
### Error Resilience
|
|
372
|
+
```javascript
|
|
373
|
+
// Graceful error handling pattern
|
|
374
|
+
async function executeWithFallback(primaryFn, fallbackFn) {
|
|
375
|
+
try {
|
|
376
|
+
return await primaryFn();
|
|
377
|
+
} catch (primaryError) {
|
|
378
|
+
logger.warn('Primary execution failed, attempting fallback', {
|
|
379
|
+
error: primaryError.message
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
try {
|
|
383
|
+
return await fallbackFn();
|
|
384
|
+
} catch (fallbackError) {
|
|
385
|
+
logger.error('Both primary and fallback failed', {
|
|
386
|
+
primaryError: primaryError.message,
|
|
387
|
+
fallbackError: fallbackError.message
|
|
388
|
+
});
|
|
389
|
+
throw new McpError(ErrorCode.InternalError, 'All execution attempts failed');
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
## ๐ Security Architecture
|
|
396
|
+
|
|
397
|
+
### Input Validation
|
|
398
|
+
```javascript
|
|
399
|
+
// utils/validators.js
|
|
400
|
+
export function validateChatInput(args) {
|
|
401
|
+
const errors = [];
|
|
402
|
+
|
|
403
|
+
if (!args.prompt || typeof args.prompt !== 'string') {
|
|
404
|
+
errors.push('prompt must be a non-empty string');
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
if (args.files && !Array.isArray(args.files)) {
|
|
408
|
+
errors.push('files must be an array');
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
return { valid: errors.length === 0, errors };
|
|
412
|
+
}
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
### Path Security
|
|
416
|
+
```javascript
|
|
417
|
+
// Prevent path traversal attacks
|
|
418
|
+
function validateFilePath(filePath) {
|
|
419
|
+
const normalized = path.resolve(filePath);
|
|
420
|
+
const allowed = path.resolve(process.cwd());
|
|
421
|
+
|
|
422
|
+
if (!normalized.startsWith(allowed)) {
|
|
423
|
+
throw new McpError(ErrorCode.InvalidParams, 'Access denied: Path outside allowed directory');
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
return normalized;
|
|
427
|
+
}
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
### API Key Protection
|
|
431
|
+
- **Environment Variables**: Keys never hardcoded
|
|
432
|
+
- **No Logging**: API keys excluded from all logs
|
|
433
|
+
- **Provider Isolation**: Keys scoped to specific providers
|
|
434
|
+
- **Error Sanitization**: Error messages don't expose keys
|
|
435
|
+
|
|
436
|
+
## ๐ Observability
|
|
437
|
+
|
|
438
|
+
### Structured Logging
|
|
439
|
+
```javascript
|
|
440
|
+
// utils/logger.js
|
|
441
|
+
export const logger = {
|
|
442
|
+
info: (message, meta = {}) => {
|
|
443
|
+
console.log(JSON.stringify({
|
|
444
|
+
level: 'info',
|
|
445
|
+
timestamp: new Date().toISOString(),
|
|
446
|
+
message,
|
|
447
|
+
...meta
|
|
448
|
+
}));
|
|
449
|
+
},
|
|
450
|
+
|
|
451
|
+
error: (message, error = {}) => {
|
|
452
|
+
console.error(JSON.stringify({
|
|
453
|
+
level: 'error',
|
|
454
|
+
timestamp: new Date().toISOString(),
|
|
455
|
+
message,
|
|
456
|
+
error: {
|
|
457
|
+
name: error.name,
|
|
458
|
+
message: error.message,
|
|
459
|
+
stack: error.stack
|
|
460
|
+
}
|
|
461
|
+
}));
|
|
462
|
+
}
|
|
463
|
+
};
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
### Request Tracing
|
|
467
|
+
```javascript
|
|
468
|
+
// Request correlation IDs
|
|
469
|
+
function generateRequestId() {
|
|
470
|
+
return `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// All operations tagged with request ID
|
|
474
|
+
logger.info('Processing chat request', {
|
|
475
|
+
requestId,
|
|
476
|
+
provider: 'openai',
|
|
477
|
+
model: 'gpt-5'
|
|
478
|
+
});
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
## ๐ Extensibility
|
|
482
|
+
|
|
483
|
+
### Adding New Providers
|
|
484
|
+
1. Create provider module in `src/providers/`
|
|
485
|
+
2. Implement the provider contract
|
|
486
|
+
3. Export as default
|
|
487
|
+
4. Provider auto-registers if API key is available
|
|
488
|
+
|
|
489
|
+
### Adding New Tools
|
|
490
|
+
1. Create tool module in `src/tools/`
|
|
491
|
+
2. Define tool schema and handler
|
|
492
|
+
3. Register in main server setup
|
|
493
|
+
4. Add to MCP tool list
|
|
494
|
+
|
|
495
|
+
### Configuration Extensions
|
|
496
|
+
```javascript
|
|
497
|
+
// Adding new configuration options
|
|
498
|
+
export const config = {
|
|
499
|
+
// ... existing config
|
|
500
|
+
|
|
501
|
+
// New feature config
|
|
502
|
+
experimental: {
|
|
503
|
+
enableFeatureX: process.env.ENABLE_FEATURE_X === 'true',
|
|
504
|
+
featureXTimeout: parseInt(process.env.FEATURE_X_TIMEOUT) || 30000
|
|
505
|
+
}
|
|
506
|
+
};
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
## ๐งช Testing Architecture
|
|
510
|
+
|
|
511
|
+
### Test Strategy
|
|
512
|
+
- **Unit Tests**: Individual functions and modules
|
|
513
|
+
- **Integration Tests**: Provider interactions (mocked APIs)
|
|
514
|
+
- **E2E Tests**: Full request/response cycles
|
|
515
|
+
- **Contract Tests**: Provider interface compliance
|
|
516
|
+
|
|
517
|
+
### Test Organization
|
|
518
|
+
```
|
|
519
|
+
tests/
|
|
520
|
+
โโโ unit/ # Unit tests for individual functions
|
|
521
|
+
โโโ integration/ # Integration tests with mocked dependencies
|
|
522
|
+
โโโ e2e/ # End-to-end tests
|
|
523
|
+
โโโ fixtures/ # Test data and mocks
|
|
524
|
+
โโโ helpers/ # Test utilities
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
## ๐ง Development Workflow
|
|
528
|
+
|
|
529
|
+
### Hot Reload Development
|
|
530
|
+
```bash
|
|
531
|
+
# Development with auto-restart
|
|
532
|
+
npm run dev
|
|
533
|
+
|
|
534
|
+
# Debug mode with inspection
|
|
535
|
+
npm run debug
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
### Code Quality Pipeline
|
|
539
|
+
```bash
|
|
540
|
+
# Full validation pipeline
|
|
541
|
+
npm run validate
|
|
542
|
+
|
|
543
|
+
# Individual checks
|
|
544
|
+
npm run lint
|
|
545
|
+
npm run typecheck
|
|
546
|
+
npm run test
|
|
547
|
+
npm run format:check
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
---
|
|
551
|
+
|
|
552
552
|
This architecture emphasizes **simplicity**, **reliability**, and **extensibility** while maintaining high performance through parallel execution and efficient resource management.
|