learningfocused-mcp / scripts /deploy_mcp.sh
LeoWalker's picture
entrypoints
7a7d4fa verified
Raw History Blame Contribute Delete
5.64 kB
#!/usr/bin/env bash
# Deploy the FastMCP HTTP server to Cloud Run (inferpoker / us-west1).
# Does not deploy to GCP_PROJECT_ID from .env (that is a different project).
# Does not --reset-chroma, re-transcribe, or touch the podcast_downloads rsync.
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "${ROOT}"
PROJECT="${GCP_BACKUP_PROJECT:-inferpoker}"
REGION="${GCP_REGION:-us-west1}"
if [[ "${PROJECT}" == "inferpoker" ]]; then
echo "Refusing to deploy Learning Focused onto GCP project inferpoker." >&2
echo "Use ./scripts/deploy_hf.sh (Hugging Face Space). InferPoker billing is off-limits." >&2
exit 2
fi
SERVICE="${CLOUD_RUN_SERVICE:-learningfocused-mcp}"
SA_NAME="${CLOUD_RUN_SA:-learningfocused-mcp}"
SA_EMAIL="${SA_NAME}@${PROJECT}.iam.gserviceaccount.com"
BUCKET="${GCS_CORPUS_BUCKET:-inferpoker-learningfocused}"
AR_REPO="${AR_REPO:-learningfocused}"
SECRET_OPENAI="${SECRET_OPENAI:-learningfocused-openai-api-key}"
SECRET_GOOGLE="${SECRET_GOOGLE:-learningfocused-google-api-key}"
SECRET_ASSEMBLYAI="${SECRET_ASSEMBLYAI:-learningfocused-assemblyai-api-key}"
ENV_FILE="${ROOT}/.env"
UPDATE_SECRETS="${UPDATE_SECRETS:-0}"
if [[ ! -f "${ENV_FILE}" ]]; then
echo "Missing ${ENV_FILE} (needed to seed Secret Manager). Not committed." >&2
exit 2
fi
gcloud config set project "${PROJECT}" >/dev/null
echo ">> Enabling APIs on ${PROJECT}"
gcloud services enable \
run.googleapis.com \
secretmanager.googleapis.com \
artifactregistry.googleapis.com \
cloudbuild.googleapis.com \
storage.googleapis.com \
--project="${PROJECT}"
if ! gcloud artifacts repositories describe "${AR_REPO}" --location="${REGION}" --project="${PROJECT}" >/dev/null 2>&1; then
echo ">> Creating Artifact Registry ${AR_REPO}"
gcloud artifacts repositories create "${AR_REPO}" \
--repository-format=docker \
--location="${REGION}" \
--description="LearningFocused MCP" \
--project="${PROJECT}"
fi
if ! gcloud iam service-accounts describe "${SA_EMAIL}" --project="${PROJECT}" >/dev/null 2>&1; then
echo ">> Creating service account ${SA_EMAIL}"
gcloud iam service-accounts create "${SA_NAME}" \
--display-name="LearningFocused MCP Cloud Run" \
--project="${PROJECT}"
fi
echo ">> Granting ${SA_EMAIL} objectViewer on gs://${BUCKET}"
gcloud storage buckets add-iam-policy-binding "gs://${BUCKET}" \
--member="serviceAccount:${SA_EMAIL}" \
--role="roles/storage.objectViewer" \
--project="${PROJECT}" >/dev/null
read_env_value() {
local key="$1"
uv run python - "$ENV_FILE" "$key" <<'PY'
import sys
from pathlib import Path
from dotenv import dotenv_values
path, key = sys.argv[1], sys.argv[2]
vals = dotenv_values(path)
value = vals.get(key)
if not value:
raise SystemExit(f"Missing {key} in {path}")
sys.stdout.write(value)
PY
}
ensure_secret() {
local name="$1"
local env_key="$2"
if gcloud secrets describe "${name}" --project="${PROJECT}" >/dev/null 2>&1; then
if [[ "${UPDATE_SECRETS}" != "1" ]]; then
echo ">> Secret ${name} exists (set UPDATE_SECRETS=1 to add a version)"
return 0
fi
else
echo ">> Creating secret ${name}"
gcloud secrets create "${name}" --replication-policy=automatic --project="${PROJECT}"
fi
echo ">> Adding secret version ${name} from .env ${env_key}"
read_env_value "${env_key}" | gcloud secrets versions add "${name}" --data-file=- --project="${PROJECT}" >/dev/null
}
ensure_secret "${SECRET_OPENAI}" "OPENAI_API_KEY"
ensure_secret "${SECRET_GOOGLE}" "GOOGLE_API_KEY"
ensure_secret "${SECRET_ASSEMBLYAI}" "ASSEMBLYAI_API_KEY"
for secret in "${SECRET_OPENAI}" "${SECRET_GOOGLE}" "${SECRET_ASSEMBLYAI}"; do
echo ">> Granting secretAccessor on ${secret}"
gcloud secrets add-iam-policy-binding "${secret}" \
--member="serviceAccount:${SA_EMAIL}" \
--role="roles/secretmanager.secretAccessor" \
--project="${PROJECT}" >/dev/null
done
PROJECT_NUMBER="$(gcloud projects describe "${PROJECT}" --format='value(projectNumber)')"
COMPUTE_SA="${PROJECT_NUMBER}-compute@developer.gserviceaccount.com"
echo ">> Cloud Run --source builds as ${COMPUTE_SA} (project Editor). Runtime SA is ${SA_EMAIL}."
echo ">> Deploying ${SERVICE} to Cloud Run (${REGION})"
gcloud run deploy "${SERVICE}" \
--quiet \
--source "${ROOT}" \
--project="${PROJECT}" \
--region="${REGION}" \
--service-account="${SA_EMAIL}" \
--allow-unauthenticated \
--min-instances=1 \
--max-instances=3 \
--memory=2Gi \
--cpu=1 \
--timeout=300 \
--cpu-boost \
--no-cpu-throttling \
--port=8080 \
--set-env-vars="MCP_HOST=0.0.0.0,HYDRATE_CHROMA=1,GCS_CHROMA_BUCKET=${BUCKET},GCP_BACKUP_PROJECT=${PROJECT},CHROMA_DIR=/data/chroma_db,MCP_RATE_LIMIT_PER_MINUTE=60,MCP_SNIPPET_LIMIT=32768" \
--set-secrets="OPENAI_API_KEY=${SECRET_OPENAI}:latest,GOOGLE_API_KEY=${SECRET_GOOGLE}:latest,ASSEMBLYAI_API_KEY=${SECRET_ASSEMBLYAI}:latest"
echo ">> Ensuring allUsers can invoke (public MCP)"
gcloud run services add-iam-policy-binding "${SERVICE}" \
--region="${REGION}" \
--member="allUsers" \
--role="roles/run.invoker" \
--project="${PROJECT}" >/dev/null
URL="$(gcloud run services describe "${SERVICE}" --region="${REGION}" --project="${PROJECT}" --format='value(status.url)')"
echo
echo "Service: ${SERVICE}"
echo "Region: ${REGION}"
echo "URL: ${URL}"
echo "MCP: ${URL}/mcp"
echo "Health: ${URL}/health"
echo "Auth: allUsers (unauthenticated)"
echo "Min inst: 1"
echo "Secrets: ${SECRET_OPENAI}, ${SECRET_GOOGLE}, ${SECRET_ASSEMBLYAI}"
echo
echo "Cursor mcp.json:"
cat <<EOF
{
"mcpServers": {
"learningfocused": {
"url": "${URL}/mcp"
}
}
}
EOF