ABAP-ADT-API MCP-Server

by mario-andreschak
Verified
import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js"; import { BaseHandler } from './BaseHandler.js'; import type { ToolDefinition } from '../types/tools.js'; import { ADTClient } from 'abap-adt-api'; export class CodeAnalysisHandlers extends BaseHandler { getTools(): ToolDefinition[] { return [ { name: 'syntaxCheckCode', description: 'Perform ABAP syntax check with source code', inputSchema: { type: 'object', properties: { code: { type: 'string' }, url: { type: 'string', optional: true }, mainUrl: { type: 'string', optional: true }, mainProgram: { type: 'string', optional: true }, version: { type: 'string', optional: true } }, required: ['code'] } }, { name: 'syntaxCheckCdsUrl', description: 'Perform ABAP syntax check with CDS URL', inputSchema: { type: 'object', properties: { cdsUrl: { type: 'string' } }, required: ['cdsUrl'] } }, { name: 'codeCompletion', description: 'Get code completion suggestions', inputSchema: { type: 'object', properties: { sourceUrl: { type: 'string' }, source: { type: 'string' }, line: { type: 'number' }, column: { type: 'number' } }, required: ['sourceUrl', 'source', 'line', 'column'] } }, { name: 'findDefinition', description: 'Find symbol definition', inputSchema: { type: 'object', properties: { url: { type: 'string' }, source: { type: 'string' }, line: { type: 'number' }, startCol: { type: 'number' }, endCol: { type: 'number' }, implementation: { type: 'boolean', optional: true }, mainProgram: { type: 'string', optional: true } }, required: ['url', 'source', 'line', 'startCol', 'endCol'] } }, { name: 'usageReferences', description: 'Find symbol references', inputSchema: { type: 'object', properties: { url: { type: 'string' }, line: { type: 'number', optional: true }, column: { type: 'number', optional: true } }, required: ['url'] } }, { name: 'syntaxCheckTypes', description: 'Retrieves syntax check types.', inputSchema: { type: 'object', properties: {} } }, { name: 'codeCompletionFull', description: 'Performs full code completion.', inputSchema: { type: 'object', properties: { sourceUrl: { type: 'string' }, source: { type: 'string' }, line: { type: 'number' }, column: { type: 'number' }, patternKey: { type: 'string' } }, required: ['sourceUrl', 'source', 'line', 'column', 'patternKey'] } }, { name: 'runClass', description: 'Runs a class.', inputSchema: { type: 'object', properties: { className: { type: 'string' } }, required: ['className'] } }, { name: 'codeCompletionElement', description: 'Retrieves code completion element information.', inputSchema: { type: 'object', properties: { sourceUrl: { type: 'string' }, source: { type: 'string' }, line: { type: 'number' }, column: { type: 'number' } }, required: ['sourceUrl', 'source', 'line', 'column'] } }, { name: 'usageReferenceSnippets', description: 'Retrieves usage reference snippets.', inputSchema: { type: 'object', properties: { references: { type: 'array' } }, required: ['references'] } }, { name: 'fixProposals', description: 'Retrieves fix proposals.', inputSchema: { type: 'object', properties: { url: { type: 'string' }, source: { type: 'string' }, line: { type: 'number' }, column: { type: 'number' } }, required: ['url', 'source', 'line', 'column'] } }, { name: 'fixEdits', description: 'Applies fix edits.', inputSchema: { type: 'object', properties: { proposal: { type: 'string' }, source: { type: 'string' } }, required: ['proposal', 'source'] } }, { name: 'fragmentMappings', description: 'Retrieves fragment mappings.', inputSchema: { type: 'object', properties: { url: { type: 'string' }, type: { type: 'string' }, name: { type: 'string' } }, required: ['url', 'type', 'name'] } }, { name: 'abapDocumentation', description: 'Retrieves ABAP documentation.', inputSchema: { type: 'object', properties: { objectUri: { type: 'string' }, body: { type: 'string' }, line: { type: 'number' }, column: { type: 'number' }, language: { type: 'string', optional: true } }, required: ['objectUri', 'body', 'line', 'column'] } } ]; } async handle(toolName: string, args: any): Promise<any> { switch (toolName) { case 'syntaxCheckCode': return this.handleSyntaxCheckCode(args); case 'syntaxCheckCdsUrl': return this.handleSyntaxCheckCdsUrl(args); case 'codeCompletion': return this.handleCodeCompletion(args); case 'findDefinition': return this.handleFindDefinition(args); case 'usageReferences': return this.handleUsageReferences(args); case 'syntaxCheckTypes': return this.handleSyntaxCheckTypes(args); case 'codeCompletionFull': return this.handleCodeCompletionFull(args); case 'runClass': return this.handleRunClass(args); case 'codeCompletionElement': return this.handleCodeCompletionElement(args); case 'usageReferenceSnippets': return this.handleUsageReferenceSnippets(args); case 'fixProposals': return this.handleFixProposals(args); case 'fixEdits': return this.handleFixEdits(args); case 'fragmentMappings': return this.handleFragmentMappings(args); case 'abapDocumentation': return this.handleAbapDocumentation(args); default: throw new McpError(ErrorCode.MethodNotFound, `Unknown code analysis tool: ${toolName}`); } } async handleSyntaxCheckCdsUrl(args: any): Promise<any> { const startTime = performance.now(); try { const result = await this.adtclient.syntaxCheck(args.cdsUrl); this.trackRequest(startTime, true); return { content: [ { type: 'text', text: JSON.stringify({ status: 'success', result }) } ] }; } catch (error: any) { this.trackRequest(startTime, false); throw new McpError( ErrorCode.InternalError, `Syntax check failed: ${error.message || 'Unknown error'}` ); } } async handleSyntaxCheckCode(args: any): Promise<any> { const startTime = performance.now(); try { const result = await this.adtclient.syntaxCheck(args.url, args?.mainUrl, args?.code, args?.mainProgram, args?.version); this.trackRequest(startTime, true); return { content: [ { type: 'text', text: JSON.stringify({ status: 'success', result }) } ] }; } catch (error: any) { this.trackRequest(startTime, false); throw new McpError( ErrorCode.InternalError, `Syntax check failed: ${error.message || 'Unknown error'}` ); } } async handleCodeCompletion(args: any): Promise<any> { const startTime = performance.now(); try { const result = await this.adtclient.codeCompletion( args.sourceUrl, args.source, args.line, args.column ); this.trackRequest(startTime, true); return { content: [ { type: 'text', text: JSON.stringify({ status: 'success', result }) } ] }; } catch (error: any) { this.trackRequest(startTime, false); throw new McpError( ErrorCode.InternalError, `Code completion failed: ${error.message || 'Unknown error'}` ); } } async handleFindDefinition(args: any): Promise<any> { const startTime = performance.now(); try { const result = await this.adtclient.findDefinition( args.url, args.source, args.line, args.startCol, args.endCol, args.implementation, args.mainProgram ); this.trackRequest(startTime, true); return { content: [ { type: 'text', text: JSON.stringify({ status: 'success', result }) } ] }; } catch (error: any) { this.trackRequest(startTime, false); throw new McpError( ErrorCode.InternalError, `Find definition failed: ${error.message || 'Unknown error'}` ); } } async handleUsageReferences(args: any): Promise<any> { const startTime = performance.now(); try { const result = await this.adtclient.usageReferences( args.url, args.line, args.column ); this.trackRequest(startTime, true); return { content: [ { type: 'text', text: JSON.stringify({ status: 'success', result }) } ] }; } catch (error: any) { this.trackRequest(startTime, false); throw new McpError( ErrorCode.InternalError, `Usage references failed: ${error.message || 'Unknown error'}` ); } } async handleSyntaxCheckTypes(args: any): Promise<any> { const startTime = performance.now(); try { const result = await this.adtclient.syntaxCheckTypes(); this.trackRequest(startTime, true); return { content: [ { type: 'text', text: JSON.stringify({ status: 'success', result }) } ] }; } catch (error: any) { this.trackRequest(startTime, false); throw new McpError( ErrorCode.InternalError, `Syntax check types failed: ${error.message || 'Unknown error'}` ); } } async handleCodeCompletionFull(args: any): Promise<any> { const startTime = performance.now(); try { const result = await this.adtclient.codeCompletionFull(args.sourceUrl, args.source, args.line, args.column, args.patternKey); this.trackRequest(startTime, true); return { content: [ { type: 'text', text: JSON.stringify({ status: 'success', result }) } ] }; } catch (error: any) { this.trackRequest(startTime, false); throw new McpError( ErrorCode.InternalError, `Code completion full failed: ${error.message || 'Unknown error'}` ); } } async handleRunClass(args: any): Promise<any> { const startTime = performance.now(); try { const result = await this.adtclient.runClass(args.className); this.trackRequest(startTime, true); return { content: [ { type: 'text', text: JSON.stringify({ status: 'success', result }) } ] }; } catch (error: any) { this.trackRequest(startTime, false); throw new McpError( ErrorCode.InternalError, `Run class failed: ${error.message || 'Unknown error'}` ); } } async handleCodeCompletionElement(args: any): Promise<any> { const startTime = performance.now(); try { const result = await this.adtclient.codeCompletionElement(args.sourceUrl, args.source, args.line, args.column); this.trackRequest(startTime, true); return { content: [ { type: 'text', text: JSON.stringify({ status: 'success', result }) } ] }; } catch (error: any) { this.trackRequest(startTime, false); throw new McpError( ErrorCode.InternalError, `Code completion element failed: ${error.message || 'Unknown error'}` ); } } async handleUsageReferenceSnippets(args: any): Promise<any> { const startTime = performance.now(); try { const result = await this.adtclient.usageReferenceSnippets(args.references); this.trackRequest(startTime, true); return { content: [ { type: 'text', text: JSON.stringify({ status: 'success', result }) } ] }; } catch (error: any) { this.trackRequest(startTime, false); throw new McpError( ErrorCode.InternalError, `Usage reference snippets failed: ${error.message || 'Unknown error'}` ); } } async handleFixProposals(args: any): Promise<any> { const startTime = performance.now(); try { const result = await this.adtclient.fixProposals(args.url, args.source, args.line, args.column); this.trackRequest(startTime, true); return { content: [ { type: 'text', text: JSON.stringify({ status: 'success', result }) } ] }; } catch (error: any) { this.trackRequest(startTime, false); throw new McpError( ErrorCode.InternalError, `Fix proposals failed: ${error.message || 'Unknown error'}` ); } } async handleFixEdits(args: any): Promise<any> { const startTime = performance.now(); try { const result = await this.adtclient.fixEdits(args.proposal, args.source); this.trackRequest(startTime, true); return { content: [ { type: 'text', text: JSON.stringify({ status: 'success', result }) } ] }; } catch (error: any) { this.trackRequest(startTime, false); throw new McpError( ErrorCode.InternalError, `Fix edits failed: ${error.message || 'Unknown error'}` ); } } async handleFragmentMappings(args: any): Promise<any> { const startTime = performance.now(); try { const result = await this.adtclient.fragmentMappings(args.url, args.type, args.name); this.trackRequest(startTime, true); return { content: [ { type: 'text', text: JSON.stringify({ status: 'success', result }) } ] }; } catch (error: any) { this.trackRequest(startTime, false); throw new McpError( ErrorCode.InternalError, `Fragment mappings failed: ${error.message || 'Unknown error'}` ); } } async handleAbapDocumentation(args: any): Promise<any> { const startTime = performance.now(); try { const result = await this.adtclient.abapDocumentation(args.objectUri, args.body, args.line, args.column, args.language); this.trackRequest(startTime, true); return { content: [ { type: 'text', text: JSON.stringify({ status: 'success', result }) } ] }; } catch (error: any) { this.trackRequest(startTime, false); throw new McpError( ErrorCode.InternalError, `ABAP documentation failed: ${error.message || 'Unknown error'}` ); } } }