Skip to main content
Glama
2389-research

MCP Agent Social Media Server

index.ts7.33 kB
// ABOUTME: Main entry point for the MCP Agent Social Media Server // ABOUTME: Initializes and starts the MCP server with social media tools import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { ApiClient } from './api-client.js'; import { ENV_KEYS, config, validateConfig, version } from './config.js'; import { hooksManager } from './hooks/index.js'; import { HttpMcpServer } from './http-server.js'; import { logger } from './logger.js'; import { metrics } from './metrics.js'; import { registerPrompts } from './prompts/index.js'; import { registerResources } from './resources/index.js'; import { registerRoots } from './roots/index.js'; import { SessionManager } from './session-manager.js'; import { registerTools } from './tools/index.js'; // Initialize shared components const sessionManager = new SessionManager(); const apiClient = new ApiClient(); // Server instances let mcpServer: McpServer | null = null; let httpServer: HttpMcpServer | null = null; // Store cleanup interval globally for shutdown let cleanupInterval: ReturnType<typeof setInterval> | null = null; let keepAliveInterval: ReturnType<typeof setInterval> | null = null; async function main() { try { // Log startup information logger.info('MCP Server starting', { nodeVersion: process.version, platform: process.platform, pid: process.pid, logLevel: process.env[ENV_KEYS.LOG_LEVEL] || 'INFO', }); validateConfig(); // Log configuration (without sensitive data) logger.info(`Starting MCP server for team: ${config.teamName}`); logger.debug('Configuration loaded', { baseUrl: config.socialApiBaseUrl, teamName: config.teamName, timeout: config.apiTimeout, logLevel: config.logLevel, }); // Determine transport mode const transportMode = process.env[ENV_KEYS.MCP_TRANSPORT] || 'stdio'; if (transportMode === 'http') { // HTTP mode const httpPort = Number.parseInt(process.env[ENV_KEYS.MCP_HTTP_PORT] || '3000', 10); const httpHost = process.env[ENV_KEYS.MCP_HTTP_HOST] || 'localhost'; logger.info('Starting in HTTP mode', { port: httpPort, host: httpHost }); httpServer = new HttpMcpServer(sessionManager, apiClient, { port: httpPort, host: httpHost, enableJsonResponse: process.env[ENV_KEYS.MCP_ENABLE_JSON] === 'true', corsOrigin: process.env[ENV_KEYS.MCP_CORS_ORIGIN] || '*', }); await httpServer.start(); } else { // Stdio mode (default) logger.info('Starting in stdio mode'); mcpServer = new McpServer({ name: 'mcp-agent-social', version, }); const transport = new StdioServerTransport(); // Register all capabilities registerTools(mcpServer, { sessionManager, apiClient, hooksManager }); registerResources(mcpServer, { apiClient, sessionManager, hooksManager }); registerPrompts(mcpServer, { apiClient, sessionManager, hooksManager }); registerRoots(mcpServer, { apiClient, sessionManager, hooksManager }); // Connect to transport logger.debug('Connecting server to transport'); await mcpServer.connect(transport); logger.info('Server connected successfully'); } // The transport itself doesn't expose error handlers, but we can handle stdio events process.stdin.on('error', (error) => { logger.error('Stdin error', { error: error.message, stack: error.stack }); shutdown('STDIN_ERROR'); }); process.stdout.on('error', (error) => { logger.error('Stdout error', { error: error.message, stack: error.stack }); shutdown('STDOUT_ERROR'); }); process.stdin.on('close', () => { logger.warn('Stdin closed unexpectedly'); shutdown('STDIN_CLOSE'); }); process.stdin.on('end', () => { logger.warn('Stdin ended'); shutdown('STDIN_END'); }); logger.info('MCP Agent Social Server running', { toolsCount: 3, resourcesCount: 6, promptsCount: 8, rootsEnabled: true, hooksCount: hooksManager.getAllHooks().length, transport: 'stdio', }); // Set up graceful shutdown process.on('SIGINT', () => shutdown('SIGINT')); process.on('SIGTERM', () => shutdown('SIGTERM')); // Handle uncaught errors to prevent sudden crashes process.on('uncaughtException', (error) => { // Prevent infinite loops when logging EPIPE errors if (error instanceof Error && 'code' in error && error.code === 'EPIPE') { // Don't try to log EPIPE errors - just shutdown silently shutdown('UNCAUGHT_EXCEPTION'); return; } logger.error('Uncaught exception', { error: error.message, stack: error.stack }); shutdown('UNCAUGHT_EXCEPTION'); }); process.on('unhandledRejection', (reason, promise) => { // Prevent infinite loops when rejection is due to EPIPE errors if (reason instanceof Error && 'code' in reason && reason.code === 'EPIPE') { // Don't try to log EPIPE-related rejections - just shutdown silently shutdown('UNHANDLED_REJECTION'); return; } logger.error('Unhandled rejection', { reason, promise }); shutdown('UNHANDLED_REJECTION'); }); // Set up periodic session cleanup (every 30 minutes) cleanupInterval = setInterval(async () => { const removed = await sessionManager.cleanupOldSessions(3600000); // 1 hour if (removed > 0) { logger.info(`Cleaned up ${removed} old sessions`); } }, 1800000); // 30 minutes // Set up keepalive to prevent connection timeout keepAliveInterval = setInterval(() => { logger.debug('Keepalive ping', { uptime: process.uptime() }); }, 30000); // Every 30 seconds } catch (error) { logger.error('Failed to start server', { error: error instanceof Error ? error.message : String(error), stack: error instanceof Error ? error.stack : undefined, }); process.exit(1); } } async function shutdown(signal: string) { logger.serverShutdown(signal); logger.warn(`Received ${signal}, shutting down gracefully...`); // Clear intervals if (cleanupInterval) { clearInterval(cleanupInterval); } if (keepAliveInterval) { clearInterval(keepAliveInterval); } // Shutdown metrics collector metrics.shutdown(); // Clean up sessions const sessionCount = sessionManager.getSessionCount(); if (sessionCount > 0) { logger.info(`Cleaning up ${sessionCount} active sessions...`); await sessionManager.clearAllSessions(); } // Close server try { if (httpServer) { await httpServer.stop(); logger.info('HTTP server closed successfully'); } else if (mcpServer) { await mcpServer.close(); logger.info('MCP server closed successfully'); } } catch (error) { logger.error('Error closing server', { error }); } // Exit with appropriate code const exitCode = signal === 'UNCAUGHT_EXCEPTION' || signal === 'UNHANDLED_REJECTION' ? 1 : 0; process.exit(exitCode); } main().catch((error) => { // Write to stderr to avoid polluting JSON-RPC stream in stdio mode process.stderr.write(`Unhandled error: ${error}\n`); process.exit(1); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/2389-research/mcp-socialmedia'

If you have feedback or need assistance with the MCP directory API, please join our Discord server