Tool

Proxy OAuth authentication for MCP clients

mcp-proxy is a Go proxy that adds OAuth 2.0 (PKCE) between MCP clients like Claude Code and remote MCP servers, over stdio or HTTP/SSE.


46
Spark score
out of 100
Updated 8 months ago
Version 0.1.0

Add to Favorites

Why it matters

Enable MCP clients without native OAuth support to securely authenticate and connect to protected services by handling OAuth 2.0/PKCE flows and token lifecycle management on their behalf.

Outcomes

What it gets done

01

Handle OAuth 2.0 authorization code flow with PKCE for secure authentication

02

Manage access token refresh and expiration automatically

03

Proxy authenticated requests between MCP clients and protected services

04

Store and rotate credentials securely without client-side OAuth implementation

Install

Add it to your toolbox

Run in your project directory:

curl -fsSL https://spark.entire.vc/get/mcp-mcp-proxy | bash

Overview

Mcp Proxy

mcp-proxy is a Go proxy that connects MCP clients such as Claude Code, Claude Desktop, Cursor, and Windsurf to a remote MCP server, handling OAuth 2.0 with PKCE on the client's behalf. It runs as a stdio proxy or an HTTP/SSE server and can filter which tools are exposed. Use it when a remote MCP server needs OAuth and clients should share one authenticated connection, or when stdio isn't available. Not needed for MCP servers with no OAuth requirement.

What it does

mcp-proxy is a Go implementation of a proxy that sits between MCP clients (Claude Code, Claude Desktop, Cursor, Windsurf) and a remote MCP server, adding full OAuth 2.0 support - PKCE, dynamic client registration, and token refresh - on top of the connection. It runs in one of two modes: a stdio proxy for local MCP clients, or an HTTP/SSE server that multiple clients can share against a single upstream connection. Internally it is a bidirectional JSON-RPC proxy that can also filter out specific tools before they reach the client.

When to use - and when NOT to

Use mcp-proxy when a remote MCP server requires OAuth and the client itself has no native way to run that OAuth flow, or when several MCP clients need to share one authenticated upstream connection instead of each doing its own OAuth exchange. The HTTP/SSE server mode is specifically useful for web-based MCP clients, for sharing a single upstream connection among multiple clients, or in environments where stdio is not available; the --resource flag additionally supports running multiple isolated OAuth sessions, for example per tenant, against the same proxy binary. It is not needed for MCP servers that don't require OAuth, or for a single client that can already reach its MCP server directly over the transport it prefers.

Capabilities

  • Dual operation mode: stdio (default, for Claude Code/Desktop, Cursor, Windsurf) or an HTTP/SSE server exposing GET /sse and POST /message.
  • Full OAuth 2.0 with PKCE, dynamic client registration, and automatic token refresh; the flow opens a browser on a 401, exchanges the returned code for tokens via PKCE, then persists and auto-refreshes them from a per-OS cache directory.
  • Configurable upstream transport strategy - http-first (default, falls back to SSE on 404), sse-first (falls back to HTTP on 405), or http-only/sse-only with no fallback.
  • Tool filtering via --ignore-tool (wildcard patterns) to block specific tools from being exposed to clients.
  • Custom request headers (--header, e.g. a bearer token) and multi-tenant isolation via the --resource OAuth parameter.
  • Internally organized as a stdio transport, an HTTP/SSE transport, an OAuth/PKCE auth package, and a bidirectional JSON-RPC proxy core with tool filtering.

How to install

brew install mikluko/tap/mcp-proxy

Also installable via go install github.com/mikluko/mcp-proxy/cmd/mcp-proxy@latest, or as a prebuilt binary from GitHub Releases. Once installed, point it at a remote MCP server - for Claude Code: claude mcp add remote-example mcp-proxy https://remote.mcp.server/sse; for Claude Desktop, Cursor, or Windsurf, add it as an mcpServers entry running the mcp-proxy command with the server URL as an argument.

Who it's for

Developers and teams running MCP clients like Claude Code, Claude Desktop, Cursor, or Windsurf against a remote MCP server that requires OAuth, especially where multiple clients need to share one authenticated connection or where per-tenant OAuth isolation is needed. Released under MIT, with prior art acknowledged from the original TypeScript (geelen/mcp-remote) and Python (sparfenyuk/mcp-proxy) implementations.

Source README

mcp-proxy

A Go implementation of a proxy that connects MCP clients to remote MCP servers with OAuth support.

Features

  • Dual mode operation: Stdio (default) or HTTP/SSE server
  • OAuth 2.0: Full support with PKCE, dynamic client registration, and token refresh
  • Transport flexibility: HTTP and SSE with configurable fallback strategies
  • Tool filtering: Block specific tools from being exposed to clients

Installation

Homebrew (macOS/Linux)

brew install mikluko/tap/mcp-proxy

Go

go install github.com/mikluko/mcp-proxy/cmd/mcp-proxy@latest

Binary releases

Download from GitHub Releases.

Usage

Stdio Mode (Default)

Standard mode for MCP clients that communicate via stdio (Claude Code, Claude Desktop, Cursor, Windsurf).

Claude Code
claude mcp add remote-example mcp-proxy https://remote.mcp.server/sse
Claude Desktop, Cursor, Windsurf
{
  "mcpServers": {
    "remote-example": {
      "command": "mcp-proxy",
      "args": [
        "https://remote.mcp.server/sse"
      ]
    }
  }
}

HTTP/SSE Server Mode

Run as an HTTP server that accepts MCP client connections via SSE. Useful for:

  • Web-based MCP clients
  • Sharing a single upstream connection among multiple clients
  • Environments where stdio isn't available
# Listen on port 8080
mcp-proxy https://remote.mcp.server/sse --listen :8080

# Listen on specific interface
mcp-proxy https://remote.mcp.server/sse --listen localhost:8080

Clients connect via:

  • SSE endpoint: GET /sse - Establishes SSE stream for server→client messages
  • Message endpoint: POST /message - Sends client→server messages

Custom Headers

{
  "mcpServers": {
    "remote-example": {
      "command": "mcp-proxy",
      "args": [
        "https://remote.mcp.server/sse",
        "--header",
        "Authorization:Bearer ${AUTH_TOKEN}"
      ],
      "env": {
        "AUTH_TOKEN": "your-token"
      }
    }
  }
}

Flags

Flag Description Default
--listen, -l Listen address for HTTP mode (e.g., :8080) -
--header, -H Custom headers (KEY:VALUE) -
--transport Upstream transport strategy (http-first, sse-first, http-only, sse-only) http-first
--host OAuth callback hostname localhost
--allow-http Allow non-HTTPS connections false
--log-level Log level (debug, info, warn, error) info
--enable-proxy Use HTTP_PROXY/HTTPS_PROXY false
--ignore-tool Ignore tools matching pattern (wildcards) -
--auth-timeout OAuth callback timeout (seconds) 30
--resource OAuth resource parameter -
--static-oauth-client-metadata Static OAuth client metadata (JSON or @file) -
--static-oauth-client-info Static OAuth client info (JSON or @file) -

Transport Strategy

Controls how mcp-proxy connects to the upstream server:

  • http-first (default): Try HTTP, fall back to SSE on 404
  • sse-first: Try SSE, fall back to HTTP on 405
  • http-only/sse-only: No fallback

Multiple Instances

Use --resource to isolate OAuth sessions:

{
  "mcpServers": {
    "tenant1": {
      "command": "mcp-proxy",
      "args": [
        "https://mcp.example.com/sse",
        "--resource",
        "https://tenant1.example.com/"
      ]
    },
    "tenant2": {
      "command": "mcp-proxy",
      "args": [
        "https://mcp.example.com/sse",
        "--resource",
        "https://tenant2.example.com/"
      ]
    }
  }
}

Cache Location

OAuth tokens and client registration are stored in:

  • Linux: ~/.cache/mcp-proxy/
  • macOS: ~/Library/Caches/mcp-proxy/
  • Windows: %LocalAppData%\mcp-proxy\

To clear authentication state:

rm -rf ~/.cache/mcp-proxy # Linux
rm -rf ~/Library/Caches/mcp-proxy # macOS

Architecture

                     ┌─────────────────────────────────────────┐
                     │              mcp-proxy                  │
                     │                                         │
  ┌──────────┐       │  ┌────────────┐      ┌────────────────┐ │       ┌──────────┐
  │  Client  │──────▶│  │ Downstream │──────│     Proxy      │─│──────▶│ Upstream │
  │ (stdio   │       │  │ Transport  │      │   (filtering,  │ │       │  Server  │
  │  or SSE) │◀──────│  │            │◀─────│  modification) │◀│───────│          │
  └──────────┘       │  └────────────┘      └────────────────┘ │       └──────────┘
                     │     stdio or              HTTP or       │
                     │     HTTP/SSE                SSE         │
                     └─────────────────────────────────────────┘

Directory Structure

cmd/
└── mcp-proxy/          # Main CLI binary

internal/
├── auth/               # OAuth 2.0 implementation (PKCE, tokens, callback server)
├── config/             # Cross-platform cache directory management
├── proxy/              # Bidirectional JSON-RPC proxy with tool filtering
├── server/             # HTTP/SSE server for downstream client connections
├── stdio/              # Stdio transport for downstream client connections
└── transport/          # HTTP and SSE transports for upstream connections

pkg/
└── jsonrpc/            # JSON-RPC 2.0 message types

OAuth Flow

  1. auth.Provider.Initialize() loads existing tokens or prepares for auth
  2. On 401: start callback server, open browser to authorization URL
  3. Callback receives code, exchanges for tokens via PKCE
  4. Tokens persisted to cache dir, auto-refreshed on expiry

Release

Releases via goreleaser + GitHub Actions. Creates:

  • Multi-platform binaries (linux/darwin/windows × amd64/arm64)
  • Homebrew formula in mikluko/homebrew-tap

Prior Art

FAQ

Common questions

Discussion

Questions & comments · 0

Sign In Sign in to leave a comment.