Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Dependencies
node_modules/
.pnpm-store/

# Build output
dist/

# Git
.git/
.gitignore

# IDE
.vscode/
.idea/
*.swp
*.swo

# OS
.DS_Store
Thumbs.db

# Logs
*.log
npm-debug.log*
pnpm-debug.log*

# Environment files
.env
.env.local
.env.*.local

# Testing
coverage/
.nyc_output/

# Documentation
*.md
!README.md
!LICENSE

# CI/CD
.github/
.claude/

# Docker
Dockerfile
.dockerignore
docker-compose*.yml
2 changes: 2 additions & 0 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
name: Check

# This workflow ensures code quality by running prettier formatting checks
# and TypeScript type checking on all pull requests
on:
pull_request:
branches:
Expand Down
70 changes: 70 additions & 0 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
name: Build and Publish Docker Image

# This workflow builds multi-platform Docker images and publishes them to
# GitHub Container Registry (ghcr.io). Images are built for linux/amd64 and
# linux/arm64 platforms.
#
# Images are published on:
# - Push to main branch (tagged as 'main' and 'latest')
# - Version tags (tagged as version number)
# - NOT published on pull requests (build only for validation)
on:
push:
branches:
- main
tags:
- 'v*'
pull_request:
branches:
- main
workflow_dispatch:

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to the Container registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=raw,value=latest,enable={{is_default_branch}}

- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
26 changes: 0 additions & 26 deletions .github/workflows/publish.yml

This file was deleted.

103 changes: 103 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Commands

### Build & Development

```bash
pnpm build # Compile TypeScript to JavaScript (tsup)
pnpm build:watch # Watch mode for development
pnpm check # Run prettier check + TypeScript type checking
pnpm lint-fix # Auto-fix formatting with prettier
```

### Running the Proxy

```bash
# After building, run the proxy
node dist/proxy.js <remote-server-url> [port] [options]

# Test client mode
node dist/client.js <remote-server-url> [options]
```

### Docker

```bash
# Build Docker image
docker build -t mcp-remote:latest .

# Run with Docker
docker run -it mcp-remote:latest https://remote.mcp.server/sse

# Run with docker-compose
docker-compose run mcp-remote https://remote.mcp.server/sse

# Run with persistent token storage
docker run -it -v mcp-auth:/home/mcp/.mcp-auth mcp-remote:latest https://remote.mcp.server/sse
```

## Architecture Overview

This project acts as a bidirectional proxy between:

- **Local stdio transport** (used by Claude Desktop, Cursor, Windsurf)
- **Remote HTTP/SSE transport** with OAuth 2.0 authentication

### Key Components

1. **proxy.ts**: Main entry point that handles stdio ↔ remote communication

- Reads from stdin, writes to stdout
- Implements transport strategy pattern (http-first, sse-first, http-only, sse-only)
- Manages OAuth authentication flow when needed

2. **client.ts**: Standalone test client for debugging remote connections

- Useful for testing OAuth flows outside of MCP clients
- Run with `npx -p mcp-remote@latest mcp-remote-client <url>`

3. **lib/coordination.ts**: OAuth flow orchestration

- Spins up temporary Express server for OAuth callbacks
- Handles authorization code exchange
- Manages browser opening for auth

4. **lib/mcp-auth-config.ts**: Token persistence and management

- Stores OAuth tokens in `~/.mcp-auth/`
- Automatic token refresh
- Debug logging to `~/.mcp-auth/{server_hash}_debug.log` when --debug flag is used

5. **lib/utils.ts**: Core transport and connection logic
- Implements reconnection with exponential backoff
- Handles both HTTP and SSE transports
- Message proxying between transports

### OAuth Flow

1. If server requires auth, proxy intercepts auth error
2. Spins up local Express server on configurable port (default: 3334)
3. Opens browser for user authorization
4. Receives callback with authorization code
5. Exchanges code for tokens
6. Stores tokens locally for reuse
7. Automatically refreshes tokens when expired

### Transport Strategies

- Default behavior: Try HTTP first, fall back to SSE
- Configurable via `--transport` flag
- Handles connection failures and retries gracefully

## Important Notes

- No test suite exists - testing is manual via the client command
- Uses pnpm package manager (v10.11.0)
- TypeScript strict mode enabled
- ES modules output only
- Prettier formatting with 140 char lines, single quotes, no semicolons
- OAuth tokens stored in `~/.mcp-auth/` (or `$MCP_REMOTE_CONFIG_DIR`)
- Debug logs available with `--debug` flag for troubleshooting auth issues
63 changes: 63 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Build stage
FROM node:22-alpine AS builder

# Install pnpm globally with specific version from package.json
RUN corepack enable && corepack prepare pnpm@10.11.0 --activate

# Set working directory
WORKDIR /app

# Copy package files
COPY package.json pnpm-lock.yaml ./

# Install dependencies
RUN pnpm install --frozen-lockfile

# Copy source code
COPY tsconfig.json ./
COPY src ./src

# Build the project
RUN pnpm build

# Runtime stage
FROM node:22-alpine

# Install pnpm for runtime (needed for running the tool)
RUN corepack enable && corepack prepare pnpm@10.11.0 --activate

# Create non-root user
RUN addgroup -g 1001 -S mcp && \
adduser -S mcp -u 1001

# Set working directory
WORKDIR /app

# Copy package files
COPY package.json pnpm-lock.yaml ./

# Install production dependencies only
RUN pnpm install --prod --frozen-lockfile && \
pnpm store prune

# Copy built files from builder
COPY --from=builder /app/dist ./dist

# Copy other necessary files
COPY README.md LICENSE ./

# Create directory for auth tokens (will be mounted as volume)
RUN mkdir -p /home/mcp/.mcp-auth && \
chown -R mcp:mcp /home/mcp/.mcp-auth

# Switch to non-root user
USER mcp

# Set environment variable for config directory
ENV MCP_REMOTE_CONFIG_DIR=/home/mcp/.mcp-auth

# Default to proxy command
ENTRYPOINT ["node", "dist/proxy.js"]

# Default arguments (can be overridden)
CMD []
Loading
Loading