UNPKG

synaptra

Version:

A high-performance Model Context Protocol server for GraphQL APIs with advanced features, type-safety, and developer experience improvements

194 lines 8.21 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createMcpGraphQLServer = exports.McpGraphQLServer = void 0; const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js"); const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js"); const types_js_1 = require("@modelcontextprotocol/sdk/types.js"); const types_1 = require("./types"); const graphql_client_1 = require("./services/graphql-client"); const logger_1 = require("./utils/logger"); // Import tools const introspect_schema_1 = require("./tools/introspect-schema"); const query_graphql_1 = require("./tools/query-graphql"); const analyze_query_1 = require("./tools/analyze-query"); class McpGraphQLServer { server; config; client; constructor(config) { this.config = types_1.McpConfigSchema.parse(config); // Initialize logger (0, logger_1.createLogger)({ level: this.config.logging.level, enableQueries: this.config.logging.queries, enablePerformance: this.config.logging.performance, }); // Initialize GraphQL client const clientHeaders = { ...this.config.headers }; // Add default API key to headers if provided and no Authorization header exists if (this.config.defaultApiKey && !clientHeaders.Authorization && !clientHeaders.authorization) { clientHeaders.Authorization = `Bearer ${this.config.defaultApiKey}`; } this.client = new graphql_client_1.GraphQLClientService({ url: this.config.endpoint, headers: clientHeaders, timeout: this.config.timeout, }, this.config.retries); // Initialize MCP server this.server = new index_js_1.Server({ name: this.config.name, version: '1.0.0', }, { capabilities: { tools: {}, }, }); this.setupHandlers(); } setupHandlers() { const logger = (0, logger_1.getLogger)(); // List available tools this.server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => { const tools = [introspect_schema_1.introspectSchemaTool, query_graphql_1.queryGraphQLTool, analyze_query_1.analyzeQueryTool]; logger.debug('Listing available tools', { toolCount: tools.length, tools: tools.map(t => t.name), }); return { tools }; }); // Handle tool calls this.server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; logger.info(`Tool called: ${name}`, { args }); try { switch (name) { case 'introspect-schema': const schemaResult = await (0, introspect_schema_1.handleIntrospectSchema)(args, this.client); return { content: [ { type: 'text', text: JSON.stringify(schemaResult, null, 2), }, ], }; case 'query-graphql': const queryResult = await (0, query_graphql_1.handleQueryGraphQL)(args, this.client, this.config.allowMutations, this.config.allowSubscriptions); return { content: [ { type: 'text', text: JSON.stringify(queryResult, null, 2), }, ], }; case 'analyze-query': const analysisResult = await (0, analyze_query_1.handleAnalyzeQuery)(args, this.client); return { content: [ { type: 'text', text: JSON.stringify(analysisResult, null, 2), }, ], }; default: throw new Error(`Unknown tool: ${name}`); } } catch (error) { logger.error(`Tool execution failed: ${name}`, { error: error instanceof Error ? error.message : 'Unknown error', args, }); return { content: [ { type: 'text', text: JSON.stringify({ error: error instanceof Error ? error.message : 'Unknown error', tool: name, args, }, null, 2), }, ], isError: true, }; } }); } async start() { const logger = (0, logger_1.getLogger)(); try { // Initialize schema if introspection is allowed if (this.config.security.allowIntrospection) { logger.info('Initializing GraphQL schema...'); await this.client.introspectSchema(); logger.info('Schema initialized successfully'); } // Start the server const transport = new stdio_js_1.StdioServerTransport(); await this.server.connect(transport); logger.info(`MCP GraphQL server started`, { name: this.config.name, endpoint: this.config.endpoint, allowMutations: this.config.allowMutations, allowSubscriptions: this.config.allowSubscriptions, }); } catch (error) { logger.error('Failed to start MCP server', { error: error instanceof Error ? error.message : 'Unknown error', }); throw error; } } async stop() { const logger = (0, logger_1.getLogger)(); logger.info('Stopping MCP GraphQL server...'); // Close server connection await this.server.close(); logger.info('MCP GraphQL server stopped'); } // Utility methods for configuration updates updateConfig(newConfig) { this.config = types_1.McpConfigSchema.parse({ ...this.config, ...newConfig }); // Update logger if logging config changed if (newConfig.logging) { const logger = (0, logger_1.getLogger)(); logger.updateConfig({ level: this.config.logging.level, enableQueries: this.config.logging.queries, enablePerformance: this.config.logging.performance, }); } // Update client endpoint if changed if (newConfig.endpoint || newConfig.headers || newConfig.timeout || newConfig.defaultApiKey) { const clientHeaders = { ...this.config.headers }; // Add default API key to headers if provided and no Authorization header exists if (this.config.defaultApiKey && !clientHeaders.Authorization && !clientHeaders.authorization) { clientHeaders.Authorization = `Bearer ${this.config.defaultApiKey}`; } this.client.updateEndpoint({ url: this.config.endpoint, headers: clientHeaders, timeout: this.config.timeout, }); } } getConfig() { return { ...this.config }; } getClient() { return this.client; } } exports.McpGraphQLServer = McpGraphQLServer; // Factory function for easy server creation const createMcpGraphQLServer = (config) => { return new McpGraphQLServer(config); }; exports.createMcpGraphQLServer = createMcpGraphQLServer; // Default export for convenience exports.default = McpGraphQLServer; //# sourceMappingURL=server.js.map