UNPKG

@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

169 lines (162 loc) 6.67 kB
import * as yaml from 'js-yaml'; import { OpenAI } from 'openai'; import * as dotenv from 'dotenv'; dotenv.config(); const openai = process.env.OPENAI_API_KEY ? new OpenAI({ apiKey: process.env.OPENAI_API_KEY, }) : null; const smitheryAnalysisPrompt = `You are an expert at analyzing MCP server configurations from smithery.yml files. Your task is to: 1. Identify all required and optional arguments 2. Identify all required and optional environment variables 3. Understand the configuration schema 4. Suggest safe default values 5. Extract or infer usage examples 6. Identify security considerations Return a JSON object following the SmitheryAnalysis interface.`; export async function analyzeSmitheryConfig(smitheryContent, readmeContent) { if (!openai) { // Fallback to basic parsing without AI return basicSmitheryParsing(smitheryContent); } try { const prompt = `Analyze this smithery.yml configuration: \`\`\`yaml ${smitheryContent} \`\`\` ${readmeContent ? `\nREADME content for additional context:\n${readmeContent.substring(0, 3000)}` : ''} Provide a comprehensive analysis including: 1. What arguments are required vs optional 2. What environment variables are needed 3. Safe default values for a typical user 4. Usage examples 5. Security considerations Focus on practical, user-friendly defaults.`; const response = await openai.chat.completions.create({ model: 'gpt-4o-mini', messages: [ { role: 'system', content: smitheryAnalysisPrompt }, { role: 'user', content: prompt } ], temperature: 0.2, max_tokens: 1500, response_format: { type: 'json_object' } }); const content = response.choices[0]?.message?.content; if (!content) { throw new Error('No response from OpenAI'); } return JSON.parse(content); } catch (error) { console.error('Error analyzing smithery config with AI:', error); // Fallback to basic parsing return basicSmitheryParsing(smitheryContent); } } function basicSmitheryParsing(smitheryContent) { try { const config = yaml.load(smitheryContent); const analysis = { requiredArgs: [], optionalArgs: [], requiredEnvs: [], optionalEnvs: [], suggestedDefaults: {}, usageExamples: [], securityConsiderations: [] }; // Parse start command configuration if (config.startCommand) { const { args, env, configSchema } = config.startCommand; // Extract arguments if (args) { analysis.requiredArgs = args; } // Extract environment variables if (env) { analysis.requiredEnvs = Object.keys(env); analysis.suggestedDefaults = { ...env }; } // Parse config schema for more details if (configSchema?.properties) { analysis.configSchema = configSchema; for (const [key, schema] of Object.entries(configSchema.properties)) { if (configSchema.required?.includes(key)) { if (!analysis.requiredEnvs.includes(key)) { analysis.requiredEnvs.push(key); } } else { if (!analysis.optionalEnvs.includes(key)) { analysis.optionalEnvs.push(key); } } // Extract defaults from schema if (schema.default !== undefined) { analysis.suggestedDefaults[key] = schema.default; } else if (schema.example !== undefined) { analysis.suggestedDefaults[key] = schema.example; } // Add security considerations for sensitive fields if (key.toLowerCase().includes('key') || key.toLowerCase().includes('token') || key.toLowerCase().includes('secret')) { analysis.securityConsiderations.push(`${key} appears to be a sensitive value - ensure it's kept secure`); } } } } // Extract examples from config if available if (config.examples) { analysis.usageExamples = Array.isArray(config.examples) ? config.examples : [config.examples]; } return analysis; } catch (error) { console.error('Error parsing smithery config:', error); return null; } } // Enhanced configuration suggestion based on smithery analysis export async function suggestConfigFromSmithery(serverName, smitheryAnalysis, userContext) { const args = [...smitheryAnalysis.requiredArgs]; const envVars = {}; // Start with suggested defaults Object.assign(envVars, smitheryAnalysis.suggestedDefaults); // Customize based on user context if (userContext) { // Replace placeholders with user-specific values for (const [key, value] of Object.entries(envVars)) { if (typeof value === 'string') { envVars[key] = value .replace('$HOME', userContext.homeDir) .replace('~', userContext.homeDir) .replace('$USER', userContext.username || 'user'); } } // For filesystem-related servers, suggest safe paths if (serverName.toLowerCase().includes('filesystem') || serverName.toLowerCase().includes('file')) { if (args.length === 0 || !args.some(arg => arg.includes('/'))) { args.push(`${userContext.homeDir}/Desktop`); } } } // Ensure sensitive values are clearly marked for (const key of smitheryAnalysis.requiredEnvs) { if (!envVars[key] && (key.toLowerCase().includes('key') || key.toLowerCase().includes('token') || key.toLowerCase().includes('secret'))) { envVars[key] = `YOUR_${key.toUpperCase()}_HERE`; } } const explanation = `Configuration based on smithery.yml analysis. ` + `Required args: ${smitheryAnalysis.requiredArgs.join(', ') || 'none'}. ` + `Required environment variables: ${smitheryAnalysis.requiredEnvs.join(', ') || 'none'}.`; return { args, envVars, explanation }; }