vibe-coder-mcp
Version:
Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.
148 lines (147 loc) • 5.73 kB
JavaScript
import { z } from 'zod';
import fs from 'fs-extra';
import path from 'path';
export class ConfigValidator {
static instance = null;
envSchema = z.object({
OPENROUTER_API_KEY: z.string().min(1, 'API key is required'),
OPENROUTER_BASE_URL: z.string().url().optional().default('https://openrouter.ai/api/v1'),
VIBE_TASK_MANAGER_READ_DIR: z.string().optional(),
VIBE_CODER_OUTPUT_DIR: z.string().optional(),
VIBE_TASK_MANAGER_SECURITY_MODE: z.enum(['strict', 'permissive']).optional().default('strict'),
CODE_MAP_ALLOWED_DIR: z.string().optional(),
GEMINI_MODEL: z.string().optional().default('google/gemini-2.5-flash-preview-05-20'),
PERPLEXITY_MODEL: z.string().optional().default('perplexity/sonar')
});
llmConfigSchema = z.object({
llm_mapping: z.record(z.string()).refine((mapping) => mapping['default_generation'] !== undefined, { message: 'default_generation mapping is required' })
});
static getInstance() {
if (!ConfigValidator.instance) {
ConfigValidator.instance = new ConfigValidator();
}
return ConfigValidator.instance;
}
async validateEnvFile(envPath) {
const result = {
valid: true,
errors: [],
warnings: [],
suggestions: []
};
try {
const envContent = await fs.readFile(envPath, 'utf-8');
const envVars = {};
envContent.split('\n').forEach(line => {
const trimmed = line.trim();
if (trimmed && !trimmed.startsWith('#')) {
const [key, ...valueParts] = trimmed.split('=');
if (key) {
envVars[key.trim()] = valueParts.join('=').trim();
}
}
});
const parseResult = this.envSchema.safeParse(envVars);
if (!parseResult.success) {
result.valid = false;
parseResult.error.errors.forEach(err => {
result.errors.push(`${err.path.join('.')}: ${err.message}`);
if (err.path[0] === 'OPENROUTER_API_KEY') {
result.suggestions.push('Get your API key at https://openrouter.ai/');
}
});
}
if (!envVars.VIBE_CODER_OUTPUT_DIR) {
result.warnings.push('VIBE_CODER_OUTPUT_DIR not set, using ./VibeCoderOutput');
}
}
catch (error) {
result.valid = false;
result.errors.push(`Failed to read env file: ${error}`);
}
return result;
}
async validateLLMConfig(config) {
const result = {
valid: true,
errors: [],
warnings: [],
suggestions: []
};
const parseResult = this.llmConfigSchema.safeParse(config);
if (!parseResult.success) {
result.valid = false;
parseResult.error.errors.forEach(err => {
result.errors.push(err.message);
});
result.suggestions.push('Ensure llm_config.json has a default_generation mapping');
}
else {
const recommended = [
'task_decomposition',
'intent_recognition',
'research_query'
];
const mappings = parseResult.data.llm_mapping;
recommended.forEach(key => {
if (!mappings[key]) {
result.warnings.push(`Missing recommended mapping: ${key}`);
result.suggestions.push(`Add ${key} mapping for better performance`);
}
});
}
return result;
}
async detectMissingConfigs() {
const missing = [];
const requiredConfigs = [
{
file: '.env',
required: true,
description: 'Environment variables including API key',
defaultPath: path.join(process.cwd(), '.env')
},
{
file: 'llm_config.json',
required: true,
description: 'LLM model mappings for different operations',
defaultPath: path.join(process.cwd(), 'llm_config.json')
},
{
file: 'mcp-config.json',
required: false,
description: 'MCP tool configurations',
defaultPath: path.join(process.cwd(), 'mcp-config.json')
}
];
for (const config of requiredConfigs) {
const exists = await fs.pathExists(config.defaultPath);
if (!exists) {
missing.push(config);
}
}
return missing;
}
suggestFixes(issues) {
const fixes = [];
issues.forEach(issue => {
if (issue.suggestedFix) {
fixes.push(issue.suggestedFix);
}
else {
switch (issue.field) {
case 'OPENROUTER_API_KEY':
fixes.push('1. Visit https://openrouter.ai/ to get an API key');
fixes.push('2. Add OPENROUTER_API_KEY=your_key to .env file');
break;
case 'llm_mapping.default_generation':
fixes.push('Add "default_generation": "google/gemini-2.5-flash-preview-05-20" to llm_config.json');
break;
default:
fixes.push(`Check ${issue.field} configuration`);
}
}
});
return [...new Set(fixes)];
}
}