UIFlowchartCreator

import axios, { AxiosInstance } from 'axios'; import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js'; import { ToolDefinition, ToolResource } from '../../types/index.js'; interface PostmanToolOptions { baseURL?: string; acceptHeader?: string; } type ToolMapping = { [key: string]: any }; /** * Base class for Postman API tools * Provides common functionality and HTTP client setup */ export class BasePostmanTool { /** * Protected HTTP client for making API requests * All derived classes should use this for Postman API calls */ protected readonly client: AxiosInstance; constructor( apiKey: string | null, options: PostmanToolOptions = {}, existingClient?: AxiosInstance ) { const baseURL = options.baseURL || 'https://api.getpostman.com'; if (existingClient) { this.client = existingClient; } else { // Create new client with API key if (!apiKey) { throw new Error('API key is required when not providing an existing client'); } this.client = axios.create({ baseURL, headers: { 'X-Api-Key': apiKey, 'Content-Type': 'application/json' } }); } // Add custom Accept header if provided if (options.acceptHeader) { this.client.interceptors.request.use(config => { config.headers['Accept'] = options.acceptHeader; return config; }); } // Add response interceptor for error handling this.client.interceptors.response.use( response => response, error => { if (error.response) { // Map HTTP status codes to appropriate MCP error codes switch (error.response.status) { case 400: throw new McpError( ErrorCode.InvalidRequest, error.response.data?.error?.message || 'Invalid request parameters' ); case 401: throw new McpError( ErrorCode.InvalidRequest, 'Unauthorized: Invalid or missing API key' ); case 403: throw new McpError( ErrorCode.InvalidRequest, 'Forbidden: Insufficient permissions or feature unavailable' ); case 404: throw new McpError( ErrorCode.InvalidRequest, 'Resource not found' ); case 422: throw new McpError( ErrorCode.InvalidRequest, error.response.data?.error?.message || 'Invalid request parameters' ); case 429: throw new McpError( ErrorCode.InvalidRequest, 'Rate limit exceeded' ); default: throw new McpError( ErrorCode.InternalError, error.response.data?.error?.message || 'Internal server error' ); } } else if (error.request) { throw new McpError( ErrorCode.InternalError, 'No response received from Postman API' ); } else { throw new McpError( ErrorCode.InternalError, `Error making request: ${error.message}` ); } } ); } /** * Generate tool mappings from tool definitions * Each derived class should implement getToolDefinitions() to provide its specific tools * @returns Object mapping tool names to the tool handler instance */ public getToolMappings(): ToolMapping { const toolDefinitions = this.getToolDefinitions(); const mappings: ToolMapping = {}; toolDefinitions.forEach(tool => { mappings[tool.name] = this; }); return mappings; } /** * Get tool definitions for this tool class * Must be implemented by derived classes */ public getToolDefinitions(): ToolDefinition[] { throw new Error('getToolDefinitions() must be implemented by derived class'); } /** * List resources that this tool can interact with * Should be implemented by derived classes that handle resources */ public async listToolResources(): Promise<ToolResource[]> { return []; } /** * Get details about how this tool can interact with a specific resource * Should be implemented by derived classes that handle resources * @throws {McpError} If the resource cannot be handled by this tool */ public async getToolResourceDetails(resourceUri: string): Promise<ToolResource> { throw new McpError( ErrorCode.InvalidRequest, `Resource ${resourceUri} cannot be handled by this tool` ); } /** * Check if this tool can handle a specific resource */ public async canHandleResource(resourceUri: string): Promise<boolean> { try { await this.getToolResourceDetails(resourceUri); return true; } catch (error) { return false; } } }