@pluggedin/pluggedin-mcp-proxy
Version:
Unified MCP proxy that aggregates all your MCP servers (STDIO, SSE, Streamable HTTP) into one powerful interface. Access any tool through a single connection, search across unified documents with built-in RAG, and receive notifications from any model. Tes
375 lines (374 loc) • 14.3 kB
JavaScript
import { Router } from 'express';
import { suggestServerConfiguration, analyzeServerPatterns, validateConfiguration } from '../services/ai-config-assistant';
import { db } from '../db';
import { mcpServers } from '../schema';
import { eq, inArray, isNull, or } from 'drizzle-orm';
const router = Router();
// Suggest configuration for a specific server
router.post('/suggest/:serverId', async (req, res) => {
try {
const { serverId } = req.params;
const { userSystem, installedServers, preferredPaths } = req.body;
// Get server details
const server = await db.query.mcpServers.findFirst({
where: eq(mcpServers.id, serverId)
});
if (!server) {
return res.status(404).json({ error: 'Server not found' });
}
if (!server.command || !server.args) {
return res.status(400).json({
error: 'Server missing command information',
message: 'This server does not have installation instructions yet'
});
}
const suggestion = await suggestServerConfiguration({
serverName: server.name,
serverType: server.serverType,
command: server.command,
args: server.args || [],
envs: server.envs || [],
repositoryUrl: server.repositoryUrl || undefined,
description: server.description,
userSystem,
installedServers,
preferredPaths
});
if (!suggestion) {
return res.status(503).json({
error: 'AI service unavailable',
message: 'Could not generate configuration suggestion'
});
}
// Validate the suggestion
const validation = validateConfiguration(suggestion, server.serverType || 'STDIO');
res.json({
server: {
id: server.id,
name: server.name,
command: server.command,
serverType: server.serverType
},
suggestion,
validation,
needsUserInput: validation.issues.some(issue => issue.includes('needs a real value') ||
issue.includes('PLACEHOLDER'))
});
}
catch (error) {
console.error('Error suggesting configuration:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// Batch configuration suggestions for multiple servers
router.post('/suggest-batch', async (req, res) => {
try {
const { serverIds, userSystem } = req.body;
if (!Array.isArray(serverIds) || serverIds.length === 0) {
return res.status(400).json({ error: 'Server IDs array required' });
}
// Limit batch size
if (serverIds.length > 10) {
return res.status(400).json({
error: 'Too many servers',
message: 'Maximum 10 servers per batch'
});
}
// Get all servers
const servers = await db.query.mcpServers.findMany({
where: (mcpServers, { inArray }) => inArray(mcpServers.id, serverIds)
});
const suggestions = await Promise.all(servers
.filter(server => server.command && server.args)
.map(async (server) => {
const suggestion = await suggestServerConfiguration({
serverName: server.name,
serverType: server.serverType,
command: server.command,
args: server.args || [],
envs: server.envs || [],
repositoryUrl: server.repositoryUrl || undefined,
description: server.description,
userSystem
});
if (!suggestion)
return null;
const validation = validateConfiguration(suggestion, server.serverType || 'STDIO');
return {
serverId: server.id,
serverName: server.name,
suggestion,
validation
};
}));
res.json({
suggestions: suggestions.filter(Boolean),
failed: servers.length - suggestions.filter(Boolean).length
});
}
catch (error) {
console.error('Error batch suggesting:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// Analyze patterns across servers
router.post('/analyze-patterns', async (req, res) => {
try {
const { category } = req.body;
let servers;
if (category) {
servers = await db.query.mcpServers.findMany({
where: eq(mcpServers.category, category),
limit: 20
});
}
else {
servers = await db.query.mcpServers.findMany({
limit: 20
});
}
const serversWithCommands = servers
.filter(s => s.command && s.args)
.map(s => ({
name: s.name,
command: s.command,
args: s.args || [],
envs: s.envs || []
}));
if (serversWithCommands.length === 0) {
return res.json({
commonPatterns: [],
recommendations: ['No servers with commands found for analysis']
});
}
const analysis = await analyzeServerPatterns(serversWithCommands);
res.json(analysis || {
commonPatterns: [],
recommendations: ['AI analysis unavailable']
});
}
catch (error) {
console.error('Error analyzing patterns:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// Check if AI service is available
router.get('/status', async (_req, res) => {
const hasApiKey = !!process.env.OPENAI_API_KEY;
res.json({
available: hasApiKey,
model: 'gpt-4o-mini',
features: hasApiKey ? [
'configuration_suggestions',
'batch_suggestions',
'pattern_analysis',
'fix_server',
'batch_fix'
] : []
});
});
// Fix a server's configuration with AI
router.post('/fix-server', async (req, res) => {
try {
const { serverData, customDirectives, userSystem } = req.body;
if (!serverData || !serverData.name) {
return res.status(400).json({
error: 'Invalid server data',
message: 'Server name is required'
});
}
// Build the configuration request
const configRequest = {
serverName: serverData.name,
serverType: serverData.type || 'STDIO',
command: serverData.command || 'npx',
args: Array.isArray(serverData.args) ? serverData.args :
(serverData.args ? serverData.args.split(' ').filter(Boolean) : []),
envs: serverData.envs || [],
repositoryUrl: serverData.url,
description: serverData.description,
userSystem
};
// Get AI suggestion
const suggestion = await suggestServerConfiguration(configRequest);
if (!suggestion) {
return res.status(503).json({
error: 'AI service unavailable',
message: 'Could not generate configuration suggestion'
});
}
// Apply custom directives if provided
let finalSuggestion = suggestion;
if (customDirectives && customDirectives.trim()) {
// Re-run with custom directives
const enhancedRequest = {
...configRequest,
description: `${configRequest.description}\n\nCustom requirements: ${customDirectives}`
};
const customSuggestion = await suggestServerConfiguration(enhancedRequest);
if (customSuggestion) {
finalSuggestion = customSuggestion;
finalSuggestion.explanation += `\n\nApplied custom directives: ${customDirectives}`;
}
}
// Validate the suggestion
const validation = validateConfiguration(finalSuggestion, serverData.type || 'STDIO');
res.json({
fixed: true,
original: {
command: serverData.command,
args: serverData.args,
envs: serverData.envs
},
suggestion: finalSuggestion,
validation,
needsUserInput: validation.issues.some(issue => issue.includes('needs a real value') ||
issue.includes('PLACEHOLDER'))
});
}
catch (error) {
console.error('Error fixing server configuration:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// Batch fix multiple servers
router.post('/batch-fix', async (req, res) => {
try {
const { serverIds, customDirectives, userSystem } = req.body;
if (!Array.isArray(serverIds) || serverIds.length === 0) {
return res.status(400).json({
error: 'Invalid request',
message: 'Server IDs array is required'
});
}
// Limit batch size
if (serverIds.length > 20) {
return res.status(400).json({
error: 'Too many servers',
message: 'Maximum 20 servers per batch'
});
}
// Get servers that need fixing
const servers = await db.select()
.from(mcpServers)
.where(inArray(mcpServers.id, serverIds));
// Filter servers that likely need configuration
const serversNeedingFix = servers.filter(server => {
if (!server.command)
return true;
if (!server.args || server.args.length === 0)
return true;
if (server.args.length === 1 && !server.args[0].includes('/')) {
// Likely missing directory or configuration
return true;
}
return false;
});
const results = [];
let successCount = 0;
let failureCount = 0;
for (const server of serversNeedingFix) {
try {
const suggestion = await suggestServerConfiguration({
serverName: server.name,
serverType: server.serverType,
command: server.command || 'npx',
args: server.args || [],
envs: server.envs || [],
repositoryUrl: server.repositoryUrl || undefined,
description: server.description,
userSystem
});
if (suggestion) {
const validation = validateConfiguration(suggestion, server.serverType || 'STDIO');
results.push({
serverId: server.id,
serverName: server.name,
status: 'success',
suggestion,
validation
});
successCount++;
}
else {
results.push({
serverId: server.id,
serverName: server.name,
status: 'failed',
error: 'Could not generate suggestion'
});
failureCount++;
}
}
catch (error) {
results.push({
serverId: server.id,
serverName: server.name,
status: 'error',
error: error instanceof Error ? error.message : 'Unknown error'
});
failureCount++;
}
// Small delay to avoid rate limits
await new Promise(resolve => setTimeout(resolve, 100));
}
res.json({
totalServers: serverIds.length,
processedServers: serversNeedingFix.length,
skippedServers: serverIds.length - serversNeedingFix.length,
successCount,
failureCount,
results
});
}
catch (error) {
console.error('Error batch fixing servers:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// Get servers that need configuration fixes
router.get('/servers-needing-fix', async (req, res) => {
try {
const limit = Math.min(parseInt(req.query.limit) || 50, 100);
// Find servers that likely need configuration
const servers = await db.select()
.from(mcpServers)
.where(or(isNull(mcpServers.command), isNull(mcpServers.args)))
.limit(limit);
// Also find servers with potentially incomplete args
const additionalServers = await db.select()
.from(mcpServers)
.where(eq(mcpServers.serverType, 'STDIO'))
.limit(limit);
const serversNeedingFix = [
...servers,
...additionalServers.filter(server => {
if (!server.args || server.args.length === 0)
return true;
if (server.args.length === 1 && !server.args[0].includes('/')) {
// Likely missing directory or configuration
return true;
}
return false;
})
];
// Remove duplicates
const uniqueServers = Array.from(new Map(serversNeedingFix.map(s => [s.id, s])).values());
res.json({
count: uniqueServers.length,
servers: uniqueServers.map(server => ({
id: server.id,
name: server.name,
command: server.command,
args: server.args,
needsFix: !server.command || !server.args || server.args.length === 0 ||
(server.args.length === 1 && !server.args[0].includes('/'))
}))
});
}
catch (error) {
console.error('Error finding servers needing fix:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
export default router;