Notre avis
Ce guide configure l'intégration de l'API Google Calendar avec un compte de service via une procédure pas à pas, en utilisant gcloud pour automatiser la plupart des étapes.
Points forts
- Automatisation des commandes gcloud pour réduire les erreurs manuelles.
- Détection de l'état actuel pour reprendre à l'étape appropriée.
- Séparation claire entre actions automatiques et actions humaines requises.
Limites
- Nécessite des interventions manuelles dans la console Google Cloud et l'Admin Console Google Workspace.
- Dépend de l'installation de gcloud CLI.
- Ne gère pas la synchronisation bidirectionnelle en temps réel.
Utilisez cette compétence pour mettre en place une synchronisation d'événements avec Google Calendar via un compte de service et une délégation à l'échelle du domaine.
Ne l'utilisez pas si vous avez besoin d'une synchronisation bidirectionnelle en temps réel ou si vous ne disposez pas des accès administrateur Google Workspace nécessaires.
Analyse de sécurité
SûrThe skill uses only safe bash commands for system state checks and guides the user through standard Google Cloud CLI operations. No destructive actions, data exfiltration, or obfuscation are present.
Aucun point d'attention détecté
Exemples
Set up Google Calendar API integration with a service account for my application.Start the Google Calendar sync wizard to create a service account and enable calendar API.I need to finish setting up Google Calendar sync. Check the current state and continue from where I left off.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
gcloudCLI 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 ==="
# Check 1: gcloud installed?
if command -v gcloud &> /dev/null; then
echo "gcloud: INSTALLED"
else
echo "gcloud: MISSING -> Start at Step 0.1"
fi
# Check 2: gcloud authenticated?
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
# Check 3: Project configured?
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
# Check 4: Calendar API enabled?
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
# Check 5: Service account exists?
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
# Check 6: Credentials file exists?
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
# Check 7: Environment variables?
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
# Check 8: googleapis package?
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-sdkmacOS (Manual):
curl https://sdk.cloud.google.com | bash exec -l $SHELLLinux (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-cliLinux (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.shWindows: 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" # Get from user
CALENDAR_ID="calendar-id@group.calendar.google.com" # Get from user
cat >> .env.local << EOF
# Google Calendar Integration
GOOG_CREDENTIALS_JSON="$CREDS_B64"
GOOG_CALENDAR_IMPERSONATE_USER="$IMPERSONATE_USER"
GOOG_CALENDAR_ID="$CALENDAR_ID"
EOF
Step 5.2: Add to .env.example
cat >> .env.example << 'EOF'
# Google Calendar Integration
# See: .claude/skills/google-calendar-sync/SKILL.md
GOOG_CREDENTIALS_JSON=
GOOG_CALENDAR_IMPERSONATE_USER=
GOOG_CALENDAR_ID=
EOF
Step 5.3: Update .gitignore
echo "google-credentials.json" >> .gitignore
echo "google-credentials.b64" >> .gitignore
Step 5.4: Install googleapis package
if command -v pnpm &> /dev/null; then
pnpm add googleapis
elif command -v yarn &> /dev/null; then
yarn add googleapis
else
npm install googleapis
fi
Step 5.5: Create calendar utility module
Create lib/google-calendar.ts with the following content:
import { google } from 'googleapis'
function getCalendarClient() {
const credentials = JSON.parse(
Buffer.from(process.env.GOOG_CREDENTIALS_JSON!, 'base64').toString()
)
const auth = new google.auth.GoogleAuth({
credentials,
scopes: ['https://www.googleapis.com/auth/calendar.events'],
clientOptions: {
subject: process.env.GOOG_CALENDAR_IMPERSONATE_USER,
},
})
return google.calendar({ version: 'v3', auth })
}
export async function createCalendarEvent(event: {
summary: string
description?: string
start: Date
end: Date
location?: string
}) {
const calendar = getCalendarClient()
const calendarId = process.env.GOOG_CALENDAR_ID!
return calendar.events.insert({
calendarId,
requestBody: {
summary: event.summary,
description: event.description,
location: event.location,
start: {
dateTime: event.start.toISOString(),
timeZone: 'UTC',
},
end: {
dateTime: event.end.toISOString(),
timeZone: 'UTC',
},
},
})
}
export async function updateCalendarEvent(
eventId: string,
event: {
summary?: string
description?: string
start?: Date
end?: Date
location?: string
}
) {
const calendar = getCalendarClient()
const calendarId = process.env.GOOG_CALENDAR_ID!
return calendar.events.patch({
calendarId,
eventId,
requestBody: {
...(event.summary && { summary: event.summary }),
...(event.description && { description: event.description }),
...(event.location && { location: event.location }),
...(event.start && {
start: { dateTime: event.start.toISOString(), timeZone: 'UTC' },
}),
...(event.end && {
end: { dateTime: event.end.toISOString(), timeZone: 'UTC' },
}),
},
})
}
export async function deleteCalendarEvent(eventId: string) {
const calendar = getCalendarClient()
const calendarId = process.env.GOOG_CALENDAR_ID!
return calendar.events.delete({
calendarId,
eventId,
})
}
export async function listCalendarEvents(options?: {
timeMin?: Date
timeMax?: Date
maxResults?: number
}) {
const calendar = getCalendarClient()
const calendarId = process.env.GOOG_CALENDAR_ID!
return calendar.events.list({
calendarId,
timeMin: options?.timeMin?.toISOString(),
timeMax: options?.timeMax?.toISOString(),
maxResults: options?.maxResults ?? 10,
singleEvents: true,
orderBy: 'startTime',
})
}
Phase 6: Verification
Step 6.1: Verify environment variables
echo "=== VERIFYING ENV VARS ==="
if [ -f ".env.local" ]; then
source .env.local 2>/dev/null
elif [ -f ".env" ]; then
source .env 2>/dev/null
fi
MISSING=""
[ -z "$GOOG_CREDENTIALS_JSON" ] && MISSING="$MISSING GOOG_CREDENTIALS_JSON"
[ -z "$GOOG_CALENDAR_IMPERSONATE_USER" ] && MISSING="$MISSING GOOG_CALENDAR_IMPERSONATE_USER"
[ -z "$GOOG_CALENDAR_ID" ] && MISSING="$MISSING GOOG_CALENDAR_ID"
if [ -n "$MISSING" ]; then
echo "MISSING ENV VARS:$MISSING"
echo "Please complete Phase 5 before testing."
exit 1
else
echo "All required env vars are set!"
fi
Step 6.2: Test connection
if [ -f ".env.local" ]; then
export $(grep -v '^#' .env.local | xargs)
elif [ -f ".env" ]; then
export $(grep -v '^#' .env | xargs)
fi
node -e "
const { google } = require('googleapis');
if (!process.env.GOOG_CREDENTIALS_JSON) {
console.error('ERROR: GOOG_CREDENTIALS_JSON not set');
process.exit(1);
}
const creds = JSON.parse(Buffer.from(process.env.GOOG_CREDENTIALS_JSON, 'base64').toString());
const auth = new google.auth.GoogleAuth({
credentials: creds,
scopes: ['https://www.googleapis.com/auth/calendar.events'],
clientOptions: { subject: process.env.GOOG_CALENDAR_IMPERSONATE_USER }
});
const calendar = google.calendar({ version: 'v3', auth });
console.log('Testing connection to calendar:', process.env.GOOG_CALENDAR_ID);
console.log('Impersonating user:', process.env.GOOG_CALENDAR_IMPERSONATE_USER);
calendar.events.list({
calendarId: process.env.GOOG_CALENDAR_ID,
maxResults: 5,
timeMin: new Date().toISOString(),
singleEvents: true,
orderBy: 'startTime'
}).then(r => {
console.log('SUCCESS! Connection working.');
console.log('Found', r.data.items?.length || 0, 'upcoming events');
if (r.data.items?.length > 0) {
console.log('Next event:', r.data.items[0].summary);
}
}).catch(e => {
console.error('ERROR:', e.message);
if (e.message.includes('insufficient')) {
console.error('-> Check domain-wide delegation is configured correctly');
}
if (e.message.includes('not found')) {
console.error('-> Check GOOG_CALENDAR_ID is correct');
}
process.exit(1);
});
"
Step 6.3: Cleanup sensitive files
rm -f google-credentials.json google-credentials.b64
echo "Sensitive files cleaned up"
Troubleshooting
"Insufficient Permission" errors
- Verify domain-wide delegation uses correct Client ID
- Scope must match exactly:
https://www.googleapis.com/auth/calendar.events - Impersonation user must have calendar access
"Key creation is not allowed"
gcloud org-policies reset iam.disableServiceAccountKeyCreation --project="$(gcloud config get-value project)"
Find calendar ID via API
node -e "
const { google } = require('googleapis');
const creds = JSON.parse(Buffer.from(process.env.GOOG_CREDENTIALS_JSON, 'base64').toString());
const auth = new google.auth.GoogleAuth({
credentials: creds,
scopes: ['https://www.googleapis.com/auth/calendar.readonly'],
clientOptions: { subject: process.env.GOOG_CALENDAR_IMPERSONATE_USER }
});
const calendar = google.calendar({ version: 'v3', auth });
calendar.calendarList.list().then(r => {
r.data.items?.forEach(c => console.log(c.id, '-', c.summary));
}).catch(e => console.error('Error:', e.message));
"
Checklist
- [ ]
gcloudCLI installed and authenticated - [ ] Google Cloud project created/selected
- [ ] Google Calendar API enabled
- [ ] Service account created
- [ ] Service account key generated and base64-encoded
- [ ] Domain-wide delegation configured (Workspace only)
- [ ] Environment variables added to
.env - [ ]
googleapispackage installed - [ ] Calendar utility module created
- [ ] Connection tested
- [ ] Sensitive files cleaned up
Expert Next.js App Router
Developpement
Un skill qui transforme Claude en expert Next.js App Router.
Générateur de README
Developpement
Crée des README.md professionnels et complets pour vos projets.
Rédacteur de Documentation API
Developpement
Génère de la documentation API complète au format OpenAPI/Swagger.