Skip to main content
Glama
gws8820
by gws8820
README.md
# secure-mcp-proxy

![GitHub License](https://img.shields.io/github/license/sparfenyuk/mcp-proxy)
![PyPI - Python Version](https://img.shields.io/pypi/pyversions/mcp-proxy)

A secure MCP (Model Context Protocol) proxy that enables switching between server transports with built-in API token authentication.

> **Note:** This project is forked from [sparfenyuk/mcp-proxy](https://github.com/sparfenyuk/mcp-proxy) and enhanced with additional security features.

- [secure-mcp-proxy](#secure-mcp-proxy)
  - [About](#about)
  - [Features](#features)
  - [Quick Start](#quick-start)
  - [Installation](#installation)
  - [Running the Proxy](#running-the-proxy)
  - [Configuration](#configuration)
  - [Authentication](#authentication)
  - [Docker Support](#docker-support)
  - [Command Line Reference](#command-line-reference)

## About

The `mcp-proxy` is a tool that lets you switch between server transports. There are two supported modes:

1. stdio to SSE/StreamableHTTP
2. SSE to stdio

### 1. stdio to SSE/StreamableHTTP

Run a proxy server from stdio that connects to a remote SSE server.

This mode allows clients like Claude Desktop to communicate to a remote server over SSE even though it is not supported
natively.

```mermaid
graph LR
    A["Claude Desktop"] <--> |stdio| B["mcp-proxy"]
    B <--> |SSE| C["External MCP Server"]

    style A fill:#ffe6f9,stroke:#333,color:black,stroke-width:2px
    style B fill:#e6e6ff,stroke:#333,color:black,stroke-width:2px
    style C fill:#e6ffe6,stroke:#333,color:black,stroke-width:2px
```
### 2. SSE to stdio

Run a proxy server exposing a SSE server that connects to a local stdio server.

This allows remote connections to the local stdio server. The `mcp-proxy` opens a port to listen for SSE requests,
spawns a local stdio server that handles MCP requests.

```mermaid
graph LR
    A["LLM Client"] <-->|SSE| B["mcp-proxy"]
    B <-->|stdio| C["Local MCP Server"]

    style A fill:#ffe6f9,stroke:#333,color:black,stroke-width:2px
    style B fill:#e6e6ff,stroke:#333,color:black,stroke-width:2px
    style C fill:#e6ffe6,stroke:#333,color:black,stroke-width:2px
```

## Features

- **Transport switching**: stdio ↔ SSE/StreamableHTTP
- **API token authentication**: Secure your MCP servers with Bearer token auth
- **CORS support**: Configure allowed origins for web clients
- **Multiple servers**: Host multiple named MCP servers simultaneously
- **Docker support**: Container-ready with Docker and Docker Compose
- **Flexible configuration**: CLI arguments, environment variables, and JSON config files
- **Developer friendly**: Built-in token generator and status endpoint

## Quick Start

```bash
# CLI Mode (No Auth)
uv run python -m secure_mcp_proxy --port 3000 uvx mcp-server-fetch # example

# Named Server Mode
export MCP_PROXY_API_TOKEN=your-secret-token
uv run python -m secure_mcp_proxy --named-server-config servers.json --port 3000 --host 0.0.0.0
```

## Installation

### From Source

```bash
# Clone the repository
git clone https://github.com/your-username/secure-mcp-proxy
cd secure-mcp-proxy

# Install dependencies with uv (Python package manager)
uv sync

# Now you can run the proxy using: uv run python -m secure_mcp_proxy
```

## Running the Proxy

### CLI Mode

```bash
# Single server
uv run python -m secure_mcp_proxy --port 3000 uvx mcp-server-fetch

# Multiple servers
uv run python -m secure_mcp_proxy --port 3000 \
  --named-server fetch 'uvx mcp-server-fetch' \
  --named-server github 'npx -y @modelcontextprotocol/server-github'

# Custom host and port
uv run python -m secure_mcp_proxy --host 0.0.0.0 --port 8080 uvx mcp-server-fetch

# With environment variables and args
uv run python -m secure_mcp_proxy --port 3000 -e API_KEY value -- uvx mcp-server-fetch --timeout 30
```

### Named Server Mode

```bash
export MCP_PROXY_API_TOKEN=token
uv run python -m secure_mcp_proxy --named-server-config servers.json --port 3000 --host 0.0.0.0
```

### Client Mode (Connect to Remote Server)

```bash
# Connect to remote SSE server
uv run python -m secure_mcp_proxy http://remote-server.com/sse

# With authentication
uv run python -m secure_mcp_proxy --headers Authorization 'Bearer remote-token' http://remote-server.com/sse

# Using StreamableHTTP transport
uv run python -m secure_mcp_proxy --transport streamablehttp http://remote-server.com/mcp
```

## Configuration

### Environment Variables

- `MCP_PROXY_API_TOKEN`: API token for authentication (optional)
- `API_ACCESS_TOKEN`: Token for connecting to remote servers (client mode)

### Named Server Configuration File

Create a `servers.json` file:

```json
{
  "mcpServers": {
    "filesystem": {
      "enabled": true,
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/username/Downloads"],
      "auth": true,
      "env": {
        "CUSTOM_VAR": "value"
      }
    },
    "fetch": {
      "enabled": true,
      "command": "uvx",
      "args": ["mcp-server-fetch"],
      "auth": false
    },
    "time": {
      "enabled": true,
      "command": "uvx",
      "args": ["mcp-server-time"]
      // No "auth" field defaults to false
    },
    "github": {
      "enabled": false,
      "command": "npx", 
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "auth": true,
      "env": {
        "GITHUB_PERSONAL_ACCESS_TOKEN": "your-github-token"
      }
    }
  }
}
```

**Configuration fields:**
- `enabled`: Whether to start this server (default: `true`)
- `command`: Command to execute
- `args`: Command arguments as array
- `auth`: Whether authentication is required (default: `false`)
- `env`: Environment variables for this server

## Authentication

Authentication is controlled by the `MCP_PROXY_API_TOKEN` environment variable. When set, you can configure which servers require authentication on a per-server basis.

### Authentication Modes

1. **No authentication**: When `MCP_PROXY_API_TOKEN` is not set, all endpoints are public
2. **Selective authentication**: When `MCP_PROXY_API_TOKEN` is set, authentication is controlled per-server:
  - **CLI Mode**: Always public (no authentication required)
  - **Named Server Mode**: Follow the `auth` field setting (`true` = auth required, `false` or omitted = public)

### Setting Up Authentication

```bash
# Option 1: Environment variable
export MCP_PROXY_API_TOKEN=your-secret-token

# Option 2: .env file
echo 'MCP_PROXY_API_TOKEN="your-secret-token"' > .env

# Option 3: Inline
export MCP_PROXY_API_TOKEN=your-secret-token
uv run python -m secure_mcp_proxy --named-server-config servers.json --port 3000 
```

### Per-Server Authentication

You can configure which servers require authentication using the `auth` field in your configuration file:

```json
{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"],
      "auth": true    // Requires authentication
    },
    "fetch": {
      "command": "uvx", 
      "args": ["mcp-server-fetch"],
      "auth": false   // No authentication required
    },
    "time": {
      "command": "uvx",
      "args": ["mcp-server-time"]
      // No "auth" field = defaults to false (no authentication)
    }
  }
}
```

### Generate Secure Tokens

```bash
# Generate a production-ready token
uv run python -m secure_mcp_proxy.token_generator --production

# Generate multiple test tokens
uv run python -m secure_mcp_proxy.token_generator
```

## Docker Support

### Basic Docker Usage

```bash
# Build custom image with uv
cat > Dockerfile << 'EOF'
FROM ghcr.io/sparfenyuk/mcp-proxy:latest
RUN python3 -m ensurepip && pip install --no-cache-dir uv
ENV PATH="/usr/local/bin:$PATH" UV_PYTHON_PREFERENCE=only-system
ENTRYPOINT ["mcp-proxy"]
EOF

docker build -t secure-mcp-proxy .
export MCP_PROXY_API_TOKEN=token
docker run -e MCP_PROXY_API_TOKEN=$MCP_PROXY_API_TOKEN -p 3000:3000 secure-mcp-proxy \
  --pass-environment --port=3000 --host=0.0.0.0 uvx mcp-server-fetch
```

### Docker Compose

```yaml
# docker-compose.yml
services:
  secure-mcp-proxy:
    build: .
    ports:
      - "3000:3000"
    environment:
      - MCP_PROXY_API_TOKEN=your-secret-token
    volumes:
      - ./servers.json:/app/servers.json
    command: >
      --pass-environment 
      --port=3000 
      --host=0.0.0.0 
      --named-server-config=/app/servers.json
    restart: unless-stopped
```

## Command Line Reference

### Common Options

| Option | Description | Example |
|--------|-------------|---------|
| `--port` | Server port | `--port 3000` |
| `--host` | Server host | `--host 0.0.0.0` |
| `--named-server-config` | Config file path | `--named-server-config servers.json` |
| `--named-server` | Define named server | `--named-server fetch 'uvx mcp-server-fetch'` |
| `--allow-origin` | CORS origins | `--allow-origin "*"` |
| `--debug` | Enable debug logging | `--debug` |

## Troubleshooting

### Common Issues

1. **ENOENT error**: Use full path to binary in Claude Desktop config
2. **Port already in use**: Change port with `--port` option
3. **401/403 errors**: Check your `MCP_PROXY_API_TOKEN` environment variable
4. **Module not found**: Ensure you're in the correct directory and dependencies are installed

### Debug Mode

```bash
# Enable detailed logging
uv run python -m secure_mcp_proxy --debug --port 3000 uvx mcp-server-fetch
```