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
52 changes: 26 additions & 26 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -225,21 +225,21 @@ jobs:
cgo: "1"
name: mcpproxy-darwin-arm64
archive_format: tar.gz
# Teams edition (Linux only) — uncomment when teams MVP is ready
# Server edition (Linux only) — uncomment when server MVP is ready
# - os: ubuntu-latest
# goos: linux
# goarch: amd64
# cgo: "0"
# name: mcpproxy-teams-linux-amd64
# name: mcpproxy-server-linux-amd64
# archive_format: tar.gz
# edition: teams
# edition: server
# - os: ubuntu-latest
# goos: linux
# goarch: arm64
# cgo: "0"
# name: mcpproxy-teams-linux-arm64
# name: mcpproxy-server-linux-arm64
# archive_format: tar.gz
# edition: teams
# edition: server

runs-on: ${{ matrix.os }}

Expand Down Expand Up @@ -389,9 +389,9 @@ jobs:
# Determine clean binary name and build flags
EDITION="${{ matrix.edition }}"
BUILD_TAGS=""
if [ "$EDITION" = "teams" ]; then
CLEAN_BINARY="mcpproxy-teams"
BUILD_TAGS="-tags teams"
if [ "$EDITION" = "server" ]; then
CLEAN_BINARY="mcpproxy-server"
BUILD_TAGS="-tags server"
elif [ "${{ matrix.goos }}" = "windows" ]; then
CLEAN_BINARY="mcpproxy.exe"
else
Expand All @@ -402,7 +402,7 @@ jobs:
go build ${BUILD_TAGS} -ldflags "${LDFLAGS}" -o ${CLEAN_BINARY} ./cmd/mcpproxy

# Build tray binary for platforms with GUI support (macOS and Windows, personal only)
if [ "$EDITION" != "teams" ] && { [ "${{ matrix.goos }}" = "darwin" ] || [ "${{ matrix.goos }}" = "windows" ]; }; then
if [ "$EDITION" != "server" ] && { [ "${{ matrix.goos }}" = "darwin" ] || [ "${{ matrix.goos }}" = "windows" ]; }; then
echo "Building mcpproxy-tray for ${{ matrix.goos }}..."

# Determine tray binary name
Expand All @@ -418,14 +418,14 @@ jobs:
# Code sign macOS binaries
if [ "${{ matrix.goos }}" = "darwin" ]; then
echo "Code signing macOS binary..."

# Debug: List all available certificates
echo "Available certificates:"
security find-identity -v -p codesigning

# Find the Developer ID certificate identity
CERT_IDENTITY=$(security find-identity -v -p codesigning | grep "Developer ID Application" | head -1 | grep -o '"[^"]*"' | tr -d '"')

# Verify we found a valid certificate
if [ -n "${CERT_IDENTITY}" ]; then
echo "✅ Found Developer ID certificate: ${CERT_IDENTITY}"
Expand All @@ -434,7 +434,7 @@ jobs:
CERT_IDENTITY="${{ secrets.APPLE_TEAM_ID }}"
echo "⚠️ Using fallback identity: ${CERT_IDENTITY}"
fi

# Validate entitlements file formatting (Apple's recommendation)
echo "=== Validating entitlements file ==="
if [ -f "scripts/entitlements.plist" ]; then
Expand All @@ -445,14 +445,14 @@ jobs:
echo "❌ Entitlements file has formatting issues"
exit 1
fi

# Convert to XML format if needed (Apple's recommendation)
plutil -convert xml1 scripts/entitlements.plist
echo "✅ Entitlements converted to XML format"
else
echo "⚠️ No entitlements file found"
fi

# Sign both binaries with proper Developer ID certificate, hardened runtime, and timestamp
echo "=== Signing binaries with hardened runtime ==="

Expand Down Expand Up @@ -527,7 +527,7 @@ jobs:
echo "❌ All tray binary signing attempts failed"
exit 1
fi

# Verify signing, hardened runtime, and timestamp using Apple's recommended methods
echo "=== Verifying binary signatures (Apple's recommended verification) ==="

Expand Down Expand Up @@ -558,7 +558,7 @@ jobs:
echo "❌ Strict verification FAILED - will not pass notarization"
exit 1
fi

# Check for secure timestamp (Apple's recommended check)
echo "=== Checking for secure timestamp ==="
TIMESTAMP_CHECK=$(codesign -dvv ${CLEAN_BINARY} 2>&1)
Expand All @@ -570,14 +570,14 @@ jobs:
echo "Full output:"
echo "$TIMESTAMP_CHECK"
fi

# Display detailed signature info
codesign --display --verbose=4 ${CLEAN_BINARY}

# Check entitlements formatting (Apple's recommendation)
echo "=== Checking entitlements formatting ==="
codesign --display --entitlements - ${CLEAN_BINARY} | head -10

# Verify with spctl (Gatekeeper assessment) - expected to fail before notarization
echo "=== Gatekeeper assessment (expected to fail before notarization) ==="
if spctl --assess --verbose ${CLEAN_BINARY}; then
Expand All @@ -586,7 +586,7 @@ jobs:
echo "⚠️ Gatekeeper assessment: REJECTED (expected - binary needs notarization)"
echo "This is normal - the binary will pass after Apple completes notarization"
fi

echo "✅ Binary signed successfully with hardened runtime and timestamp"
fi

Expand Down Expand Up @@ -859,7 +859,7 @@ jobs:
if [ -f .keychain_name ]; then
TEMP_KEYCHAIN=$(cat .keychain_name)
echo "Cleaning up keychain: ${TEMP_KEYCHAIN}"

# Remove from search list and delete
security delete-keychain "$TEMP_KEYCHAIN" 2>/dev/null || echo "Keychain already cleaned up"
rm -f .keychain_name
Expand Down Expand Up @@ -988,7 +988,7 @@ jobs:
build-docker:
runs-on: ubuntu-latest
needs: [build]
# Disabled until teams MVP is ready — uncomment to enable
# Disabled until server MVP is ready — uncomment to enable
if: false && startsWith(github.ref, 'refs/tags/v')
permissions:
contents: read
Expand All @@ -1010,11 +1010,11 @@ jobs:
COMMIT=${{ github.sha }}
BUILD_DATE=${{ github.event.head_commit.timestamp }}
tags: |
ghcr.io/smart-mcp-proxy/mcpproxy-teams:${{ github.ref_name }}
ghcr.io/smart-mcp-proxy/mcpproxy-teams:latest
ghcr.io/smart-mcp-proxy/mcpproxy-server:${{ github.ref_name }}
ghcr.io/smart-mcp-proxy/mcpproxy-server:latest

release:
needs: [build, sign-windows, generate-notes] # add build-docker when teams MVP is ready
needs: [build, sign-windows, generate-notes] # add build-docker when server MVP is ready
runs-on: ubuntu-latest
environment: production

Expand Down
87 changes: 78 additions & 9 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ When operating to complete a task, adhere strictly to the following constraints

### Must-Do (Defaults & Assumptions)
- **Zero Interruption Policy**: If a decision is needed and no explicit instruction exists, you MUST make an informed, safe assumption based on idiomatic Go best practices and document it in the PR/commit. Do NOT ask for human clarification mid-task.
- **Test-Driven Progress**: You must write a failing Go test (`_test.go`) for every sub-task before implementing the feature.
- **Test-Driven Progress**: You must write a failing Go test (`_test.go`) for every sub-task before implementing the feature.
- **Graceful Fallbacks**: If an API or dependency lacks documentation, use mock interfaces or a simplified implementation rather than blocking the task.
- **Continuous Logging**: Document every step completed in an `execution_log.md` within the current working directory to maintain state.

Expand All @@ -28,23 +28,30 @@ Only halt execution and ask a human IF:

MCPProxy is a Go-based desktop application that acts as a smart proxy for AI agents using the Model Context Protocol (MCP). It provides intelligent tool discovery, massive token savings, and built-in security quarantine against malicious MCP servers.

## Editions (Personal & Teams)
## Editions (Personal & Server)

MCPProxy is built in two editions from the same codebase using Go build tags:

| Edition | Build Command | Binary | Distribution |
|---------|--------------|--------|-------------|
| **Personal** (default) | `go build ./cmd/mcpproxy` | `mcpproxy` | macOS DMG, Windows installer, Linux tar.gz |
| **Teams** | `go build -tags teams ./cmd/mcpproxy` | `mcpproxy-teams` | Docker image, .deb package, Linux tar.gz |
| **Server** | `go build -tags server ./cmd/mcpproxy` | `mcpproxy-server` | Docker image, .deb package, Linux tar.gz |

> Every feature decision should ask: "Does this make the personal edition so good that developers tell their teammates about it?"

### Key Directories

| Directory | Purpose |
|-----------|---------|
| `cmd/mcpproxy/edition.go` | Default edition = "personal" |
| `cmd/mcpproxy/edition_teams.go` | Build-tagged override for teams |
| `cmd/mcpproxy/teams_register.go` | Teams feature registration entry point |
| `internal/teams/` | Teams-only code (all files have `//go:build teams`) |
| `cmd/mcpproxy/edition_teams.go` | Build-tagged override for server edition |
| `cmd/mcpproxy/teams_register.go` | Server feature registration entry point |
| `internal/teams/` | Server-only code (all files have `//go:build server`) |
| `internal/teams/auth/` | OAuth authentication, session management, JWT tokens, middleware |
| `internal/teams/users/` | User/session models, BBolt store, user server management |
| `internal/teams/workspace/` | Per-user workspace manager for personal upstream servers |
| `internal/teams/multiuser/` | Multi-user router, tool filtering, activity isolation |
| `internal/teams/api/` | Server REST API endpoints (user, admin, auth) |
| `native/macos/` | Future Swift tray app (placeholder) |
| `native/windows/` | Future C# tray app (placeholder) |

Expand All @@ -54,6 +61,66 @@ The binary self-identifies its edition:
- `mcpproxy version` → `MCPProxy v0.21.0 (personal) darwin/arm64`
- `/api/v1/status` → `{"edition": "personal", ...}`

## Server Multi-User Authentication (Spec 024)

Server edition supports OAuth-based multi-user authentication with Google, GitHub, or Microsoft identity providers.

### Server Configuration

```json
{
"teams": {
"enabled": true,
"admin_emails": ["admin@company.com"],
"oauth": {
"provider": "google",
"client_id": "xxx.apps.googleusercontent.com",
"client_secret": "GOCSPX-xxx",
"tenant_id": "",
"allowed_domains": ["company.com"]
},
"session_ttl": "24h",
"bearer_token_ttl": "24h",
"workspace_idle_timeout": "30m",
"max_user_servers": 20
}
}
```

### Server API Endpoints

| Endpoint | Auth | Description |
|----------|------|-------------|
| `GET /api/v1/auth/login` | Public | Initiate OAuth login flow |
| `GET /api/v1/auth/callback` | Public | OAuth callback (creates session) |
| `GET /api/v1/auth/me` | Session/JWT | Get current user profile |
| `POST /api/v1/auth/token` | Session | Generate JWT bearer token for MCP |
| `POST /api/v1/auth/logout` | Session | Invalidate session |
| `GET /api/v1/user/servers` | Session/JWT | List user's servers (personal + shared) |
| `POST /api/v1/user/servers` | Session/JWT | Add personal upstream server |
| `GET /api/v1/user/activity` | Session/JWT | User's activity log |
| `GET /api/v1/user/diagnostics` | Session/JWT | Server health for user's servers |
| `GET /api/v1/admin/users` | Admin | List all users |
| `POST /api/v1/admin/users/{id}/disable` | Admin | Disable a user |
| `GET /api/v1/admin/activity` | Admin | All users' activity logs |
| `GET /api/v1/admin/sessions` | Admin | List active sessions |

### Server Architecture

- **Auth flow**: OAuth 2.0 + PKCE → Session cookie (Web UI) + JWT bearer (MCP/API)
- **Server types**: Shared (config file, single connection) + Personal (DB, per-user connections)
- **Isolation**: Users see only shared + own personal servers. Activity logs user-scoped.
- **Admin**: Identified by `admin_emails` config. Sees all activity, manages users.
- **Build tag**: All server code behind `//go:build server`. Personal edition unaffected.

### Server Testing

```bash
go test -tags server ./internal/teams/... -v -race # All server unit + integration tests
go build -tags server ./cmd/mcpproxy # Build server edition
go build ./cmd/mcpproxy # Verify personal edition unaffected
```

## Architecture: Core + Tray Split

- **Core Server** (`mcpproxy`): Headless HTTP API server with MCP proxy functionality
Expand All @@ -66,11 +133,11 @@ The binary self-identifies its edition:
### Build
```bash
go build -o mcpproxy ./cmd/mcpproxy # Core server (personal)
go build -tags teams -o mcpproxy-teams ./cmd/mcpproxy # Core server (teams)
go build -tags server -o mcpproxy-server ./cmd/mcpproxy # Core server (server edition)
GOOS=darwin CGO_ENABLED=1 go build -o mcpproxy-tray ./cmd/mcpproxy-tray # Tray app
make build # Frontend and backend (personal)
make build-teams # Frontend and backend (teams)
make build-docker # Teams Docker image
make build-server # Frontend and backend (server)
make build-docker # Server Docker image
./scripts/build.sh # Cross-platform build
```

Expand Down Expand Up @@ -546,6 +613,8 @@ See `docs/prerelease-builds.md` for download instructions.
- `~/.mcpproxy/mcp_config.json` (config file), `~/.mcpproxy/config.db` (BBolt - not directly used) (027-status-command)
- Go 1.24 (toolchain go1.24.10) + Cobra (CLI), Chi router (HTTP), BBolt (storage), Zap (logging), mcp-go (MCP protocol), crypto/hmac + crypto/sha256 (token hashing) (028-agent-tokens)
- BBolt database (`~/.mcpproxy/config.db`) — new `agent_tokens` bucket (028-agent-tokens)
- Go 1.24 (toolchain go1.24.10) + TypeScript 5.9 / Vue 3.5 + Chi router, BBolt, Zap logging, mcp-go, golang-jwt/jwt/v5, Vue 3, Pinia, DaisyUI (024-teams-multiuser-oauth)
- BBolt database (`~/.mcpproxy/config.db`) - new buckets for users, sessions, user servers (024-teams-multiuser-oauth)

## Recent Changes
- 001-update-version-display: Added Go 1.24 (toolchain go1.24.10)
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ COPY . .
RUN cd frontend && npm ci && npm run build
RUN mkdir -p web/frontend && cp -r frontend/dist web/frontend/

# Build teams binary
# Build server edition binary
ARG VERSION=dev
ARG COMMIT=unknown
ARG BUILD_DATE=unknown
RUN CGO_ENABLED=0 go build \
-tags teams \
-tags server \
-ldflags "-X main.version=${VERSION} -X main.commit=${COMMIT} -X main.date=${BUILD_DATE} -X github.com/smart-mcp-proxy/mcpproxy-go/internal/httpapi.buildVersion=${VERSION} -s -w" \
-o /mcpproxy ./cmd/mcpproxy

Expand Down
34 changes: 17 additions & 17 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# MCPProxy Makefile

.PHONY: help build build-teams build-docker build-deb swagger swagger-verify frontend-build frontend-dev backend-dev clean test test-coverage test-e2e test-e2e-oauth lint dev-setup docs-setup docs-dev docs-build docs-clean
.PHONY: help build build-server build-docker build-deb swagger swagger-verify frontend-build frontend-dev backend-dev clean test test-coverage test-e2e test-e2e-oauth lint dev-setup docs-setup docs-dev docs-build docs-clean

SWAGGER_BIN ?= $(HOME)/go/bin/swag
SWAGGER_OUT ?= oas
Expand All @@ -23,10 +23,10 @@ help:
@echo " make lint - Run linter"
@echo " make dev-setup - Install development dependencies (swag, frontend, Playwright)"
@echo ""
@echo "Teams Edition:"
@echo " make build-teams - Build Teams edition binary (with -tags teams)"
@echo " make build-docker - Build Teams Docker image"
@echo " make build-deb - Build Teams .deb package (TODO)"
@echo "Server Edition:"
@echo " make build-server - Build Server edition binary (with -tags server)"
@echo " make build-docker - Build Server Docker image"
@echo " make build-deb - Build Server .deb package (TODO)"
@echo ""
@echo "Documentation Commands:"
@echo " make docs-setup - Install documentation dependencies"
Expand Down Expand Up @@ -91,28 +91,28 @@ backend-dev:
@echo "🚀 Run: ./mcpproxy-dev serve"
@echo "🌐 In dev mode, make sure frontend dev server is running on port 3000"

# Build Teams edition
build-teams: swagger frontend-build
@echo "🔨 Building Teams edition binary (version: $(VERSION))..."
go build -tags teams -ldflags "$(LDFLAGS)" -o mcpproxy-teams ./cmd/mcpproxy
@echo "✅ Teams build completed! Run: ./mcpproxy-teams serve"
# Build Server edition
build-server: swagger frontend-build
@echo "🔨 Building Server edition binary (version: $(VERSION))..."
go build -tags server -ldflags "$(LDFLAGS)" -o mcpproxy-server ./cmd/mcpproxy
@echo "✅ Server build completed! Run: ./mcpproxy-server serve"

# Build Teams Docker image
# Build Server Docker image
build-docker:
@echo "🐳 Building Teams Docker image (version: $(VERSION))..."
docker build --build-arg VERSION=$(VERSION) --build-arg COMMIT=$(COMMIT) --build-arg BUILD_DATE=$(BUILD_DATE) -t mcpproxy-teams:$(VERSION) -t mcpproxy-teams:latest .
@echo "✅ Docker image built: mcpproxy-teams:$(VERSION)"
@echo "🐳 Building Server Docker image (version: $(VERSION))..."
docker build --build-arg VERSION=$(VERSION) --build-arg COMMIT=$(COMMIT) --build-arg BUILD_DATE=$(BUILD_DATE) -t mcpproxy-server:$(VERSION) -t mcpproxy-server:latest .
@echo "✅ Docker image built: mcpproxy-server:$(VERSION)"

# Build Teams .deb package (placeholder)
# Build Server .deb package (placeholder)
build-deb:
@echo "📦 Building Teams .deb package..."
@echo "📦 Building Server .deb package..."
@echo "⚠️ TODO: Implement deb package build (nfpm or dpkg-deb)"
@echo " See: https://nfpm.goreleaser.com/"

# Clean build artifacts
clean:
@echo "🧹 Cleaning build artifacts..."
rm -f mcpproxy mcpproxy-dev mcpproxy-tray mcpproxy-teams
rm -f mcpproxy mcpproxy-dev mcpproxy-tray mcpproxy-server
rm -rf frontend/dist frontend/node_modules web/frontend
go clean
@echo "✅ Cleanup completed"
Expand Down
4 changes: 2 additions & 2 deletions cmd/mcpproxy/edition_teams.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//go:build teams
//go:build server

package main

func init() {
Edition = "teams"
Edition = "server"
}
Loading
Loading