mcp-proxy

repository·master·Indexed 19 days ago

https://github.com/tbxark/mcp-proxy

An aggregator that exposes multiple Model Context Protocol (MCP) servers behind a single HTTP entrypoint. It allows clients to access combined tools, prompts, and resources via SSE or streamable HTTP, supporting stdio, sse, and streamable-http client types. Key features include tool filtering via ToolFilter, OAuth 2.1 flow management for remote servers, and deployment options via Go, Docker, or Docker Compose.

Tokens
8.8K
Snippets
36
Records
42
Agent score
68%

What's inside mcp-proxy

  1. What is MCP Proxy Server?

    master

    MCP Proxy Server is an aggregator that sits in front of multiple Model Context Protocol (MCP) servers. It provides a single HTTP entrypoint that exposes the combined tools, prompts, and resources from all configured downstream servers.

    Key capabilities include:

    • Aggregation: Combine multiple MCP clients into one interface.
    • Transport Support: Serves via Server-Sent Events (SSE) or streamable HTTP.
    • Client Types: Supports stdio, sse, and streamable-http client types in its configuration.
    • OAuth Handling: Can manage interactive OAuth flows (e.g., for Notion) by authorizing once and then managing token refresh for all subsequent callers.
  2. Configure downstream mcpServers

    master

    The mcpServers object contains entries for each MCP server the proxy should manage. The proxy automatically determines the client type based on the provided fields:

    • stdio: Implicitly used when command is provided. Requires command, args, and optionally env.
    • sse: Implicitly used when url is provided and transportType is NOT streamable-http.
    • streamable-http: Explicitly used when transportType is set to "streamable-http". Requires url and optionally timeout.

    Common fields for all server types:

    • url: The endpoint for remote servers.
    • headers: Custom headers for the connection.
    • oauth: Configuration for servers requiring interactive OAuth 2.1 flows.
    • options: Per-server overrides for settings like logging, authentication, or tool filtering.
    {
      "mcpServers": {
        "github": {
          "command": "npx",
          "args": ["-y", "@modelcontextprotocol/server-github"],
          "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "<YOUR_TOKEN>" }
        },
        "amap": {
          "url": "https://mcp.amap.com/sse?key=<YOUR_TOKEN>"
        },
        "notion": {
          "url": "https://mcp.notion.com/mcp",
          "transportType": "streamable-http",
          "oauth": { "scopes": [] }
        }
      }
    }
  3. Authenticate requests using Bearer tokens

    master

    If options.authTokens is configured for a specific server, all requests to that server must include a bearer token in the header:

    Authorization: <token>

    Workaround for clients without header support: If your client cannot set custom headers, you can embed the token directly in the route key. For example, if the server key is fetch, use the path fetch/<token>.

    Authorization: <token>
  4. Security best practices for MCP Proxy

    master

    When configuring MCP Proxy, follow these security guidelines:

    • Authentication: Prefer using specific authTokens for each downstream server. Only use the default mcpProxy token when appropriate.
    • Token Embedding: If a downstream server is unable to process custom headers, you can embed a token directly into the route key (e.g., fetch/<token>) and route via that specific path.
    • Fail-Fast Configuration: For critical servers, set options.panicIfInvalid: true to ensure the proxy fails immediately if it encounters a misconfiguration.
  5. Run MCP Proxy with Docker

    master

    MCP Proxy can be run as a Docker container. The image supports launching MCP servers via npx and uvx. You can either mount a local configuration file or provide a URL to a remote configuration file.

    To mount a local config:

    docker run -d -p 9090:9090 -v /path/to/config.json:/config/config.json ghcr.io/tbxark/mcp-proxy:latest

    To use a remote config:

    docker run -d -p 9090:9090 ghcr.io/tbxark/mcp-proxy:latest --config https://example.com/config.json
  6. Deploy MCP Proxy using Docker Compose

    master

    For Docker Compose deployments, you can either mount a local configuration file directly into the app service or use a sidecar pattern (e.g., using Caddy) to serve the configuration over HTTP. The sidecar pattern is useful if you want to avoid host mounts into the main application container.

    # Minimal compose file with local volume mount
    services:
      app:
        image: ghcr.io/tbxark/mcp-proxy:latest
        pull_policy: always
        volumes:
          - ./config.json:/config/config.json
        ports:
          - "9090:9090"
        restart: always
    
    # Serving config via an internal file server (Caddy sidecar)
    services:
      caddy:
        image: caddy:latest
        pull_policy: always
        expose:
          - "80"
        volumes:
          - ./config.json:/config/config.json
        command: ["caddy", "file-server", "--root", "/config"]
    
      app:
        image: ghcr.io/tbxark/mcp-proxy:latest
        pull_policy: always
        ports:
          - "9090:9090"
        restart: always
        depends_on:
          - caddy
        command: ["--config", "http://caddy/config.json"]
  7. Validate configuration with -check-config

    master

    Use the -check-config flag in CI, init containers, or deployment scripts to validate proxy settings and all downstream servers without binding the HTTP port.

    Validation checks include:

    • Transport requirements
    • Absolute HTTP URLs
    • OAuth callback safety
    • Authentication tokens
    • Tool-filter modes

    If the configuration is invalid, the process exits with a non-zero status and identifies the affected field or server name.

    mcp-proxy -config config.json -check-config
    # Output example: Config OK: 3 MCP server(s) configured
  8. Configure OAuth for remote MCP servers

    master

    For remote servers (like Notion) that require the OAuth 2.1 authorization-code flow and reject static bearer tokens, use an oauth block within an sse or streamable-http server configuration. The proxy acts as the OAuth client.

    Key Configuration Options:

    • clientId / clientSecret: Static credentials. If omitted, the proxy uses RFC 7591 dynamic client registration during the first authorization.
    • redirectUri: The local callback URL. Defaults to http://localhost:8090/oauth/callback. Must use http and a loopback host (e.g., localhost, 127.0.0.1).
    • scopes: A list of requested OAuth scopes.
    • pkceDisabled: Boolean to disable PKCE (enabled by default).
    • authServerMetadataUrl: Use this to override discovery if the provider's metadata location doesn't follow the standard OpenID Connect Discovery convention.

    Important Lifecycle Notes:

    1. Authorization: You must run the proxy with the -authorize flag once to complete the interactive flow.
    2. Persistence: Tokens are stored in <user config dir>/mcp-proxy/oauth/<server>.json. If using dynamic registration, the client credentials are stored in <user config dir>/mcp-proxy/oauth/<server>.client.json. Do not delete these files, or the proxy will fail to refresh tokens after the initial access token expires.
    {
      "mcpServers": {
        "notion": {
          "url": "https://mcp.notion.com/mcp",
          "transportType": "streamable-http",
          "oauth": {
            "clientId": "my-client-id",
            "scopes": ["read_user", "read_content"]
          }
        }
      }
    }
  9. Build MCP Proxy from source

    master

    To build the MCP Proxy from the source code, clone the repository, use make build to compile the binary, and then run the resulting binary by providing a path to your configuration file using the --config flag.

    git clone https://github.com/tbxark/mcp-proxy.git
    cd mcp-proxy
    make build
    ./build/mcp-proxy --config path/to/config.json
  10. Deploy MCP Proxy using Docker

    master

    You can run the MCP Proxy container using either a local configuration file mounted as a volume or by providing a remote configuration URL via the --config flag. The container image includes support for launching MCP servers using npx and uvx by default.

    To use a local config file, mount it to /config/config.json inside the container. To use a remote config, pass the URL as a command-line argument.

    # Run with a local config file mounted into the container
    docker run -d \
      -p 9090:9090 \
      -v /path/to/config.json:/config/config.json \
      ghcr.io/tbxark/mcp-proxy:latest
    
    # Or reference a remote config URL
    docker run -d -p 9090:9090 \
      ghcr.io/tbxark/mcp-proxy:latest \
      --config https://example.com/config.json
  11. Authorize an OAuth downstream server

    master

    For servers configured with an oauth block, you must run a one-time interactive authorization flow before starting the daemon. This must be done in a session with a real browser (not in an unattended service/container).

    1. Run the -authorize command with the name of the server entry and the path to your config: mcp-proxy -authorize <server_name> -config <path_to_config.json>
    2. Follow the browser prompts to complete the OAuth consent.
    3. The token is saved to disk.
    4. Restart the daemon: A server's HTTP route is only mounted upon a successful connection at startup. If the daemon was running during authorization, it must be restarted to pick up the new token.

    Tokens refresh automatically once authorized. Re-run -authorize only if the server reports the token is no longer valid (e.g., access was revoked).

    mcp-proxy -authorize notion -config path/to/config.json