MCP Server — Cloud Run Deployment Notes
V1 (API Key, In-Memory Sessions)
- Sessions are stored in process memory; each Cloud Run instance holds its own session map.
- Session affinity is required: deploy with
--session-affinityflag so all requests for a given session reach the same instance.
gcloud run deploy flowpos-backend \
--image gcr.io/YOUR_PROJECT/flowpos-backend:latest \
--region us-central1 \
--platform managed \
--session-affinity \
--min-instances 1 \
--max-instances 10 \
--memory 512Mi \
--cpu 1 \
--port 4000 \
--set-env-vars NODE_ENV=production \
--set-secrets MCP_TOKEN_SECRET=MCP_TOKEN_SECRET:latest \
--service-account YOUR_SA@YOUR_PROJECT.iam.gserviceaccount.com \
--allow-unauthenticated
Replace
YOUR_PROJECTandYOUR_SAwith your GCP project ID and service account email. The--session-affinityflag is critical for MCP session routing — without it, subsequent JSON-RPC requests may land on a different instance and receive a404 Session not founderror.
- No additional infrastructure needed beyond the existing PostgreSQL instance (for
mcp_api_keytable).
V2 (OAuth Tokens, Redis-Backed Sessions)
- Sessions created via Firebase token exchange persist the session principal in Redis with TTL = token expiry.
- Required environment variables:
| Variable | Description | Example |
|---|---|---|
MCP_TOKEN_SECRET | JWT signing secret (min 32 chars) | Set in GCP Secret Manager |
MCP_TOKEN_TTL_SECONDS | Token lifetime in seconds | 86400 (24h default) |
REDIS_HOST | Redis hostname | 10.x.x.x (VPC-internal) |
REDIS_PORT | Redis port | 6379 |
REDIS_PASSWORD | Redis password (if auth enabled) | Set in GCP Secret Manager |
- Redis instance: Use a Memorystore for Redis instance in the same VPC as Cloud Run. Serverless VPC Access connector required.
--session-affinityis still required for reliable MCP traffic routing. Redis persistence keeps principal mutations (for exampleset_active_business) in sync, but the active streamable HTTP transport remains in-memory per instance.
gcloud run deploy flowpos-backend \
--image gcr.io/YOUR_PROJECT/flowpos-backend:latest \
--region us-central1 \
--platform managed \
--session-affinity \
--min-instances 1 \
--max-instances 10 \
--memory 512Mi \
--cpu 1 \
--port 4000 \
--set-env-vars NODE_ENV=production,MCP_TOKEN_TTL_SECONDS=86400,REDIS_HOST=10.x.x.x,REDIS_PORT=6379 \
--set-secrets MCP_TOKEN_SECRET=MCP_TOKEN_SECRET:latest,REDIS_PASSWORD=REDIS_PASSWORD:latest \
--vpc-connector projects/YOUR_PROJECT/locations/us-central1/connectors/YOUR_VPC_CONNECTOR \
--service-account YOUR_SA@YOUR_PROJECT.iam.gserviceaccount.com \
--allow-unauthenticated
GCP Secret Manager Setup
# Create MCP token secret
echo -n "$(openssl rand -hex 32)" | \
gcloud secrets create MCP_TOKEN_SECRET --data-file=-
# Grant Cloud Run service account access
gcloud secrets add-iam-policy-binding MCP_TOKEN_SECRET \
--member="serviceAccount:YOUR_SA@PROJECT.iam.gserviceaccount.com" \
--role="roles/secretmanager.secretAccessor"
Health Check
The MCP server does not expose a dedicated health endpoint. Use the standard /health endpoint of the backend application to confirm the container is healthy before routing MCP traffic.
Deployment verification checklist
- OAuth discovery endpoints are reachable
GET /.well-known/oauth-authorization-serverGET /.well-known/oauth-protected-resource
- Token exchange works
POST /mcp/tokenreturnsaccessTokenandexpiresIn
- MCP initialize works and returns session header
POST /mcpwith{ "method": "initialize" }- response includes
mcp-session-id
- Authenticated session close works
DELETE /mcpwith bothAuthorizationandmcp-session-id- returns successful close
- Redis principal key appears for OAuth session
- key pattern:
mcp:session:{sessionId}
- key pattern: