Skip to content

Commit 1c1821c

Browse files
Merge pull request #1 from offendingcommit/feat/docker-support
feat(docker): add Docker support with multi-platform builds
2 parents 2a0665d + 36ecf89 commit 1c1821c

10 files changed

Lines changed: 720 additions & 255 deletions

File tree

.dockerignore

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Dependencies
2+
node_modules/
3+
.pnpm-store/
4+
5+
# Build output
6+
dist/
7+
8+
# Git
9+
.git/
10+
.gitignore
11+
12+
# IDE
13+
.vscode/
14+
.idea/
15+
*.swp
16+
*.swo
17+
18+
# OS
19+
.DS_Store
20+
Thumbs.db
21+
22+
# Logs
23+
*.log
24+
npm-debug.log*
25+
pnpm-debug.log*
26+
27+
# Environment files
28+
.env
29+
.env.local
30+
.env.*.local
31+
32+
# Testing
33+
coverage/
34+
.nyc_output/
35+
36+
# Documentation
37+
*.md
38+
!README.md
39+
!LICENSE
40+
41+
# CI/CD
42+
.github/
43+
.claude/
44+
45+
# Docker
46+
Dockerfile
47+
.dockerignore
48+
docker-compose*.yml

.github/workflows/check.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
name: Check
22

3+
# This workflow ensures code quality by running prettier formatting checks
4+
# and TypeScript type checking on all pull requests
35
on:
46
pull_request:
57
branches:

.github/workflows/docker.yml

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
name: Build and Publish Docker Image
2+
3+
# This workflow builds multi-platform Docker images and publishes them to
4+
# GitHub Container Registry (ghcr.io). Images are built for linux/amd64 and
5+
# linux/arm64 platforms.
6+
#
7+
# Images are published on:
8+
# - Push to main branch (tagged as 'main' and 'latest')
9+
# - Version tags (tagged as version number)
10+
# - NOT published on pull requests (build only for validation)
11+
on:
12+
push:
13+
branches:
14+
- main
15+
tags:
16+
- 'v*'
17+
pull_request:
18+
branches:
19+
- main
20+
workflow_dispatch:
21+
22+
env:
23+
REGISTRY: ghcr.io
24+
IMAGE_NAME: ${{ github.repository }}
25+
26+
jobs:
27+
build:
28+
runs-on: ubuntu-latest
29+
permissions:
30+
contents: read
31+
packages: write
32+
33+
steps:
34+
- name: Checkout repository
35+
uses: actions/checkout@v4
36+
37+
- name: Set up Docker Buildx
38+
uses: docker/setup-buildx-action@v3
39+
40+
- name: Log in to the Container registry
41+
if: github.event_name != 'pull_request'
42+
uses: docker/login-action@v3
43+
with:
44+
registry: ${{ env.REGISTRY }}
45+
username: ${{ github.actor }}
46+
password: ${{ secrets.GITHUB_TOKEN }}
47+
48+
- name: Extract metadata
49+
id: meta
50+
uses: docker/metadata-action@v5
51+
with:
52+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
53+
tags: |
54+
type=ref,event=branch
55+
type=ref,event=pr
56+
type=semver,pattern={{version}}
57+
type=semver,pattern={{major}}.{{minor}}
58+
type=semver,pattern={{major}}
59+
type=raw,value=latest,enable={{is_default_branch}}
60+
61+
- name: Build and push Docker image
62+
uses: docker/build-push-action@v5
63+
with:
64+
context: .
65+
platforms: linux/amd64,linux/arm64
66+
push: ${{ github.event_name != 'pull_request' }}
67+
tags: ${{ steps.meta.outputs.tags }}
68+
labels: ${{ steps.meta.outputs.labels }}
69+
cache-from: type=gha
70+
cache-to: type=gha,mode=max

.github/workflows/publish.yml

Lines changed: 0 additions & 26 deletions
This file was deleted.

CLAUDE.md

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Commands
6+
7+
### Build & Development
8+
9+
```bash
10+
pnpm build # Compile TypeScript to JavaScript (tsup)
11+
pnpm build:watch # Watch mode for development
12+
pnpm check # Run prettier check + TypeScript type checking
13+
pnpm lint-fix # Auto-fix formatting with prettier
14+
```
15+
16+
### Running the Proxy
17+
18+
```bash
19+
# After building, run the proxy
20+
node dist/proxy.js <remote-server-url> [port] [options]
21+
22+
# Test client mode
23+
node dist/client.js <remote-server-url> [options]
24+
```
25+
26+
### Docker
27+
28+
```bash
29+
# Build Docker image
30+
docker build -t mcp-remote:latest .
31+
32+
# Run with Docker
33+
docker run -it mcp-remote:latest https://remote.mcp.server/sse
34+
35+
# Run with docker-compose
36+
docker-compose run mcp-remote https://remote.mcp.server/sse
37+
38+
# Run with persistent token storage
39+
docker run -it -v mcp-auth:/home/mcp/.mcp-auth mcp-remote:latest https://remote.mcp.server/sse
40+
```
41+
42+
## Architecture Overview
43+
44+
This project acts as a bidirectional proxy between:
45+
46+
- **Local stdio transport** (used by Claude Desktop, Cursor, Windsurf)
47+
- **Remote HTTP/SSE transport** with OAuth 2.0 authentication
48+
49+
### Key Components
50+
51+
1. **proxy.ts**: Main entry point that handles stdio ↔ remote communication
52+
53+
- Reads from stdin, writes to stdout
54+
- Implements transport strategy pattern (http-first, sse-first, http-only, sse-only)
55+
- Manages OAuth authentication flow when needed
56+
57+
2. **client.ts**: Standalone test client for debugging remote connections
58+
59+
- Useful for testing OAuth flows outside of MCP clients
60+
- Run with `npx -p mcp-remote@latest mcp-remote-client <url>`
61+
62+
3. **lib/coordination.ts**: OAuth flow orchestration
63+
64+
- Spins up temporary Express server for OAuth callbacks
65+
- Handles authorization code exchange
66+
- Manages browser opening for auth
67+
68+
4. **lib/mcp-auth-config.ts**: Token persistence and management
69+
70+
- Stores OAuth tokens in `~/.mcp-auth/`
71+
- Automatic token refresh
72+
- Debug logging to `~/.mcp-auth/{server_hash}_debug.log` when --debug flag is used
73+
74+
5. **lib/utils.ts**: Core transport and connection logic
75+
- Implements reconnection with exponential backoff
76+
- Handles both HTTP and SSE transports
77+
- Message proxying between transports
78+
79+
### OAuth Flow
80+
81+
1. If server requires auth, proxy intercepts auth error
82+
2. Spins up local Express server on configurable port (default: 3334)
83+
3. Opens browser for user authorization
84+
4. Receives callback with authorization code
85+
5. Exchanges code for tokens
86+
6. Stores tokens locally for reuse
87+
7. Automatically refreshes tokens when expired
88+
89+
### Transport Strategies
90+
91+
- Default behavior: Try HTTP first, fall back to SSE
92+
- Configurable via `--transport` flag
93+
- Handles connection failures and retries gracefully
94+
95+
## Important Notes
96+
97+
- No test suite exists - testing is manual via the client command
98+
- Uses pnpm package manager (v10.11.0)
99+
- TypeScript strict mode enabled
100+
- ES modules output only
101+
- Prettier formatting with 140 char lines, single quotes, no semicolons
102+
- OAuth tokens stored in `~/.mcp-auth/` (or `$MCP_REMOTE_CONFIG_DIR`)
103+
- Debug logs available with `--debug` flag for troubleshooting auth issues

Dockerfile

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Build stage
2+
FROM node:22-alpine AS builder
3+
4+
# Install pnpm globally with specific version from package.json
5+
RUN corepack enable && corepack prepare pnpm@10.11.0 --activate
6+
7+
# Set working directory
8+
WORKDIR /app
9+
10+
# Copy package files
11+
COPY package.json pnpm-lock.yaml ./
12+
13+
# Install dependencies
14+
RUN pnpm install --frozen-lockfile
15+
16+
# Copy source code
17+
COPY tsconfig.json ./
18+
COPY src ./src
19+
20+
# Build the project
21+
RUN pnpm build
22+
23+
# Runtime stage
24+
FROM node:22-alpine
25+
26+
# Install pnpm for runtime (needed for running the tool)
27+
RUN corepack enable && corepack prepare pnpm@10.11.0 --activate
28+
29+
# Create non-root user
30+
RUN addgroup -g 1001 -S mcp && \
31+
adduser -S mcp -u 1001
32+
33+
# Set working directory
34+
WORKDIR /app
35+
36+
# Copy package files
37+
COPY package.json pnpm-lock.yaml ./
38+
39+
# Install production dependencies only
40+
RUN pnpm install --prod --frozen-lockfile && \
41+
pnpm store prune
42+
43+
# Copy built files from builder
44+
COPY --from=builder /app/dist ./dist
45+
46+
# Copy other necessary files
47+
COPY README.md LICENSE ./
48+
49+
# Create directory for auth tokens (will be mounted as volume)
50+
RUN mkdir -p /home/mcp/.mcp-auth && \
51+
chown -R mcp:mcp /home/mcp/.mcp-auth
52+
53+
# Switch to non-root user
54+
USER mcp
55+
56+
# Set environment variable for config directory
57+
ENV MCP_REMOTE_CONFIG_DIR=/home/mcp/.mcp-auth
58+
59+
# Default to proxy command
60+
ENTRYPOINT ["node", "dist/proxy.js"]
61+
62+
# Default arguments (can be overridden)
63+
CMD []

0 commit comments

Comments
 (0)