| name | google-calendar-sync |
| description | Wizard to set up Google Calendar API integration with service account authentication. Use when setting up calendar sync, Google Calendar API, service accounts, or domain-wide delegation. |
| allowed-tools | ["Bash","Read","Write","Edit","Glob","Grep","AskUserQuestion"] |
Google Calendar Sync Setup Wizard
This skill guides you through configuring a Google Cloud service account to sync events with Google Calendar. It uses gcloud CLI to automate most Google Cloud steps.
How This Works
This is a guided wizard with two types of steps:
- AGENT ACTION - Steps you perform automatically via
gcloud CLI or code
- HUMAN ACTION REQUIRED - Steps requiring manual intervention (browser auth, Admin Console)
When you hit a HUMAN ACTION REQUIRED step, use the AskUserQuestion tool to pause and wait for confirmation before continuing.
State Detection
Run this first to determine current progress:
echo "=== GOOGLE CALENDAR SYNC STATE CHECK ==="
if command -v gcloud &> /dev/null; then
echo "gcloud: INSTALLED"
else
echo "gcloud: MISSING -> Start at Step 0.1"
fi
if gcloud auth list --filter="status:ACTIVE" --format="value(account)" 2>/dev/null | grep -q "@"; then
echo "gcloud_auth: AUTHENTICATED as $(gcloud auth list --filter='status:ACTIVE' --format='value(account)' | head -1)"
else
echo "gcloud_auth: NOT_AUTHENTICATED -> Complete Step 0.2"
fi
PROJECT=$(gcloud config get-value project 2>/dev/null)
if [ -n "$PROJECT" ] && [ "$PROJECT" != "(unset)" ]; then
echo "project: CONFIGURED as $PROJECT"
else
echo "project: NOT_SET -> Start at Phase 2"
fi
if [ -n "$PROJECT" ] && [ "$PROJECT" != "(unset)" ]; then
if gcloud services list --enabled --filter="name:calendar" --format="value(name)" 2>/dev/null | grep -q "calendar"; then
echo "calendar_api: ENABLED"
else
echo "calendar_api: NOT_ENABLED -> Complete Step 2.2"
fi
fi
if [ -n "$PROJECT" ] && [ "$PROJECT" != "(unset)" ]; then
SA_EMAIL="calendar-sync@${PROJECT}.iam.gserviceaccount.com"
if gcloud iam service-accounts describe "$SA_EMAIL" &>/dev/null; then
echo "service_account: EXISTS ($SA_EMAIL)"
else
echo "service_account: NOT_FOUND -> Complete Step 2.3"
fi
fi
if [ -f "google-credentials.json" ]; then
echo "credentials_file: EXISTS (needs base64 encoding)"
elif [ -f "google-credentials.b64" ]; then
echo "credentials_b64: EXISTS"
else
echo "credentials: NOT_FOUND -> Complete Step 2.5"
fi
echo "=== ENV VAR CHECK ==="
if [ -f ".env.local" ]; then
grep -q "GOOG_CREDENTIALS_JSON" .env.local 2>/dev/null && echo "GOOG_CREDENTIALS_JSON: SET in .env.local" || echo "GOOG_CREDENTIALS_JSON: MISSING"
grep -q "GOOG_CALENDAR_IMPERSONATE_USER" .env.local 2>/dev/null && echo "GOOG_CALENDAR_IMPERSONATE_USER: SET in .env.local" || echo "GOOG_CALENDAR_IMPERSONATE_USER: MISSING"
grep -q "GOOG_CALENDAR_ID" .env.local 2>/dev/null && echo "GOOG_CALENDAR_ID: SET in .env.local" || echo "GOOG_CALENDAR_ID: MISSING"
elif [ -f ".env" ]; then
grep -q "GOOG_CREDENTIALS_JSON" .env 2>/dev/null && echo "GOOG_CREDENTIALS_JSON: SET in .env" || echo "GOOG_CREDENTIALS_JSON: MISSING"
grep -q "GOOG_CALENDAR_IMPERSONATE_USER" .env 2>/dev/null && echo "GOOG_CALENDAR_IMPERSONATE_USER: SET in .env" || echo "GOOG_CALENDAR_IMPERSONATE_USER: MISSING"
grep -q "GOOG_CALENDAR_ID" .env 2>/dev/null && echo "GOOG_CALENDAR_ID: SET in .env" || echo "GOOG_CALENDAR_ID: MISSING"
else
echo "env_file: NOT_FOUND -> Complete Phase 5"
fi
if [ -f "package.json" ]; then
grep -q "googleapis" package.json && echo "googleapis: INSTALLED" || echo "googleapis: NOT_INSTALLED -> Complete Step 5.4"
fi
echo "=== END STATE CHECK ==="
Based on the state check output, skip to the appropriate phase.
Prerequisites (Agent Checks + Human Installs)
Check for required tools and guide the user through installing anything missing.
Step 0.1: Check for gcloud CLI
if command -v gcloud &> /dev/null; then
echo "INSTALLED: gcloud $(gcloud --version 2>/dev/null | head -1)"
else
echo "NOT_INSTALLED: gcloud"
fi
If NOT_INSTALLED, use AskUserQuestion to prompt installation:
Install the Google Cloud CLI:
macOS (Homebrew): brew install google-cloud-sdk
macOS (Manual):
curl https://sdk.cloud.google.com | bash
exec -l $SHELL
Linux (Debian/Ubuntu):
sudo apt-get install apt-transport-https ca-certificates gnupg curl
curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo gpg --dearmor -o /usr/share/keyrings/cloud.google.gpg
echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" | sudo tee -a /etc/apt/sources.list.d/google-cloud-sdk.list
sudo apt-get update && sudo apt-get install google-cloud-cli
Linux (Other):
curl -O https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-cli-linux-x86_64.tar.gz
tar -xf google-cloud-cli-linux-x86_64.tar.gz
./google-cloud-sdk/install.sh
Windows: Download from https://cloud.google.com/sdk/docs/install
Step 0.2: Check gcloud authentication
if gcloud auth list --filter="status:ACTIVE" --format="value(account)" 2>/dev/null | head -1 | grep -q "@"; then
echo "AUTHENTICATED: $(gcloud auth list --filter='status:ACTIVE' --format='value(account)' | head -1)"
else
echo "NOT_AUTHENTICATED"
fi
If NOT_AUTHENTICATED, prompt the user to run:
gcloud auth login
This opens a browser for Google account authentication. Wait for confirmation.
Step 0.3: Check for Node.js
if command -v node &> /dev/null; then
echo "INSTALLED: node $(node --version)"
else
echo "NOT_INSTALLED: node"
fi
If NOT_INSTALLED, prompt installation:
- macOS:
brew install node
- Linux:
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash - && sudo apt-get install -y nodejs
- Or use nvm: https://github.com/nvm-sh/nvm
Step 0.4: Check for package manager
if command -v pnpm &> /dev/null; then
echo "INSTALLED: pnpm $(pnpm --version)"
elif command -v yarn &> /dev/null; then
echo "INSTALLED: yarn $(yarn --version)"
elif command -v npm &> /dev/null; then
echo "INSTALLED: npm $(npm --version)"
else
echo "NOT_INSTALLED: package manager"
fi
Phase 1: Project Discovery
Step 1.1: Check existing Google credentials
grep -r "GOOG_" .env* 2>/dev/null || echo "No existing Google config found"
grep -r "GOOGLE_" .env* 2>/dev/null || echo "No existing Google config found"
Step 1.2: List existing gcloud projects
gcloud projects list --format="table(projectId,name,createTime)"
Use AskUserQuestion: Use an existing project or create a new one?
Phase 2: Google Cloud Setup (Mostly Automated)
Step 2.1: Create or select project
Create new project:
PROJECT_ID="calendar-sync-$(date +%s | tail -c 7)"
PROJECT_NAME="Calendar Sync"
gcloud projects create "$PROJECT_ID" --name="$PROJECT_NAME"
gcloud config set project "$PROJECT_ID"
Or select existing:
gcloud config set project YOUR_PROJECT_ID
Step 2.2: Enable Google Calendar API
gcloud services enable calendar-json.googleapis.com
Verify:
gcloud services list --enabled --filter="name:calendar"
Step 2.3: Create service account
SERVICE_ACCOUNT_NAME="calendar-sync"
PROJECT_ID=$(gcloud config get-value project)
gcloud iam service-accounts create "$SERVICE_ACCOUNT_NAME" \
--display-name="Calendar Sync Service" \
--description="Manages calendar events for the application"
Step 2.4: Grant permissions
PROJECT_ID=$(gcloud config get-value project)
SERVICE_ACCOUNT_EMAIL="calendar-sync@${PROJECT_ID}.iam.gserviceaccount.com"
gcloud projects add-iam-policy-binding "$PROJECT_ID" \
--member="serviceAccount:${SERVICE_ACCOUNT_EMAIL}" \
--role="roles/owner"
Step 2.5: Create service account key
PROJECT_ID=$(gcloud config get-value project)
SERVICE_ACCOUNT_EMAIL="calendar-sync@${PROJECT_ID}.iam.gserviceaccount.com"
gcloud iam service-accounts keys create ./google-credentials.json \
--iam-account="$SERVICE_ACCOUNT_EMAIL"
If this fails with "Key creation is not allowed", prompt user:
Disable the org policy constraint. Run (requires org admin):
gcloud org-policies reset iam.disableServiceAccountKeyCreation --project="$PROJECT_ID"
Or via Console: Organization Policies > search iam.disableServiceAccountKeyCreation > Override > Not enforced.
Step 2.6: Base64 encode credentials
cat google-credentials.json | base64 -w 0 > google-credentials.b64
echo "Credentials encoded to google-credentials.b64"
cat google-credentials.b64
Step 2.7: Get Client ID for domain-wide delegation
PROJECT_ID=$(gcloud config get-value project)
SERVICE_ACCOUNT_EMAIL="calendar-sync@${PROJECT_ID}.iam.gserviceaccount.com"
gcloud iam service-accounts describe "$SERVICE_ACCOUNT_EMAIL" \
--format="value(uniqueId)"
Save this Client ID for Phase 3.
Phase 3: Domain-Wide Delegation (Human Action Required)
Note: Only required for Google Workspace. Skip if using personal Gmail with calendar sharing.
Step 3.1: Configure Domain-Wide Delegation
HUMAN ACTION REQUIRED - Use AskUserQuestion to guide:
- Go to Google Admin Console
- Navigate to Security > Access and data control > API controls
- Click Manage Domain Wide Delegation
- Click Add new
- Enter the Client ID from Step 2.7
- Add OAuth scope:
https://www.googleapis.com/auth/calendar.events
- Click Authorize
Wait for "Delegation configured" confirmation.
Step 3.2: Identify Impersonation User
HUMAN ACTION REQUIRED
Ask for the email of a Google Workspace user who has access to the target calendar. This becomes GOOG_CALENDAR_IMPERSONATE_USER.
Phase 4: Get Calendar ID
Step 4.1: Find the calendar ID
Option A - Primary calendar: The calendar ID is the user's email address.
Option B - Shared/secondary calendar:
HUMAN ACTION REQUIRED - Use AskUserQuestion:
- Open Google Calendar
- Find your calendar in the left sidebar
- Click the three dots > Settings and sharing
- Scroll to Integrate calendar
- Copy the Calendar ID (looks like
abc123@group.calendar.google.com)
Phase 5: Environment Configuration
Step 5.1: Add environment variables
After getting the base64 credentials, impersonation user, and calendar ID from the user:
CREDS_B64=$(cat google-credentials.b64)
IMPERSONATE_USER="user@yourdomain.com"