vibe-coder-mcp
Version:
Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.
187 lines (186 loc) ⢠9.42 kB
JavaScript
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
import express from "express";
import cors from "cors";
import dotenv from "dotenv";
import path from 'path';
import { fileURLToPath } from 'url';
import chalk from 'chalk';
import ora from 'ora';
import logger, { registerShutdownCallback } from "./logger.js";
import { initializeToolEmbeddings } from './services/routing/embeddingStore.js';
import { OpenRouterConfigManager } from './utils/openrouter-config-manager.js';
import { ToolRegistry } from './services/routing/toolRegistry.js';
import { transportManager } from './services/transport-manager/index.js';
import { PortAllocator } from './utils/port-allocator.js';
import { setupWizard } from './setup-wizard.js';
import { UnifiedSecurityConfigManager } from './tools/vibe-task-manager/security/unified-security-config.js';
const isMessageHandlingTransport = (t) => t !== null && typeof t === 'object' && 'handlePostMessage' in t && typeof t.handlePostMessage === 'function';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const envPath = path.resolve(__dirname, '../.env');
const args = process.argv.slice(2);
const useSSE = args.includes('--sse');
const showHelp = args.includes('--help') || args.includes('-h');
const runSetup = args.includes('--setup') || args.includes('--reconfigure');
if (showHelp) {
console.log(chalk.cyan.bold('\nš¤ Vibe Coder MCP Server\n'));
console.log(chalk.white('Usage: vibe-coder-mcp [options]\n'));
console.log(chalk.yellow('Options:'));
console.log(chalk.green(' --help, -h ') + chalk.gray('Show this help message'));
console.log(chalk.green(' --setup ') + chalk.gray('Run the setup wizard'));
console.log(chalk.green(' --reconfigure ') + chalk.gray('Reconfigure settings'));
console.log(chalk.green(' --sse ') + chalk.gray('Use Server-Sent Events transport'));
console.log(chalk.green(' --port <number> ') + chalk.gray('Specify port for SSE mode (default: 3000)'));
console.log('\n' + chalk.gray('For more information: https://github.com/freshtechbro/Vibe-Coder-MCP'));
process.exit(0);
}
async function initialize() {
if (runSetup || await setupWizard.isFirstRun()) {
console.log(chalk.cyan.bold('\nš Vibe Coder MCP Setup\n'));
const success = await setupWizard.run();
if (!success) {
console.log(chalk.red('\nā Setup failed. Exiting...'));
process.exit(1);
}
console.log(chalk.green('\nā
Setup complete! Starting server...\n'));
}
const dotenvResult = dotenv.config({ path: envPath });
if (dotenvResult.error) {
logger.warn({ err: dotenvResult.error, path: envPath }, `Could not load .env file from explicit path.`);
}
else {
logger.info({ path: envPath, loaded: dotenvResult.parsed ? Object.keys(dotenvResult.parsed) : [] }, `Loaded environment variables from .env file.`);
}
const spinner = ora({
text: 'Starting Vibe Coder MCP Server...',
color: 'cyan'
}).start();
try {
await startServer(spinner);
}
catch (error) {
spinner.fail('Failed to start server');
logger.error({ err: error }, 'Server startup failed');
console.log(chalk.red('\nā Server startup failed. Check logs for details.'));
process.exit(1);
}
}
async function startServer(spinner) {
const configManager = OpenRouterConfigManager.getInstance();
await configManager.initialize();
const openRouterConfig = await configManager.getOpenRouterConfig();
ToolRegistry.getInstance(openRouterConfig);
const securityManager = UnifiedSecurityConfigManager.getInstance();
securityManager.initializeFromMCPConfig(openRouterConfig);
logger.info('Unified Security Configuration initialized with centralized config');
try {
await initializeToolEmbeddings();
logger.info('Tool embeddings initialized');
}
catch (error) {
logger.error({ err: error }, 'Failed to initialize tool embeddings');
}
const { createServer } = await import("./server.js");
const mcpServer = createServer(openRouterConfig);
async function main(mcpServer) {
try {
if (useSSE) {
const app = express();
app.use(cors());
app.use(express.json());
const allocatedSsePort = transportManager.getServicePort('sse');
const port = allocatedSsePort ||
(process.env.SSE_PORT ? parseInt(process.env.SSE_PORT) : undefined) ||
(process.env.PORT ? parseInt(process.env.PORT) : 3000);
app.get('/health', (req, res) => {
res.status(200).json({ status: 'ok' });
});
app.get('/sse', (req, res) => {
const sessionId = req.query.sessionId || `sse-${Math.random().toString(36).substring(2)}`;
const transport = new SSEServerTransport('/messages', res);
req.sessionId = sessionId;
logger.info({ sessionId, transportSessionId: transport.sessionId }, 'Established SSE connection');
mcpServer.connect(transport).catch((error) => {
logger.error({ err: error }, 'Failed to connect transport');
});
});
app.post('/messages', async (req, res) => {
if (!req.body) {
return res.status(400).json({ error: 'Invalid request body' });
}
try {
const sessionId = req.query.sessionId || req.body.session_id;
if (!sessionId) {
return res.status(400).json({ error: 'Missing session ID. Establish an SSE connection first.' });
}
const transport = mcpServer.server.transport;
if (!transport) {
return res.status(400).json({ error: 'No active SSE connection' });
}
if (isMessageHandlingTransport(transport)) {
const context = {
sessionId,
transportType: sessionId === 'stdio-session' ? 'stdio' : 'sse',
timestamp: Date.now()
};
await transport.handlePostMessage(req, res, context);
}
else {
logger.error('Active transport does not support handlePostMessage or is not defined.');
if (!res.headersSent) {
res.status(500).json({ error: 'Transport does not support POST messages' });
}
}
}
catch (error) {
logger.error({ err: error }, 'Error handling POST message');
if (!res.headersSent) {
res.status(500).json({ error: 'Internal server error' });
}
}
});
const server = app.listen(port, () => {
spinner.succeed(`Vibe Coder MCP Server running on port ${port} (SSE mode)`);
console.log(chalk.green(`\nā
Server ready at: `) + chalk.cyan(`http://localhost:${port}`));
console.log(chalk.gray('Health check: ') + chalk.cyan(`http://localhost:${port}/health`));
console.log(chalk.gray('SSE endpoint: ') + chalk.cyan(`http://localhost:${port}/sse\n`));
});
registerShutdownCallback(async () => {
server.close();
await PortAllocator.cleanupOrphanedPorts();
});
}
else {
const transport = new StdioServerTransport();
await mcpServer.connect(transport);
spinner.succeed('Vibe Coder MCP Server running (stdio mode)');
console.log(chalk.green('\nā
Server ready!'));
console.log(chalk.gray('Connected via stdio transport'));
console.log(chalk.gray('Ready to receive requests from MCP client\n'));
}
}
catch (error) {
spinner.fail('Server startup failed');
logger.error({ err: error }, 'Failed to start MCP server');
throw error;
}
}
await main(mcpServer);
}
process.on('uncaughtException', (error) => {
logger.error({ err: error }, 'Uncaught exception');
console.error(chalk.red('\nā Uncaught exception:'), error);
process.exit(1);
});
process.on('unhandledRejection', (reason, promise) => {
logger.error({ err: reason, promise }, 'Unhandled rejection');
console.error(chalk.red('\nā Unhandled rejection:'), reason);
process.exit(1);
});
initialize().catch((error) => {
logger.error({ err: error }, 'Failed to initialize');
console.error(chalk.red('\nā Initialization failed:'), error);
process.exit(1);
});