Skip to content

Latest commit

 

History

History
569 lines (430 loc) · 18.7 KB

File metadata and controls

569 lines (430 loc) · 18.7 KB

DevRev MCP Server - Cloud Run Deployment

This directory contains configuration files for deploying the DevRev MCP Server to Google Cloud Run with per-user DevRev PAT authentication.

Architecture

  • Container Registry: Artifact Registry (preferred over Container Registry)
  • Deployment: Cloud Run with --allow-unauthenticated for public access
  • Authentication: Per-user DevRev Personal Access Token (PAT) via Bearer token
  • Secrets: Google Secret Manager with Cloud Run runtime service account access
  • CI/CD: GitHub Actions with Workload Identity Federation (WIF)

Authentication Model

The MCP server supports two authentication modes:

Per-User PAT Authentication (Default - Recommended)

Mode: MCP_AUTH_MODE=devrev-pat

Each user sends their own DevRev Personal Access Token as the Bearer token in the Authorization header. The server:

  1. Validates the PAT against the DevRev API
  2. Extracts user identity from the PAT
  3. Creates a per-request DevRev client with the user's credentials
  4. Optionally restricts access by email domain (e.g., augmentcode.com — without @ prefix)

Benefits:

  • No shared secrets - each user uses their own DevRev credentials
  • Audit trail shows actual user identity in DevRev
  • Fine-grained access control via domain restrictions
  • Automatic token validation and caching (5-minute TTL by default)

User Configuration: Users configure their MCP client with their own DevRev PAT:

{
  "mcpServers": {
    "devrev": {
      "type": "http",
      "url": "https://devrev-mcp-server-xxx.run.app/mcp",
      "headers": {
        "Authorization": "Bearer <your-devrev-personal-access-token>"
      }
    }
  }
}

Static Token Authentication (Legacy)

Mode: MCP_AUTH_MODE=static-token

All users share a single MCP_AUTH_TOKEN secret. This mode is maintained for backward compatibility but is not recommended for production use.

Prerequisites

  1. Google Cloud CLI installed and configured:

    gcloud --version
    gcloud auth login
    gcloud config set project YOUR_PROJECT_ID
  2. Enable required APIs:

    gcloud services enable \
      cloudbuild.googleapis.com \
      run.googleapis.com \
      artifactregistry.googleapis.com \
      secretmanager.googleapis.com \
      iamcredentials.googleapis.com
  3. Create Artifact Registry repository (if not exists):

    gcloud artifacts repositories create devrev-mcp \
      --repository-format=docker \
      --location=us-central1 \
      --description="DevRev MCP Server Docker images"
  4. Get your project number (needed for IAM):

    PROJECT_NUMBER=$(gcloud projects describe $(gcloud config get-value project) --format='value(projectNumber)')
    echo $PROJECT_NUMBER

Setup: Create Secrets in Google Secret Manager

1. Create DevRev API Token Secret (Optional)

Note: With MCP_AUTH_MODE=devrev-pat (the default), this secret is not needed. Each user's DevRev PAT is sent directly via the Authorization header. Only create this secret if you use MCP_AUTH_MODE=static-token or need stdio transport fallback for testing.

# Create the secret (interactive)
gcloud secrets create devrev-api-token \
  --replication-policy=automatic \
  --data-file=-

# Or from environment variable
echo -n "$DEVREV_API_TOKEN" | gcloud secrets create devrev-api-token \
  --replication-policy=automatic \
  --data-file=-

2. Grant Cloud Run Service Account Access to Secrets

PROJECT_NUMBER=$(gcloud projects describe $(gcloud config get-value project) --format='value(projectNumber)')
COMPUTE_SA="$PROJECT_NUMBER-compute@developer.gserviceaccount.com"

# Grant access to devrev-api-token (if created)
gcloud secrets add-iam-policy-binding devrev-api-token \
  --member=serviceAccount:$COMPUTE_SA \
  --role=roles/secretmanager.secretAccessor

Legacy: Static Token Mode Setup

If you need to use the legacy MCP_AUTH_MODE=static-token mode:

# Create MCP auth token secret
echo -n "$MCP_AUTH_TOKEN" | gcloud secrets create mcp-auth-token \
  --replication-policy=automatic \
  --data-file=-

# Grant access
gcloud secrets add-iam-policy-binding mcp-auth-token \
  --member=serviceAccount:$COMPUTE_SA \
  --role=roles/secretmanager.secretAccessor

# Update deployment to use static-token mode
# Add to cloudbuild.yaml: MCP_AUTH_MODE=static-token
# Add to secrets: MCP_AUTH_TOKEN=mcp-auth-token:latest

Deployment Methods

Method 1: Deploy with Cloud Build (Recommended)

This method builds the Docker image and deploys to Cloud Run in one step:

# Deploy from the project root
gcloud builds submit \
  --config=deploy/cloudbuild.yaml \
  --substitutions=_REGION=us-central1

# Or with a version tag
gcloud builds submit \
  --config=deploy/cloudbuild.yaml \
  --substitutions=_REGION=us-central1,_TAG_NAME=v1.2.3

Method 2: Deploy with service.yaml

This method uses a declarative YAML configuration:

# First, update PROJECT_ID and PROJECT_NUMBER in service.yaml
sed -i "s/PROJECT_ID/$(gcloud config get-value project)/g" deploy/service.yaml
sed -i "s/PROJECT_NUMBER/$(gcloud projects describe $(gcloud config get-value project) --format='value(projectNumber)')/g" deploy/service.yaml

# Deploy the service
gcloud run services replace deploy/service.yaml --region=us-central1

Method 3: Manual Docker Build and Deploy

# Build the image
docker build -t gcr.io/$(gcloud config get-value project)/devrev-mcp-server:latest .

# Push to Container Registry
docker push gcr.io/$(gcloud config get-value project)/devrev-mcp-server:latest

# Deploy to Cloud Run
gcloud run deploy devrev-mcp-server \
  --image=gcr.io/$(gcloud config get-value project)/devrev-mcp-server:latest \
  --region=us-central1 \
  --platform=managed \
  --allow-unauthenticated \
  --port=8080 \
  --memory=512Mi \
  --cpu=1 \
  --min-instances=0 \
  --max-instances=10 \
  --timeout=300s \
  --concurrency=80 \
  --set-env-vars=MCP_TRANSPORT=streamable-http,MCP_HOST=0.0.0.0,MCP_PORT=8080,MCP_LOG_FORMAT=json,MCP_LOG_LEVEL=INFO \
  --set-secrets=DEVREV_API_TOKEN=devrev-api-token:latest

Setup: Configure GitHub Actions for Automated Deployment

1. Create Workload Identity Federation (WIF) Configuration

WIF allows GitHub Actions to authenticate to Google Cloud without storing JSON keys.

# Create a service account for GitHub Actions
gcloud iam service-accounts create github-actions-deployer \
  --display-name="GitHub Actions Cloud Run Deployer"

# Grant Cloud Build and Cloud Run permissions
gcloud projects add-iam-policy-binding $(gcloud config get-value project) \
  --member=serviceAccount:github-actions-deployer@$(gcloud config get-value project).iam.gserviceaccount.com \
  --role=roles/cloudbuild.builds.editor

gcloud projects add-iam-policy-binding $(gcloud config get-value project) \
  --member=serviceAccount:github-actions-deployer@$(gcloud config get-value project).iam.gserviceaccount.com \
  --role=roles/run.admin

gcloud projects add-iam-policy-binding $(gcloud config get-value project) \
  --member=serviceAccount:github-actions-deployer@$(gcloud config get-value project).iam.gserviceaccount.com \
  --role=roles/iam.serviceAccountUser

2. Create Workload Identity Pool and Provider

PROJECT_ID=$(gcloud config get-value project)

# Create the workload identity pool
gcloud iam workload-identity-pools create github \
  --project=$PROJECT_ID \
  --location=global \
  --display-name="GitHub Actions"

# Create the workload identity provider
gcloud iam workload-identity-pools providers create-oidc github \
  --project=$PROJECT_ID \
  --location=global \
  --workload-identity-pool=github \
  --display-name="GitHub" \
  --attribute-mapping="google.subject=assertion.sub,attribute.actor=assertion.actor,attribute.repository=assertion.repository" \
  --issuer-uri=https://token.actions.githubusercontent.com

# Get the provider resource name
PROVIDER=$(gcloud iam workload-identity-pools providers describe github \
  --project=$PROJECT_ID \
  --location=global \
  --workload-identity-pool=github \
  --format='value(name)')
echo "Provider: $PROVIDER"

3. Configure Service Account Impersonation

PROJECT_ID=$(gcloud config get-value project)
PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')
REPO_OWNER="mgmonteleone"  # Change to your GitHub username
REPO_NAME="py-dev-rev"

# Allow GitHub Actions to impersonate the service account
# Note: principalSet requires the GCP project NUMBER, not the project ID
gcloud iam service-accounts add-iam-policy-binding \
  github-actions-deployer@$PROJECT_ID.iam.gserviceaccount.com \
  --project=$PROJECT_ID \
  --role=roles/iam.workloadIdentityUser \
  --member="principalSet://iam.googleapis.com/projects/$PROJECT_NUMBER/locations/global/workloadIdentityPools/github/attribute.repository/$REPO_OWNER/$REPO_NAME"

4. Add GitHub Secrets

Add these secrets to your GitHub repository (Settings > Secrets and variables > Actions):

GCP_PROJECT_ID: <your-project-id>
GCP_REGION: us-central1
WIF_PROVIDER: <provider-resource-name-from-step-2>
WIF_SERVICE_ACCOUNT: github-actions-deployer@<project-id>.iam.gserviceaccount.com

Verify Deployment

  1. Get the service URL:

    gcloud run services describe devrev-mcp-server \
      --region=us-central1 \
      --format='value(status.url)'
  2. Test the health endpoint:

    SERVICE_URL=$(gcloud run services describe devrev-mcp-server --region=us-central1 --format='value(status.url)')
    curl $SERVICE_URL/health
  3. Test MCP endpoint with DevRev PAT authentication:

    SERVICE_URL=$(gcloud run services describe devrev-mcp-server --region=us-central1 --format='value(status.url)')
    DEVREV_PAT="your-devrev-personal-access-token"
    
    curl -X POST $SERVICE_URL/mcp/v1/initialize \
      -H "Authorization: Bearer $DEVREV_PAT" \
      -H "Content-Type: application/json" \
      -d '{"protocolVersion": "2024-11-05", "capabilities": {}, "clientInfo": {"name": "test", "version": "1.0.0"}}'

    Expected response: JSON with server info and capabilities.

    • 401 error: Authorization header is missing or malformed — check the Bearer <token> format
    • 403 error: Invalid PAT or disallowed domain — check your PAT is valid, not expired, and your email domain matches MCP_AUTH_ALLOWED_DOMAINS (if configured)
  4. View logs:

    gcloud run services logs read devrev-mcp-server --region=us-central1 --limit=50

Configuration

Environment Variables

The following environment variables are set in the deployment:

  • MCP_TRANSPORT=streamable-http - Use HTTP transport for Cloud Run
  • MCP_HOST=0.0.0.0 - Bind to all interfaces
  • MCP_PORT=8080 - Cloud Run default port
  • MCP_LOG_FORMAT=json - Structured logging for Cloud Logging
  • MCP_LOG_LEVEL=INFO - Production log level
  • MCP_AUTH_MODE=devrev-pat - Per-user PAT authentication (default)
  • MCP_AUTH_ALLOWED_DOMAINS=["augmentcode.com"] - Restrict access by email domain
  • MCP_AUTH_CACHE_TTL_SECONDS=300 - PAT validation cache TTL (5 minutes)

Secrets from Secret Manager

  • DEVREV_API_TOKEN - DevRev API token (optional - only for stdio/testing fallback)

Legacy mode only:

  • MCP_AUTH_TOKEN - Shared bearer token for static-token mode (not recommended)

Resource Limits

  • Memory: 512Mi (sufficient for MCP server operations)
  • CPU: 1 vCPU (adequate for concurrent requests)
  • Concurrency: 80 requests per instance
  • Timeout: 300s (5 minutes for long-running MCP sessions)
  • Auto-scaling: 0-10 instances (scales to zero when idle)

Customizing Deployment

Edit deploy/cloudbuild.yaml substitutions to customize:

substitutions:
  _REGION: 'us-central1'           # GCP region
  _SERVICE_NAME: 'devrev-mcp-server'  # Cloud Run service name
  _REPO: 'devrev-mcp'              # Artifact Registry repository
  _IMAGE: 'devrev-mcp-server'      # Image name
  _CPU: '1'                        # CPU allocation
  _MEMORY: '512Mi'                 # Memory allocation
  _TAG_NAME: 'latest'              # Image tag (set by CI/CD)

Cost Optimization

  1. Scale to zero: With min-instances=0, the service scales to zero when idle, minimizing costs.

  2. Right-size resources: Start with 512Mi/1 CPU and monitor. Adjust if needed:

    gcloud run services update devrev-mcp-server \
      --region=us-central1 \
      --memory=1Gi \
      --cpu=2
  3. Monitor usage:

    # View metrics in Cloud Console
    gcloud run services describe devrev-mcp-server --region=us-central1
  4. Set budget alerts in Google Cloud Console to track spending.

Troubleshooting

Container fails to start

Check logs for errors:

gcloud run services logs read devrev-mcp-server --region=us-central1 --limit=100

Secret access denied

Ensure the compute service account has access:

gcloud secrets add-iam-policy-binding devrev-api-token \
  --member=serviceAccount:PROJECT_NUMBER-compute@developer.gserviceaccount.com \
  --role=roles/secretmanager.secretAccessor

Health check failures

Verify the health endpoint is responding:

# Port-forward to test locally
gcloud run services proxy devrev-mcp-server --region=us-central1
curl http://localhost:8080/health

Security Considerations

Public Access with Per-User PAT Authentication

The deployment uses --allow-unauthenticated for public HTTP access, but requires each user's DevRev Personal Access Token for MCP endpoint calls:

# Health endpoint (no auth required)
curl https://devrev-mcp-server-xxx.run.app/health

# MCP endpoint (requires user's DevRev PAT)
curl -X POST https://devrev-mcp-server-xxx.run.app/mcp/v1/initialize \
  -H "Authorization: Bearer $DEVREV_PAT" \
  -H "Content-Type: application/json" \
  -d '{...}'

Authentication Security

  1. Per-User PATs: Each user authenticates with their own DevRev credentials

    • No shared secrets between users
    • Audit trail shows actual user identity in DevRev
    • Tokens are validated against DevRev API on each request (with 5-minute cache)
  2. Domain Restrictions: Optional email domain filtering

    • Configure MCP_AUTH_ALLOWED_DOMAINS to restrict access (e.g., ["augmentcode.com"])
    • Users with PATs from other domains will be rejected
  3. Token Caching: PAT validation results are cached for 5 minutes

    • Reduces load on DevRev API
    • Configurable via MCP_AUTH_CACHE_TTL_SECONDS
    • Cache is per-token, so revoked tokens expire from cache within TTL

Secret Management Best Practices

  1. Secrets in Secret Manager: Never commit secrets to Git

    • devrev-api-token - DevRev API credentials (optional - only for stdio/testing)
  2. Service Account Permissions: Cloud Run runtime SA has minimal permissions

    • Only access to required secrets via IAM bindings
    • No direct access to other GCP resources
  3. Workload Identity Federation: GitHub Actions uses WIF instead of JSON keys

    • No long-lived credentials stored in GitHub
    • Time-limited OIDC tokens for each deployment

Network Security

  1. CORS: Configure allowed origins via MCP_CORS_ALLOWED_ORIGINS environment variable.

  2. Rate limiting: The server includes built-in rate limiting (120 RPM by default). Adjust via MCP_RATE_LIMIT_RPM.

  3. Cloud Run Security:

    • Runs in isolated containers
    • Automatic HTTPS with managed certificates
    • DDoS protection via Google Cloud Armor (optional)

Audit and Monitoring

Application-Level Audit Logging

The MCP server includes built-in structured audit logging for compliance. When enabled (default), every authentication event and tool invocation is logged with structured JSON fields.

Audit Event Schema:

{
  "event_type": "audit",
  "action": "tool_invocation",
  "user": {"id": "don:...", "email": "user@example.com", "pat_hash": "sha256:..."},
  "tool": {"name": "devrev_works_list", "category": "read"},
  "request": {"client_ip": "203.0.113.42"},
  "outcome": "success",
  "duration_ms": 142
}

Configuration:

# Enable/disable audit logging (default: true)
MCP_AUDIT_LOG_ENABLED=true

Querying audit logs:

# View recent audit events
gcloud logging read 'resource.type="cloud_run_revision" AND jsonPayload.event_type="audit"' \
  --project=PROJECT_ID --limit=50 --format=json

# View auth failures only
gcloud logging read 'jsonPayload.event_type="audit" AND jsonPayload.action="auth_failure"' \
  --project=PROJECT_ID --limit=50

# View tool invocations for a specific user
gcloud logging read 'jsonPayload.event_type="audit" AND jsonPayload.user.email="user@example.com"' \
  --project=PROJECT_ID --limit=50

Immutable Audit Log Storage

For compliance with immutable log requirements, set up a WORM-compliant Cloud Storage bucket with a Cloud Logging sink:

# Run the setup script
./deploy/audit-logging-setup.sh --project=YOUR_PROJECT_ID

# With WORM lock (IRREVERSIBLE — enables permanent retention):
./deploy/audit-logging-setup.sh --project=YOUR_PROJECT_ID --lock

This creates:

  1. A GCS bucket with a 365-day retention policy
  2. A Cloud Logging sink that exports event_type="audit" entries
  3. IAM bindings for the sink's service account

See deploy/audit-logging-setup.sh --help for all options.

Monitoring and Alerting

Alert policies and a dashboard are provided in deploy/monitoring/:

# Validate prerequisites
./deploy/monitoring/validate-config.sh YOUR_PROJECT_ID

# Create a notification channel
gcloud alpha monitoring channels create \
  --display-name="MCP Audit Alerts" \
  --type=email \
  --channel-labels=email_address=alerts@yourcompany.com

# Apply alert policies (edit alert-policies.yaml first with your project/channel IDs)
gcloud alpha monitoring policies create \
  --policy-from-file=deploy/monitoring/alert-policies.yaml

# Import dashboard
gcloud monitoring dashboards create \
  --config-from-file=deploy/monitoring/dashboard.json

Alert policies include:

  • High auth failure rate (>10 failures in 5 min)
  • High destructive operation rate (>20 deletes in 10 min)
  • Elevated tool error rate (>20% failure over 5 min)

See deploy/monitoring/README.md for full details.

  1. Cloud Logging: All requests and audit events logged in JSON format

    gcloud run services logs read devrev-mcp-server --region=us-central1
  2. Cloud Monitoring: Set up alerts for error rates, latency, etc.

  3. Secret Access Audit: Monitor secret access in Cloud Audit Logs

    gcloud logging read "resource.type=secretmanager.googleapis.com" --limit=50

Additional Resources