polydev-ai 1.0.0
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 +359 -0
- package/lib/cliManager.js +541 -0
- package/lib/cliManager.ts +428 -0
- package/mcp/manifest.json +462 -0
- package/mcp/package.json +46 -0
- package/mcp/server.js +598 -0
- package/mcp/stdio-wrapper.js +472 -0
- package/package.json +83 -0
package/mcp/server.js
ADDED
|
@@ -0,0 +1,598 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const CLIManager = require('../lib/cliManager').default;
|
|
6
|
+
|
|
7
|
+
class MCPServer {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.capabilities = {
|
|
10
|
+
tools: {},
|
|
11
|
+
resources: {},
|
|
12
|
+
prompts: {}
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
this.tools = new Map();
|
|
16
|
+
this.cliManager = new CLIManager();
|
|
17
|
+
this.loadManifest();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
loadManifest() {
|
|
21
|
+
try {
|
|
22
|
+
const manifestPath = path.join(__dirname, 'manifest.json');
|
|
23
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
24
|
+
|
|
25
|
+
// Register tools from manifest
|
|
26
|
+
if (manifest.tools) {
|
|
27
|
+
manifest.tools.forEach(tool => {
|
|
28
|
+
this.tools.set(tool.name, tool);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
this.manifest = manifest;
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.error('Failed to load manifest:', error);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async handleRequest(request) {
|
|
40
|
+
const { method, params, id } = request;
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
switch (method) {
|
|
44
|
+
case 'initialize':
|
|
45
|
+
return this.handleInitialize(params, id);
|
|
46
|
+
|
|
47
|
+
case 'tools/list':
|
|
48
|
+
return this.handleToolsList(id);
|
|
49
|
+
|
|
50
|
+
case 'tools/call':
|
|
51
|
+
return await this.handleToolCall(params, id);
|
|
52
|
+
|
|
53
|
+
default:
|
|
54
|
+
return {
|
|
55
|
+
jsonrpc: '2.0',
|
|
56
|
+
id,
|
|
57
|
+
error: {
|
|
58
|
+
code: -32601,
|
|
59
|
+
message: 'Method not found'
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
} catch (error) {
|
|
64
|
+
return {
|
|
65
|
+
jsonrpc: '2.0',
|
|
66
|
+
id,
|
|
67
|
+
error: {
|
|
68
|
+
code: -32603,
|
|
69
|
+
message: 'Internal error',
|
|
70
|
+
data: error.message
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
handleInitialize(params, id) {
|
|
77
|
+
return {
|
|
78
|
+
jsonrpc: '2.0',
|
|
79
|
+
id,
|
|
80
|
+
result: {
|
|
81
|
+
protocolVersion: '2024-11-05',
|
|
82
|
+
capabilities: this.capabilities,
|
|
83
|
+
serverInfo: {
|
|
84
|
+
name: this.manifest.name,
|
|
85
|
+
version: this.manifest.version
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
handleToolsList(id) {
|
|
92
|
+
const tools = Array.from(this.tools.values()).map(tool => ({
|
|
93
|
+
name: tool.name,
|
|
94
|
+
description: tool.description,
|
|
95
|
+
inputSchema: tool.inputSchema
|
|
96
|
+
}));
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
jsonrpc: '2.0',
|
|
100
|
+
id,
|
|
101
|
+
result: { tools }
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async handleToolCall(params, id) {
|
|
106
|
+
const { name, arguments: args } = params;
|
|
107
|
+
|
|
108
|
+
if (!this.tools.has(name)) {
|
|
109
|
+
return {
|
|
110
|
+
jsonrpc: '2.0',
|
|
111
|
+
id,
|
|
112
|
+
error: {
|
|
113
|
+
code: -32602,
|
|
114
|
+
message: `Unknown tool: ${name}`
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
let result;
|
|
121
|
+
|
|
122
|
+
switch (name) {
|
|
123
|
+
case 'get_perspectives':
|
|
124
|
+
result = await this.callPerspectivesAPI(args);
|
|
125
|
+
break;
|
|
126
|
+
|
|
127
|
+
case 'polydev.force_cli_detection':
|
|
128
|
+
result = await this.forceCliDetection(args);
|
|
129
|
+
break;
|
|
130
|
+
|
|
131
|
+
case 'polydev.get_cli_status':
|
|
132
|
+
result = await this.getCliStatus(args);
|
|
133
|
+
break;
|
|
134
|
+
|
|
135
|
+
case 'polydev.send_cli_prompt':
|
|
136
|
+
result = await this.sendCliPrompt(args);
|
|
137
|
+
break;
|
|
138
|
+
|
|
139
|
+
default:
|
|
140
|
+
throw new Error(`Tool ${name} not implemented`);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
jsonrpc: '2.0',
|
|
145
|
+
id,
|
|
146
|
+
result: {
|
|
147
|
+
content: [
|
|
148
|
+
{
|
|
149
|
+
type: 'text',
|
|
150
|
+
text: this.formatResponse(name, result)
|
|
151
|
+
}
|
|
152
|
+
]
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
} catch (error) {
|
|
156
|
+
return {
|
|
157
|
+
jsonrpc: '2.0',
|
|
158
|
+
id,
|
|
159
|
+
error: {
|
|
160
|
+
code: -32603,
|
|
161
|
+
message: error.message
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async callPerspectivesAPI(args) {
|
|
168
|
+
const serverUrl = 'https://www.polydev.ai/api/mcp';
|
|
169
|
+
|
|
170
|
+
// Validate required arguments
|
|
171
|
+
if (!args.prompt || typeof args.prompt !== 'string') {
|
|
172
|
+
throw new Error('prompt is required and must be a string');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Support both parameter token (Claude Code) and environment token (Cline)
|
|
176
|
+
const userToken = args.user_token || process.env.POLYDEV_USER_TOKEN;
|
|
177
|
+
|
|
178
|
+
// Debug logging
|
|
179
|
+
console.error(`[Polydev MCP] Debug - args.user_token: ${args.user_token ? 'present' : 'missing'}`);
|
|
180
|
+
console.error(`[Polydev MCP] Debug - process.env.POLYDEV_USER_TOKEN: ${process.env.POLYDEV_USER_TOKEN ? 'present' : 'missing'}`);
|
|
181
|
+
console.error(`[Polydev MCP] Debug - final userToken: ${userToken ? 'present' : 'missing'}`);
|
|
182
|
+
|
|
183
|
+
if (!userToken || typeof userToken !== 'string') {
|
|
184
|
+
throw new Error('user_token is required. Either:\n' +
|
|
185
|
+
'1. Pass user_token parameter, or\n' +
|
|
186
|
+
'2. Set POLYDEV_USER_TOKEN environment variable\n' +
|
|
187
|
+
'Generate token at: https://polydev.ai/dashboard/mcp-tokens');
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
console.error(`[Polydev MCP] Getting perspectives for: "${args.prompt.substring(0, 60)}${args.prompt.length > 60 ? '...' : ''}"`);
|
|
191
|
+
console.error(`[Polydev MCP] Models: ${args.models ? args.models.join(', ') : 'using user preferences'}`);
|
|
192
|
+
console.error(`[Polydev MCP] Project memory: ${args.project_memory || 'none'}`);
|
|
193
|
+
|
|
194
|
+
// Call the MCP endpoint directly with proper MCP format
|
|
195
|
+
const response = await fetch(serverUrl, {
|
|
196
|
+
method: 'POST',
|
|
197
|
+
headers: {
|
|
198
|
+
'Content-Type': 'application/json',
|
|
199
|
+
'Authorization': `Bearer ${userToken}`,
|
|
200
|
+
'User-Agent': 'polydev-perspectives-mcp/1.0.0'
|
|
201
|
+
},
|
|
202
|
+
body: JSON.stringify({
|
|
203
|
+
jsonrpc: '2.0',
|
|
204
|
+
method: 'tools/call',
|
|
205
|
+
params: {
|
|
206
|
+
name: 'get_perspectives',
|
|
207
|
+
arguments: args
|
|
208
|
+
},
|
|
209
|
+
id: 1
|
|
210
|
+
})
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
if (!response.ok) {
|
|
214
|
+
const errorText = await response.text();
|
|
215
|
+
let errorMessage;
|
|
216
|
+
|
|
217
|
+
try {
|
|
218
|
+
const errorData = JSON.parse(errorText);
|
|
219
|
+
errorMessage = errorData.error || errorData.message || `HTTP ${response.status}`;
|
|
220
|
+
} catch {
|
|
221
|
+
errorMessage = errorText || `HTTP ${response.status}`;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (response.status === 401) {
|
|
225
|
+
throw new Error(`Authentication failed: ${errorMessage}. Generate a new token at https://polydev.ai/dashboard/mcp-tools`);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
throw new Error(`Polydev API error: ${errorMessage}`);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const result = await response.json();
|
|
232
|
+
|
|
233
|
+
// Handle MCP JSON-RPC response format
|
|
234
|
+
if (result.result && result.result.content && result.result.content[0]) {
|
|
235
|
+
const content = result.result.content[0].text;
|
|
236
|
+
console.error(`[Polydev MCP] Got MCP response successfully`);
|
|
237
|
+
return { content };
|
|
238
|
+
} else if (result.error) {
|
|
239
|
+
throw new Error(result.error.message || 'MCP API error');
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
console.error(`[Polydev MCP] Unexpected response format:`, result);
|
|
243
|
+
return result;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
formatResponse(toolName, result) {
|
|
247
|
+
switch (toolName) {
|
|
248
|
+
case 'get_perspectives':
|
|
249
|
+
return this.formatPerspectivesResponse(result);
|
|
250
|
+
|
|
251
|
+
case 'polydev.force_cli_detection':
|
|
252
|
+
return this.formatCliDetectionResponse(result);
|
|
253
|
+
|
|
254
|
+
case 'polydev.get_cli_status':
|
|
255
|
+
return this.formatCliStatusResponse(result);
|
|
256
|
+
|
|
257
|
+
case 'polydev.send_cli_prompt':
|
|
258
|
+
return this.formatCliPromptResponse(result);
|
|
259
|
+
|
|
260
|
+
default:
|
|
261
|
+
return JSON.stringify(result, null, 2);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
formatPerspectivesResponse(result) {
|
|
266
|
+
// Handle MCP response format (already formatted text)
|
|
267
|
+
if (result.content) {
|
|
268
|
+
return result.content;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Handle legacy response format
|
|
272
|
+
if (!result.responses || result.responses.length === 0) {
|
|
273
|
+
return 'No perspectives received from models.';
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
let formatted = `# Multiple AI Perspectives\n\n`;
|
|
277
|
+
formatted += `Got ${result.responses.length} perspectives in ${result.total_latency_ms}ms using ${result.total_tokens} tokens.\n\n`;
|
|
278
|
+
|
|
279
|
+
result.responses.forEach((response, index) => {
|
|
280
|
+
const modelName = response.model.toUpperCase();
|
|
281
|
+
|
|
282
|
+
if (response.error) {
|
|
283
|
+
formatted += `## ${modelName} - ERROR\n`;
|
|
284
|
+
formatted += `❌ ${response.error}\n\n`;
|
|
285
|
+
} else {
|
|
286
|
+
formatted += `## ${modelName} Perspective\n`;
|
|
287
|
+
formatted += `${response.content}\n\n`;
|
|
288
|
+
if (response.tokens_used) {
|
|
289
|
+
formatted += `*Tokens: ${response.tokens_used}, Latency: ${response.latency_ms}ms*\n\n`;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (index < result.responses.length - 1) {
|
|
294
|
+
formatted += '---\n\n';
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
return formatted;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
formatCliDetectionResponse(result) {
|
|
302
|
+
if (!result.success) {
|
|
303
|
+
return `❌ CLI Detection Failed: ${result.error}`;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
let formatted = `# CLI Provider Detection Results\n\n`;
|
|
307
|
+
formatted += `✅ Detection completed at ${result.timestamp}\n`;
|
|
308
|
+
formatted += `${result.message}\n\n`;
|
|
309
|
+
|
|
310
|
+
if (result.results && Object.keys(result.results).length > 0) {
|
|
311
|
+
// Count working vs broken providers
|
|
312
|
+
let workingProviders = 0;
|
|
313
|
+
let compatibilityIssues = 0;
|
|
314
|
+
|
|
315
|
+
Object.entries(result.results).forEach(([providerId, status]) => {
|
|
316
|
+
formatted += `## ${providerId.toUpperCase().replace('_', ' ')}\n`;
|
|
317
|
+
|
|
318
|
+
if (status.available) {
|
|
319
|
+
formatted += `✅ **Available** at ${status.path}\n`;
|
|
320
|
+
if (status.version) {
|
|
321
|
+
formatted += `📦 Version: ${status.version}\n`;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if (status.authenticated) {
|
|
325
|
+
formatted += `🔐 **Authenticated** - Ready to use\n`;
|
|
326
|
+
workingProviders++;
|
|
327
|
+
} else {
|
|
328
|
+
formatted += `❌ **Not Authenticated**\n`;
|
|
329
|
+
if (status.error) {
|
|
330
|
+
// Special formatting for compatibility issues
|
|
331
|
+
if (status.error.includes('Compatibility Issue')) {
|
|
332
|
+
formatted += `\n🚨 **COMPATIBILITY ISSUE**\n`;
|
|
333
|
+
formatted += `${status.error}\n\n`;
|
|
334
|
+
formatted += `**Need Help?** Contact support at https://polydev.ai/support with this error message.\n`;
|
|
335
|
+
compatibilityIssues++;
|
|
336
|
+
} else {
|
|
337
|
+
formatted += `💡 ${status.error}\n`;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
} else {
|
|
342
|
+
formatted += `❌ **Not Available**\n`;
|
|
343
|
+
if (status.error) {
|
|
344
|
+
formatted += `💡 ${status.error}\n`;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
formatted += `⏰ Last checked: ${new Date(status.last_checked).toLocaleString()}\n\n`;
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
// Add summary
|
|
352
|
+
formatted += `---\n\n`;
|
|
353
|
+
formatted += `## 📊 System Status Summary\n`;
|
|
354
|
+
formatted += `- **Working Providers**: ${workingProviders}/3 CLI tools ready\n`;
|
|
355
|
+
if (compatibilityIssues > 0) {
|
|
356
|
+
formatted += `- **⚠️ Compatibility Issues**: ${compatibilityIssues} provider(s) need attention\n`;
|
|
357
|
+
formatted += `- **Recommendation**: Update Node.js or CLI tools as suggested above\n`;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (workingProviders >= 2) {
|
|
361
|
+
formatted += `- **Status**: ✅ System operational with ${workingProviders} working providers\n`;
|
|
362
|
+
} else if (workingProviders >= 1) {
|
|
363
|
+
formatted += `- **Status**: ⚠️ Limited functionality - only ${workingProviders} provider working\n`;
|
|
364
|
+
} else {
|
|
365
|
+
formatted += `- **Status**: ❌ No CLI providers working - check installations\n`;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
return formatted;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
formatCliStatusResponse(result) {
|
|
373
|
+
if (!result.success) {
|
|
374
|
+
return `❌ CLI Status Check Failed: ${result.error}`;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
let formatted = `# CLI Provider Status\n\n`;
|
|
378
|
+
formatted += `Status retrieved at ${result.timestamp}\n\n`;
|
|
379
|
+
|
|
380
|
+
if (result.results && Object.keys(result.results).length > 0) {
|
|
381
|
+
Object.entries(result.results).forEach(([providerId, status]) => {
|
|
382
|
+
const providerName = providerId.toUpperCase().replace('_', ' ');
|
|
383
|
+
const statusIcon = status.available && status.authenticated ? '🟢' :
|
|
384
|
+
status.available ? '🟡' : '🔴';
|
|
385
|
+
|
|
386
|
+
formatted += `${statusIcon} **${providerName}**: `;
|
|
387
|
+
|
|
388
|
+
if (status.available && status.authenticated) {
|
|
389
|
+
formatted += `Ready to use`;
|
|
390
|
+
} else if (status.available) {
|
|
391
|
+
formatted += `Available but not authenticated`;
|
|
392
|
+
} else {
|
|
393
|
+
formatted += `Not available`;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
formatted += `\n`;
|
|
397
|
+
});
|
|
398
|
+
} else {
|
|
399
|
+
formatted += `No CLI providers detected.\n`;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
return formatted;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
formatCliPromptResponse(result) {
|
|
406
|
+
if (!result.success) {
|
|
407
|
+
return `❌ CLI Prompt Failed: ${result.error}`;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
let formatted = `# CLI Response from ${result.provider.toUpperCase().replace('_', ' ')}\n\n`;
|
|
411
|
+
formatted += `${result.content}\n\n`;
|
|
412
|
+
formatted += `---\n`;
|
|
413
|
+
formatted += `📊 **Stats**: ${result.tokens_used} tokens, ${result.latency_ms}ms latency\n`;
|
|
414
|
+
formatted += `⚙️ **Mode**: ${result.mode} | **Time**: ${result.timestamp}`;
|
|
415
|
+
|
|
416
|
+
return formatted;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Force CLI detection for all providers using MCP Supabase integration
|
|
421
|
+
*/
|
|
422
|
+
async forceCliDetection(args) {
|
|
423
|
+
console.log('[MCP Server] Force CLI detection requested');
|
|
424
|
+
|
|
425
|
+
try {
|
|
426
|
+
const providerId = args.provider_id; // Optional - detect specific provider
|
|
427
|
+
console.log('[MCP Server] Calling CLI Manager forceCliDetection with providerId:', providerId);
|
|
428
|
+
|
|
429
|
+
// Force detection using CLI Manager
|
|
430
|
+
const results = await this.cliManager.forceCliDetection(providerId);
|
|
431
|
+
console.log('[MCP Server] CLI detection completed, results:', Object.keys(results));
|
|
432
|
+
|
|
433
|
+
// CLI status is cached locally in CLIManager - no database updates needed
|
|
434
|
+
// This MCP server runs in customer environments independently
|
|
435
|
+
|
|
436
|
+
return {
|
|
437
|
+
success: true,
|
|
438
|
+
results,
|
|
439
|
+
message: `CLI detection completed for ${providerId || 'all providers'}`,
|
|
440
|
+
timestamp: new Date().toISOString()
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
} catch (error) {
|
|
444
|
+
console.error('[MCP Server] CLI detection error:', error);
|
|
445
|
+
return {
|
|
446
|
+
success: false,
|
|
447
|
+
error: error.message,
|
|
448
|
+
timestamp: new Date().toISOString()
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Get CLI status with caching using MCP integration
|
|
455
|
+
*/
|
|
456
|
+
async getCliStatus(args) {
|
|
457
|
+
console.log('[MCP Server] Get CLI status requested');
|
|
458
|
+
|
|
459
|
+
try {
|
|
460
|
+
const providerId = args.provider_id;
|
|
461
|
+
|
|
462
|
+
let results = {};
|
|
463
|
+
|
|
464
|
+
if (providerId) {
|
|
465
|
+
// Get specific provider status
|
|
466
|
+
const allStatus = await this.cliManager.getCliStatus(providerId);
|
|
467
|
+
results[providerId] = allStatus[providerId];
|
|
468
|
+
} else {
|
|
469
|
+
// Get all providers status
|
|
470
|
+
const providers = this.cliManager.getAvailableProviders();
|
|
471
|
+
for (const provider of providers) {
|
|
472
|
+
const allStatus = await this.cliManager.getCliStatus(provider.id);
|
|
473
|
+
results[provider.id] = allStatus[provider.id];
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
return {
|
|
478
|
+
success: true,
|
|
479
|
+
results,
|
|
480
|
+
message: 'CLI status retrieved successfully',
|
|
481
|
+
timestamp: new Date().toISOString()
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
} catch (error) {
|
|
485
|
+
console.error('[MCP Server] CLI status error:', error);
|
|
486
|
+
return {
|
|
487
|
+
success: false,
|
|
488
|
+
error: error.message,
|
|
489
|
+
timestamp: new Date().toISOString()
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Send prompt to CLI provider using MCP integration
|
|
496
|
+
*/
|
|
497
|
+
async sendCliPrompt(args) {
|
|
498
|
+
console.log('[MCP Server] Send CLI prompt requested');
|
|
499
|
+
|
|
500
|
+
try {
|
|
501
|
+
const { provider_id, prompt, mode = 'args', timeout_ms = 30000, user_id } = args;
|
|
502
|
+
|
|
503
|
+
if (!provider_id || !prompt) {
|
|
504
|
+
throw new Error('provider_id and prompt are required');
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// Send prompt using CLI Manager
|
|
508
|
+
const response = await this.cliManager.sendCliPrompt(
|
|
509
|
+
provider_id,
|
|
510
|
+
prompt,
|
|
511
|
+
mode,
|
|
512
|
+
timeout_ms
|
|
513
|
+
);
|
|
514
|
+
|
|
515
|
+
// CLI usage is tracked locally - no database integration needed
|
|
516
|
+
// This MCP server runs independently in customer environments
|
|
517
|
+
|
|
518
|
+
return {
|
|
519
|
+
success: response.success,
|
|
520
|
+
content: response.content,
|
|
521
|
+
error: response.error,
|
|
522
|
+
tokens_used: response.tokens_used,
|
|
523
|
+
latency_ms: response.latency_ms,
|
|
524
|
+
provider: provider_id,
|
|
525
|
+
mode,
|
|
526
|
+
timestamp: new Date().toISOString()
|
|
527
|
+
};
|
|
528
|
+
|
|
529
|
+
} catch (error) {
|
|
530
|
+
console.error('[MCP Server] CLI prompt error:', error);
|
|
531
|
+
return {
|
|
532
|
+
success: false,
|
|
533
|
+
error: error.message,
|
|
534
|
+
timestamp: new Date().toISOString()
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// CLI status and usage tracking is handled locally by CLIManager
|
|
540
|
+
// No database integration needed - this MCP server runs independently
|
|
541
|
+
|
|
542
|
+
async start() {
|
|
543
|
+
console.log('Starting Polydev Perspectives MCP Server...');
|
|
544
|
+
|
|
545
|
+
// Handle JSONRPC communication over stdio
|
|
546
|
+
process.stdin.setEncoding('utf8');
|
|
547
|
+
|
|
548
|
+
let buffer = '';
|
|
549
|
+
|
|
550
|
+
process.stdin.on('data', async (chunk) => {
|
|
551
|
+
buffer += chunk;
|
|
552
|
+
|
|
553
|
+
const lines = buffer.split('\n');
|
|
554
|
+
buffer = lines.pop() || '';
|
|
555
|
+
|
|
556
|
+
for (const line of lines) {
|
|
557
|
+
if (line.trim()) {
|
|
558
|
+
try {
|
|
559
|
+
const request = JSON.parse(line);
|
|
560
|
+
const response = await this.handleRequest(request);
|
|
561
|
+
process.stdout.write(JSON.stringify(response) + '\n');
|
|
562
|
+
} catch (error) {
|
|
563
|
+
console.error('Failed to process request:', error);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
process.stdin.on('end', () => {
|
|
570
|
+
console.log('MCP Server shutting down...');
|
|
571
|
+
process.exit(0);
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
// Handle process signals
|
|
575
|
+
process.on('SIGINT', () => {
|
|
576
|
+
console.log('Received SIGINT, shutting down...');
|
|
577
|
+
process.exit(0);
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
process.on('SIGTERM', () => {
|
|
581
|
+
console.log('Received SIGTERM, shutting down...');
|
|
582
|
+
process.exit(0);
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
console.log('MCP Server ready and listening on stdin...');
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// Start the server if this file is run directly
|
|
590
|
+
if (require.main === module) {
|
|
591
|
+
const server = new MCPServer();
|
|
592
|
+
server.start().catch(error => {
|
|
593
|
+
console.error('Failed to start MCP server:', error);
|
|
594
|
+
process.exit(1);
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
module.exports = MCPServer;
|