polydev-ai 1.4.0 → 1.4.2

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/mcp/server.js DELETED
@@ -1,959 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // Register ts-node for TypeScript support
4
- try {
5
- require('ts-node/register');
6
- } catch (e) {
7
- // ts-node not available, proceed without it
8
- }
9
-
10
- const fs = require('fs');
11
- const path = require('path');
12
- const CLIManager = require('../lib/cliManager').default;
13
-
14
- let UniversalMemoryExtractor;
15
- try {
16
- UniversalMemoryExtractor = require('../src/lib/universalMemoryExtractor').UniversalMemoryExtractor;
17
- } catch (e) {
18
- // Fallback if TypeScript module is not available
19
- console.warn('UniversalMemoryExtractor not available, memory features will be disabled');
20
- UniversalMemoryExtractor = class {
21
- async detectMemorySources() { return []; }
22
- async extractMemory() { return {}; }
23
- async getRecentConversations() { return []; }
24
- async getRelevantContext() { return ''; }
25
- async getPreferences() { return {}; }
26
- async updatePreferences() { return {}; }
27
- async resetPreferences() { return {}; }
28
- };
29
- }
30
-
31
- class MCPServer {
32
- constructor() {
33
- this.capabilities = {
34
- tools: {},
35
- resources: {},
36
- prompts: {}
37
- };
38
-
39
- this.tools = new Map();
40
- this.cliManager = new CLIManager();
41
- this.memoryExtractor = new UniversalMemoryExtractor();
42
- this.loadManifest();
43
- }
44
-
45
- loadManifest() {
46
- try {
47
- const manifestPath = path.join(__dirname, 'manifest.json');
48
- const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
49
-
50
- // Register tools from manifest
51
- if (manifest.tools) {
52
- manifest.tools.forEach(tool => {
53
- this.tools.set(tool.name, tool);
54
- });
55
- }
56
-
57
- this.manifest = manifest;
58
- } catch (error) {
59
- console.error('Failed to load manifest:', error);
60
- process.exit(1);
61
- }
62
- }
63
-
64
- async handleRequest(request) {
65
- const { method, params, id } = request;
66
-
67
- try {
68
- switch (method) {
69
- case 'initialize':
70
- return this.handleInitialize(params, id);
71
-
72
- case 'tools/list':
73
- return this.handleToolsList(id);
74
-
75
- case 'tools/call':
76
- return await this.handleToolCall(params, id);
77
-
78
- default:
79
- return {
80
- jsonrpc: '2.0',
81
- id,
82
- error: {
83
- code: -32601,
84
- message: 'Method not found'
85
- }
86
- };
87
- }
88
- } catch (error) {
89
- return {
90
- jsonrpc: '2.0',
91
- id,
92
- error: {
93
- code: -32603,
94
- message: 'Internal error',
95
- data: error.message
96
- }
97
- };
98
- }
99
- }
100
-
101
- handleInitialize(params, id) {
102
- return {
103
- jsonrpc: '2.0',
104
- id,
105
- result: {
106
- protocolVersion: '2024-11-05',
107
- capabilities: this.capabilities,
108
- serverInfo: {
109
- name: this.manifest.name,
110
- version: this.manifest.version
111
- }
112
- }
113
- };
114
- }
115
-
116
- handleToolsList(id) {
117
- const tools = Array.from(this.tools.values()).map(tool => ({
118
- name: tool.name,
119
- description: tool.description,
120
- inputSchema: tool.inputSchema
121
- }));
122
-
123
- return {
124
- jsonrpc: '2.0',
125
- id,
126
- result: { tools }
127
- };
128
- }
129
-
130
- async handleToolCall(params, id) {
131
- const { name, arguments: args } = params;
132
-
133
- if (!this.tools.has(name)) {
134
- return {
135
- jsonrpc: '2.0',
136
- id,
137
- error: {
138
- code: -32602,
139
- message: `Unknown tool: ${name}`
140
- }
141
- };
142
- }
143
-
144
- try {
145
- let result;
146
-
147
- switch (name) {
148
- case 'get_perspectives':
149
- result = await this.callPerspectivesAPI(args);
150
- break;
151
-
152
- case 'force_cli_detection':
153
- result = await this.forceCliDetection(args);
154
- break;
155
-
156
- case 'get_cli_status':
157
- result = await this.getCliStatus(args);
158
- break;
159
-
160
- case 'send_cli_prompt':
161
- result = await this.sendCliPrompt(args);
162
- break;
163
-
164
- case 'detect_memory_sources':
165
- result = await this.detectMemorySources(args);
166
- break;
167
-
168
- case 'extract_memory':
169
- result = await this.extractMemory(args);
170
- break;
171
-
172
- case 'get_recent_conversations':
173
- result = await this.getRecentConversations(args);
174
- break;
175
-
176
- case 'get_memory_context':
177
- result = await this.getMemoryContext(args);
178
- break;
179
-
180
- case 'manage_memory_preferences':
181
- result = await this.manageMemoryPreferences(args);
182
- break;
183
-
184
- default:
185
- throw new Error(`Tool ${name} not implemented`);
186
- }
187
-
188
- return {
189
- jsonrpc: '2.0',
190
- id,
191
- result: {
192
- content: [
193
- {
194
- type: 'text',
195
- text: this.formatResponse(name, result)
196
- }
197
- ]
198
- }
199
- };
200
- } catch (error) {
201
- return {
202
- jsonrpc: '2.0',
203
- id,
204
- error: {
205
- code: -32603,
206
- message: error.message
207
- }
208
- };
209
- }
210
- }
211
-
212
- async callPerspectivesAPI(args) {
213
- const serverUrl = 'https://www.polydev.ai/api/mcp';
214
-
215
- // Validate required arguments
216
- if (!args.prompt || typeof args.prompt !== 'string') {
217
- throw new Error('prompt is required and must be a string');
218
- }
219
-
220
- // Auto-inject memory context if enabled and not already provided
221
- let enhancedPrompt = args.prompt;
222
- if (!args.project_memory && args.auto_inject_memory !== false) {
223
- try {
224
- console.error('[Polydev MCP] Attempting to inject memory context...');
225
- const memoryContext = await this.getMemoryContext({
226
- prompt: args.prompt,
227
- cli_tools: ['all'],
228
- max_entries: 3
229
- });
230
-
231
- if (memoryContext.success && memoryContext.context.entries.length > 0) {
232
- const contextText = memoryContext.context.entries
233
- .map(entry => `[${entry.source}] ${entry.content.substring(0, 500)}`)
234
- .join('\n\n');
235
-
236
- enhancedPrompt = `Context from previous work:\n${contextText}\n\nCurrent request:\n${args.prompt}`;
237
- console.error(`[Polydev MCP] Injected ${memoryContext.context.entries.length} memory entries`);
238
- }
239
- } catch (error) {
240
- console.error('[Polydev MCP] Memory injection failed, continuing without:', error.message);
241
- }
242
- }
243
-
244
- // Support both parameter token (Claude Code) and environment token (Cline)
245
- const userToken = args.user_token || process.env.POLYDEV_USER_TOKEN;
246
-
247
- // Debug logging
248
- console.error(`[Polydev MCP] Debug - args.user_token: ${args.user_token ? 'present' : 'missing'}`);
249
- console.error(`[Polydev MCP] Debug - process.env.POLYDEV_USER_TOKEN: ${process.env.POLYDEV_USER_TOKEN ? 'present' : 'missing'}`);
250
- console.error(`[Polydev MCP] Debug - final userToken: ${userToken ? 'present' : 'missing'}`);
251
-
252
- if (!userToken || typeof userToken !== 'string') {
253
- throw new Error('user_token is required. Either:\n' +
254
- '1. Pass user_token parameter, or\n' +
255
- '2. Set POLYDEV_USER_TOKEN environment variable\n' +
256
- 'Generate token at: https://polydev.ai/dashboard/mcp-tokens');
257
- }
258
-
259
- console.error(`[Polydev MCP] Getting perspectives for: "${args.prompt.substring(0, 60)}${args.prompt.length > 60 ? '...' : ''}"`);
260
- console.error(`[Polydev MCP] Models: ${args.models ? args.models.join(', ') : 'using user preferences'}`);
261
- console.error(`[Polydev MCP] Project memory: ${args.project_memory || 'none'}`);
262
-
263
- // Call the MCP endpoint directly with proper MCP format
264
- const response = await fetch(serverUrl, {
265
- method: 'POST',
266
- headers: {
267
- 'Content-Type': 'application/json',
268
- 'Authorization': `Bearer ${userToken}`,
269
- 'User-Agent': 'polydev-perspectives-mcp/1.0.0'
270
- },
271
- body: JSON.stringify({
272
- jsonrpc: '2.0',
273
- method: 'tools/call',
274
- params: {
275
- name: 'get_perspectives',
276
- arguments: {
277
- ...args,
278
- prompt: enhancedPrompt,
279
- project_memory: enhancedPrompt !== args.prompt ? 'auto-injected' : args.project_memory
280
- }
281
- },
282
- id: 1
283
- })
284
- });
285
-
286
- if (!response.ok) {
287
- const errorText = await response.text();
288
- let errorMessage;
289
-
290
- try {
291
- const errorData = JSON.parse(errorText);
292
- errorMessage = errorData.error || errorData.message || `HTTP ${response.status}`;
293
- } catch {
294
- errorMessage = errorText || `HTTP ${response.status}`;
295
- }
296
-
297
- if (response.status === 401) {
298
- throw new Error(`Authentication failed: ${errorMessage}. Generate a new token at https://polydev.ai/dashboard/mcp-tools`);
299
- }
300
-
301
- throw new Error(`Polydev API error: ${errorMessage}`);
302
- }
303
-
304
- const result = await response.json();
305
-
306
- // Handle MCP JSON-RPC response format
307
- if (result.result && result.result.content && result.result.content[0]) {
308
- const content = result.result.content[0].text;
309
- console.error(`[Polydev MCP] Got MCP response successfully`);
310
- return { content };
311
- } else if (result.error) {
312
- throw new Error(result.error.message || 'MCP API error');
313
- }
314
-
315
- console.error(`[Polydev MCP] Unexpected response format:`, result);
316
- return result;
317
- }
318
-
319
- formatResponse(toolName, result) {
320
- switch (toolName) {
321
- case 'get_perspectives':
322
- return this.formatPerspectivesResponse(result);
323
-
324
- case 'force_cli_detection':
325
- return this.formatCliDetectionResponse(result);
326
-
327
- case 'get_cli_status':
328
- return this.formatCliStatusResponse(result);
329
-
330
- case 'send_cli_prompt':
331
- return this.formatCliPromptResponse(result);
332
-
333
- case 'detect_memory_sources':
334
- case 'extract_memory':
335
- case 'get_recent_conversations':
336
- case 'get_memory_context':
337
- case 'manage_memory_preferences':
338
- return this.formatMemoryResponse(result);
339
-
340
- default:
341
- return JSON.stringify(result, null, 2);
342
- }
343
- }
344
-
345
- formatPerspectivesResponse(result) {
346
- // Handle MCP response format (already formatted text)
347
- if (result.content) {
348
- return result.content;
349
- }
350
-
351
- // Handle legacy response format
352
- if (!result.responses || result.responses.length === 0) {
353
- return 'No perspectives received from models.';
354
- }
355
-
356
- let formatted = `# Multiple AI Perspectives\n\n`;
357
- formatted += `Got ${result.responses.length} perspectives in ${result.total_latency_ms}ms using ${result.total_tokens} tokens.\n\n`;
358
-
359
- result.responses.forEach((response, index) => {
360
- const modelName = response.model.toUpperCase();
361
-
362
- if (response.error) {
363
- formatted += `## ${modelName} - ERROR\n`;
364
- formatted += `❌ ${response.error}\n\n`;
365
- } else {
366
- formatted += `## ${modelName} Perspective\n`;
367
- formatted += `${response.content}\n\n`;
368
- if (response.tokens_used) {
369
- formatted += `*Tokens: ${response.tokens_used}, Latency: ${response.latency_ms}ms*\n\n`;
370
- }
371
- }
372
-
373
- if (index < result.responses.length - 1) {
374
- formatted += '---\n\n';
375
- }
376
- });
377
-
378
- return formatted;
379
- }
380
-
381
- formatCliDetectionResponse(result) {
382
- if (!result.success) {
383
- return `❌ CLI Detection Failed: ${result.error}`;
384
- }
385
-
386
- let formatted = `# CLI Provider Detection Results\n\n`;
387
- formatted += `✅ Detection completed at ${result.timestamp}\n`;
388
- formatted += `${result.message}\n\n`;
389
-
390
- if (result.results && Object.keys(result.results).length > 0) {
391
- // Count working vs broken providers
392
- let workingProviders = 0;
393
- let compatibilityIssues = 0;
394
-
395
- Object.entries(result.results).forEach(([providerId, status]) => {
396
- formatted += `## ${providerId.toUpperCase().replace('_', ' ')}\n`;
397
-
398
- if (status.available) {
399
- formatted += `✅ **Available** at ${status.path}\n`;
400
- if (status.version) {
401
- formatted += `📦 Version: ${status.version}\n`;
402
- }
403
-
404
- if (status.authenticated) {
405
- formatted += `🔐 **Authenticated** - Ready to use\n`;
406
- workingProviders++;
407
- } else {
408
- formatted += `❌ **Not Authenticated**\n`;
409
- if (status.error) {
410
- // Special formatting for compatibility issues
411
- if (status.error.includes('Compatibility Issue')) {
412
- formatted += `\n🚨 **COMPATIBILITY ISSUE**\n`;
413
- formatted += `${status.error}\n\n`;
414
- formatted += `**Need Help?** Contact support at https://polydev.ai/support with this error message.\n`;
415
- compatibilityIssues++;
416
- } else {
417
- formatted += `💡 ${status.error}\n`;
418
- }
419
- }
420
- }
421
- } else {
422
- formatted += `❌ **Not Available**\n`;
423
- if (status.error) {
424
- formatted += `💡 ${status.error}\n`;
425
- }
426
- }
427
-
428
- formatted += `⏰ Last checked: ${new Date(status.last_checked).toLocaleString()}\n\n`;
429
- });
430
-
431
- // Add summary
432
- formatted += `---\n\n`;
433
- formatted += `## 📊 System Status Summary\n`;
434
- formatted += `- **Working Providers**: ${workingProviders}/3 CLI tools ready\n`;
435
- if (compatibilityIssues > 0) {
436
- formatted += `- **⚠️ Compatibility Issues**: ${compatibilityIssues} provider(s) need attention\n`;
437
- formatted += `- **Recommendation**: Update Node.js or CLI tools as suggested above\n`;
438
- }
439
-
440
- if (workingProviders >= 2) {
441
- formatted += `- **Status**: ✅ System operational with ${workingProviders} working providers\n`;
442
- } else if (workingProviders >= 1) {
443
- formatted += `- **Status**: ⚠️ Limited functionality - only ${workingProviders} provider working\n`;
444
- } else {
445
- formatted += `- **Status**: ❌ No CLI providers working - check installations\n`;
446
- }
447
- }
448
-
449
- return formatted;
450
- }
451
-
452
- formatCliStatusResponse(result) {
453
- if (!result.success) {
454
- return `❌ CLI Status Check Failed: ${result.error}`;
455
- }
456
-
457
- let formatted = `# CLI Provider Status\n\n`;
458
- formatted += `Status retrieved at ${result.timestamp}\n\n`;
459
-
460
- if (result.results && Object.keys(result.results).length > 0) {
461
- Object.entries(result.results).forEach(([providerId, status]) => {
462
- const providerName = providerId.toUpperCase().replace('_', ' ');
463
- const statusIcon = status.available && status.authenticated ? '🟢' :
464
- status.available ? '🟡' : '🔴';
465
-
466
- formatted += `${statusIcon} **${providerName}**: `;
467
-
468
- if (status.available && status.authenticated) {
469
- formatted += `Ready to use`;
470
- } else if (status.available) {
471
- formatted += `Available but not authenticated`;
472
- } else {
473
- formatted += `Not available`;
474
- }
475
-
476
- formatted += `\n`;
477
- });
478
- } else {
479
- formatted += `No CLI providers detected.\n`;
480
- }
481
-
482
- return formatted;
483
- }
484
-
485
- formatCliPromptResponse(result) {
486
- if (!result.success) {
487
- return `❌ CLI Prompt Failed: ${result.error}`;
488
- }
489
-
490
- let formatted = `# CLI Response from ${result.provider.toUpperCase().replace('_', ' ')}\n\n`;
491
- formatted += `${result.content}\n\n`;
492
- formatted += `---\n`;
493
- formatted += `📊 **Stats**: ${result.tokens_used} tokens, ${result.latency_ms}ms latency\n`;
494
- formatted += `⚙️ **Mode**: ${result.mode} | **Time**: ${result.timestamp}`;
495
-
496
- return formatted;
497
- }
498
-
499
- /**
500
- * Force CLI detection for all providers using MCP Supabase integration
501
- */
502
- async forceCliDetection(args) {
503
- console.log('[MCP Server] Force CLI detection requested');
504
-
505
- try {
506
- const providerId = args.provider_id; // Optional - detect specific provider
507
- console.log('[MCP Server] Calling CLI Manager forceCliDetection with providerId:', providerId);
508
-
509
- // Force detection using CLI Manager
510
- const results = await this.cliManager.forceCliDetection(providerId);
511
- console.log('[MCP Server] CLI detection completed, results:', Object.keys(results));
512
-
513
- // CLI status is cached locally in CLIManager - no database updates needed
514
- // This MCP server runs in customer environments independently
515
-
516
- return {
517
- success: true,
518
- results,
519
- message: `CLI detection completed for ${providerId || 'all providers'}`,
520
- timestamp: new Date().toISOString()
521
- };
522
-
523
- } catch (error) {
524
- console.error('[MCP Server] CLI detection error:', error);
525
- return {
526
- success: false,
527
- error: error.message,
528
- timestamp: new Date().toISOString()
529
- };
530
- }
531
- }
532
-
533
- /**
534
- * Get CLI status with caching using MCP integration
535
- */
536
- async getCliStatus(args) {
537
- console.log('[MCP Server] Get CLI status requested');
538
-
539
- try {
540
- const providerId = args.provider_id;
541
-
542
- let results = {};
543
-
544
- if (providerId) {
545
- // Get specific provider status
546
- const allStatus = await this.cliManager.getCliStatus(providerId);
547
- results[providerId] = allStatus[providerId];
548
- } else {
549
- // Get all providers status
550
- const providers = this.cliManager.getAvailableProviders();
551
- for (const provider of providers) {
552
- const allStatus = await this.cliManager.getCliStatus(provider.id);
553
- results[provider.id] = allStatus[provider.id];
554
- }
555
- }
556
-
557
- return {
558
- success: true,
559
- results,
560
- message: 'CLI status retrieved successfully',
561
- timestamp: new Date().toISOString()
562
- };
563
-
564
- } catch (error) {
565
- console.error('[MCP Server] CLI status error:', error);
566
- return {
567
- success: false,
568
- error: error.message,
569
- timestamp: new Date().toISOString()
570
- };
571
- }
572
- }
573
-
574
- /**
575
- * Send prompt to CLI provider using MCP integration
576
- */
577
- async sendCliPrompt(args) {
578
- console.log('[MCP Server] Send CLI prompt requested');
579
-
580
- try {
581
- const { provider_id, prompt, mode = 'args', timeout_ms = 30000, user_id } = args;
582
-
583
- // Ensure timeout_ms is valid (not undefined, null, Infinity, or negative)
584
- let validTimeout = timeout_ms;
585
- if (!validTimeout || validTimeout === Infinity || validTimeout < 1 || validTimeout > 300000) {
586
- validTimeout = 30000 // Default to 30 seconds
587
- }
588
-
589
- if (!provider_id || !prompt) {
590
- throw new Error('provider_id and prompt are required');
591
- }
592
-
593
- // Send prompt using CLI Manager
594
- const response = await this.cliManager.sendCliPrompt(
595
- provider_id,
596
- prompt,
597
- mode,
598
- validTimeout
599
- );
600
-
601
- // CLI usage is tracked locally - no database integration needed
602
- // This MCP server runs independently in customer environments
603
-
604
- return {
605
- success: response.success,
606
- content: response.content,
607
- error: response.error,
608
- tokens_used: response.tokens_used,
609
- latency_ms: response.latency_ms,
610
- provider: provider_id,
611
- mode,
612
- timestamp: new Date().toISOString()
613
- };
614
-
615
- } catch (error) {
616
- console.error('[MCP Server] CLI prompt error:', error);
617
- return {
618
- success: false,
619
- error: error.message,
620
- timestamp: new Date().toISOString()
621
- };
622
- }
623
- }
624
-
625
- /**
626
- * Detect available memory sources across CLI tools
627
- */
628
- async detectMemorySources(args) {
629
- console.log('[MCP Server] Detect memory sources requested');
630
-
631
- try {
632
- const { cli_tools = ['all'] } = args;
633
-
634
- const sources = await this.memoryExtractor.detectMemorySources(cli_tools);
635
-
636
- return {
637
- success: true,
638
- sources,
639
- message: `Detected ${Object.keys(sources).length} memory sources`,
640
- timestamp: new Date().toISOString()
641
- };
642
- } catch (error) {
643
- console.error('[MCP Server] Memory detection error:', error);
644
- return {
645
- success: false,
646
- error: error.message,
647
- timestamp: new Date().toISOString()
648
- };
649
- }
650
- }
651
-
652
- /**
653
- * Extract memory from detected sources
654
- */
655
- async extractMemory(args) {
656
- console.log('[MCP Server] Extract memory requested');
657
-
658
- try {
659
- const { cli_tools = ['all'], memory_types = ['all'], project_path } = args;
660
-
661
- const extractedMemory = await this.memoryExtractor.extractMemory({
662
- cliTools: cli_tools,
663
- memoryTypes: memory_types,
664
- projectPath: project_path
665
- });
666
-
667
- return {
668
- success: true,
669
- memory: extractedMemory,
670
- message: `Extracted ${extractedMemory.totalEntries} memory entries`,
671
- timestamp: new Date().toISOString()
672
- };
673
- } catch (error) {
674
- console.error('[MCP Server] Memory extraction error:', error);
675
- return {
676
- success: false,
677
- error: error.message,
678
- timestamp: new Date().toISOString()
679
- };
680
- }
681
- }
682
-
683
- /**
684
- * Get recent conversations from CLI tools
685
- */
686
- async getRecentConversations(args) {
687
- console.log('[MCP Server] Get recent conversations requested');
688
-
689
- try {
690
- const { cli_tools = ['all'], limit = 10, project_path } = args;
691
-
692
- const conversations = await this.memoryExtractor.getRecentConversations({
693
- cliTools: cli_tools,
694
- limit,
695
- projectPath: project_path
696
- });
697
-
698
- return {
699
- success: true,
700
- conversations,
701
- message: `Retrieved ${conversations.length} recent conversations`,
702
- timestamp: new Date().toISOString()
703
- };
704
- } catch (error) {
705
- console.error('[MCP Server] Conversation retrieval error:', error);
706
- return {
707
- success: false,
708
- error: error.message,
709
- timestamp: new Date().toISOString()
710
- };
711
- }
712
- }
713
-
714
- /**
715
- * Get relevant memory context for current prompt
716
- */
717
- async getMemoryContext(args) {
718
- console.log('[MCP Server] Get memory context requested');
719
-
720
- try {
721
- const { prompt, cli_tools = ['all'], max_entries = 5, project_path } = args;
722
-
723
- if (!prompt) {
724
- throw new Error('prompt is required for context retrieval');
725
- }
726
-
727
- const context = await this.memoryExtractor.getRelevantContext({
728
- prompt,
729
- cliTools: cli_tools,
730
- maxEntries: max_entries,
731
- projectPath: project_path
732
- });
733
-
734
- return {
735
- success: true,
736
- context,
737
- message: `Found ${context.entries.length} relevant context entries`,
738
- relevanceScore: context.averageRelevance,
739
- timestamp: new Date().toISOString()
740
- };
741
- } catch (error) {
742
- console.error('[MCP Server] Context retrieval error:', error);
743
- return {
744
- success: false,
745
- error: error.message,
746
- timestamp: new Date().toISOString()
747
- };
748
- }
749
- }
750
-
751
- /**
752
- * Manage memory preferences and settings
753
- */
754
- async manageMemoryPreferences(args) {
755
- console.log('[MCP Server] Manage memory preferences requested');
756
-
757
- try {
758
- const { action = 'get', preferences = {} } = args;
759
-
760
- let result;
761
-
762
- switch (action) {
763
- case 'get':
764
- result = await this.memoryExtractor.getPreferences();
765
- break;
766
-
767
- case 'set':
768
- result = await this.memoryExtractor.updatePreferences(preferences);
769
- break;
770
-
771
- case 'reset':
772
- result = await this.memoryExtractor.resetPreferences();
773
- break;
774
-
775
- default:
776
- throw new Error(`Unknown action: ${action}`);
777
- }
778
-
779
- return {
780
- success: true,
781
- preferences: result,
782
- message: `Memory preferences ${action} completed`,
783
- timestamp: new Date().toISOString()
784
- };
785
- } catch (error) {
786
- console.error('[MCP Server] Preferences management error:', error);
787
- return {
788
- success: false,
789
- error: error.message,
790
- timestamp: new Date().toISOString()
791
- };
792
- }
793
- }
794
-
795
- /**
796
- * Format memory tool responses
797
- */
798
- formatMemoryResponse(result) {
799
- if (!result.success) {
800
- return `❌ Memory Operation Failed: ${result.error}`;
801
- }
802
-
803
- let formatted = `# Memory Operation Results\n\n`;
804
- formatted += `✅ ${result.message}\n`;
805
- formatted += `⏰ ${result.timestamp}\n\n`;
806
-
807
- // Format specific results based on operation type
808
- if (result.sources) {
809
- formatted += this.formatMemorySourcesResult(result.sources);
810
- } else if (result.memory) {
811
- formatted += this.formatExtractedMemoryResult(result.memory);
812
- } else if (result.conversations) {
813
- formatted += this.formatConversationsResult(result.conversations);
814
- } else if (result.context) {
815
- formatted += this.formatContextResult(result.context, result.relevanceScore);
816
- } else if (result.preferences) {
817
- formatted += this.formatPreferencesResult(result.preferences);
818
- }
819
-
820
- return formatted;
821
- }
822
-
823
- formatMemorySourcesResult(sources) {
824
- let formatted = `## 📁 Memory Sources Detected\n\n`;
825
-
826
- Object.entries(sources).forEach(([cliTool, toolSources]) => {
827
- formatted += `### ${cliTool.toUpperCase().replace('_', ' ')}\n`;
828
-
829
- Object.entries(toolSources).forEach(([memoryType, files]) => {
830
- const icon = memoryType === 'global' ? '🌍' : memoryType === 'project' ? '📂' : '💬';
831
- formatted += `${icon} **${memoryType}**: ${files.length} sources\n`;
832
-
833
- files.forEach(file => {
834
- const status = file.exists ? '✅' : '❌';
835
- formatted += ` ${status} \`${file.path}\`\n`;
836
- });
837
- });
838
-
839
- formatted += `\n`;
840
- });
841
-
842
- return formatted;
843
- }
844
-
845
- formatExtractedMemoryResult(memory) {
846
- let formatted = `## 🧠 Extracted Memory\n\n`;
847
- formatted += `📊 **Total Entries**: ${memory.totalEntries}\n`;
848
- formatted += `💾 **Total Size**: ${(memory.totalSize / 1024).toFixed(1)}KB\n\n`;
849
-
850
- Object.entries(memory.byCliTool).forEach(([cliTool, toolMemory]) => {
851
- formatted += `### ${cliTool.toUpperCase().replace('_', ' ')}\n`;
852
- formatted += `- Global: ${toolMemory.global.length} entries\n`;
853
- formatted += `- Project: ${toolMemory.project.length} entries\n`;
854
- formatted += `- Conversations: ${toolMemory.conversations.length} entries\n\n`;
855
- });
856
-
857
- return formatted;
858
- }
859
-
860
- formatConversationsResult(conversations) {
861
- let formatted = `## 💬 Recent Conversations\n\n`;
862
-
863
- conversations.forEach((conv, index) => {
864
- formatted += `### ${index + 1}. ${conv.cliTool.toUpperCase().replace('_', ' ')}\n`;
865
- formatted += `📅 ${new Date(conv.timestamp).toLocaleString()}\n`;
866
- formatted += `💭 ${conv.messageCount} messages, ${(conv.size / 1024).toFixed(1)}KB\n`;
867
-
868
- if (conv.topics && conv.topics.length > 0) {
869
- formatted += `🏷️ Topics: ${conv.topics.join(', ')}\n`;
870
- }
871
-
872
- formatted += `\n`;
873
- });
874
-
875
- return formatted;
876
- }
877
-
878
- formatContextResult(context, relevanceScore) {
879
- let formatted = `## 🎯 Relevant Context\n\n`;
880
- formatted += `📊 **Average Relevance**: ${(relevanceScore * 100).toFixed(1)}%\n\n`;
881
-
882
- context.entries.forEach((entry, index) => {
883
- formatted += `### ${index + 1}. ${entry.source} (${(entry.relevance * 100).toFixed(1)}%)\n`;
884
- formatted += `${entry.content.substring(0, 200)}...\n\n`;
885
- });
886
-
887
- return formatted;
888
- }
889
-
890
- formatPreferencesResult(preferences) {
891
- let formatted = `## ⚙️ Memory Preferences\n\n`;
892
-
893
- Object.entries(preferences).forEach(([key, value]) => {
894
- formatted += `- **${key}**: ${typeof value === 'object' ? JSON.stringify(value) : value}\n`;
895
- });
896
-
897
- return formatted;
898
- }
899
-
900
- // CLI status and usage tracking is handled locally by CLIManager
901
- // No database integration needed - this MCP server runs independently
902
-
903
- async start() {
904
- console.log('Starting Polydev Perspectives MCP Server...');
905
-
906
- // Handle JSONRPC communication over stdio
907
- process.stdin.setEncoding('utf8');
908
-
909
- let buffer = '';
910
-
911
- process.stdin.on('data', async (chunk) => {
912
- buffer += chunk;
913
-
914
- const lines = buffer.split('\n');
915
- buffer = lines.pop() || '';
916
-
917
- for (const line of lines) {
918
- if (line.trim()) {
919
- try {
920
- const request = JSON.parse(line);
921
- const response = await this.handleRequest(request);
922
- process.stdout.write(JSON.stringify(response) + '\n');
923
- } catch (error) {
924
- console.error('Failed to process request:', error);
925
- }
926
- }
927
- }
928
- });
929
-
930
- process.stdin.on('end', () => {
931
- console.log('MCP Server shutting down...');
932
- process.exit(0);
933
- });
934
-
935
- // Handle process signals
936
- process.on('SIGINT', () => {
937
- console.log('Received SIGINT, shutting down...');
938
- process.exit(0);
939
- });
940
-
941
- process.on('SIGTERM', () => {
942
- console.log('Received SIGTERM, shutting down...');
943
- process.exit(0);
944
- });
945
-
946
- console.log('MCP Server ready and listening on stdin...');
947
- }
948
- }
949
-
950
- // Start the server if this file is run directly
951
- if (require.main === module) {
952
- const server = new MCPServer();
953
- server.start().catch(error => {
954
- console.error('Failed to start MCP server:', error);
955
- process.exit(1);
956
- });
957
- }
958
-
959
- module.exports = MCPServer;