MCP ABAP ADT

by mario-andreschak
Verified
  • src
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from '@modelcontextprotocol/sdk/types.js'; import path from 'path'; import dotenv from 'dotenv'; // Import handler functions import { handleGetProgram } from './handlers/handleGetProgram'; import { handleGetClass } from './handlers/handleGetClass'; import { handleGetFunctionGroup } from './handlers/handleGetFunctionGroup'; import { handleGetFunction } from './handlers/handleGetFunction'; import { handleGetTable } from './handlers/handleGetTable'; import { handleGetStructure } from './handlers/handleGetStructure'; import { handleGetTableContents } from './handlers/handleGetTableContents'; import { handleGetPackage } from './handlers/handleGetPackage'; import { handleGetInclude } from './handlers/handleGetInclude'; import { handleGetTypeInfo } from './handlers/handleGetTypeInfo'; import { handleGetInterface } from './handlers/handleGetInterface'; import { handleGetTransaction } from './handlers/handleGetTransaction'; import { handleSearchObject } from './handlers/handleSearchObject'; // Import shared utility functions and types import { getBaseUrl, getAuthHeaders, createAxiosInstance, makeAdtRequest, return_error, return_response } from './lib/utils'; // Load environment variables from .env file dotenv.config({ path: path.resolve(__dirname, '../.env') }); // Interface for SAP configuration export interface SapConfig { url: string; username: string; password: string; client: string; } /** * Retrieves SAP configuration from environment variables. * * @returns {SapConfig} The SAP configuration object. * @throws {Error} If any required environment variable is missing. */ export function getConfig(): SapConfig { const url = process.env.SAP_URL; const username = process.env.SAP_USERNAME; const password = process.env.SAP_PASSWORD; const client = process.env.SAP_CLIENT; // Check if all required environment variables are set if (!url || !username || !password || !client) { throw new Error(`Missing required environment variables. Required variables: - SAP_URL - SAP_USERNAME - SAP_PASSWORD - SAP_CLIENT`); } return { url, username, password, client }; } /** * Server class for interacting with ABAP systems via ADT. */ export class mcp_abap_adt_server { private server: Server; // Instance of the MCP server private sapConfig: SapConfig; // SAP configuration /** * Constructor for the mcp_abap_adt_server class. */ constructor() { this.sapConfig = getConfig(); // Load SAP configuration this.server = new Server( // Initialize the MCP server { name: 'mcp-abap-adt', // Server name version: '0.1.0', // Server version }, { capabilities: { tools: {}, // Initially, no tools are registered }, } ); this.setupHandlers(); // Setup request handlers } /** * Sets up request handlers for listing and calling tools. * @private */ private setupHandlers() { // Setup tool handlers // Handler for ListToolsRequest this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ // Define available tools { name: 'GetProgram', description: 'Retrieve ABAP program source code', inputSchema: { type: 'object', properties: { program_name: { type: 'string', description: 'Name of the ABAP program' } }, required: ['program_name'] } }, { name: 'GetClass', description: 'Retrieve ABAP class source code', inputSchema: { type: 'object', properties: { class_name: { type: 'string', description: 'Name of the ABAP class' } }, required: ['class_name'] } }, { name: 'GetFunctionGroup', description: 'Retrieve ABAP Function Group source code', inputSchema: { type: 'object', properties: { function_group: { type: 'string', description: 'Name of the function module' } }, required: ['function_group'] } }, { name: 'GetFunction', description: 'Retrieve ABAP Function Module source code', inputSchema: { type: 'object', properties: { function_name: { type: 'string', description: 'Name of the function module' }, function_group: { type: 'string', description: 'Name of the function group' } }, required: ['function_name', 'function_group'] } }, { name: 'GetStructure', description: 'Retrieve ABAP Structure', inputSchema: { type: 'object', properties: { structure_name: { type: 'string', description: 'Name of the ABAP Structure' } }, required: ['structure_name'] } }, { name: 'GetTable', description: 'Retrieve ABAP table structure', inputSchema: { type: 'object', properties: { table_name: { type: 'string', description: 'Name of the ABAP table' } }, required: ['table_name'] } }, { name: 'GetTableContents', description: 'Retrieve contents of an ABAP table', inputSchema: { type: 'object', properties: { table_name: { type: 'string', description: 'Name of the ABAP table' }, max_rows: { type: 'number', description: 'Maximum number of rows to retrieve', default: 100 } }, required: ['table_name'] } }, { name: 'GetPackage', description: 'Retrieve ABAP package details', inputSchema: { type: 'object', properties: { package_name: { type: 'string', description: 'Name of the ABAP package' } }, required: ['package_name'] } }, { name: 'GetTypeInfo', description: 'Retrieve ABAP type information', inputSchema: { type: 'object', properties: { type_name: { type: 'string', description: 'Name of the ABAP type' } }, required: ['type_name'] } }, { name: 'GetInclude', description: 'Retrieve ABAP Include Source Code', inputSchema: { type: 'object', properties: { include_name: { type: 'string', description: 'Name of the ABAP Include' } }, required: ['include_name'] } }, { name: 'SearchObject', description: 'Search for ABAP objects using quick search', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query string' }, maxResults: { type: 'number', description: 'Maximum number of results to return', default: 100 } }, required: ['query'] } }, { name: 'GetTransaction', description: 'Retrieve ABAP transaction details', inputSchema: { type: 'object', properties: { transaction_name: { type: 'string', description: 'Name of the ABAP transaction' } }, required: ['transaction_name'] } }, { name: 'GetInterface', description: 'Retrieve ABAP interface source code', inputSchema: { type: 'object', properties: { interface_name: { type: 'string', description: 'Name of the ABAP interface' } }, required: ['interface_name'] } } ] }; }); // Handler for CallToolRequest this.server.setRequestHandler(CallToolRequestSchema, async (request) => { switch (request.params.name) { case 'GetProgram': return await handleGetProgram(request.params.arguments); case 'GetClass': return await handleGetClass(request.params.arguments); case 'GetFunction': return await handleGetFunction(request.params.arguments); case 'GetFunctionGroup': return await handleGetFunctionGroup(request.params.arguments); case 'GetStructure': return await handleGetStructure(request.params.arguments); case 'GetTable': return await handleGetTable(request.params.arguments); case 'GetTableContents': return await handleGetTableContents(request.params.arguments); case 'GetPackage': return await handleGetPackage(request.params.arguments); case 'GetTypeInfo': return await handleGetTypeInfo(request.params.arguments); case 'GetInclude': return await handleGetInclude(request.params.arguments); case 'SearchObject': return await handleSearchObject(request.params.arguments); case 'GetInterface': return await handleGetInterface(request.params.arguments); case 'GetTransaction': return await handleGetTransaction(request.params.arguments); default: throw new McpError( ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}` ); } }); // Handle server shutdown on SIGINT (Ctrl+C) process.on('SIGINT', async () => { await this.server.close(); process.exit(0); }); } /** * Starts the MCP server and connects it to the transport. */ async run() { const transport = new StdioServerTransport(); await this.server.connect(transport); } } // Create and run the server const server = new mcp_abap_adt_server(); server.run().catch((error) => { process.exit(1); });