MCP ABAP ADT

by mario-andreschak
Verified
import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js'; import axios, { AxiosError, AxiosInstance } from 'axios'; import { Agent } from 'https'; import { AxiosResponse } from 'axios'; import { getConfig, SapConfig } from '../index'; // getConfig needs to be exported from index.ts export { McpError, ErrorCode, AxiosResponse }; export function return_response(response: AxiosResponse) { return { isError: false, content: [{ type: 'text', text: response.data }] }; } export function return_error(error: any) { return { isError: true, content: [{ type: 'text', text: `Error: ${error instanceof AxiosError ? String(error.response?.data) : error instanceof Error ? error.message : String(error)}` }] }; } let axiosInstance: AxiosInstance | null = null; export function createAxiosInstance() { if (!axiosInstance) { axiosInstance = axios.create({ httpsAgent: new Agent({ rejectUnauthorized: false // Allow self-signed certificates }) }); } return axiosInstance; } // Cleanup function for tests export function cleanup() { if (axiosInstance) { // Clear any interceptors const reqInterceptor = axiosInstance.interceptors.request.use((config) => config); const resInterceptor = axiosInstance.interceptors.response.use((response) => response); axiosInstance.interceptors.request.eject(reqInterceptor); axiosInstance.interceptors.response.eject(resInterceptor); } axiosInstance = null; config = undefined; csrfToken = null; cookies = null; } let config: SapConfig | undefined; let csrfToken: string | null = null; let cookies: string | null = null; // Variable to store cookies export async function getBaseUrl() { if (!config) { config = getConfig(); } const { url } = config; try { const urlObj = new URL(url); const baseUrl = Buffer.from(`${urlObj.origin}`); return baseUrl; } catch (error) { const errorMessage = `Invalid URL in configuration: ${error instanceof Error ? error.message : error}`; throw new Error(errorMessage); } } export async function getAuthHeaders() { if (!config) { config = getConfig(); } const { username, password, client } = config; const auth = Buffer.from(`${username}:${password}`).toString('base64'); // Create Basic Auth string return { 'Authorization': `Basic ${auth}`, // Basic Authentication header 'X-SAP-Client': client // SAP client header }; } async function fetchCsrfToken(url: string): Promise<string> { try { const response = await createAxiosInstance()({ method: 'GET', url, headers: { ...(await getAuthHeaders()), 'x-csrf-token': 'fetch' } }); const token = response.headers['x-csrf-token']; if (!token) { throw new Error('No CSRF token in response headers'); } // Extract and store cookies if (response.headers['set-cookie']) { cookies = response.headers['set-cookie'].join('; '); } return token; } catch (error) { // Even if the request fails, try to get token from error response if (error instanceof AxiosError && error.response?.headers['x-csrf-token']) { const token = error.response.headers['x-csrf-token']; if (token) { // Extract and store cookies from the error response as well if (error.response.headers['set-cookie']) { cookies = error.response.headers['set-cookie'].join('; '); } return token; } } // If we couldn't get token from error response either, throw the original error throw new Error(`Failed to fetch CSRF token: ${error instanceof Error ? error.message : String(error)}`); } } export async function makeAdtRequest(url: string, method: string, timeout: number, data?: any, params?: any) { // For POST/PUT requests, ensure we have a CSRF token if ((method === 'POST' || method === 'PUT') && !csrfToken) { try { csrfToken = await fetchCsrfToken(url); } catch (error) { throw new Error('CSRF token is required for POST/PUT requests but could not be fetched'); } } const requestHeaders = { ...(await getAuthHeaders()) }; // Add CSRF token for POST/PUT requests if ((method === 'POST' || method === 'PUT') && csrfToken) { requestHeaders['x-csrf-token'] = csrfToken; } // Add cookies if available if (cookies) { requestHeaders['Cookie'] = cookies; } const config: any = { method, url, headers: requestHeaders, timeout, params: params }; // Include data in the request configuration if provided if (data) { config.data = data; } try { const response = await createAxiosInstance()(config); return response; } catch (error) { // If we get a 403 with "CSRF token validation failed", try to fetch a new token and retry if (error instanceof AxiosError && error.response?.status === 403 && error.response.data?.includes('CSRF')) { csrfToken = await fetchCsrfToken(url); config.headers['x-csrf-token'] = csrfToken; return await createAxiosInstance()(config); } throw error; } }