Content
# 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)
```bash
brew install mikluko/tap/mcp-proxy
```
### Go
```bash
go install github.com/mikluko/mcp-proxy/cmd/mcp-proxy@latest
```
### Binary releases
Download from [GitHub Releases](https://github.com/mikluko/mcp-proxy/releases).
## Usage
### Stdio Mode (Default)
Standard mode for MCP clients that communicate via stdio (Claude Code, Claude Desktop, Cursor, Windsurf).
#### Claude Code
```bash
claude mcp add remote-example mcp-proxy https://remote.mcp.server/sse
```
#### Claude Desktop, Cursor, Windsurf
```json
{
"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
```bash
# 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
```json
{
"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:
```json
{
"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:
```bash
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
- [geelen/mcp-remote](https://github.com/geelen/mcp-remote) - Original TypeScript implementation
- [sparfenyuk/mcp-proxy](https://github.com/sparfenyuk/mcp-proxy) - Python implementation
- [Model Context Protocol](https://modelcontextprotocol.io/) - MCP specification
## License
[MIT](./LICENSE)