Skip to main content
Glama
index.ts8.15 kB
#!/usr/bin/env node import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from '@modelcontextprotocol/sdk/types.js'; import { z } from "zod"; import { getToolDefinitions, executeTool } from './tools/index.js'; import { MewsAuthConfigSchema, type MewsAuthConfig } from './types/auth.js'; // Configuration schema for Smithery export const configSchema = z.object({ clientToken: z.string().describe("Mews API client token"), accessToken: z.string().describe("Mews API access token"), client: z.string().default("mews-mcp").describe("Client identifier for API requests"), baseUrl: z.string().default("https://api.mews.com").describe("Mews API base URL"), }); // Load configuration from environment variables or config object function loadConfig(configOverride?: z.infer<typeof configSchema>): MewsAuthConfig { try { console.error('[DEBUG] Loading configuration...'); console.error('[DEBUG] Environment variables:', { MEWS_CLIENT_TOKEN: process.env.MEWS_CLIENT_TOKEN ? '***SET***' : 'NOT_SET', MEWS_ACCESS_TOKEN: process.env.MEWS_ACCESS_TOKEN ? '***SET***' : 'NOT_SET', MEWS_CLIENT: process.env.MEWS_CLIENT || 'NOT_SET', MEWS_BASE_URL: process.env.MEWS_BASE_URL || 'NOT_SET' }); console.error('[DEBUG] Config override:', configOverride ? 'PROVIDED' : 'NOT_PROVIDED'); // Use config override from Smithery if provided, otherwise use environment variables const config = MewsAuthConfigSchema.parse({ clientToken: configOverride?.clientToken || process.env.MEWS_CLIENT_TOKEN, accessToken: configOverride?.accessToken || process.env.MEWS_ACCESS_TOKEN, client: configOverride?.client || process.env.MEWS_CLIENT || 'mews-mcp/1.0.0', baseUrl: configOverride?.baseUrl || process.env.MEWS_BASE_URL || 'https://api.mews.com', }); console.error('[DEBUG] Configuration loaded successfully'); return config; } catch (error) { console.error('[ERROR] Failed to load configuration:', error); throw new McpError( ErrorCode.InvalidRequest, `Invalid Mews configuration. Please set MEWS_CLIENT_TOKEN, MEWS_ACCESS_TOKEN environment variables. ${error}` ); } } // Helper function to convert JSON schema properties to Zod schema function convertToZodSchema(properties: Record<string, any>): Record<string, any> { const zodProperties: Record<string, any> = {}; for (const [key, prop] of Object.entries(properties)) { const propDef = prop as any; let zodType: any = z.string(); if (propDef.type === 'number') { zodType = z.number(); } else if (propDef.type === 'boolean') { zodType = z.boolean(); } else if (propDef.type === 'array') { zodType = z.array(z.any()); } else if (propDef.type === 'object') { zodType = z.object({}).passthrough(); } if (propDef.description) { zodType = zodType.describe(propDef.description); } // Handle optional properties if (!propDef.required) { zodType = zodType.optional(); } zodProperties[key] = zodType; } return zodProperties; } // Reusable function to initialize MCP server (for both Smithery and stdio modes) function initServer(configOverride?: z.infer<typeof configSchema>) { return { smitheryServer: () => { // Create a new MCP server per MCP spec for Smithery const server = new McpServer({ name: "mews-mcp", version: "1.0.0", }); // Get tool definitions and add each tool to the server const toolDefinitions = getToolDefinitions(); for (const tool of toolDefinitions) { // Convert MCP tool definition to McpServer format const inputSchema = tool.inputSchema; let properties: Record<string, any> = {}; if (inputSchema?.properties) { properties = convertToZodSchema(inputSchema.properties); } server.tool( tool.name, tool.description || "", properties, async (args) => { try { const mewsConfig = loadConfig(configOverride); const result = await executeTool(tool.name, mewsConfig, args); return result; } catch (error) { if (error instanceof McpError) { throw error; } throw new McpError( ErrorCode.InternalError, `Tool execution failed: ${error instanceof Error ? error.message : 'Unknown error'}` ); } } ); } return server.server; }, stdioServer: async () => { console.error('[DEBUG] Initializing stdio server...'); const server = new Server( { name: 'mews-mcp', version: '1.0.0', }, { capabilities: { tools: {}, }, } ); console.error('[DEBUG] Setting up request handlers...'); // Set up request handlers for stdio mode server.setRequestHandler(ListToolsRequestSchema, async () => { console.error('[DEBUG] Handling ListTools request'); try { const tools = getToolDefinitions(); console.error(`[DEBUG] Returning ${tools.length} tools`); return { tools }; } catch (error) { console.error('[ERROR] Failed to get tool definitions:', error); throw error; } }); server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args = {} } = request.params; console.error(`[DEBUG] Handling CallTool request for: ${name}`); try { const config = loadConfig(configOverride); const result = await executeTool(name, config, args); console.error(`[DEBUG] Tool ${name} executed successfully`); return result; } catch (error) { console.error(`[ERROR] Tool ${name} execution failed:`, error); if (error instanceof McpError) { throw error; } throw new McpError( ErrorCode.InternalError, `Tool execution failed: ${error instanceof Error ? error.message : 'Unknown error'}` ); } }); console.error('[DEBUG] Creating transport and connecting...'); const transport = new StdioServerTransport(); await server.connect(transport); console.error('[INFO] Mews MCP server running on stdio'); } }; } // Smithery default export - creates an MCP server with the given config export default function ({ config }: { config: z.infer<typeof configSchema> }) { return initServer(config).smitheryServer(); } // Check if we should run stdio server (only when running directly, not as module) async function main() { console.error('[DEBUG] Main function called'); console.error('[DEBUG] process.argv[1]:', process.argv[1]); console.error('[DEBUG] process.argv:', process.argv); // Only run stdio server if this file is the main entry point // This prevents conflicts when the file is imported as a module // For CommonJS builds, we check argv instead of import.meta.url const isMainModule = process.argv[1]?.endsWith('index.js') || process.argv[1]?.endsWith('dist/index.js') || process.argv[1]?.endsWith('src/index.ts'); if (isMainModule) { console.error('[DEBUG] Running as main module - starting stdio server...'); await initServer().stdioServer(); } else { console.error('[DEBUG] Running as imported module - skipping stdio server initialization'); } } // Only run main if this is not being imported as a module if (typeof process !== 'undefined' && process.argv) { main().catch((error) => { console.error('[ERROR] Server failed to start:', error); 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/code-rabi/mews-mcp'

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