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/README.md +121 -320
- package/package.json +29 -84
- package/{mcp/stdio-wrapper.js → stdio-wrapper.js} +9 -4
- package/lib/cliManager.ts +0 -755
- package/lib/smartCliCache.ts +0 -189
- package/lib/universalMemoryExtractor.js +0 -607
- package/lib/zeroKnowledgeEncryption.js +0 -289
- package/mcp/README.md +0 -160
- package/mcp/package.json +0 -46
- package/mcp/server.js +0 -959
- package/mcp/stdio-wrapper-fixed.js +0 -169
- /package/{lib/cliManager.js → cliManager.js} +0 -0
- /package/{mcp/manifest.json → manifest.json} +0 -0
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;
|