Skip to main content
Glama
server.ts12.2 kB
#!/usr/bin/env node import express from 'express'; import cors from 'cors'; import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js"; import dotenv from 'dotenv'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import { dynamicTools } from "./dynamic-tools"; import { supportedTools } from './mcp/allToolList.js'; import {getToolByCategory} from './toolRegistry' import { CategoryEndpoints } from './toolRegistry'; dotenv.config(); const API_EXECUTE_ENDPOINT = process.env.API_EXECUTE_ENDPOINT || 'https://hive-proxy-api-84541061662.asia-south1.run.app/api/execute'//'https://dev.hiveintelligence.xyz/api/execute'; function getDynamicTools(){ const allToolEndpoints = supportedTools const allTools = dynamicTools(allToolEndpoints) // const tools = [ // ...allTools.map(endpoint => ( // endpoint.tool // )) // ] const tools = [ ...allTools.map(endpoint => { const tool = endpoint.tool; // Ensure the tool has the required MCP structure if (!tool || typeof tool !== 'object') { console.error('Invalid tool structure:', tool); return null; } // Validate required fields if (!tool.name || typeof tool.name !== 'string') { console.error('Tool missing name:', tool); return null; } if (!tool.inputSchema || typeof tool.inputSchema !== 'object') { console.error('Tool missing inputSchema:', tool); return null; } return tool; }).filter(Boolean) // Remove null entries ] return { allTools, tools } } function staticTools(category:number){ const allToolEndpoints = getToolByCategory(category) return { allTools:[], tools: allToolEndpoints } } export class HiveMCPServer { private server: Server; private allTools:any; private tools: any; private isDynamicTools: boolean; // currently dynamic tools include all categories private category: number; // category of tool (refer tool registry) it will be null for dynamic tools constructor(_isDynamicTools:boolean, _category:number) { this.isDynamicTools = _isDynamicTools this.category = _category this.server = new Server( { name: "hive-mcp-server", version: "0.1.0", }, { capabilities: { tools: {}, }, } ); if(this.isDynamicTools){ const { allTools, tools } = getDynamicTools(); this.allTools = allTools; this.tools = tools; } else{ const { allTools, tools } = staticTools(this.category); this.allTools = allTools; this.tools = tools; } this.setupToolHandlers(); } private async dynamicToolsHandler(request:any){ const highLevelToolUsed = this.allTools.find( // @ts-ignore tool => tool.tool.name === request.params.name ); if(request.params.name == "call_api_endpoint"){ const toolName:any= request.params.arguments?.endpoint_name try { // Call the API server's /execute endpoint const response = await fetch(API_EXECUTE_ENDPOINT, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ toolName: toolName, arguments: request.params.arguments?.args }) }); const result = await response.json(); if (!response.ok) { return { content: [ { type: "text", text: `Error executing hive tool: ${result.error || 'Request failed'}`, }, ], }; } return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], }; } catch (error) { return { content: [ { type: "text", text: `Error executing hive tool: ${error}`, }, ], }; } } else { try { // @ts-ignore const result = await highLevelToolUsed?.handler( request.params.arguments ); return result || { content: [ { type: "text", text: "No result returned from tool handler", }, ], }; } catch (error) { // Track failed tool usage return { content: [ { type: "text", text: `Error executing hive tool: ${error}`, }, ], }; } } } private async staticToolsHandler(request:any){ const toolName:any= request.params.name console.log("toolName ", toolName) try { // Call the API server's /execute endpoint const response = await fetch(API_EXECUTE_ENDPOINT, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ toolName: toolName, arguments: request.params.arguments }) }); const result = await response.json(); if (!response.ok) { return { content: [ { type: "text", text: `Error executing hive tool: ${result.error || 'Request failed'}`, }, ], }; } return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], }; } catch (error) { return { content: [ { type: "text", text: `Error executing hive tool: ${error}`, }, ], }; } } private setupToolHandlers() { this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: this.tools as Tool }; }); this.server.setRequestHandler(CallToolRequestSchema, this.isDynamicTools ? this.dynamicToolsHandler.bind(this) : this.staticToolsHandler.bind(this) ); } getServer() { return this.server; } } async function main() { console.error("Starting MCP server in stdio mode..."); const isDynamicTools = true; const category = 0; const transport = new StdioServerTransport(); const server = new HiveMCPServer(isDynamicTools, category); await server.getServer().connect(transport); console.error("MCP server is running in stdio mode..."); } main().catch((error) => { console.error("Server error:", error); process.exit(1); }); // // Factory function to create server instances - following doc pattern // const getServer = (isDynamicTools:boolean, category:number) => { // return new HiveMCPServer(isDynamicTools, category); // }; // // Create Express app // const app = express(); // const port = process.env.PORT || 8080; // // Add CORS middleware before your MCP routes - as per doc // app.use(cors({ // origin: '*', // Or your specific frontend origins in prod // exposedHeaders: ['Mcp-Session-Id'], // allowedHeaders: ['Content-Type', 'mcp-session-id', 'Authorization'], // })); // // Parse JSON bodies - Don't use globally as mcpAuthRouter handles its own parsing // // app.use(express.json()); // // Health check endpoint (accessible at /mcp/ping) // app.get('/ping', (req, res) => { // res.json({ // status: 'ok', // message: 'Hive MCP Server is running', // version: '0.1.0', // endpoints: { // health: '/mcp/ping', // mcp: '/mcp' // } // }); // }); // // POST: Stateless handler - Following exact doc pattern (accessible at /mcp) // // Apply rate limiting after auth: 20 requests per minute per user (based on sub claim) // const mcpMiddlewares = [ // express.json({ // limit: '10mb', // Prevent large payload DoS attacks // type: 'application/json' // }), // ]; // app.post('/mcp/', ...mcpMiddlewares, async (req: express.Request, res: express.Response) => { // try { // const serverInstance = getServer(true, 0); // const server = serverInstance.getServer(); // const transport: StreamableHTTPServerTransport = new StreamableHTTPServerTransport({ // sessionIdGenerator: undefined, // }); // res.on('close', () => { // console.log('Request closed'); // transport.close(); // server.close(); // }); // await server.connect(transport); // await transport.handleRequest(req, res, req.body); // } catch (error) { // console.error('Error handling MCP request:', error); // if (!res.headersSent) { // res.status(500).json({ // jsonrpc: '2.0', // error: { // code: -32603, // message: 'Internal server error', // }, // id: null, // }); // } // } // }); // // Dynamically create category-specific endpoints // Object.entries(CategoryEndpoints).forEach(([categoryIndex, endpointPath]) => { // const index = parseInt(categoryIndex); // app.post(endpointPath, ...mcpMiddlewares, async (req: express.Request, res: express.Response) => { // try { // const serverInstance = getServer(false, index); // const server = serverInstance.getServer(); // const transport: StreamableHTTPServerTransport = new StreamableHTTPServerTransport({ // sessionIdGenerator: undefined, // }); // res.on('close', () => { // console.log(`Request closed for ${endpointPath}`); // transport.close(); // server.close(); // // Ensure analytics are flushed when request closes // }); // await server.connect(transport); // await transport.handleRequest(req, res, req.body); // } catch (error) { // console.error(`Error handling MCP request for ${endpointPath}:`, error); // if (!res.headersSent) { // res.status(500).json({ // jsonrpc: '2.0', // error: { // code: -32603, // message: 'Internal server error', // }, // id: null, // }); // } // } // }); // }); // app.get('/mcp/', async (req: express.Request, res: express.Response) => { // console.log('Received GET MCP request'); // res.writeHead(405).end(JSON.stringify({ // jsonrpc: "2.0", // error: { // code: -32000, // message: "Method not allowed." // }, // id: null // })); // }); // app.delete('/mcp/', async (req: express.Request, res: express.Response) => { // console.log('Received DELETE MCP request'); // res.writeHead(405).end(JSON.stringify({ // jsonrpc: "2.0", // error: { // code: -32000, // message: "Method not allowed." // }, // id: null // })); // }); // // Root health check for the service itself (optional, for container health checks) // app.get('/', (req, res) => { // res.json({ // status: 'ok', // message: 'Hive MCP Service is running', // version: '0.1.0', // note: 'Main service available at /mcp/' // }); // }); // // Setup and start server - Following doc pattern // const setupServer = async () => { // // Any setup logic here // console.log('Setting up Hive MCP server...'); // }; // // Start the server - Following exact doc pattern // // const PORT = port; // // setupServer().then(() => { // // app.listen(PORT, (error?: any) => { // // if (error) { // // console.error('Failed to start server:', error); // // process.exit(1); // // } // // console.log(`MCP Stateless Streamable HTTP Server listening on port ${PORT}`); // // }); // // }).catch(error => { // // console.error('Failed to set up the server:', error); // // process.exit(1); // // }); // // export default app;

Implementation Reference

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/hive-intel/hive-crypto-mcp'

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