#
tokens: 12792/50000 14/14 files
lines: off (toggle) GitHub
raw markdown copy
# Directory Structure

```
├── .env.example
├── .gitignore
├── config.json
├── examples
│   └── swagger-pet-store.json
├── jest.config.js
├── LICENSE
├── package.json
├── README.md
├── src
│   ├── config.ts
│   ├── mcp-server.ts
│   ├── server.ts
│   ├── types
│   │   └── index.ts
│   └── types.ts
├── tests
│   └── test-pets.js
├── tsconfig.json
└── yarn.lock
```

# Files

--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------

```
# Server Configuration
PORT=3000

# API Authentication
API_USERNAME=
API_PASSWORD=
API_TOKEN=

# Default API Configuration
DEFAULT_API_BASE_URL=
DEFAULT_SWAGGER_URL= 
```

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
# Dependencies
node_modules/
yarn-debug.log*
yarn-error.log*

# Build output
dist/
build/

# Environment variables
.env
.env.local
.env.*.local

# IDE and editor files
.idea/
.vscode/
*.swp
*.swo
.DS_Store

# Test coverage
coverage/

# Logs
logs/
*.log
npm-debug.log* 
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
# Swagger MCP Server

A server that ingests and serves Swagger/OpenAPI specifications through the Model Context Protocol (MCP).

## Features

- Loads Swagger/OpenAPI specifications
- Supports multiple authentication methods:
  - Basic Auth
  - Bearer Token
  - API Key (header or query)
  - OAuth2
- Automatically generates MCP tools from API endpoints
- Server-Sent Events (SSE) support for real-time communication
- TypeScript support

## Security

This is a personal server!! Do not expose it to the public internet.
If the underlying API requires authentication, you should not expose the MCP server to the public internet.

## TODO

- secrets - the MCP server should be able to use secrets from the user to authenticate requests to the API
- Comprehensive test suite

## Prerequisites

- Node.js (v18 or higher)
- Yarn package manager
- TypeScript

## Installation

1. Clone the repository:
```bash
git clone https://github.com/dcolley/swagger-mcp.git
cd swagger-mcp
```

2. Install dependencies:
```bash
yarn install
```

3. Create a `.env` file based on the example:
```bash
cp .env.example .env
```

4. Configure your Swagger/OpenAPI specification:
   - Place your Swagger file in the project (e.g., `swagger.json`)
   - Or provide a URL to your Swagger specification

5. Update the configuration in `config.json` with your server settings:
```json
{
  "server": {
    "host": "localhost",
    "port": 3000
  },
  "swagger": {
    "url": "url-or-path/to/your/swagger.json",
    "apiBaseUrl": "https://api.example.com",  // Fallback if not specified in Swagger
    "defaultAuth": {  // Fallback if not specified in Swagger
      "type": "apiKey",
      "apiKey": "your-api-key",
      "apiKeyName": "api_key",
      "apiKeyIn": "header"
    }
  }
}
```

Note: The server prioritizes settings from the Swagger specification over the config file:
- If the Swagger file contains a `servers` array, the first server URL will be used as the base URL
- If the Swagger file defines security schemes, they will be used for authentication
- The config file settings serve as fallbacks when not specified in the Swagger file

## Usage

1. Start the development server:
```bash
yarn dev
```

2. Build for production:
```bash
yarn build
```

3. Start the production server:
```bash
yarn start
```

## API Endpoints

- `GET /health` - Check server health status
- `GET /sse` - Establish Server-Sent Events connection
- `POST /messages` - Send messages to the MCP server

## Testing

Run the test suite:
```bash
# Run tests once
yarn test

# Run tests in watch mode
yarn test:watch

# Run tests with coverage report
yarn test:coverage
```

## Authentication

The server supports various authentication methods. Configure them in the `config.json` file as fallbacks when not specified in the Swagger file:

### Basic Auth
```json
{
  "defaultAuth": {
    "type": "basic",
    "username": "your-username",
    "password": "your-password"
  }
}
```

### Bearer Token
```json
{
  "defaultAuth": {
    "type": "bearer",
    "token": "your-bearer-token"
  }
}
```

### API Key
```json
{
  "defaultAuth": {
    "type": "apiKey",
    "apiKey": "your-api-key",
    "apiKeyName": "X-API-Key",
    "apiKeyIn": "header"
  }
}
```

### OAuth2
```json
{
  "defaultAuth": {
    "type": "oauth2",
    "token": "your-oauth-token"
  }
}
```

## Development

1. Start the development server:
```bash
yarn dev
```

<!-- 2. Make changes to the code

3. Run tests to ensure everything works:
```bash
yarn test
```

4. Build the project:
```bash
yarn build
``` -->

<!-- ## Contributing

1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request -->

## License

This project is licensed under the Apache 2.0 License.

## Environment Variables

- `PORT`: Server port (default: 3000)
- `API_USERNAME`: Username for API authentication (fallback)
- `API_PASSWORD`: Password for API authentication (fallback)
- `API_TOKEN`: API token for authentication (fallback)
- `DEFAULT_API_BASE_URL`: Default base URL for API endpoints (fallback)
- `DEFAULT_SWAGGER_URL`: Default Swagger specification URL

```

--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------

```javascript
module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
  roots: ['<rootDir>/tests'],
  transform: {
    '^.+\\.tsx?$': 'ts-jest',
  },
  testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$',
  moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
}; 
```

--------------------------------------------------------------------------------
/config.json:
--------------------------------------------------------------------------------

```json
{
  "swagger": {
    "url": "https://petstore.swagger.io/v2/swagger.json",
    "apiBaseUrl": "https://petstore.swagger.io/v2",
    "defaultAuth": {
      "type": "apiKey",
      "apiKey": "special-key",
      "apiKeyName": "api_key",
      "apiKeyIn": "header"
    }
  },
  "log": {
    "level": "info"
  },
  "server": {
    "port": 3000,
    "host": "0.0.0.0"
  }
}
```

--------------------------------------------------------------------------------
/src/types.ts:
--------------------------------------------------------------------------------

```typescript
export interface SwaggerConfig {
  swaggerUrl?: string;
  swaggerFile?: string;
  apiBaseUrl: string;
  auth?: AuthConfig;
}

export interface AuthConfig {
  type: 'basic' | 'bearer' | 'apiKey' | 'oauth2';
  username?: string;
  password?: string;
  token?: string;
  apiKey?: string;
  apiKeyName?: string;
  apiKeyIn?: 'header' | 'query';
}

export interface ToolInput {
  auth?: AuthConfig;
  [key: string]: any;
}

export interface SecurityScheme {
  type: string;
  description?: string;
  name?: string;
  in?: string;
  scheme?: string;
  flows?: {
    implicit?: {
      authorizationUrl: string;
      scopes: Record<string, string>;
    };
    [key: string]: any;
  };
} 
```

--------------------------------------------------------------------------------
/src/types/index.ts:
--------------------------------------------------------------------------------

```typescript
export interface SwaggerConfig {
  swaggerFile?: string;
  swaggerUrl?: string;
  apiBaseUrl: string;
  auth?: AuthConfig;
}

export interface ServerConfig {
  port: number;
  username?: string;
  password?: string;
  token?: string;
}

export interface AuthConfig {
  type: 'basic' | 'bearer' | 'apiKey' | 'oauth2';
  username?: string;
  password?: string;
  token?: string;
  apiKey?: string;
  apiKeyIn?: 'header' | 'query';
  apiKeyName?: string;
}

export interface SecurityScheme {
  type: string;
  description?: string;
  name?: string;
  in?: string;
  scheme?: string;
  bearerFormat?: string;
  flows?: any;
}

export interface ToolInput {
  auth?: AuthConfig;
  [key: string]: any;
} 
```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
{
  "name": "swagger-mcp",
  "version": "1.0.0",
  "description": "Server that ingests and serves Swagger/OpenAPI specifications",
  "main": "dist/server.js",
  "scripts": {
    "start": "node dist/server.js",
    "dev": "NODE_OPTIONS='--loader ts-node/esm' ts-node src/server.ts",
    "build": "tsc",
    "watch": "tsc -w",
    "test": "jest",
    "test:watch": "jest --watch",
    "test:coverage": "jest --coverage"
  },
  "keywords": [
    "swagger",
    "openapi",
    "api",
    "documentation"
  ],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@apidevtools/swagger-parser": "^10.1.0",
    "@modelcontextprotocol/sdk": "^1.7.0",
    "@types/cors": "^2.8.17",
    "@types/express": "^5.0.0",
    "@types/swagger-parser": "^7.0.1",
    "axios": "^1.8.3",
    "cors": "^2.8.5",
    "dotenv": "^16.4.5",
    "eventsource": "^3.0.5",
    "express": "^4.18.3",
    "node-fetch": "^3.3.2",
    "openapi-types": "^12.1.3",
    "ts-node": "^10.9.2",
    "tslib": "^2.8.1",
    "typescript": "^5.4.2",
    "zod": "^3.22.4"
  },
  "devDependencies": {
    "@types/jest": "^29.5.14",
    "@types/supertest": "^6.0.2",
    "jest": "^29.7.0",
    "supertest": "^7.0.0",
    "ts-jest": "^29.2.6"
  },
  "packageManager": "[email protected]+sha1.1959a18351b811cdeedbd484a8f86c3cc3bbaf72"
}

```

--------------------------------------------------------------------------------
/tests/test-pets.js:
--------------------------------------------------------------------------------

```javascript
import fetch from 'node-fetch';
import { EventSource } from 'eventsource';

async function findPetsBySold() {
  try {
    // First check if server is ready
    const healthResponse = await fetch('http://localhost:3000/health');
    const health = await healthResponse.json();
    
    if (health.mcpServer !== 'initialized') {
      console.error('Server is not ready. Please wait for it to initialize.');
      return;
    }

    // Create SSE connection
    const eventSource = new EventSource('http://localhost:3000/sse');
    
    eventSource.onopen = async () => {
      console.log('SSE connection opened');
      
      // Send the request
      try {
        const response = await fetch('http://localhost:3000/messages', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({
            type: 'invoke',
            tool: 'findPetsByStatus',
            input: {
              status: ['sold']
            }
          })
        });
        
        const data = await response.json();
        console.log('Response:', data);
        eventSource.close();
      } catch (error) {
        console.error('Error sending message:', error.message);
        eventSource.close();
      }
    };
    
    eventSource.onerror = (error) => {
      console.error('SSE error:', error);
      eventSource.close();
    };
    
    eventSource.onmessage = (event) => {
      console.log('Received:', event.data);
    };
  } catch (error) {
    console.error('Error:', error.message);
    if (error.message.includes('ECONNREFUSED')) {
      console.log('Make sure the server is running on port 3000');
    }
  }
}

// Run the test
findPetsBySold(); 
```

--------------------------------------------------------------------------------
/src/config.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from 'zod';
import fs from 'fs/promises';
import path from 'path';

// Define the configuration schema
export const ConfigSchema = z.object({
  swagger: z.object({
    url: z.string().url(),
    apiBaseUrl: z.string().url(),
    defaultAuth: z.object({
      type: z.enum(['basic', 'bearer', 'apiKey', 'oauth2']),
      token: z.string().optional(),
      username: z.string().optional(),
      password: z.string().optional(),
      apiKey: z.string().optional(),
      apiKeyName: z.string().optional(),
      apiKeyIn: z.enum(['header', 'query']).optional(),
    }).optional(),
  }),
  log: z.object({
    level: z.enum(['debug', 'info', 'warn', 'error']),
  }),
  server: z.object({
    port: z.number().default(3000),
    host: z.string().default('0.0.0.0'),
  }),
});

export type Config = z.infer<typeof ConfigSchema>;

const defaultConfig: Config = {
  swagger: {
    url: 'https://petstore.swagger.io/v2/swagger.json',
    apiBaseUrl: 'https://petstore.swagger.io/v2',
    defaultAuth: {
      type: 'apiKey',
      apiKey: 'special-key',
      apiKeyName: 'api_key',
      apiKeyIn: 'header',
    },
  },
  log: {
    level: 'info',
  },
  server: {
    port: 3000,
    host: '0.0.0.0',
  },
};

export async function loadConfig(configPath?: string): Promise<Config> {
  try {
    // If no config path provided, create default config file
    if (!configPath) {
      configPath = path.join(process.cwd(), 'config.json');
      // Check if config file exists, if not create it with default values
      try {
        await fs.access(configPath);
      } catch {
        await fs.writeFile(configPath, JSON.stringify(defaultConfig, null, 2));
        console.log(`Created default configuration file at ${configPath}`);
      }
    }

    const configFile = await fs.readFile(configPath, 'utf-8');
    const config = JSON.parse(configFile);
    return ConfigSchema.parse(config);
  } catch (error) {
    if (error instanceof z.ZodError) {
      console.error('Invalid configuration:', error.errors);
    } else {
      console.error('Error loading configuration:', error);
    }
    console.log('Using default configuration');
    return defaultConfig;
  }
} 
```

--------------------------------------------------------------------------------
/src/server.ts:
--------------------------------------------------------------------------------

```typescript
import express, { Request, Response, Router } from 'express';
import cors from 'cors';
import dotenv from 'dotenv';
import { SwaggerMcpServer } from './mcp-server';
import { loadConfig } from './config';

// Load environment variables
dotenv.config();

const app = express();
const router = Router();
let mcpServer: SwaggerMcpServer | null = null;

// Middleware
// app.use(cors());
// app.use(express.json());

// Routes
const handleSSE = async (req: Request, res: Response) => {
  console.debug('SSE connection request received');
  if (!mcpServer) {
    console.warn('MCP server not initialized - rejecting SSE connection');
    res.status(400).json({ error: 'MCP server not initialized' });
    return;
  }
  console.debug('Establishing SSE connection...');
  mcpServer.handleSSE(res);
};

const handleMessage = async (req: Request, res: Response) => {
  console.debug('Message received:', {
    method: req.method,
    path: req.path,
    body: req.body
  });
  if (!mcpServer) {
    console.warn('MCP server not initialized - rejecting message');
    res.status(400).json({ error: 'MCP server not initialized' });
    return;
  }
  mcpServer.handleMessage(req, res);
};

const handleHealth = (_req: Request, res: Response) => {
  console.debug('Health check request received');
  res.json({ 
    status: 'ok',
    mcpServer: mcpServer ? 'initialized' : 'not initialized'
  });
};

// // Register routes
// router.get('/sse', handleSSE);
// router.post('/messages', handleMessage);
// router.get('/health', handleHealth);

// Mount router
// app.use('/', router);

app.get('/sse', handleSSE);
app.post('/messages', handleMessage);
app.get('/health', handleHealth);

// Initialize server
async function initializeServer() {
  try {
    console.log('Starting server initialization...');
    
    // Load configuration
    const config = await loadConfig();
    // set app logging level
    process.env.LOG_LEVEL = config.log?.level || 'info';

    console.debug('Configuration loaded:', {
      swaggerUrl: config.swagger.url,
      apiBaseUrl: config.swagger.apiBaseUrl,
      hasDefaultAuth: !!config.swagger.defaultAuth
    });
    
    // Create and initialize MCP server
    console.log('Creating MCP server instance...');
    mcpServer = new SwaggerMcpServer(config.swagger.apiBaseUrl, config.swagger.defaultAuth);
    
    console.log('Loading Swagger specification...');
    await mcpServer.loadSwaggerSpec(config.swagger.url);
    console.debug('Swagger specification loaded successfully');
    
    // Start the server
    app.listen(config.server.port, config.server.host, () => {
      console.log('Server initialization complete');
      console.log(`Server is running on http://${config.server.host}:${config.server.port}`);
      console.log('Swagger specification loaded from:', config.swagger.url);
      console.log('API Base URL:', config.swagger.apiBaseUrl);
    });
  } catch (error) {
    console.error('Failed to initialize server:', error);
    process.exit(1);
  }
}

// Start the server
initializeServer();
```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
{
  "compilerOptions": {
    /* Visit https://aka.ms/tsconfig to read more about this file */

    /* Projects */
    // "incremental": true,                              /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
    // "composite": true,                                /* Enable constraints that allow a TypeScript project to be used with project references. */
    // "tsBuildInfoFile": "./.tsbuildinfo",              /* Specify the path to .tsbuildinfo incremental compilation file. */
    // "disableSourceOfProjectReferenceRedirect": true,  /* Disable preferring source files instead of declaration files when referencing composite projects. */
    // "disableSolutionSearching": true,                 /* Opt a project out of multi-project reference checking when editing. */
    // "disableReferencedProjectLoad": true,             /* Reduce the number of projects loaded automatically by TypeScript. */

    /* Language and Environment */
    "target": "ES2020",                                  /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
    // "lib": [],                                        /* Specify a set of bundled library declaration files that describe the target runtime environment. */
    // "jsx": "preserve",                                /* Specify what JSX code is generated. */
    // "libReplacement": true,                           /* Enable lib replacement. */
    // "experimentalDecorators": true,                   /* Enable experimental support for legacy experimental decorators. */
    // "emitDecoratorMetadata": true,                    /* Emit design-type metadata for decorated declarations in source files. */
    // "jsxFactory": "",                                 /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
    // "jsxFragmentFactory": "",                         /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
    // "jsxImportSource": "",                            /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
    // "reactNamespace": "",                             /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
    // "noLib": true,                                    /* Disable including any library files, including the default lib.d.ts. */
    // "useDefineForClassFields": true,                  /* Emit ECMAScript-standard-compliant class fields. */
    // "moduleDetection": "auto",                        /* Control what method is used to detect module-format JS files. */

    /* Modules */
    "module": "NodeNext",                                /* Specify what module code is generated. */
    "moduleResolution": "NodeNext",                     /* Specify how TypeScript looks up a file from a given module specifier. */
    // "baseUrl": "./",                                  /* Specify the base directory to resolve non-relative module names. */
    // "paths": {},                                      /* Specify a set of entries that re-map imports to additional lookup locations. */
    // "rootDirs": [],                                   /* Allow multiple folders to be treated as one when resolving modules. */
    // "typeRoots": [],                                  /* Specify multiple folders that act like './node_modules/@types'. */
    // "types": [],                                      /* Specify type package names to be included without being referenced in a source file. */
    // "allowUmdGlobalAccess": true,                     /* Allow accessing UMD globals from modules. */
    // "moduleSuffixes": [],                             /* List of file name suffixes to search when resolving a module. */
    // "allowImportingTsExtensions": true,               /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
    // "rewriteRelativeImportExtensions": true,          /* Rewrite '.ts', '.tsx', '.mts', and '.cts' file extensions in relative import paths to their JavaScript equivalent in output files. */
    // "resolvePackageJsonExports": true,                /* Use the package.json 'exports' field when resolving package imports. */
    // "resolvePackageJsonImports": true,                /* Use the package.json 'imports' field when resolving imports. */
    // "customConditions": [],                           /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
    // "noUncheckedSideEffectImports": true,             /* Check side effect imports. */
    // "resolveJsonModule": true,                        /* Enable importing .json files. */
    // "allowArbitraryExtensions": true,                 /* Enable importing files with any extension, provided a declaration file is present. */
    // "noResolve": true,                                /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */

    /* JavaScript Support */
    // "allowJs": true,                                  /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
    // "checkJs": true,                                  /* Enable error reporting in type-checked JavaScript files. */
    // "maxNodeModuleJsDepth": 1,                        /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */

    /* Emit */
    "declaration": true,                              /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
    "declarationMap": true,                           /* Create sourcemaps for d.ts files. */
    "emitDeclarationOnly": false,                     /* Only output d.ts files and not JavaScript files. */
    "sourceMap": true,                                /* Create source map files for emitted JavaScript files. */
    "noEmit": false,                                  /* Disable emitting files from a compilation. */
    "outDir": "dist",                                 /* Specify an output folder for all emitted files. */
    "removeComments": true,                           /* Disable emitting comments. */
    "importHelpers": true,                            /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
    "downlevelIteration": true,                       /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
    "sourceRoot": "",                                 /* Specify the root path for debuggers to find the reference source code. */
    "mapRoot": "",                                    /* Specify the location where debugger should locate map files instead of generated locations. */
    "inlineSources": true,                            /* Include source code in the sourcemaps inside the emitted JavaScript. */
    "emitBOM": true,                                  /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
    "newLine": "crlf",                                /* Set the newline character for emitting files. */
    "stripInternal": true,                            /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
    "noEmitHelpers": true,                            /* Disable generating custom helper functions like '__extends' in compiled output. */
    "noEmitOnError": true,                            /* Disable emitting files if any type checking errors are reported. */
    "preserveConstEnums": true,                       /* Disable erasing 'const enum' declarations in generated code. */

    /* Interop Constraints */
    // "isolatedModules": true,                          /* Ensure that each file can be safely transpiled without relying on other imports. */
    // "verbatimModuleSyntax": true,                     /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
    // "isolatedDeclarations": true,                     /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */
    // "erasableSyntaxOnly": true,                       /* Do not allow runtime constructs that are not part of ECMAScript. */
    // "allowSyntheticDefaultImports": true,             /* Allow 'import x from y' when a module doesn't have a default export. */
    "esModuleInterop": true,                             /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
    // "preserveSymlinks": true,                         /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
    "forceConsistentCasingInFileNames": true,            /* Ensure that casing is correct in imports. */

    /* Type Checking */
    "strict": true,                                      /* Enable all strict type-checking options. */
    // "noImplicitAny": true,                            /* Enable error reporting for expressions and declarations with an implied 'any' type. */
    // "strictNullChecks": true,                         /* When type checking, take into account 'null' and 'undefined'. */
    // "strictFunctionTypes": true,                      /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
    // "strictBindCallApply": true,                      /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
    // "strictPropertyInitialization": true,             /* Check for class properties that are declared but not set in the constructor. */
    // "strictBuiltinIteratorReturn": true,              /* Built-in iterators are instantiated with a 'TReturn' type of 'undefined' instead of 'any'. */
    // "noImplicitThis": true,                           /* Enable error reporting when 'this' is given the type 'any'. */
    // "useUnknownInCatchVariables": true,               /* Default catch clause variables as 'unknown' instead of 'any'. */
    // "alwaysStrict": true,                             /* Ensure 'use strict' is always emitted. */
    // "noUnusedLocals": true,                           /* Enable error reporting when local variables aren't read. */
    // "noUnusedParameters": true,                       /* Raise an error when a function parameter isn't read. */
    // "exactOptionalPropertyTypes": true,               /* Interpret optional property types as written, rather than adding 'undefined'. */
    // "noImplicitReturns": true,                        /* Enable error reporting for codepaths that do not explicitly return in a function. */
    // "noFallthroughCasesInSwitch": true,               /* Enable error reporting for fallthrough cases in switch statements. */
    // "noUncheckedIndexedAccess": true,                 /* Add 'undefined' to a type when accessed using an index. */
    // "noImplicitOverride": true,                       /* Ensure overriding members in derived classes are marked with an override modifier. */
    // "noPropertyAccessFromIndexSignature": true,       /* Enforces using indexed accessors for keys declared using an indexed type. */
    // "allowUnusedLabels": true,                        /* Disable error reporting for unused labels. */
    // "allowUnreachableCode": true,                     /* Disable error reporting for unreachable code. */

    /* Completeness */
    // "skipDefaultLibCheck": true,                      /* Skip type checking .d.ts files that are included with TypeScript. */
    "skipLibCheck": true                                 /* Skip type checking all .d.ts files. */
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

```

--------------------------------------------------------------------------------
/examples/swagger-pet-store.json:
--------------------------------------------------------------------------------

```json
{
  "swagger":"2.0",
  "info":{
    "description":"This is a sample server Petstore server.  You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/).  For this sample, you can use the api key `special-key` to test the authorization filters.","version":"1.0.7","title":"Swagger Petstore","termsOfService":"http://swagger.io/terms/","contact":{"email":"[email protected]"},"license":{"name":"Apache 2.0","url":"http://www.apache.org/licenses/LICENSE-2.0.html"}
  },
  "host":"petstore.swagger.io",
  "basePath":"/v2",
  "tags":[{"name":"pet","description":"Everything about your Pets","externalDocs":{"description":"Find out more","url":"http://swagger.io"}},{"name":"store","description":"Access to Petstore orders"},{"name":"user","description":"Operations about user",
  "externalDocs":{"description":"Find out more about our store","url":"http://swagger.io"}}],
  "schemes":["https","http"],
  "paths":{

    "/pet/{petId}/uploadImage":{"post":{"tags":["pet"],"summary":"uploads an image","description":"","operationId":"uploadFile","consumes":["multipart/form-data"],"produces":["application/json"],"parameters":[{"name":"petId","in":"path","description":"ID of pet to update","required":true,"type":"integer","format":"int64"},{"name":"additionalMetadata","in":"formData","description":"Additional data to pass to server","required":false,"type":"string"},{"name":"file","in":"formData","description":"file to upload","required":false,"type":"file"}],"responses":{"200":{"description":"successful operation","schema":{"$ref":"#/definitions/ApiResponse"}}},"security":[{"petstore_auth":["write:pets","read:pets"]}]}},
    
    "/pet":{
      "post":{"tags":["pet"],"summary":"Add a new pet to the store","description":"","operationId":"addPet","consumes":["application/json","application/xml"],"produces":["application/json","application/xml"],"parameters":[{"in":"body","name":"body","description":"Pet object that needs to be added to the store","required":true,"schema":{"$ref":"#/definitions/Pet"}}],"responses":{"405":{"description":"Invalid input"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]},"put":{"tags":["pet"],"summary":"Update an existing pet","description":"","operationId":"updatePet","consumes":["application/json","application/xml"],"produces":["application/json","application/xml"],"parameters":[{"in":"body","name":"body","description":"Pet object that needs to be added to the store","required":true,"schema":{"$ref":"#/definitions/Pet"}}],"responses":{"400":{"description":"Invalid ID supplied"},"404":{"description":"Pet not found"},"405":{"description":"Validation exception"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]}
    },
    
    "/pet/findByStatus":{
      "get":{"tags":["pet"],"summary":"Finds Pets by status","description":"Multiple status values can be provided with comma separated strings","operationId":"findPetsByStatus","produces":["application/json","application/xml"],
        "parameters":[
          {"name":"status","in":"query","description":"Status values that need to be considered for filter","required":true,"type":"array","items":{"type":"string","enum":["available","pending","sold"],"default":"available"},"collectionFormat":"multi"}
          ],
          "responses":{
            "200":{"description":"successful operation","schema":{"type":"array","items":{"$ref":"#/definitions/Pet"}}},
            "400":{"description":"Invalid status value"}
          },
          "security":[{"petstore_auth":["write:pets","read:pets"]}
        ]
      }
    },
    
    "/pet/findByTags":{"get":{"tags":["pet"],"summary":"Finds Pets by tags","description":"Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.","operationId":"findPetsByTags","produces":["application/json","application/xml"],"parameters":[{"name":"tags","in":"query","description":"Tags to filter by","required":true,"type":"array","items":{"type":"string"},"collectionFormat":"multi"}],"responses":{"200":{"description":"successful operation","schema":{"type":"array","items":{"$ref":"#/definitions/Pet"}}},"400":{"description":"Invalid tag value"}},"security":[{"petstore_auth":["write:pets","read:pets"]}],"deprecated":true}},
    
    "/pet/{petId}":{"get":{"tags":["pet"],"summary":"Find pet by ID","description":"Returns a single pet","operationId":"getPetById","produces":["application/json","application/xml"],"parameters":[{"name":"petId","in":"path","description":"ID of pet to return","required":true,"type":"integer","format":"int64"}],"responses":{"200":{"description":"successful operation","schema":{"$ref":"#/definitions/Pet"}},"400":{"description":"Invalid ID supplied"},"404":{"description":"Pet not found"}},"security":[{"api_key":[]}]},"post":{"tags":["pet"],"summary":"Updates a pet in the store with form data","description":"","operationId":"updatePetWithForm","consumes":["application/x-www-form-urlencoded"],"produces":["application/json","application/xml"],"parameters":[{"name":"petId","in":"path","description":"ID of pet that needs to be updated","required":true,"type":"integer","format":"int64"},{"name":"name","in":"formData","description":"Updated name of the pet","required":false,"type":"string"},{"name":"status","in":"formData","description":"Updated status of the pet","required":false,"type":"string"}],"responses":{"405":{"description":"Invalid input"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]},"delete":{"tags":["pet"],"summary":"Deletes a pet","description":"","operationId":"deletePet","produces":["application/json","application/xml"],"parameters":[{"name":"api_key","in":"header","required":false,"type":"string"},{"name":"petId","in":"path","description":"Pet id to delete","required":true,"type":"integer","format":"int64"}],"responses":{"400":{"description":"Invalid ID supplied"},"404":{"description":"Pet not found"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]}},
    
    "/store/inventory":{"get":{"tags":["store"],"summary":"Returns pet inventories by status","description":"Returns a map of status codes to quantities","operationId":"getInventory","produces":["application/json"],"parameters":[],"responses":{"200":{"description":"successful operation","schema":{"type":"object","additionalProperties":{"type":"integer","format":"int32"}}}},"security":[{"api_key":[]}]}},
    
    "/store/order":{"post":{"tags":["store"],"summary":"Place an order for a pet","description":"","operationId":"placeOrder","consumes":["application/json"],"produces":["application/json","application/xml"],"parameters":[{"in":"body","name":"body","description":"order placed for purchasing the pet","required":true,"schema":{"$ref":"#/definitions/Order"}}],"responses":{"200":{"description":"successful operation","schema":{"$ref":"#/definitions/Order"}},"400":{"description":"Invalid Order"}}}},
    
    "/store/order/{orderId}":{"get":{"tags":["store"],"summary":"Find purchase order by ID","description":"For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions","operationId":"getOrderById","produces":["application/json","application/xml"],"parameters":[{"name":"orderId","in":"path","description":"ID of pet that needs to be fetched","required":true,"type":"integer","maximum":10,"minimum":1,"format":"int64"}],"responses":{"200":{"description":"successful operation","schema":{"$ref":"#/definitions/Order"}},"400":{"description":"Invalid ID supplied"},"404":{"description":"Order not found"}}},"delete":{"tags":["store"],"summary":"Delete purchase order by ID","description":"For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors","operationId":"deleteOrder","produces":["application/json","application/xml"],"parameters":[{"name":"orderId","in":"path","description":"ID of the order that needs to be deleted","required":true,"type":"integer","minimum":1,"format":"int64"}],"responses":{"400":{"description":"Invalid ID supplied"},"404":{"description":"Order not found"}}}},
    
    "/user/createWithList":{"post":{"tags":["user"],"summary":"Creates list of users with given input array","description":"","operationId":"createUsersWithListInput","consumes":["application/json"],"produces":["application/json","application/xml"],"parameters":[{"in":"body","name":"body","description":"List of user object","required":true,"schema":{"type":"array","items":{"$ref":"#/definitions/User"}}}],"responses":{"default":{"description":"successful operation"}}}},
    
    "/user/{username}":{
      "get":{"tags":["user"],"summary":"Get user by user name","description":"","operationId":"getUserByName","produces":["application/json","application/xml"],"parameters":[{"name":"username","in":"path","description":"The name that needs to be fetched. Use user1 for testing. ","required":true,"type":"string"}],"responses":{"200":{"description":"successful operation","schema":{"$ref":"#/definitions/User"}},"400":{"description":"Invalid username supplied"},"404":{"description":"User not found"}}},
      "put":{"tags":["user"],"summary":"Updated user","description":"This can only be done by the logged in user.","operationId":"updateUser","consumes":["application/json"],"produces":["application/json","application/xml"],"parameters":[{"name":"username","in":"path","description":"name that need to be updated","required":true,"type":"string"},{"in":"body","name":"body","description":"Updated user object","required":true,"schema":{"$ref":"#/definitions/User"}}],"responses":{"400":{"description":"Invalid user supplied"},"404":{"description":"User not found"}}},
      "delete":{"tags":["user"],"summary":"Delete user","description":"This can only be done by the logged in user.","operationId":"deleteUser","produces":["application/json","application/xml"],"parameters":[{"name":"username","in":"path","description":"The name that needs to be deleted","required":true,"type":"string"}],"responses":{"400":{"description":"Invalid username supplied"},"404":{"description":"User not found"}}}},
    
    "/user/login":{
      "get":{"tags":["user"],"summary":"Logs user into the system","description":"","operationId":"loginUser","produces":["application/json","application/xml"],"parameters":[{"name":"username","in":"query","description":"The user name for login","required":true,"type":"string"},{"name":"password","in":"query","description":"The password for login in clear text","required":true,"type":"string"}],"responses":{"200":{"description":"successful operation","headers":{"X-Expires-After":{"type":"string","format":"date-time","description":"date in UTC when token expires"},"X-Rate-Limit":{"type":"integer","format":"int32","description":"calls per hour allowed by the user"}},"schema":{"type":"string"}},"400":{"description":"Invalid username/password supplied"}}}
    },
    
    "/user/logout":{
      "get":{"tags":["user"],"summary":"Logs out current logged in user session","description":"","operationId":"logoutUser","produces":["application/json","application/xml"],"parameters":[],"responses":{"default":{"description":"successful operation"}}}
    },
    
    "/user/createWithArray":{
      "post":{"tags":["user"],"summary":"Creates list of users with given input array","description":"","operationId":"createUsersWithArrayInput","consumes":["application/json"],"produces":["application/json","application/xml"],"parameters":[{"in":"body","name":"body","description":"List of user object","required":true,"schema":{"type":"array","items":{"$ref":"#/definitions/User"}}}],"responses":{"default":{"description":"successful operation"}}}
    },
    
    "/user":{
      "post":{"tags":["user"],"summary":"Create user","description":"This can only be done by the logged in user.","operationId":"createUser","consumes":["application/json"],"produces":["application/json","application/xml"],"parameters":[{"in":"body","name":"body","description":"Created user object","required":true,"schema":{"$ref":"#/definitions/User"}}],"responses":{"default":{"description":"successful operation"}}}
    }
  },

  "securityDefinitions":{
    "api_key":{"type":"apiKey","name":"api_key","in":"header"},
    "petstore_auth":{
      "type":"oauth2",
      "authorizationUrl":"https://petstore.swagger.io/oauth/authorize",
      "flow":"implicit",
      "scopes":{"read:pets":"read your pets","write:pets":"modify pets in your account"}
    }
  },

  "definitions":{
    "ApiResponse":{
      "type":"object","properties":{"code":{"type":"integer","format":"int32"},"type":{"type":"string"},"message":{"type":"string"}}
    },
    "Category":{
      "type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"}},"xml":{"name":"Category"}}
    ,
    "Pet":{
      "type":"object","required":["name","photoUrls"],"properties":{"id":{"type":"integer","format":"int64"},"category":{"$ref":"#/definitions/Category"},"name":{"type":"string","example":"doggie"},"photoUrls":{"type":"array","xml":{"wrapped":true},"items":{"type":"string","xml":{"name":"photoUrl"}}},"tags":{"type":"array","xml":{"wrapped":true},"items":{"xml":{"name":"tag"},"$ref":"#/definitions/Tag"}},"status":{"type":"string","description":"pet status in the store","enum":["available","pending","sold"]}},"xml":{"name":"Pet"}
    },
    "Tag":{
      "type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"}},"xml":{"name":"Tag"}
    },
    "Order":{
      "type":"object","properties":{"id":{"type":"integer","format":"int64"},"petId":{"type":"integer","format":"int64"},"quantity":{"type":"integer","format":"int32"},"shipDate":{"type":"string","format":"date-time"},"status":{"type":"string","description":"Order Status","enum":["placed","approved","delivered"]},"complete":{"type":"boolean"}},"xml":{"name":"Order"}
    },
    "User":{
      "type":"object","properties":{"id":{"type":"integer","format":"int64"},"username":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"password":{"type":"string"},"phone": {"type":"string"},"userStatus":{"type":"integer","format":"int32","description":"User Status"}},"xml":{"name":"User"}
    }
  },

  "externalDocs":{
    "description":"Find out more about Swagger",
    "url":"http://swagger.io"
  }
}
```

--------------------------------------------------------------------------------
/src/mcp-server.ts:
--------------------------------------------------------------------------------

```typescript
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import axios from "axios";
import SwaggerParser from "@apidevtools/swagger-parser";
import { OpenAPI } from "openapi-types";
import { Request, Response } from 'express';
import { AuthConfig, ToolInput, SecurityScheme } from './types.js';

let transport: SSEServerTransport | null = null;

export class SwaggerMcpServer {
  private mcpServer: McpServer;
  private swaggerSpec: OpenAPI.Document | null = null;
  private apiBaseUrl: string;
  private defaultAuth: AuthConfig | undefined;
  private securitySchemes: Record<string, SecurityScheme> = {};

  constructor(apiBaseUrl: string, defaultAuth?: AuthConfig) {
    console.debug('constructor', apiBaseUrl, defaultAuth);
    this.apiBaseUrl = apiBaseUrl;
    this.defaultAuth = defaultAuth;
    this.mcpServer = new McpServer({
      name: "Swagger API MCP Server",
      version: "1.0.0",
    });
    this.mcpServer.tool('test', 'test', {
      input: z.object({
        test: z.string(),
      }),
    }, async ({ input }) => {
      return { content: [{ type: "text", text: "Hello, world!" }] };
    });
  }

  private getAuthHeaders(auth?: AuthConfig, operation?: OpenAPI.Operation): Record<string, string> {
    // Use provided auth or fall back to default auth
    const authConfig = auth || this.defaultAuth;
    if (!authConfig) return {};

    // Check if operation requires specific security
    const requiredSchemes = operation?.security || (this.swaggerSpec as any)?.security || [];
    if (requiredSchemes.length === 0) return {};

    switch (authConfig.type) {
      case 'basic':
        if (authConfig.username && authConfig.password) {
          const credentials = Buffer.from(`${authConfig.username}:${authConfig.password}`).toString('base64');
          return { 'Authorization': `Basic ${credentials}` };
        }
        break;
      case 'bearer':
        if (authConfig.token) {
          return { 'Authorization': `Bearer ${authConfig.token}` };
        }
        break;
      case 'apiKey':
        // For Petstore, we know the API key goes in header named 'api_key'
        if (authConfig.apiKey) {
          return { 'api_key': authConfig.apiKey };
        }
        break;
      case 'oauth2':
        if (authConfig.token) {
          return { 'Authorization': `Bearer ${authConfig.token}` };
        }
        break;
    }
    return {};
  }

  private getAuthQueryParams(auth?: AuthConfig): Record<string, string> {
    const authConfig = auth || this.defaultAuth;
    if (!authConfig) return {};

    if (authConfig.type === 'apiKey' && authConfig.apiKey && authConfig.apiKeyName && authConfig.apiKeyIn === 'query') {
      return { [authConfig.apiKeyName]: authConfig.apiKey };
    }

    return {};
  }

  private extractSecuritySchemes() {
    if (!this.swaggerSpec) return;

    // OpenAPI 3.x
    const components = (this.swaggerSpec as any).components;
    if (components && components.securitySchemes) {
      this.securitySchemes = components.securitySchemes;
      return;
    }

    // Swagger 2.0
    const securityDefinitions = (this.swaggerSpec as any).securityDefinitions;
    if (securityDefinitions) {
      this.securitySchemes = securityDefinitions;
    }
  }

  private createAuthSchema(operation?: OpenAPI.Operation): z.ZodType<any> {
    const authTypes: string[] = ['none'];  // Start with 'none' as default
    const authSchema: any = {};

    // Check operation-specific security requirements
    const requiredSchemes = operation?.security || (this.swaggerSpec as any)?.security || [];
    const requiredSchemeNames = new Set(
      requiredSchemes.flatMap((scheme: any) => Object.keys(scheme))
    );

    for (const [key, scheme] of Object.entries(this.securitySchemes)) {
      const securityScheme = scheme as SecurityScheme;
      const isRequired = requiredSchemeNames.has(key);

      switch (securityScheme.type) {
        case 'basic':
          authTypes.push('basic');
          if (isRequired || authTypes.length === 1) {
            authSchema.username = z.string();
            authSchema.password = z.string();
          } else {
            authSchema.username = z.string().optional();
            authSchema.password = z.string().optional();
          }
          break;
        case 'bearer':
        case 'http':
          if (securityScheme.scheme === 'bearer') {
            authTypes.push('bearer');
            authSchema.token = isRequired ? z.string() : z.string().optional();
          }
          break;
        case 'apiKey':
          authTypes.push('apiKey');
          if (isRequired || authTypes.length === 1) {
            authSchema.apiKey = z.string();
            if (securityScheme.in && securityScheme.name) {
              authSchema.apiKeyIn = z.enum(['header', 'query']).default(securityScheme.in as 'header' | 'query');
              authSchema.apiKeyName = z.string().default(securityScheme.name);
            }
          } else {
            authSchema.apiKey = z.string().optional();
            if (securityScheme.in && securityScheme.name) {
              authSchema.apiKeyIn = z.enum(['header', 'query']).optional().default(securityScheme.in as 'header' | 'query');
              authSchema.apiKeyName = z.string().optional().default(securityScheme.name);
            }
          }
          break;
        case 'oauth2':
          authTypes.push('oauth2');
          // Make token optional if API Key auth is available
          authSchema.token = isRequired && !authTypes.includes('apiKey') ? z.string() : z.string().optional();
          break;
      }
    }

    // Add all auth types to the enum - ensure we have at least 'none'
    authSchema.type = z.enum(authTypes as [string, ...string[]]);

    const description = `Authentication configuration. Available methods: ${authTypes.join(', ')}. ` +
      Object.entries(this.securitySchemes)
        .map(([key, scheme]) => {
          const desc = (scheme as SecurityScheme).description || scheme.type;
          const required = requiredSchemeNames.has(key) ? ' (Required)' : ' (Optional)';
          return `${key}: ${desc}${required}`;
        })
        .join('. ');

    return z.object(authSchema).describe(description);
  }

  async loadSwaggerSpec(specUrlOrFile: string) {
    console.debug('Loading Swagger specification from:', specUrlOrFile);
    try {
      // Add auth headers for fetching the swagger spec if needed
      const headers = this.getAuthHeaders();
      this.swaggerSpec = await SwaggerParser.parse(specUrlOrFile, {
        resolve: { http: { headers } }
      }) as OpenAPI.Document;
      
      const info = this.swaggerSpec.info;
      console.debug('Loaded Swagger spec:', {
        title: info.title,
        version: info.version,
        description: info.description?.substring(0, 100) + '...'
      });
      
      // Extract security schemes
      this.extractSecuritySchemes();
      console.debug('Security schemes found:', Object.keys(this.securitySchemes));
      
      // Update server name with API info
      this.mcpServer = new McpServer({
        name: info.title || "Swagger API Server",
        version: info.version || "1.0.0",
        description: info.description || undefined
      });

      await this.registerTools();
    } catch (error) {
      console.error("Failed to load Swagger specification:", error);
      throw error;
    }
  }

  private createZodSchema(parameter: OpenAPI.Parameter): z.ZodType<any> {
    const schema = (parameter as any).schema || parameter;
    
    switch (schema.type) {
      case 'string':
        return z.string().describe(schema.description || '');
      case 'number':
        return z.number().describe(schema.description || '');
      case 'integer':
        return z.number().int().describe(schema.description || '');
      case 'boolean':
        return z.boolean().describe(schema.description || '');
      case 'array':
        return z.array(this.createZodSchema(schema.items)).describe(schema.description || '');
      case 'object':
        if (schema.properties) {
          const shape: { [key: string]: z.ZodType<any> } = {};
          Object.entries(schema.properties).forEach(([key, prop]) => {
            shape[key] = this.createZodSchema(prop as OpenAPI.Parameter);
          });
          return z.object(shape).describe(schema.description || '');
        }
        return z.object({}).describe(schema.description || '');
      default:
        return z.any().describe(schema.description || '');
    }
  }

  private async registerTools() {
    console.debug('Starting tool registration process');
    if (!this.swaggerSpec || !this.swaggerSpec.paths) {
      console.warn('No paths found in Swagger spec');
      return;
    }

    const totalPaths = Object.keys(this.swaggerSpec.paths).length;
    console.debug(`Found ${totalPaths} paths to process`);

    for (const [path, pathItem] of Object.entries(this.swaggerSpec.paths)) {
      if (!pathItem) continue;
      for (const [method, operation] of Object.entries(pathItem)) {
        if (method === '$ref' || !operation) continue;

        const op = operation as OpenAPI.Operation;
        const operationId = op.operationId || `${method}-${path}`;
        console.log(`Register endpoint: ${method.toUpperCase()} ${path} (${operationId})`);

        // Create input schema based on parameters
        const inputShape: { [key: string]: z.ZodType<any> } = {};
        const parameters = op.parameters || [];

        // Add auth parameters based on security schemes
        inputShape['auth'] = this.createAuthSchema(op);

        // Add API parameters
        parameters.forEach((param) => {
          if (param && 'name' in param && param.name) {
            inputShape[param.name] = this.createZodSchema(param);
          }
        });

        console.debug(`Registering tool: ${operationId}`, {
          parameters: Object.keys(inputShape),
          hasAuth: !!inputShape['auth']
        });

        // Register the tool
        this.mcpServer.tool(
          operationId,
          `${op.summary || `${method.toUpperCase()} ${path}`}\n\n${op.description || ''}`,
          {
            input: z.object(inputShape),
          },
          async ({ input }) => {
            console.debug(`Tool called: ${operationId}`, {
              params: Object.keys(input).filter(k => k !== 'auth'),
              hasAuth: !!input.auth
            });
            try {
              const { auth, ...params } = input as ToolInput;
              console.debug('params', params);
              let url = this.apiBaseUrl + path;
              
              // Separate path parameters from query parameters
              const pathParams = new Set();
              path.split('/').forEach(segment => {
                if (segment.startsWith('{') && segment.endsWith('}')) {
                  pathParams.add(segment.slice(1, -1));
                }
              });

              // Replace path parameters
              Object.entries(params).forEach(([key, value]) => {
                if (pathParams.has(key)) {
                  url = url.replace(`{${key}}`, encodeURIComponent(String(value)));
                }
              });

              // Build query parameters object for GET requests
              const queryObject = method === 'get' ? 
                Object.entries(params)
                  .filter(([key]) => !pathParams.has(key))
                  .reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {})
                : {};

              const headers = this.getAuthHeaders(auth, op);
              const queryParams = this.getAuthQueryParams(auth);

              console.debug('url', url);
              console.debug('method', method);
              console.debug('headers', headers);
              console.debug('params', params);
              console.debug('queryParams', queryParams);
              
              const response = await axios({
                method: method as string,
                url: url,
                headers,
                data: method !== 'get' ? params : undefined,
                params: { ...queryObject, ...queryParams },
                paramsSerializer: (params) => {
                  const searchParams = new URLSearchParams();
                  Object.entries(params).forEach(([key, value]) => {
                    if (Array.isArray(value)) {
                      // Handle arrays by adding multiple entries with the same key
                      value.forEach(v => searchParams.append(key, v));
                    } else {
                      searchParams.append(key, value as string);
                    }
                  });
                  return searchParams.toString();
                }
              });
              console.debug('response.headers', response.headers);
              console.debug('response.data', response.data);

              return {
                content: [
                  { type: "text", text: JSON.stringify(response.data, null, 2) },
                  // http status code
                  { type: "text", text: `HTTP Status Code: ${response.status}` },
                  // // http headers
                  // { type: "text", text: JSON.stringify(response.headers, null, 2) },
                ],
              };
            } catch (error) {
              console.error(`Error in ${operationId}:`, error);
              if (axios.isAxiosError(error) && error.response) {
                return {
                  content: [{ 
                    type: "text", text: `Error ${error.response.status}: ${JSON.stringify(error.response.data, null, 2)}` 
                  }],
                };
              }
              return {
                content: [{ type: "text", text: `Error: ${error}` }],
              };
            }
          }
        );
      }
    }
  }

  getServer() {
    return this.mcpServer;
  }

  handleSSE(res: Response) {
    console.debug('MCP handleSSE');
    transport = new SSEServerTransport("/messages", res);
    this.mcpServer.connect(transport);
  }

  handleMessage(req: Request, res: Response) {
    console.debug('MCP handleMessage', req.body);
    if (transport) {
      try {
        transport.handlePostMessage(req, res);
      } catch (error) {
        console.error('Error handling message:', error);
      }
    } else {
      console.warn('no transport');
    }
  }
}

```