MCPInstall
mcp-remote-go logo

mcp-remote-go

by naotama2002 6 stars naotama2002/mcp-remote-go

This Go implementation allows MCP clients that only support local (stdio) connections to connect to remote MCP servers with authentication support.

READMEmain

MCP Remote for Go

This Go implementation allows MCP clients that only support local (stdio) connections to connect to remote MCP servers with authentication support.

Overview

MCP Remote proxies between:

  • A local MCP client using stdio transport
  • A remote MCP server using Streamable HTTP or Server-Sent Events (SSE) with OAuth authentication

Features

  • Streamable HTTP transport (MCP 2025-11-25) - Single-endpoint POST/GET with session management
  • Legacy SSE transport (MCP 2024-11-05) - Traditional two-endpoint SSE connection
  • Auto-negotiation - Automatically detects server capabilities and selects the optimal transport
  • Request metadata headers (MCP 2026-07-28) - Mcp-Method and Mcp-Name are derived from each message, with the base64 sentinel encoding for values that are not header-safe
  • x-mcp-header mirroring (SEP-2243) - Tool arguments a server designates are copied into Mcp-Param-* headers, and tools with malformed annotations are withheld from tools/list
  • Stream-close cancellation (MCP 2026-07-28) - A notifications/cancelled from a modern client closes that request's response stream, which is what the revision defines as the cancellation signal
  • OAuth 2.1 with PKCE (RFC 7636) - Secure authorization with S256 code challenge
  • Automatic token renewal (RFC 6749 §6) - An access token nearing expiry is exchanged for a fresh one before it is used, and again if a server refuses one, so a long-running session outlives its tokens without sending anyone back to a browser
  • Server-advertised scopes and client authentication - scope is taken from the WWW-Authenticate challenge (RFC 6750 §3.1) or the protected resource's own scopes_supported (RFC 9728 §2), and the token endpoint authentication method from the server's published list, rather than from values the client picks for itself
  • One authorization at a time - Concurrent proxies for the same server coordinate through a lock, so a second instance waits for the first one's token instead of opening a second browser window for the same account
  • CSRF-protected callback - state binds each authorization request to its callback, and the issuer is validated per RFC 9207 to prevent mix-up attacks
  • Protected Resource Metadata (RFC 9728) - Discover authorization servers from resource endpoints, including WWW-Authenticate-driven discovery on 401 responses (§5.1)
  • Resource Indicators (RFC 8707) - The MCP server's canonical URI is sent as resource on both authorization and token requests
  • OAuth Discovery (RFC 8414) and OpenID Connect Discovery
  • Custom headers and HTTPS enforcement

Protocol revision support

The proxy forwards whichever revision the local MCP client speaks; it never picks one on the client's behalf. A client on 2025-11-25 or earlier is forwarded through the initialize handshake as before, and a client on 2026-07-28 has its per-request _meta mirrored into the routing headers that revision requires.

Both sides must therefore be on the same side of the 2026-07-28 line. Every combination works except one: a client older than 2026-07-28 talking to a server that has dropped the initialize handshake entirely. The spec gives such a client no way to negotiate forward, so only a translating proxy could connect them — deliberately out of scope here. The proxy detects the case during auto-negotiation and says so at startup rather than letting it surface as an error on the first tool call.

See test/conformance for what is verified against the official MCP SDK.

Installation

Desktop Extension (MCPB)

Download the .mcpb file for your platform from GitHub Releases:

PlatformFile
macOS (Apple Silicon)mcp-remote-go-darwin-arm64.mcpb
macOS (Intel)mcp-remote-go-darwin-amd64.mcpb
Windows (x64)mcp-remote-go-windows-amd64.mcpb
Windows (ARM64)mcp-remote-go-windows-arm64.mcpb

Install by opening the .mcpb file in Claude Desktop. A configuration UI will appear where you can set the remote server URL and other options.

Building from Source

# Clone the repository
git clone https://github.com/naotama2002/mcp-remote-go.git
cd mcp-remote-go

# Build the binary
make build

Using Docker

You can also run mcp-remote-go using Docker, which provides a consistent environment and easier deployment.

Pull from GitHub Container Registry

# Pull the latest image
docker pull ghcr.io/naotama2002/mcp-remote-go:latest

# Or pull a specific version
docker pull ghcr.io/naotama2002/mcp-remote-go:{TAG}

Build locally

# Build the Docker image
docker build -t mcp-remote-go .

Usage

Binary Usage

# Basic usage (auto-detects transport: tries Streamable HTTP first, falls back to SSE)
mcp-remote-go https://remote.mcp.server/mcp

# Force Streamable HTTP transport
mcp-remote-go https://remote.mcp.server/mcp --transport streamable-http

# Force legacy SSE transport
mcp-remote-go https://remote.mcp.server/sse --transport sse

# With custom port for OAuth callback
mcp-remote-go https://remote.mcp.server/mcp 9090

# With a custom header (useful for auth tokens, API keys, etc.)
mcp-remote-go https://remote.mcp.server/mcp --header "Authorization: Bearer YOUR_TOKEN"

# Multiple headers — repeat --header as many times as needed
# (Note: there is no --headers plural flag; `--header` takes one Name:Value at a time.)
mcp-remote-go https://remote.mcp.server/mcp \
  --header "Authorization: Bearer YOUR_TOKEN" \
  --header "X-API-Key: secret" \
  --header "X-Tenant-Id: acme"

# Allow HTTP for trusted networks (normally HTTPS is required)
mcp-remote-go http://internal.mcp.server/mcp --allow-http

# Via HTTP/HTTPS proxy
mcp-remote-go https://remote.mcp.server/mcp --https-proxy http://proxy.example.com:8080

Docker Usage

# Basic usage with Docker (auto-detects transport)
docker run --rm -it -p 3334:3334 ghcr.io/naotama2002/mcp-remote-go:latest https://remote.mcp.server/mcp

# Force Streamable HTTP transport
docker run --rm -it -p 3334:3334 ghcr.io/naotama2002/mcp-remote-go:latest https://remote.mcp.server/mcp --transport streamable-http

# Force legacy SSE transport
docker run --rm -it -p 3334:3334 ghcr.io/naotama2002/mcp-remote-go:latest https://remote.mcp.server/sse --transport sse

# With custom port for OAuth callback
docker run --rm -it -p 9090:9090 ghcr.io/naotama2002/mcp-remote-go:latest https://remote.mcp.server/mcp 9090

# With custom headers
docker run --rm -it -p 3334:3334 ghcr.io/naotama2002/mcp-remote-go:latest https://remote.mcp.server/mcp --header "Authorization: Bearer YOUR_TOKEN"

# Allow HTTP for trusted networks
docker run --rm -it -p 3334:3334 ghcr.io/naotama2002/mcp-remote-go:latest http://internal.mcp.server/mcp --allow-http

# Mount auth directory to persist OAuth tokens
docker run --rm -it -p 3334:3334 -v ~/.mcp-remote-go-auth:/home/appuser/.mcp-remote-go-auth ghcr.io/naotama2002/mcp-remote-go:latest https://remote.mcp.server/mcp

Configuration for MCP Clients

By default, mcp-remote-go auto-detects the transport (Streamable HTTP or SSE). You can force a specific transport with the --transport flag.

Detection sends a server/discover request and reads the reply. A server that answers it is on 2026-07-28 or later, and the answer also names every revision it supports; a server that rejects it in JSON-RPC is on an earlier revision but still speaks Streamable HTTP; a server that does not answer POST at all is served over the deprecated SSE transport. If the server turns out to have dropped the initialize handshake entirely, the proxy logs a warning at startup, because an MCP client older than 2026-07-28 has no way to reach it.

Claude Desktop (MCPB Extension)

The easiest way to use with Claude Desktop is via the .mcpb extension. After installing, configure through the GUI:

SettingDescriptionDefault
Remote MCP Server URLThe remote server URL (required)—
Transport Modeauto, streamable-http, or sseauto
OAuth Callback PortLocal port for OAuth callback3334
Allow HTTPAllow insecure HTTP connectionsfalse
HTTP/HTTPS ProxyProxy server URL (e.g. http://proxy:8080)—
Authorization Header ValueValue portion only — do NOT prepend Authorization:. Examples: Bearer eyJhbGc..., Basic dXNlcjpwYXNz—
Custom Header 1–5 — Name / ValueUp to five additional headers, one per Name/Value field pair. Put the header name (e.g. X-API-Key) in the Name field — it stays visible so you can see which headers are configured — and the value in the Value field, which is stored encrypted and masked. Leave the Name blank to skip a row. Authorization goes in the field above.—

Format notes

The same value can be entered three ways depending on how you launch the proxy. Each path uses a slightly different format:

WhereAuthorization headerOther headers
MCPB UI (above)Authorization Header Value field — value only (Bearer xxx)Custom Header N field pairs — name and value in separate fields
CLI --header (repeat the flag)--header "Authorization: Bearer xxx"--header "X-API-Key: secret"
Env varsMCP_AUTH_HEADER="Bearer xxx" (value only)MCP_HEADERS — newline-separated Name: Value per line (see examples below)

There is no --headers (plural) flag. Pass each header as its own --header "Name: Value"; repeat the flag for multiple headers.

If MCP_HEADERS already contains an Authorization: line, MCP_AUTH_HEADER is ignored to avoid duplicates.

Writing MCP_HEADERS in different contexts

Embedding newlines in a single env-var value looks different in each tool. All of the following set the same two headers (X-API-Key: secret and X-Tenant: acme):

# bash / zsh — $'...' is ANSI-C quoting that turns \n into a real newline
export MCP_HEADERS=$'X-API-Key: secret\nX-Tenant: acme'
# Portable shell — leave the double quotes open across a real newline
export MCP_HEADERS="X-API-Key: secret
X-Tenant: acme"
// Claude Desktop manual config — JSON \n is interpreted as a real newline
{
  "mcpServers": {
    "remote-example": {
      "command": "/path/to/mcp-remote-go",
      "args": ["https://remote.mcp.server/mcp"],
      "env": {
        "MCP_HEADERS": "X-API-Key: secret\nX-Tenant: acme"
      }
    }
  }
}
# docker-compose — YAML block scalar (|) preserves newlines
services:
  mcp-remote:
    image: ghcr.io/naotama2002/mcp-remote-go:latest
    environment:
      MCP_HEADERS: |
        X-API-Key: secret
        X-Tenant: acme
# docker run — same $'...' trick as bash/zsh
docker run --rm -it \
  -e MCP_HEADERS=$'X-API-Key: secret\nX-Tenant: acme' \
  ghcr.io/naotama2002/mcp-remote-go:latest https://remote.mcp.server/mcp
:: Windows CMD — double quotes don't expand \n, so use the literal escape
::               and the proxy will interpret it (see "Escape fallback" below).
set MCP_HEADERS=X-API-Key: secret\nX-Tenant: acme
# Windows PowerShell — backtick-n is the PowerShell newline escape
$env:MCP_HEADERS = "X-API-Key: secret`nX-Tenant: acme"

The MCPB UI does not use MCP_HEADERS; each Custom Header N Name/Value pair is passed to the proxy separately, so no separator is needed there.

Escape fallback (literal \n)

When the value passed to the binary contains no real newlines, the proxy interprets the literal two-character sequence \n (backslash + n) as a separator. This covers contexts that cannot embed real newlines: Windows CMD and plain-double-quote shells. As soon as a real newline is present, the literal \n is left untouched so a header value that legitimately contains \n survives unchanged.

Blank lines and lines without : are ignored (the latter logs a warning), so a typo in one entry never blocks the others.

Claude Desktop (Manual Configuration)

Edit the configuration file at:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json

Using Binary

{
  "mcpServers": {
    "remote-example": {
      "command": "/path/to/mcp-remote-go",
      "args": [
        "https://remote.mcp.server/mcp"
      ]
    }
  }
}

Using Docker

{
  "mcpServers": {
    "remote-example": {
      "command": "docker",
      "args": [
        "run",
        "--rm",
        "-i",
        "--net=host",
        "ghcr.io/naotama2002/mcp-remote-go:latest",
        "https://remote.mcp.server/mcp"
      ]
    }
  }
}

For persistent OAuth tokens with Docker:

{
  "mcpServers": {
    "remote-example": {
      "command": "docker",
      "args": [
        "run",
        "--rm",
        "-i",
        "--net=host",
        "-v",
        "~/.mcp-remote-go-auth:/home/appuser/.mcp-remote-go-auth",
        "ghcr.io/naotama2002/mcp-remote-go:latest",
        "https://remote.mcp.server/mcp"
      ]
    }
  }
}

Cursor

Edit the configuration file at ~/.cursor/mcp.json:

Using Binary

{
  "mcpServers": {
    "remote-example": {
      "command": "/path/to/mcp-remote-go",
      "args": [
        "https://remote.mcp.server/mcp"
      ]
    }
  }
}

Using Docker

{
  "mcpServers": {
    "remote-example": {
      "command": "docker",
      "args": [
        "run",
        "--rm",
        "-i",
        "--net=host",
        "ghcr.io/naotama2002/mcp-remote-go:latest",
        "https://remote.mcp.server/mcp"
      ]
    }
  }
}

Windsurf

Edit the configuration file at ~/.codeium/windsurf/mcp_config.json:

Using Binary

{
  "mcpServers": {
    "remote-example": {
      "command": "/path/to/mcp-remote-go",
      "args": [
        "https://remote.mcp.server/mcp"
      ]
    }
  }
}

Using Docker

{
  "mcpServers": {
    "remote-example": {
      "command": "docker",
      "args": [
        "run",
        "--rm",
        "-i",
        "--net=host",
        "ghcr.io/naotama2002/mcp-remote-go:latest",
        "https://remote.mcp.server/mcp"
      ]
    }
  }
}

Environment Variables

All options can also be set via environment variables. CLI flags take precedence when both are set.

VariableDescriptionEquivalent Flag
MCP_SERVER_URLRemote MCP server URL--server
MCP_TRANSPORTTransport mode (auto, streamable-http, sse)--transport
MCP_PORTOAuth callback port--port
MCP_ALLOW_HTTPSet to true to allow HTTP--allow-http
MCP_HTTPS_PROXYHTTP/HTTPS proxy URL--https-proxy
MCP_AUTH_HEADERAuthorization header value only (e.g. Bearer xxx) — the proxy prepends Authorization: automatically--header "Authorization: ..."
MCP_HEADERSNewline-separated Name: Value list for arbitrary headers (e.g. X-API-Key: secret)One --header "Name: Value" per entry

These environment variables are used internally by the MCPB extension to pass GUI-configured values to the binary.

Authentication

The first time you connect to a server requiring authentication, you'll be prompted to open a URL in your browser to authorize access. The program will wait for you to complete the OAuth flow and then establish the connection. The callback port for OAuth authentication will automatically use an available port if the default port is in use.

The OAuth implementation supports:

  • PKCE (RFC 7636) with S256 code challenge for enhanced security
  • Protected Resource Metadata (RFC 9728) for discovering authorization servers, with WWW-Authenticate-driven PRM lookup on 401 (§5.1)
  • Resource Indicators (RFC 8707) — the MCP server's canonical URI is sent as resource on both authorization and token requests, as required by the MCP authorization spec
  • OAuth 2.0 Authorization Server Metadata (RFC 8414) and OpenID Connect Discovery

Authorization tokens are stored in ~/.mcp-remote-go-auth/ and will be reused for future connections.

Troubleshooting

Clear Authentication Data

If you're having issues with authentication, you can clear the stored data:

rm -rf ~/.mcp-remote-go-auth

VPN/Certificate Issues

If you're behind a VPN and experiencing certificate issues, you might need to specify CA certificates:

export SSL_CERT_FILE=/path/to/ca-certificates.crt
mcp-remote-go https://remote.mcp.server/sse

Docker Issues

Port Already in Use

If you get a "port already in use" error, either stop the conflicting service or use a different port:

# Use a different port
docker run --rm -it -p 3335:3334 ghcr.io/naotama2002/mcp-remote-go:latest https://remote.mcp.server/sse

Permission Issues with Volume Mount

If you have permission issues when mounting the auth directory:

# Make sure the directory exists and has proper permissions
mkdir -p ~/.mcp-remote-go-auth
chmod 755 ~/.mcp-remote-go-auth

# Run with volume mount
docker run --rm -it -p 3334:3334 -v ~/.mcp-remote-go-auth:/home/appuser/.mcp-remote-go-auth ghcr.io/naotama2002/mcp-remote-go:latest https://remote.mcp.server/sse

Network Issues with MCP Clients

If MCP clients can't connect to the Docker container, try using host networking:

# Use host networking mode
docker run --rm -i --net=host ghcr.io/naotama2002/mcp-remote-go:latest https://remote.mcp.server/sse

License

MIT