This directory contains configuration files for deploying the DevRev MCP Server to Google Cloud Run with per-user DevRev PAT authentication.
- Container Registry: Artifact Registry (preferred over Container Registry)
- Deployment: Cloud Run with
--allow-unauthenticatedfor 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)
The MCP server supports two authentication modes:
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:
- Validates the PAT against the DevRev API
- Extracts user identity from the PAT
- Creates a per-request DevRev client with the user's credentials
- 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>"
}
}
}
}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.
-
Google Cloud CLI installed and configured:
gcloud --version gcloud auth login gcloud config set project YOUR_PROJECT_ID -
Enable required APIs:
gcloud services enable \ cloudbuild.googleapis.com \ run.googleapis.com \ artifactregistry.googleapis.com \ secretmanager.googleapis.com \ iamcredentials.googleapis.com -
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" -
Get your project number (needed for IAM):
PROJECT_NUMBER=$(gcloud projects describe $(gcloud config get-value project) --format='value(projectNumber)') echo $PROJECT_NUMBER
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=-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.secretAccessorIf 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:latestThis 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.3This 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# 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:latestWIF 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.serviceAccountUserPROJECT_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"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"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
-
Get the service URL:
gcloud run services describe devrev-mcp-server \ --region=us-central1 \ --format='value(status.url)' -
Test the health endpoint:
SERVICE_URL=$(gcloud run services describe devrev-mcp-server --region=us-central1 --format='value(status.url)') curl $SERVICE_URL/health
-
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)
- 401 error: Authorization header is missing or malformed — check the
-
View logs:
gcloud run services logs read devrev-mcp-server --region=us-central1 --limit=50
The following environment variables are set in the deployment:
MCP_TRANSPORT=streamable-http- Use HTTP transport for Cloud RunMCP_HOST=0.0.0.0- Bind to all interfacesMCP_PORT=8080- Cloud Run default portMCP_LOG_FORMAT=json- Structured logging for Cloud LoggingMCP_LOG_LEVEL=INFO- Production log levelMCP_AUTH_MODE=devrev-pat- Per-user PAT authentication (default)MCP_AUTH_ALLOWED_DOMAINS=["augmentcode.com"]- Restrict access by email domainMCP_AUTH_CACHE_TTL_SECONDS=300- PAT validation cache TTL (5 minutes)
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)
- 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)
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)-
Scale to zero: With
min-instances=0, the service scales to zero when idle, minimizing costs. -
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
-
Monitor usage:
# View metrics in Cloud Console gcloud run services describe devrev-mcp-server --region=us-central1 -
Set budget alerts in Google Cloud Console to track spending.
Check logs for errors:
gcloud run services logs read devrev-mcp-server --region=us-central1 --limit=100Ensure 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.secretAccessorVerify the health endpoint is responding:
# Port-forward to test locally
gcloud run services proxy devrev-mcp-server --region=us-central1
curl http://localhost:8080/healthThe 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 '{...}'-
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)
-
Domain Restrictions: Optional email domain filtering
- Configure
MCP_AUTH_ALLOWED_DOMAINSto restrict access (e.g.,["augmentcode.com"]) - Users with PATs from other domains will be rejected
- Configure
-
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
-
Secrets in Secret Manager: Never commit secrets to Git
devrev-api-token- DevRev API credentials (optional - only for stdio/testing)
-
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
-
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
-
CORS: Configure allowed origins via
MCP_CORS_ALLOWED_ORIGINSenvironment variable. -
Rate limiting: The server includes built-in rate limiting (120 RPM by default). Adjust via
MCP_RATE_LIMIT_RPM. -
Cloud Run Security:
- Runs in isolated containers
- Automatic HTTPS with managed certificates
- DDoS protection via Google Cloud Armor (optional)
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=trueQuerying 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=50For 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 --lockThis creates:
- A GCS bucket with a 365-day retention policy
- A Cloud Logging sink that exports
event_type="audit"entries - IAM bindings for the sink's service account
See deploy/audit-logging-setup.sh --help for all options.
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.jsonAlert 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.
-
Cloud Logging: All requests and audit events logged in JSON format
gcloud run services logs read devrev-mcp-server --region=us-central1 -
Cloud Monitoring: Set up alerts for error rates, latency, etc.
-
Secret Access Audit: Monitor secret access in Cloud Audit Logs
gcloud logging read "resource.type=secretmanager.googleapis.com" --limit=50