Skip to main content
Glama

Open Search MCP

by flyanima
MIT License
2
  • Apple
  • Linux
dual-mode-server.ts6.74 kB
import { OpenSearchMCPServer } from '../index.js'; import { HTTPAPIServer } from './http-server.js'; import { APIConfigManager } from './api-config.js'; import { defaultLogger as logger } from '../utils/logger.js'; export type ServerMode = 'mcp' | 'api' | 'dual'; export interface DualModeConfig { mode: ServerMode; enableMCP: boolean; enableAPI: boolean; } export class DualModeServer { private mcpServer: OpenSearchMCPServer; private httpServer?: HTTPAPIServer; private config: DualModeConfig; constructor() { this.config = this.loadConfig(); this.mcpServer = new OpenSearchMCPServer(); } private loadConfig(): DualModeConfig { const mode = (process.env.SERVER_MODE || 'mcp') as ServerMode; const config: DualModeConfig = { mode, enableMCP: mode === 'mcp' || mode === 'dual', enableAPI: mode === 'api' || mode === 'dual' }; logger.info('Dual Mode Server Configuration:', config); return config; } public async start(): Promise<void> { try { logger.info(`Starting Open Search Server in ${this.config.mode} mode...`); // Always initialize the MCP server components first await this.mcpServer.initialize(); logger.info('Checking API configuration:', { enableAPI: this.config.enableAPI, mode: this.config.mode }); if (this.config.enableAPI) { logger.info('Starting HTTP API server...'); await this.startHTTPAPI(); logger.info('HTTP API server startup completed'); } else { logger.info('API mode disabled, skipping HTTP server startup'); } if (this.config.enableMCP) { logger.info('Starting MCP mode with stdio transport...'); await this.startMCPMode(); } logger.info('Open Search Server started successfully'); // Keep the process running this.setupGracefulShutdown(); } catch (error) { logger.error('Failed to start server:', error); process.exit(1); } } private async startHTTPAPI(): Promise<void> { try { const apiConfig = APIConfigManager.getInstance().getConfig(); this.httpServer = new HTTPAPIServer(this.mcpServer, apiConfig); await this.httpServer.start(); logger.info('HTTP API Server started successfully'); logger.info('API Documentation available at: http://localhost:' + apiConfig.port + '/api/info'); if (apiConfig.apiKeys.length === 1 && apiConfig.apiKeys[0].startsWith('dev-key-')) { logger.warn('Using development API key. Set API_KEYS environment variable for production.'); logger.info('Development API Key:', apiConfig.apiKeys[0]); } } catch (error) { logger.error('Failed to start HTTP API server:', error); throw error; } } private async startMCPMode(): Promise<void> { try { // Import StdioServerTransport here to avoid circular dependencies const { StdioServerTransport } = await import('@modelcontextprotocol/sdk/server/stdio.js'); // Start the MCP server with stdio transport const transport = new StdioServerTransport(); await this.mcpServer.getServer().connect(transport); logger.info('MCP Server connected to stdio transport'); if (this.config.mode === 'mcp') { // In pure MCP mode, keep the process alive - wait for shutdown signal await new Promise<void>((resolve) => { const shutdown = () => { this.mcpShutdown().finally(() => resolve()); }; process.on('SIGINT', shutdown); process.on('SIGTERM', shutdown); }); } else { // In dual mode, don't block - let the process continue logger.info('MCP Server ready (dual mode - non-blocking)'); } } catch (error) { logger.error('Failed to start MCP mode:', error); throw error; } } private async mcpShutdown(): Promise<void> { logger.info('Shutting down MCP Server...'); // Delegate to the MCP server's shutdown method // Note: We can't call the private method directly, so we'll just log process.exit(0); } private setupGracefulShutdown(): void { const shutdown = async (signal: string) => { logger.info(`Received ${signal}. Shutting down gracefully...`); try { if (this.mcpServer) { await this.mcpServer.shutdown(); } logger.info('Server shutdown complete'); process.exit(0); } catch (error) { logger.error('Error during shutdown:', error); process.exit(1); } }; // Only set up these listeners if not in MCP mode (to avoid duplicates) if (this.config.mode !== 'mcp') { process.on('SIGINT', () => shutdown('SIGINT')); process.on('SIGTERM', () => shutdown('SIGTERM')); } // Handle uncaught exceptions (only once) if (process.listenerCount('uncaughtException') === 0) { process.on('uncaughtException', (error) => { logger.error('Uncaught Exception:', error); process.exit(1); }); } if (process.listenerCount('unhandledRejection') === 0) { process.on('unhandledRejection', (reason, promise) => { logger.error(`Unhandled Rejection at: ${promise}, reason: ${reason}`); process.exit(1); }); } } public getServerInfo(): any { return { mode: this.config.mode, mcpEnabled: this.config.enableMCP, apiEnabled: this.config.enableAPI, mcpTools: this.mcpServer ? this.mcpServer.getRegisteredToolsCount() : 0, apiEndpoint: this.httpServer ? `http://localhost:${APIConfigManager.getInstance().getConfig().port}` : null }; } } // CLI usage information export const CLI_USAGE = ` Open Search MCP Server - Dual Mode Usage: npm start # Start in MCP mode (default) npm run start:api # Start in HTTP API mode npm run start:dual # Start in dual mode (both MCP and API) Environment Variables: SERVER_MODE=mcp|api|dual # Server mode (default: mcp) API_PORT=3000 # HTTP API port API_HOST=0.0.0.0 # HTTP API host API_KEYS=key1,key2 # Comma-separated API keys Examples: # MCP mode for Claude Desktop integration SERVER_MODE=mcp npm start # API mode for HTTP integration SERVER_MODE=api API_PORT=8080 API_KEYS=my-secret-key npm start # Dual mode for both MCP and API SERVER_MODE=dual API_PORT=3000 API_KEYS=key1,key2 npm start `; // Main entry point when run directly if (import.meta.url === `file://${process.argv[1]}`) { const server = new DualModeServer(); server.start().catch((error) => { logger.error('Failed to start server:', error); process.exit(1); }); }

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/flyanima/open-search-mcp'

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