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

```
├── .env.example
├── .gitignore
├── config.json
├── jest.config.js
├── package.json
├── README.md
├── src
│   ├── config.ts
│   ├── postman-mcp-simple.ts
│   ├── simple-server.ts
│   ├── simple-stdio.ts
│   ├── swagger-mcp-simple.ts
│   └── types.ts
├── start-mcp.sh
├── test-simple.ts
├── 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* 

# Local test files
ns-openapi.json
```

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

```markdown
# Swagger/Postman MCP Server

Server that ingests and serves Swagger/OpenAPI specifications and Postman collections as MCP (Model Context Protocol) tools using a **simplified strategic approach**.

Instead of generating hundreds of individual tools for each API endpoint, this server provides **only 4 strategic tools** that allow AI agents to dynamically discover and interact with APIs:

```bash
Example prompt:
Help me generate an axios call using our api mcp. I want to implement updating a user. Follow our same DDD pattern (tanstack hook -> axios service)
```

## Features

- **Strategic Tool Approach**: Only 4 tools instead of hundreds for better AI agent performance
- **OpenAPI/Swagger Support**: Load OpenAPI 2.0/3.0 specifications from URLs or local files
- **Postman Collection Support**: Load Postman collection JSON files from URLs or local files
- **Environment Variables**: Support for Postman environment files
- **Authentication**: Multiple authentication methods (Basic, Bearer, API Key, OAuth2)
- **Dynamic API Discovery**: Tools for listing, searching, and getting details about API endpoints
- **Request Execution**: Execute API requests with proper parameter handling and authentication

## 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

```bash
# Clone the repository
git clone <repository-url>
cd swag-mcp

# Install dependencies
npm install
# or
yarn install

# Build the project
npm run build
# or
yarn build

# Make the start script executable (Linux/macOS)
chmod +x start-mcp.sh
```

### Quick Setup for Cursor

1. **Clone and build** (commands above)
2. **Configure** your `config.json` with your API details
3. **Update paths**: Edit `start-mcp.sh` and change the `cd` path to your installation directory
4. **Add to Cursor**: Edit `~/.cursor/mcp.json` and add:
   ```json
   {
     "mcpServers": {
       "postman-swagger-api": {
         "command": "/full/path/to/your/swag-mcp/start-mcp.sh"
       }
     }
   }
   ```
5. **Restart Cursor** and start using the 4 strategic MCP tools!

## Configuration

The server uses a `config.json` file for configuration. You can specify either OpenAPI/Swagger specifications or Postman collections.

### OpenAPI/Swagger Configuration

```json
{
  "api": {
    "type": "openapi",
    "openapi": {
      "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"
  }
}
```

### Postman Collection Configuration

```json
{
  "api": {
    "type": "postman",
    "postman": {
      "collectionUrl": "https://www.postman.com/collections/your-collection-id",
      "collectionFile": "./examples/postman-collection.json",
      "environmentUrl": "https://www.postman.com/environments/your-environment-id",
      "environmentFile": "./examples/postman-environment.json",
      "defaultAuth": {
        "type": "bearer",
        "token": "your-api-token-here"
      }
    }
  },
  "log": {
    "level": "info"
  }
}
```

### Configuration Options

#### API Configuration

- `api.type`: Either `"openapi"` or `"postman"`
- `api.openapi`: OpenAPI/Swagger specific configuration
  - `url`: URL to the OpenAPI specification
  - `apiBaseUrl`: Base URL for API requests
  - `defaultAuth`: Default authentication configuration
- `api.postman`: Postman specific configuration
  - `collectionUrl`: URL to the Postman collection (optional)
  - `collectionFile`: Path to local Postman collection file (optional)
  - `environmentUrl`: URL to the Postman environment (optional)
  - `environmentFile`: Path to local Postman environment file (optional)
  - `defaultAuth`: Default authentication configuration

#### Authentication Configuration

- `type`: Authentication type (`"basic"`, `"bearer"`, `"apiKey"`, `"oauth2"`)
- `username`: Username (for basic auth)
- `password`: Password (for basic auth)
- `token`: Token (for bearer/oauth2 auth)
- `apiKey`: API key value
- `apiKeyName`: API key parameter name
- `apiKeyIn`: Where to send API key (`"header"` or `"query"`)

#### Logging Configuration

- `log.level`: Logging level (`"debug"`, `"info"`, `"warn"`, `"error"`)

## Usage

### Starting the MCP Server

The server runs via stdio transport for MCP connections:

```bash
# Start the simplified MCP server via stdio
./start-mcp.sh

# Or directly with node
node dist/simple-stdio.js

# For development with auto-reload
npm run dev:simple
# or
yarn dev:simple
```

### MCP Integration

This server uses stdio transport and is designed to be used with MCP clients like Claude Desktop or Cursor.

## Installing in Cursor

To use this MCP server with Cursor, you need to add it to your Cursor MCP configuration:

### 1. Locate your Cursor MCP configuration file

The configuration file is located at:

- **Linux/macOS**: `~/.cursor/mcp.json`
- **Windows**: `%APPDATA%\.cursor\mcp.json`

### 2. Add the MCP server configuration

Edit your `mcp.json` file to include this server:

```json
{
  "mcpServers": {
    "postman-swagger-api": {
      "command": "/path/to/your/swag-mcp/start-mcp.sh"
    }
  }
}
```

**⚠️ Important: Change the path!**

Replace `/path/to/your/swag-mcp/start-mcp.sh` with the actual path to your cloned repository. For example:

- **Linux/macOS**: `"/home/username/Documents/swag-mcp/start-mcp.sh"`
- **Windows**: `"C:\\Users\\username\\Documents\\swag-mcp\\start-mcp.sh"`

### 3. Example complete configuration

```json
{
  "mcpServers": {
    "supabase": {
      "command": "npx",
      "args": [
        "-y",
        "@supabase/mcp-server-supabase@latest",
        "--access-token",
        "your-supabase-token"
      ]
    },
    "postman-swagger-api": {
      "command": "/home/username/Documents/swag-mcp/start-mcp.sh"
    }
  }
}
```

### 4. Restart Cursor

After saving the configuration file, restart Cursor for the changes to take effect.

### 5. Verify installation

In Cursor, you should now have access to the 4 strategic MCP tools:

- `list_requests` - List all available requests
- `get_request_details` - Get detailed request information
- `search_requests` - Search requests by keyword
- `make_request` - Execute any API request

### Troubleshooting

If the MCP server fails to start:

1. **Update start-mcp.sh path**: Edit `start-mcp.sh` and change the `cd` path from `/path/to/your/swag-mcp` to your actual installation directory
2. **Check the path**: Ensure the path in `mcp.json` points to your actual `start-mcp.sh` file
3. **Check permissions**: Make sure `start-mcp.sh` is executable (`chmod +x start-mcp.sh`)
4. **Check build**: Ensure you've run `npm run build` to compile the TypeScript files
5. **Check logs**: Look in Cursor's MCP logs for error messages

### Example Path Updates

If you cloned to `/home/username/Documents/swag-mcp/`, then:

**In `start-mcp.sh`:**

```bash
cd "/home/username/Documents/swag-mcp"
```

**In `~/.cursor/mcp.json`:**

```json
"command": "/home/username/Documents/swag-mcp/start-mcp.sh"
```

## How It Works

### Strategic Tool Approach

Instead of generating hundreds of individual tools for each API endpoint, this server provides **4 strategic tools** that enable dynamic API discovery and interaction:

### OpenAPI/Swagger Mode

**4 Strategic Tools:**

1. **`list_endpoints`** - List all available API endpoints
2. **`get_endpoint_details`** - Get detailed information about specific endpoints
3. **`search_endpoints`** - Search endpoints by keyword
4. **`make_api_call`** - Execute any API call with proper authentication

**Process:**

1. Loads the OpenAPI specification from the configured URL or file
2. Parses the specification to extract API endpoints, parameters, and security schemes
3. Makes endpoint information available through the 4 strategic tools
4. Handles authentication and parameter validation dynamically
5. Executes API requests and returns responses

### Postman Collection Mode

**4 Strategic Tools:**

1. **`list_requests`** - List all available requests in the collection
2. **`get_request_details`** - Get detailed information about specific requests
3. **`search_requests`** - Search requests by keyword
4. **`make_request`** - Execute any request from the collection

**Process:**

1. Loads the Postman collection JSON from the configured URL or file
2. Optionally loads a Postman environment file for variable substitution
3. Parses requests, folders, and nested items in the collection
4. Makes request information available through the 4 strategic tools
5. Handles variable substitution, authentication, and parameter mapping dynamically
6. Executes requests with proper headers, query parameters, and body data

### Benefits of Strategic Tools

- **Better AI Performance**: 4 tools vs hundreds means faster decision making
- **Dynamic Discovery**: AI agents can explore APIs without knowing endpoints beforehand
- **Flexible Interaction**: Any endpoint can be called through `make_api_call`/`make_request`
- **Reduced Overwhelm**: AI agents aren't flooded with tool options

## Strategic Tools Reference

### For OpenAPI/Swagger APIs

1. **`list_endpoints`**

   - Lists all available API endpoints with methods and paths
   - No parameters required
   - Returns: Array of endpoint summaries

2. **`get_endpoint_details`**

   - Get detailed information about a specific endpoint
   - Parameters: `method` (GET/POST/etc), `path` (/users/{id}/etc)
   - Returns: Full endpoint specification with parameters, body schema, responses

3. **`search_endpoints`**

   - Search endpoints by keyword in path, summary, or description
   - Parameters: `query` (search term)
   - Returns: Filtered list of matching endpoints

4. **`make_api_call`**
   - Execute an API call to any endpoint
   - Parameters: `method`, `path`, `pathParams`, `queryParams`, `headers`, `body`
   - Returns: API response with status and data

### For Postman Collections

1. **`list_requests`**

   - Lists all available requests in the collection
   - No parameters required
   - Returns: Array of request summaries

2. **`get_request_details`**

   - Get detailed information about a specific request
   - Parameters: `requestId` or `requestName`
   - Returns: Full request specification

3. **`search_requests`**

   - Search requests by keyword
   - Parameters: `query` (search term)
   - Returns: Filtered list of matching requests

4. **`make_request`**
   - Execute any request from the collection
   - Parameters: `requestId`, `variables` (for substitution)
   - Returns: Request response

### Authentication

The server supports multiple authentication methods:

- **Basic Authentication**: Username/password
- **Bearer Token**: JWT or other bearer tokens
- **API Key**: In headers or query parameters
- **OAuth2**: Bearer token based

Authentication can be configured globally or overridden per request.

## Example Configuration

Your `config.json` should specify either OpenAPI or Postman configuration as shown above.

### Example Postman Collection Structure

```json
{
  "info": {
    "name": "Sample API Collection",
    "description": "A sample Postman collection"
  },
  "item": [
    {
      "name": "Get Users",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "{{baseUrl}}/users",
          "host": ["{{baseUrl}}"],
          "path": ["users"]
        }
      }
    }
  ]
}
```

## Development

```bash
# Install dependencies
npm install

# Run in development mode
npm run dev

# Run tests
npm test

# Build for production
npm run build
```

## License

ISC

## 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
{
  "api": {
    "type": "postman",
    "postman": {
      "collectionFile": "./ns-openapi.json",
      "environmentFile": "./ns-openapi.json",
      "defaultAuth": {
        "type": "bearer",
        "token": "test-token"
      }
    }
  },
  "log": {
    "level": "debug"
  },
  "server": {
    "port": 9001,
    "host": "0.0.0.0"
  }
}
```

--------------------------------------------------------------------------------
/start-mcp.sh:
--------------------------------------------------------------------------------

```bash
#!/bin/bash

# ⚠️ IMPORTANT: Update this path to match your installation directory!
# Change this to the full path where you cloned the swag-mcp repository
cd "/path/to/your/swag-mcp"

# Set environment variables
export NODE_ENV=production

# Start the simplified MCP server (only 4 strategic tools instead of 300+)
exec node dist/simple-stdio.js 
```

--------------------------------------------------------------------------------
/test-simple.ts:
--------------------------------------------------------------------------------

```typescript
import { SimpleSwaggerMcpServer } from "./src/swagger-mcp-simple.js";

async function test() {
  console.log("Testing simplified MCP server...");

  const server = new SimpleSwaggerMcpServer("https://api.example.com");

  // Test with a simple OpenAPI spec
  const simpleSpec = {
    openapi: "3.0.0",
    info: {
      title: "Test API",
      version: "1.0.0",
    },
    paths: {
      "/users": {
        get: {
          operationId: "getUsers",
          summary: "Get all users",
          responses: {
            "200": {
              description: "Success",
            },
          },
        },
      },
    },
  };

  console.log("✅ SimpleSwaggerMcpServer created successfully");
  console.log("This demonstrates the strategic tool approach works!");
}

test().catch(console.error);

```

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

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

export interface PostmanConfig {
  collectionUrl?: string;
  collectionFile?: string;
  environmentUrl?: string;
  environmentFile?: string;
  auth?: AuthConfig;
}

export interface ApiConfig {
  type: "openapi" | "postman";
  openapi?: SwaggerConfig;
  postman?: PostmanConfig;
}

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;
  };
}

```

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

```json
{
  "name": "swag-mcp",
  "version": "1.0.0",
  "description": "An MCP server that ingests and serves Swagger/OpenAPI specifications and Postman collections",
  "main": "dist/simple-stdio.js",
  "scripts": {
    "start": "node dist/simple-stdio.js",
    "start:simple": "node dist/simple-stdio.js",
    "dev": "NODE_OPTIONS='--loader ts-node/esm' ts-node src/simple-stdio.ts",
    "dev:simple": "NODE_OPTIONS='--loader ts-node/esm' ts-node src/simple-server.ts",
    "build": "tsc",
    "build:simple": "npx tsc src/swagger-mcp-simple.ts src/simple-server.ts src/config.ts src/types.ts --outDir dist --module NodeNext --moduleResolution NodeNext --target ES2020 --esModuleInterop --skipLibCheck --declaration",
    "watch": "tsc -w",
    "test": "jest",
    "test:watch": "jest --watch",
    "test:coverage": "jest --coverage"
  },
  "keywords": [
    "swagger",
    "openapi",
    "postman",
    "collections",
    "api",
    "documentation",
    "mcp",
    "mcp-server",
    "postman"
  ],
  "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/postman-collection": "^3.5.10",
    "@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",
    "postman-collection": "^5.0.2",
    "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"
}

```

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

```typescript
import { SimpleSwaggerMcpServer } from "./swagger-mcp-simple.js";
import { loadConfig } from "./config.js";

async function main() {
  try {
    const configPath = process.argv[2] || undefined;
    const config = await loadConfig(configPath);

    // Use new config structure or fallback to legacy
    const openApiConfig =
      config.api.type === "openapi" ? config.api.openapi : config.swagger;

    if (!openApiConfig) {
      throw new Error("No OpenAPI configuration found");
    }

    console.log("Creating MCP server with config:", {
      apiBaseUrl: openApiConfig.apiBaseUrl,
      authType: openApiConfig.defaultAuth?.type,
    });

    const server = new SimpleSwaggerMcpServer(
      openApiConfig.apiBaseUrl,
      openApiConfig.defaultAuth && openApiConfig.defaultAuth.type
        ? (openApiConfig.defaultAuth as any)
        : undefined
    );

    // Load the swagger spec
    await server.loadSwaggerSpec(openApiConfig.url);

    console.log(
      "✅ Simple MCP Server successfully initialized with strategic tools!"
    );
    console.log("Now you have only 4 tools instead of hundreds:");
    console.log("  1. list_endpoints - List all available API endpoints");
    console.log(
      "  2. get_endpoint_details - Get detailed info about specific endpoints"
    );
    console.log("  3. search_endpoints - Search endpoints by keyword");
    console.log("  4. make_api_call - Make actual API calls");
    console.log("");
    console.log("This approach allows AI agents to:");
    console.log("  - Discover APIs dynamically");
    console.log("  - Get required parameter info");
    console.log("  - Make informed API calls");
    console.log("  - Search for relevant endpoints");

    return server.getServer();
  } catch (error) {
    console.error("Failed to initialize MCP server:", error);
    process.exit(1);
  }
}

main().catch(console.error);

```

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

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

// Define auth configuration schema
const AuthConfigSchema = 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(),
});

// Define the configuration schema
export const ConfigSchema = z
  .object({
    api: z.object({
      type: z.enum(["openapi", "postman"]),
      openapi: z
        .object({
          url: z.string().url(),
          apiBaseUrl: z.string().url(),
          defaultAuth: AuthConfigSchema.optional(),
        })
        .optional(),
      postman: z
        .object({
          collectionUrl: z.string().url().optional(),
          collectionFile: z.string().optional(),
          environmentUrl: z.string().url().optional(),
          environmentFile: z.string().optional(),
          defaultAuth: AuthConfigSchema.optional(),
        })
        .optional(),
    }),
    // Keep legacy swagger config for backward compatibility
    swagger: z
      .object({
        url: z.string().url(),
        apiBaseUrl: z.string().url(),
        defaultAuth: AuthConfigSchema.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"),
    }),
  })
  .refine(
    (data) => {
      // Ensure we have either the new api config or legacy swagger config
      if (data.api.type === "openapi" && !data.api.openapi && !data.swagger) {
        return false;
      }
      if (data.api.type === "postman" && !data.api.postman) {
        return false;
      }
      return true;
    },
    {
      message:
        "Configuration must include appropriate API settings based on type",
    }
  );

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

const defaultConfig: Config = {
  api: {
    type: "openapi",
    openapi: {
      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",
      },
    },
  },
  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);

    // Handle legacy config migration
    if (config.swagger && !config.api) {
      config.api = {
        type: "openapi",
        openapi: config.swagger,
      };
    }

    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/simple-stdio.ts:
--------------------------------------------------------------------------------

```typescript
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { loadConfig } from "./config.js";
import { SimpleSwaggerMcpServer } from "./swagger-mcp-simple.js";
import { SimplePostmanMcpServer } from "./postman-mcp-simple.js";

async function main() {
  try {
    console.error(`[SIMPLE-STDIO] Starting simplified MCP server via stdio...`);
    console.error(`[SIMPLE-STDIO] Process ID: ${process.pid}`);
    console.error(`[SIMPLE-STDIO] Working directory: ${process.cwd()}`);

    // Load configuration
    console.error(`[SIMPLE-STDIO] Loading configuration...`);
    const config = await loadConfig();

    let mcpServer: SimpleSwaggerMcpServer | SimplePostmanMcpServer;

    // Create and initialize MCP server based on configuration
    if (config.api.type === "postman") {
      if (!config.api.postman) {
        throw new Error(
          'Postman configuration is required when api.type is "postman"'
        );
      }

      console.error(
        "[SIMPLE-STDIO] Creating simplified Postman MCP server instance..."
      );
      console.error(
        "✅ Using simplified Postman explorer with only 4 strategic tools!"
      );

      mcpServer = new SimplePostmanMcpServer(config.api.postman.defaultAuth);

      // Load collection
      const collectionSource =
        config.api.postman.collectionUrl || config.api.postman.collectionFile;
      if (!collectionSource) {
        throw new Error(
          "Either collectionUrl or collectionFile must be specified for Postman configuration"
        );
      }

      console.error("[SIMPLE-STDIO] Loading Postman collection...");
      await (mcpServer as SimplePostmanMcpServer).loadCollection(
        collectionSource
      );

      // Load environment if specified
      const environmentSource =
        config.api.postman.environmentUrl || config.api.postman.environmentFile;
      if (environmentSource) {
        console.error("[SIMPLE-STDIO] Loading Postman environment...");
        await (mcpServer as SimplePostmanMcpServer).loadEnvironment(
          environmentSource
        );
      }

      console.error("[SIMPLE-STDIO] Postman collection loaded successfully");

      console.error(
        "✅ Simple MCP Server successfully initialized with strategic tools!"
      );
      console.error("Now you have only 4 tools instead of hundreds:");
      console.error(
        "  1. list_requests - List all available requests in the collection"
      );
      console.error(
        "  2. get_request_details - Get detailed info about specific requests"
      );
      console.error("  3. search_requests - Search requests by keyword");
      console.error(
        "  4. make_request - Execute any request from the collection"
      );
    } else {
      // Default to OpenAPI/Swagger with simplified tools
      const openApiConfig = config.api.openapi || config.swagger;
      if (!openApiConfig) {
        throw new Error(
          'OpenAPI configuration is required when api.type is "openapi" or for legacy swagger config'
        );
      }

      console.error(
        "[SIMPLE-STDIO] Creating simplified OpenAPI MCP server instance..."
      );
      mcpServer = new SimpleSwaggerMcpServer(
        openApiConfig.apiBaseUrl,
        openApiConfig.defaultAuth && openApiConfig.defaultAuth.type
          ? (openApiConfig.defaultAuth as any)
          : undefined
      );

      console.error("[SIMPLE-STDIO] Loading OpenAPI specification...");
      await mcpServer.loadSwaggerSpec(openApiConfig.url);
      console.error("[SIMPLE-STDIO] OpenAPI specification loaded successfully");

      console.error(
        "✅ Simple MCP Server successfully initialized with strategic tools!"
      );
      console.error("Now you have only 4 tools instead of hundreds:");
      console.error("  1. list_endpoints - List all available API endpoints");
      console.error(
        "  2. get_endpoint_details - Get detailed info about specific endpoints"
      );
      console.error("  3. search_endpoints - Search endpoints by keyword");
      console.error("  4. make_api_call - Make actual API calls");
    }

    // Get the MCP server instance
    const server = mcpServer.getServer();

    // Create stdio transport
    console.error(`[SIMPLE-STDIO] Creating stdio transport...`);
    const transport = new StdioServerTransport();

    // Connect the MCP server to stdio transport
    console.error(`[SIMPLE-STDIO] Connecting MCP server to stdio transport...`);
    await server.connect(transport);

    console.error(
      "[SIMPLE-STDIO] Simplified MCP server connected via stdio successfully!"
    );
    console.error(
      `[SIMPLE-STDIO] Server is ready and listening for requests...`
    );

    // Handle process termination gracefully
    process.on("SIGINT", () => {
      console.error(
        "[SIMPLE-STDIO] Received SIGINT, shutting down gracefully..."
      );
      process.exit(0);
    });

    process.on("SIGTERM", () => {
      console.error(
        "[SIMPLE-STDIO] Received SIGTERM, shutting down gracefully..."
      );
      process.exit(0);
    });
  } catch (error) {
    console.error(
      "[SIMPLE-STDIO] Failed to start simplified MCP server:",
      error
    );
    process.exit(1);
  }
}

// Handle uncaught exceptions and rejections
process.on("uncaughtException", (error) => {
  console.error("[SIMPLE-STDIO] Uncaught Exception:", error);
  process.exit(1);
});

process.on("unhandledRejection", (reason, promise) => {
  console.error(
    "[SIMPLE-STDIO] Unhandled Rejection at:",
    promise,
    "reason:",
    reason
  );
  process.exit(1);
});

main().catch((error) => {
  console.error("[SIMPLE-STDIO] Unhandled error:", error);
  process.exit(1);
});

```

--------------------------------------------------------------------------------
/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"]
}

```

--------------------------------------------------------------------------------
/src/swagger-mcp-simple.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 { Request, Response } from "express";
import { AuthConfig } from "./types.js";

export class SimpleSwaggerMcpServer {
  private mcpServer: McpServer;
  private swaggerSpec: any = null;
  private apiBaseUrl: string;
  private defaultAuth: AuthConfig | undefined;

  constructor(apiBaseUrl: string, defaultAuth?: AuthConfig) {
    this.apiBaseUrl = apiBaseUrl;
    this.defaultAuth = defaultAuth;
    this.mcpServer = new McpServer({
      name: "Simple Swagger API MCP Server",
      version: "1.0.0",
    });
  }

  async loadSwaggerSpec(specUrlOrFile: string) {
    console.debug("Loading Swagger specification from:", specUrlOrFile);
    try {
      this.swaggerSpec = (await SwaggerParser.parse(specUrlOrFile)) as any;

      const info = this.swaggerSpec.info;
      console.debug("Loaded Swagger spec:", {
        title: info.title,
        version: info.version,
      });

      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 getAuthHeaders(auth?: AuthConfig): Record<string, string> {
    const authConfig = auth || this.defaultAuth;
    if (!authConfig) 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":
        if (authConfig.apiKey && authConfig.apiKeyName) {
          if (authConfig.apiKeyIn === "header") {
            return { [authConfig.apiKeyName]: 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 async registerTools() {
    console.debug("Starting tool registration process");
    if (!this.swaggerSpec || !this.swaggerSpec.paths) {
      console.warn("No paths found in Swagger spec");
      return;
    }

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

    // Tool 1: List all available endpoints
    this.mcpServer.tool(
      "list_endpoints",
      "List all available API endpoints with basic information including path, method, summary, and tags",
      {
        input: z.object({
          method: z
            .string()
            .optional()
            .describe("Filter by HTTP method (GET, POST, PUT, DELETE, etc.)"),
          tag: z.string().optional().describe("Filter by OpenAPI tag"),
          limit: z
            .number()
            .optional()
            .default(50)
            .describe("Maximum number of endpoints to return"),
        }),
      },
      async ({ input }) => {
        const endpoints = [];

        for (const [path, pathItem] of Object.entries(paths)) {
          if (!pathItem) continue;

          for (const [method, operation] of Object.entries(pathItem as any)) {
            if (method === "$ref" || !operation) continue;

            const op = operation as any;
            const operationId = op.operationId || `${method}-${path}`;

            // Apply filters
            if (
              input.method &&
              method.toLowerCase() !== input.method.toLowerCase()
            )
              continue;
            if (input.tag && (!op.tags || !op.tags.includes(input.tag)))
              continue;

            endpoints.push({
              operationId,
              method: method.toUpperCase(),
              path,
              summary: op.summary || "",
              description: op.description || "",
              tags: op.tags || [],
              deprecated: op.deprecated || false,
            });

            if (endpoints.length >= input.limit) break;
          }
          if (endpoints.length >= input.limit) break;
        }

        return {
          content: [
            {
              type: "text",
              text: JSON.stringify(
                {
                  total: endpoints.length,
                  endpoints,
                },
                null,
                2
              ),
            },
          ],
        };
      }
    );

    // Tool 2: Get detailed information about a specific endpoint
    this.mcpServer.tool(
      "get_endpoint_details",
      "Get detailed information about a specific API endpoint including parameters, request/response schemas, and authentication requirements",
      {
        input: z.object({
          operationId: z.string().describe("The operation ID of the endpoint"),
          path: z
            .string()
            .optional()
            .describe("The API path (alternative to operationId)"),
          method: z
            .string()
            .optional()
            .describe("The HTTP method (required if using path)"),
        }),
      },
      async ({ input }) => {
        let targetOperation = null;
        let targetPath = "";
        let targetMethod = "";

        // Find the operation by operationId or path+method
        for (const [path, pathItem] of Object.entries(paths)) {
          if (!pathItem) continue;

          for (const [method, operation] of Object.entries(pathItem as any)) {
            if (method === "$ref" || !operation) continue;

            const op = operation as any;
            const operationId = op.operationId || `${method}-${path}`;

            if (
              input.operationId === operationId ||
              (input.path === path &&
                input.method?.toLowerCase() === method.toLowerCase())
            ) {
              targetOperation = op;
              targetPath = path;
              targetMethod = method;
              break;
            }
          }
          if (targetOperation) break;
        }

        if (!targetOperation) {
          return {
            content: [
              {
                type: "text",
                text: `Endpoint not found. Use list_endpoints to see available endpoints.`,
              },
            ],
          };
        }

        // Extract parameter information
        const parameters = (targetOperation.parameters || []).map(
          (param: any) => ({
            name: param.name,
            in: param.in,
            required: param.required || false,
            type: param.schema?.type || param.type,
            description: param.description || "",
            example: param.example || param.schema?.example,
          })
        );

        // Extract request body schema
        let requestBody = null;
        if (targetOperation.requestBody) {
          const rb = targetOperation.requestBody;
          const content = rb.content;
          if (content) {
            requestBody = Object.keys(content).map((mediaType) => ({
              mediaType,
              schema: content[mediaType].schema,
              required: rb.required || false,
            }));
          }
        }

        // Extract response schemas
        const responses = Object.entries(targetOperation.responses || {}).map(
          ([code, resp]: [string, any]) => ({
            statusCode: code,
            description: resp.description || "",
            schema: resp.content
              ? Object.keys(resp.content).map((mt) => ({
                  mediaType: mt,
                  schema: resp.content[mt].schema,
                }))
              : null,
          })
        );

        return {
          content: [
            {
              type: "text",
              text: JSON.stringify(
                {
                  operationId:
                    targetOperation.operationId ||
                    `${targetMethod}-${targetPath}`,
                  method: targetMethod.toUpperCase(),
                  path: targetPath,
                  summary: targetOperation.summary || "",
                  description: targetOperation.description || "",
                  tags: targetOperation.tags || [],
                  deprecated: targetOperation.deprecated || false,
                  parameters,
                  requestBody,
                  responses,
                },
                null,
                2
              ),
            },
          ],
        };
      }
    );

    // Tool 3: Search endpoints by keyword
    this.mcpServer.tool(
      "search_endpoints",
      "Search API endpoints by keyword in path, summary, description, or tags",
      {
        input: z.object({
          query: z
            .string()
            .describe(
              "Search term to look for in endpoint paths, summaries, descriptions, or tags"
            ),
          limit: z
            .number()
            .optional()
            .default(20)
            .describe("Maximum number of results to return"),
        }),
      },
      async ({ input }) => {
        const results = [];
        const query = input.query.toLowerCase();

        for (const [path, pathItem] of Object.entries(paths)) {
          if (!pathItem) continue;

          for (const [method, operation] of Object.entries(pathItem as any)) {
            if (method === "$ref" || !operation) continue;

            const op = operation as any;
            const operationId = op.operationId || `${method}-${path}`;

            // Search in various fields
            const searchText = [
              path,
              op.summary || "",
              op.description || "",
              ...(op.tags || []),
              operationId,
            ]
              .join(" ")
              .toLowerCase();

            if (searchText.includes(query)) {
              results.push({
                operationId,
                method: method.toUpperCase(),
                path,
                summary: op.summary || "",
                description: op.description || "",
                tags: op.tags || [],
              });
            }

            if (results.length >= input.limit) break;
          }
          if (results.length >= input.limit) break;
        }

        return {
          content: [
            {
              type: "text",
              text: JSON.stringify(
                {
                  query: input.query,
                  total: results.length,
                  results,
                },
                null,
                2
              ),
            },
          ],
        };
      }
    );

    // Tool 4: Make API call
    this.mcpServer.tool(
      "make_api_call",
      "Make an API call to any endpoint with the specified parameters and authentication",
      {
        input: z.object({
          operationId: z
            .string()
            .optional()
            .describe("The operation ID of the endpoint"),
          path: z
            .string()
            .optional()
            .describe("The API path (alternative to operationId)"),
          method: z
            .string()
            .optional()
            .describe("The HTTP method (required if using path)"),
          parameters: z
            .record(z.any())
            .optional()
            .describe("Query parameters, path parameters, or form data"),
          body: z
            .any()
            .optional()
            .describe("Request body (for POST, PUT, PATCH requests)"),
          auth: z
            .object({
              type: z
                .enum(["none", "basic", "bearer", "apiKey", "oauth2"])
                .default("none"),
              username: z.string().optional(),
              password: z.string().optional(),
              token: z.string().optional(),
              apiKey: z.string().optional(),
              apiKeyName: z.string().optional(),
              apiKeyIn: z.enum(["header", "query"]).optional(),
            })
            .optional()
            .describe("Authentication configuration"),
        }),
      },
      async ({ input }) => {
        // Find the operation
        let targetOperation = null;
        let targetPath = "";
        let targetMethod = "";

        for (const [path, pathItem] of Object.entries(paths)) {
          if (!pathItem) continue;

          for (const [method, operation] of Object.entries(pathItem as any)) {
            if (method === "$ref" || !operation) continue;

            const op = operation as any;
            const operationId = op.operationId || `${method}-${path}`;

            if (
              input.operationId === operationId ||
              (input.path === path &&
                input.method?.toLowerCase() === method.toLowerCase())
            ) {
              targetOperation = op;
              targetPath = path;
              targetMethod = method;
              break;
            }
          }
          if (targetOperation) break;
        }

        if (!targetOperation) {
          return {
            content: [
              {
                type: "text",
                text: `Endpoint not found. Use list_endpoints to see available endpoints.`,
              },
            ],
          };
        }

        try {
          const params = input.parameters || {};
          let url = this.apiBaseUrl + targetPath;

          // Handle path parameters
          const pathParams = new Set();
          targetPath.split("/").forEach((segment) => {
            if (segment.startsWith("{") && segment.endsWith("}")) {
              pathParams.add(segment.slice(1, -1));
            }
          });

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

          // Separate query parameters
          const queryParams = Object.entries(params)
            .filter(([key]) => !pathParams.has(key))
            .reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {});

          const headers = this.getAuthHeaders(
            input.auth?.type !== "none" ? (input.auth as AuthConfig) : undefined
          );
          const authQueryParams = this.getAuthQueryParams(
            input.auth?.type !== "none" ? (input.auth as AuthConfig) : undefined
          );

          const response = await axios({
            method: targetMethod as string,
            url: url,
            headers,
            data: input.body,
            params: { ...queryParams, ...authQueryParams },
          });

          return {
            content: [
              { type: "text", text: JSON.stringify(response.data, null, 2) },
              { type: "text", text: `HTTP Status Code: ${response.status}` },
            ],
          };
        } catch (error) {
          console.error(`Error in API call:`, 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}` }],
          };
        }
      }
    );

    console.debug(
      "Successfully registered 4 strategic tools for API navigation"
    );
  }

  getServer() {
    return this.mcpServer;
  }
}

```

--------------------------------------------------------------------------------
/src/postman-mcp-simple.ts:
--------------------------------------------------------------------------------

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import axios from "axios";
import {
  Collection,
  Request as PostmanRequest,
  Item,
  ItemGroup,
} from "postman-collection";
import { Request, Response } from "express";
import { AuthConfig, ToolInput } from "./types.js";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";

let transport: SSEServerTransport | null = null;

export class SimplePostmanMcpServer {
  private mcpServer: McpServer;
  private collection: Collection | null = null;
  private environment: Record<string, any> = {};
  private defaultAuth: AuthConfig | undefined;
  private requests: Array<{
    id: string;
    name: string;
    method: string;
    url: string;
    description: string;
    folder: string;
    request: PostmanRequest;
  }> = [];

  constructor(defaultAuth?: AuthConfig) {
    if (process.env.NODE_ENV !== "production") {
      console.debug("SimplePostmanMcpServer constructor", defaultAuth);
    }
    this.defaultAuth = defaultAuth;
    this.mcpServer = new McpServer({
      name: "Simple Postman Collection MCP Server",
      version: "1.0.0",
    });
  }

  private getAuthHeaders(auth?: AuthConfig): Record<string, string> {
    const authConfig = auth || this.defaultAuth;
    if (!authConfig) 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":
        if (
          authConfig.apiKey &&
          authConfig.apiKeyName &&
          authConfig.apiKeyIn === "header"
        ) {
          return { [authConfig.apiKeyName]: 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 createAuthSchema(): z.ZodType<any> {
    return z
      .object({
        type: z
          .enum(["none", "basic", "bearer", "apiKey", "oauth2"])
          .default("none"),
        username: z.string().optional(),
        password: z.string().optional(),
        token: z.string().optional(),
        apiKey: z.string().optional(),
        apiKeyName: z.string().optional(),
        apiKeyIn: z.enum(["header", "query"]).optional(),
      })
      .describe("Authentication configuration for the request");
  }

  async loadCollection(collectionUrlOrFile: string) {
    if (process.env.NODE_ENV !== "production") {
      console.debug("Loading Postman collection from:", collectionUrlOrFile);
    }
    try {
      let collectionData: any;

      if (collectionUrlOrFile.startsWith("http")) {
        const response = await axios.get(collectionUrlOrFile);
        collectionData = response.data;
      } else {
        const fs = await import("fs/promises");
        const fileContent = await fs.readFile(collectionUrlOrFile, "utf-8");
        collectionData = JSON.parse(fileContent);
      }

      this.collection = new Collection(collectionData);

      // Get collection info safely
      const info = {
        name:
          (this.collection as any).name ||
          collectionData.info?.name ||
          "Postman Collection",
        description:
          (this.collection as any).description ||
          collectionData.info?.description ||
          "",
        version:
          (this.collection as any).version ||
          collectionData.info?.version ||
          "1.0.0",
      };

      if (process.env.NODE_ENV !== "production") {
        console.debug("Loaded Postman collection:", {
          name: info.name,
          description:
            typeof info.description === "string"
              ? info.description.substring(0, 100) + "..."
              : "",
        });
      }

      // Update server name with collection info
      this.mcpServer = new McpServer({
        name:
          `${info.name} - Simple Explorer` ||
          "Simple Postman Collection Server",
        version: info.version || "1.0.0",
        description: `Simplified explorer for ${info.name}` || undefined,
      });

      // Parse all requests for the strategic tools
      this.parseAllRequests();

      await this.registerStrategicTools();
    } catch (error) {
      console.error("Failed to load Postman collection:", error);
      throw error;
    }
  }

  async loadEnvironment(environmentUrlOrFile: string) {
    if (process.env.NODE_ENV !== "production") {
      console.debug("Loading Postman environment from:", environmentUrlOrFile);
    }
    try {
      let environmentData: any;

      if (environmentUrlOrFile.startsWith("http")) {
        const response = await axios.get(environmentUrlOrFile);
        environmentData = response.data;
      } else {
        const fs = await import("fs/promises");
        const fileContent = await fs.readFile(environmentUrlOrFile, "utf-8");
        environmentData = JSON.parse(fileContent);
      }

      // Parse environment variables
      if (environmentData.values) {
        for (const variable of environmentData.values) {
          this.environment[variable.key] = variable.value;
        }
      }

      if (process.env.NODE_ENV !== "production") {
        console.debug(
          "Loaded environment variables:",
          Object.keys(this.environment)
        );
      }
    } catch (error) {
      console.error("Failed to load Postman environment:", error);
      throw error;
    }
  }

  private parseAllRequests() {
    if (!this.collection) return;

    this.requests = [];

    const parseItem = (
      item: Item | ItemGroup<Item>,
      folderPath: string = ""
    ) => {
      if (item instanceof Item && item.request) {
        const request = item.request;

        // Handle description safely
        let description = "";
        if (item.request.description) {
          if (typeof item.request.description === "string") {
            description = item.request.description;
          } else if (
            typeof item.request.description === "object" &&
            "content" in item.request.description
          ) {
            description = (item.request.description as any).content;
          }
        }

        this.requests.push({
          id: `${folderPath}${item.name}`
            .replace(/[^a-zA-Z0-9_]/g, "_")
            .toLowerCase(),
          name: item.name || "Unnamed Request",
          method: request.method || "GET",
          url: request.url?.toString() || "",
          description,
          folder: folderPath,
          request,
        });
      } else if (item instanceof ItemGroup) {
        // Handle folders recursively
        const newFolderPath = folderPath
          ? `${folderPath}/${item.name}`
          : item.name;
        item.items.each((subItem: Item | ItemGroup<Item>) => {
          parseItem(subItem, newFolderPath);
        });
      }
    };

    // Parse all items
    this.collection.items.each((item: Item | ItemGroup<Item>) => {
      parseItem(item);
    });

    console.log(
      `✅ Parsed ${this.requests.length} requests from Postman collection`
    );
  }

  private resolveVariables(text: string): string {
    if (!text) return text;

    // Replace {{variableName}} with actual values
    return text.replace(/\{\{(\w+)\}\}/g, (match, variableName) => {
      return this.environment[variableName] || match;
    });
  }

  private async registerStrategicTools() {
    // Tool 1: List all requests
    this.mcpServer.tool(
      "list_requests",
      "List all available requests in the Postman collection with basic information",
      {
        input: z.object({
          method: z
            .string()
            .optional()
            .describe("Filter by HTTP method (GET, POST, PUT, DELETE, etc.)"),
          folder: z.string().optional().describe("Filter by folder/path"),
          limit: z
            .number()
            .optional()
            .default(50)
            .describe("Maximum number of requests to return"),
        }),
      },
      async ({ input }) => {
        let filteredRequests = this.requests;

        // Apply filters
        if (input.method) {
          filteredRequests = filteredRequests.filter(
            (req) => req.method.toLowerCase() === input.method!.toLowerCase()
          );
        }

        if (input.folder) {
          filteredRequests = filteredRequests.filter((req) =>
            req.folder.toLowerCase().includes(input.folder!.toLowerCase())
          );
        }

        // Limit results
        const limitedRequests = filteredRequests.slice(0, input.limit);

        return {
          content: [
            {
              type: "text",
              text: JSON.stringify(
                {
                  total: limitedRequests.length,
                  requests: limitedRequests.map((req) => ({
                    id: req.id,
                    name: req.name,
                    method: req.method,
                    url: req.url,
                    folder: req.folder,
                    description:
                      req.description.substring(0, 100) +
                      (req.description.length > 100 ? "..." : ""),
                  })),
                },
                null,
                2
              ),
            },
          ],
        };
      }
    );

    // Tool 2: Get detailed information about a specific request
    this.mcpServer.tool(
      "get_request_details",
      "Get detailed information about a specific request including parameters, headers, and body structure",
      {
        input: z.object({
          requestId: z.string().describe("The ID of the request"),
          name: z
            .string()
            .optional()
            .describe("The name of the request (alternative to ID)"),
        }),
      },
      async ({ input }) => {
        let targetRequest = null;

        // Find the request by ID or name
        for (const req of this.requests) {
          if (
            req.id === input.requestId ||
            req.name.toLowerCase() === input.name?.toLowerCase()
          ) {
            targetRequest = req;
            break;
          }
        }

        if (!targetRequest) {
          return {
            content: [
              {
                type: "text",
                text: `Request not found. Use list_requests to see available requests.`,
              },
            ],
          };
        }

        const request = targetRequest.request;

        // Extract parameters information
        const parameters = {
          query: [] as any[],
          path: [] as any[],
          headers: [] as any[],
        };

        // Query parameters
        if (request.url && request.url.query) {
          request.url.query.each((param: any) => {
            if (param.key && !param.disabled) {
              parameters.query.push({
                name: param.key,
                value: param.value || "",
                description: param.description || "",
              });
            }
          });
        }

        // Path variables
        if (request.url && request.url.variables) {
          request.url.variables.each((variable: any) => {
            if (variable.key) {
              parameters.path.push({
                name: variable.key,
                value: variable.value || "",
                description: variable.description || "",
              });
            }
          });
        }

        // Headers
        if (request.headers) {
          request.headers.each((header: any) => {
            if (header.key && !header.disabled) {
              parameters.headers.push({
                name: header.key,
                value: header.value || "",
                description: header.description || "",
              });
            }
          });
        }

        // Request body info
        let bodyInfo: any = null;
        if (
          request.body &&
          ["POST", "PUT", "PATCH"].includes(request.method || "")
        ) {
          bodyInfo = {
            mode: request.body.mode,
            description: "Request body based on the collection definition",
          } as any;

          if (request.body.mode === "raw") {
            bodyInfo.example = request.body.raw || "";
          } else if (request.body.mode === "formdata") {
            bodyInfo.formFields = [];
            if (request.body.formdata) {
              request.body.formdata.each((field: any) => {
                if (field.key && !field.disabled) {
                  bodyInfo.formFields.push({
                    name: field.key,
                    type: field.type || "text",
                    value: field.value || "",
                    description: field.description || "",
                  });
                }
              });
            }
          }
        }

        return {
          content: [
            {
              type: "text",
              text: JSON.stringify(
                {
                  id: targetRequest.id,
                  name: targetRequest.name,
                  method: targetRequest.method,
                  url: targetRequest.url,
                  folder: targetRequest.folder,
                  description: targetRequest.description,
                  parameters,
                  body: bodyInfo,
                  auth: "Use the auth parameter in make_request to provide authentication",
                },
                null,
                2
              ),
            },
          ],
        };
      }
    );

    // Tool 3: Search requests by keyword
    this.mcpServer.tool(
      "search_requests",
      "Search requests by keyword in name, description, URL, or folder",
      {
        input: z.object({
          query: z
            .string()
            .describe(
              "Search term to look for in request names, descriptions, URLs, or folders"
            ),
          limit: z
            .number()
            .optional()
            .default(20)
            .describe("Maximum number of results to return"),
        }),
      },
      async ({ input }) => {
        const query = input.query.toLowerCase();
        const results = [];

        for (const req of this.requests) {
          const searchText = [
            req.name,
            req.description,
            req.url,
            req.folder,
            req.method,
          ]
            .join(" ")
            .toLowerCase();

          if (searchText.includes(query)) {
            results.push({
              id: req.id,
              name: req.name,
              method: req.method,
              url: req.url,
              folder: req.folder,
              description:
                req.description.substring(0, 100) +
                (req.description.length > 100 ? "..." : ""),
              relevance: this.calculateRelevance(query, searchText),
            });

            if (results.length >= input.limit) break;
          }
        }

        // Sort by relevance
        results.sort((a, b) => b.relevance - a.relevance);

        return {
          content: [
            {
              type: "text",
              text: JSON.stringify(
                {
                  query: input.query,
                  total: results.length,
                  results: results.map((r) => ({ ...r, relevance: undefined })), // Remove relevance from output
                },
                null,
                2
              ),
            },
          ],
        };
      }
    );

    // Tool 4: Make request
    this.mcpServer.tool(
      "make_request",
      "Execute any request from the Postman collection with the specified parameters and authentication",
      {
        input: z.object({
          requestId: z.string().optional().describe("The ID of the request"),
          name: z
            .string()
            .optional()
            .describe("The name of the request (alternative to ID)"),
          parameters: z
            .record(z.any())
            .optional()
            .describe(
              "Query parameters, path parameters, headers, or form data"
            ),
          body: z
            .any()
            .optional()
            .describe("Request body (for POST, PUT, PATCH requests)"),
          auth: this.createAuthSchema()
            .optional()
            .describe("Authentication configuration"),
        }),
      },
      async ({ input }) => {
        // Find the request
        let targetRequest = null;

        for (const req of this.requests) {
          if (
            req.id === input.requestId ||
            req.name.toLowerCase() === input.name?.toLowerCase()
          ) {
            targetRequest = req;
            break;
          }
        }

        if (!targetRequest) {
          return {
            content: [
              {
                type: "text",
                text: `Request not found. Use list_requests to see available requests.`,
              },
            ],
          };
        }

        try {
          const result = await this.executeRequest(
            targetRequest.request,
            input.parameters || {},
            input.body,
            input.auth
          );
          return {
            content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
          };
        } catch (error) {
          return {
            content: [
              {
                type: "text",
                text: `Error: ${
                  error instanceof Error ? error.message : String(error)
                }`,
              },
            ],
          };
        }
      }
    );

    console.log(
      "✅ Successfully registered 4 strategic tools for Postman collection exploration"
    );
  }

  private calculateRelevance(query: string, text: string): number {
    const queryWords = query.split(" ");
    let score = 0;

    queryWords.forEach((word) => {
      if (text.includes(word)) {
        score += 1;
        // Bonus for exact word matches
        if (
          text.includes(` ${word} `) ||
          text.startsWith(word) ||
          text.endsWith(word)
        ) {
          score += 0.5;
        }
      }
    });

    return score;
  }

  private async executeRequest(
    request: PostmanRequest,
    parameters: Record<string, any>,
    body?: any,
    auth?: AuthConfig
  ): Promise<any> {
    // Build URL
    let url = request.url?.toString() || "";
    url = this.resolveVariables(url);

    // Replace path variables
    Object.keys(parameters).forEach((key) => {
      const value = parameters[key];
      if (value !== undefined) {
        // Try different variable formats
        url = url.replace(`:${key}`, encodeURIComponent(String(value)));
        url = url.replace(`{{${key}}}`, encodeURIComponent(String(value)));
        url = url.replace(`{${key}}`, encodeURIComponent(String(value)));
      }
    });

    // Build query parameters
    const queryParams = this.getAuthQueryParams(auth);
    Object.keys(parameters).forEach((key) => {
      const value = parameters[key];
      if (
        value !== undefined &&
        !url.includes(`:${key}`) &&
        !url.includes(`{{${key}}}`)
      ) {
        queryParams[key] = value;
      }
    });

    // Build headers
    const headers = this.getAuthHeaders(auth);

    // Add any headers from parameters
    Object.keys(parameters).forEach((key) => {
      const value = parameters[key];
      if (value !== undefined && key.toLowerCase().includes("header")) {
        headers[key] = value;
      }
    });

    // Add default content type for requests with body
    if (body && !headers["Content-Type"]) {
      headers["Content-Type"] = "application/json";
    }

    // Prepare request configuration
    const config: any = {
      method: request.method || "GET",
      url,
      headers,
      params: queryParams,
    };

    // Add body if present
    if (body) {
      if (typeof body === "string") {
        config.data = body;
      } else {
        config.data = JSON.stringify(body);
      }
    }

    if (process.env.NODE_ENV !== "production") {
      console.debug("Executing request:", {
        method: config.method,
        url: config.url,
        headers: Object.keys(config.headers),
        hasBody: !!config.data,
      });
    }

    try {
      const response = await axios(config);
      return {
        status: response.status,
        statusText: response.statusText,
        headers: response.headers,
        data: response.data,
      };
    } catch (error: any) {
      if (error.response) {
        return {
          status: error.response.status,
          statusText: error.response.statusText,
          headers: error.response.headers,
          data: error.response.data,
          error: true,
        };
      }
      throw error;
    }
  }

  getServer() {
    return this.mcpServer;
  }

  handleSSE(res: Response) {
    if (!transport) {
      transport = new SSEServerTransport("/messages", res);
    }
    this.mcpServer.connect(transport);
  }

  handleMessage(req: Request, res: Response) {
    this.mcpServer.connect(transport!);
  }
}

```