Skip to main content
Glama

SAP OData to MCP Server

by Raistlin82
mcp-server.ts7.59 kB
#!/usr/bin/env node // Core MCP server implementation for SAP BTP integration // Provides MCP (Model Context Protocol) server with SAP OData service discovery and tool registry import { HierarchicalSAPToolRegistry } from './tools/hierarchical-tool-registry.js'; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; import { randomUUID } from "node:crypto"; import 'dotenv/config'; import { DestinationService } from './services/destination-service.js'; import { SAPClient } from './services/sap-client.js'; import { Logger } from './utils/logger.js'; import { Config } from './utils/config.js'; import { ErrorHandler } from './utils/error-handler.js'; import { ODataService } from './types/sap-types.js'; import { TokenStore } from './services/token-store.js'; /** * Main MCP server class that orchestrates SAP BTP integration * Handles service discovery, tool registration, and protocol communication */ export class MCPServer { private mcpServer: McpServer; // Core MCP protocol server private sapClient: SAPClient; // SAP system communication client private logger: Logger; // Centralized logging service private discoveredServices: ODataService[] = []; // Cache of discovered SAP services private toolRegistry: HierarchicalSAPToolRegistry; // Dynamic tool registry for SAP operations /** * Initialize MCP server with SAP service discovery and authentication * @param discoveredServices - Pre-discovered SAP OData services * @param tokenStore - Optional token storage for authentication * @param authServerUrl - Optional authentication server URL */ constructor( discoveredServices: ODataService[], tokenStore?: TokenStore, authServerUrl?: string ) { this.logger = new Logger('mcp-server'); const config = new Config(); // Initialize SAP connectivity components const destinationService = new DestinationService(this.logger, config); this.sapClient = new SAPClient(destinationService, this.logger); // Store discovered services for tool generation this.discoveredServices = discoveredServices; // Create core MCP server instance this.mcpServer = new McpServer({ name: "btp-sap-odata-to-mcp-server", version: "2.0.0" }); // Configure global error handling this.mcpServer.server.onerror = (error) => { this.logger.error('MCP Server Error:', error); ErrorHandler.handle(error); }; // Initialize hierarchical tool registry for dynamic SAP tool creation this.toolRegistry = new HierarchicalSAPToolRegistry( this.mcpServer, this.sapClient, this.logger, this.discoveredServices, tokenStore, authServerUrl ); } async initialize(): Promise<void> { try { this.toolRegistry.registerServiceMetadataResources(); await this.toolRegistry.registerDiscoveryTools(); this.logger.info('🔧 Registered MCP tools for SAP operations'); } catch (error) { this.logger.error('❌ Failed to initialize server:', error); throw error; } } async connectStdio(): Promise<void> { const transport = new StdioServerTransport(); await this.mcpServer.connect(transport); this.logger.info('📡 Connected to stdio transport'); } createHTTPTransport(options?: { enableDnsRebindingProtection?: boolean; allowedHosts?: string[]; }): StreamableHTTPServerTransport { return new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), enableDnsRebindingProtection: options?.enableDnsRebindingProtection || true, allowedHosts: options?.allowedHosts || ['127.0.0.1', 'localhost'] }); } getServer(): McpServer { return this.mcpServer; } getToolRegistry(): HierarchicalSAPToolRegistry { return this.toolRegistry; } } export async function createMCPServer( discoveredServices: ODataService[], tokenStore?: TokenStore, authServerUrl?: string ): Promise<MCPServer> { const server = new MCPServer(discoveredServices, tokenStore, authServerUrl); await server.initialize(); return server; } export async function runStdioServer( discoveredServices: ODataService[], tokenStore?: TokenStore, authServerUrl?: string ): Promise<void> { const logger = new Logger('sap-mcp-server'); try { const server = await createMCPServer(discoveredServices, tokenStore, authServerUrl); await server.connectStdio(); logger.info('SAP MCP Server running on stdio...'); } catch (error) { logger.error('Failed to start SAP MCP Server:', error); process.exit(1); } } // Entry point when file is executed directly if (import.meta.url === `file://${process.argv[1]}`) { const logger = new Logger('sap-mcp-server'); logger.info('🚀 Starting SAP MCP Server in stdio mode with PRODUCTION authentication...'); // Import necessary services for authentication import('./services/destination-service.js').then(({ DestinationService }) => { return import('./services/sap-client.js'); }).then(({ SAPClient }) => { return import('./utils/config.js'); }).then(({ Config }) => { return import('./services/token-store.js'); }).then(({ TokenStore }) => { const config = new Config(); // Initialize TokenStore for SAP authentication const tokenStore = new TokenStore(); // Get AuthServer URL from environment variables const authServerUrl = process.env.AUTH_SERVER_URL || `http://localhost:${process.env.AUTH_PORT || 3001}`; logger.info(`🔐 Initializing SAP Authentication:`); logger.info(` - TokenStore: ${!!tokenStore}`); logger.info(` - AuthServerUrl: ${authServerUrl}`); logger.info(` - SAP_IAS_URL: ${process.env.SAP_IAS_URL || 'NOT SET'}`); logger.info(` - SAP_IAS_CLIENT_ID: ${process.env.SAP_IAS_CLIENT_ID ? 'SET' : 'NOT SET'}`); // Discover SAP services using authenticated client const destinationService = new DestinationService(logger, config); const sapClient = new SAPClient(destinationService, logger); // Initialize service discovery return sapClient.discoverServices().then((discoveredServices) => { logger.info(`🔍 Discovered ${discoveredServices.length} SAP services for authenticated access`); // Start MCP server with full authentication return runStdioServer(discoveredServices, tokenStore, authServerUrl); }); }).catch((error) => { logger.error('❌ Failed to initialize SAP Authentication:', error); logger.info('🔄 Falling back to basic mode without authentication...'); // Fallback: start without authentication for development const discoveredServices: ODataService[] = []; runStdioServer(discoveredServices).catch((fallbackError) => { logger.error('Failed to start MCP server even in fallback mode:', fallbackError); 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/Raistlin82/btp-sap-odata-to-mcp-server-optimized'

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