Add Google Calendar integration design
Design for /gcal command following the Gmail pattern: - Subcommands: today, tomorrow, week, next, summary - Hybrid interface (subcommands + natural language) - Tiered delegation (Haiku for fetch, Sonnet for analysis) - Reuses existing OAuth credentials with added Calendar scope 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
229
plans/2025-01-02-gcal-design.md
Normal file
229
plans/2025-01-02-gcal-design.md
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
# Google Calendar Integration Design
|
||||||
|
|
||||||
|
**Date:** 2025-01-02
|
||||||
|
**Status:** Approved
|
||||||
|
**Primary Use Case:** Agenda overview
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Build a `/gcal` slash command for Google Calendar access, following the same architectural pattern as `/gmail`:
|
||||||
|
- OAuth credentials (reuse existing `~/.gmail-mcp/`)
|
||||||
|
- Python API wrapper with tiered delegation
|
||||||
|
- Skill file defining usage patterns
|
||||||
|
- Read-only by default
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
User types /gcal
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
Command (gcal.md)
|
||||||
|
│
|
||||||
|
├─→ invokes: skill:gcal
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
Skill determines tier
|
||||||
|
│
|
||||||
|
├─→ Haiku: today, tomorrow, week, next (fetch + format)
|
||||||
|
└─→ Sonnet: summary (fetch + analyze)
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
Python helper (gcal_delegate.py)
|
||||||
|
│
|
||||||
|
├─→ Google Calendar API via OAuth
|
||||||
|
└─→ Returns structured JSON
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
PA formats response for user
|
||||||
|
```
|
||||||
|
|
||||||
|
## Components
|
||||||
|
|
||||||
|
| File | Format | Purpose |
|
||||||
|
|------|--------|---------|
|
||||||
|
| `~/.claude/commands/gcal.md` | Markdown | Slash command definition |
|
||||||
|
| `~/.claude/skills/gcal/SKILL.md` | Markdown | Usage patterns and routing |
|
||||||
|
| `~/.claude/mcp/delegation/gcal_delegate.py` | Python | API wrapper + delegation |
|
||||||
|
| `~/.gmail-mcp/credentials.json` | JSON | OAuth credentials (add Calendar scope) |
|
||||||
|
|
||||||
|
## Command Interface
|
||||||
|
|
||||||
|
### Subcommands
|
||||||
|
|
||||||
|
| Subcommand | Description | Tier |
|
||||||
|
|------------|-------------|------|
|
||||||
|
| `today` | Today's agenda | Haiku |
|
||||||
|
| `tomorrow` | Tomorrow's agenda | Haiku |
|
||||||
|
| `week` | Next 7 days, grouped by day | Haiku |
|
||||||
|
| `next` | Next upcoming event | Haiku |
|
||||||
|
| `summary` | Smart analysis of your week | Sonnet |
|
||||||
|
|
||||||
|
### Routing
|
||||||
|
|
||||||
|
```
|
||||||
|
/gcal → Smart default (today if before 6pm, tomorrow if after)
|
||||||
|
/gcal today → Today's agenda
|
||||||
|
/gcal tomorrow → Tomorrow's agenda
|
||||||
|
/gcal week → Next 7 days, grouped by day
|
||||||
|
/gcal next → Next upcoming event only
|
||||||
|
/gcal summary → Sonnet-powered week analysis
|
||||||
|
/gcal <natural> → PA interprets intent, routes to appropriate subcommand
|
||||||
|
```
|
||||||
|
|
||||||
|
### Hybrid Approach
|
||||||
|
|
||||||
|
1. **Exact subcommand match first:** `today`, `tomorrow`, `week`, `next`, `summary`
|
||||||
|
2. **Natural language fallback:**
|
||||||
|
- "what's on today" → today
|
||||||
|
- "this week" → week
|
||||||
|
- "next meeting" → next
|
||||||
|
- "am I busy tomorrow" → tomorrow
|
||||||
|
- "give me an overview" → summary
|
||||||
|
3. **Ambiguous input:** Ask for clarification
|
||||||
|
|
||||||
|
### Smart Default
|
||||||
|
|
||||||
|
```python
|
||||||
|
from datetime import datetime
|
||||||
|
def default_view():
|
||||||
|
return "today" if datetime.now().hour < 18 else "tomorrow"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Output Format
|
||||||
|
|
||||||
|
### Simple List (today/tomorrow/next)
|
||||||
|
|
||||||
|
```
|
||||||
|
📅 Today — Thursday, Jan 2
|
||||||
|
|
||||||
|
9:00 AM Team standup (30m)
|
||||||
|
📍 Zoom · 👥 5 attendees
|
||||||
|
|
||||||
|
10:30 AM 1:1 with Sarah (45m)
|
||||||
|
📍 Conference Room B
|
||||||
|
|
||||||
|
2:00 PM Project review (1h)
|
||||||
|
📍 Google Meet · 👥 8 attendees
|
||||||
|
📝 Q4 roadmap discussion
|
||||||
|
|
||||||
|
No more events today.
|
||||||
|
```
|
||||||
|
|
||||||
|
### Grouped by Day (week)
|
||||||
|
|
||||||
|
```
|
||||||
|
📅 This Week — Jan 2-8
|
||||||
|
|
||||||
|
━━━ Thursday, Jan 2 ━━━
|
||||||
|
9:00 AM Team standup (30m)
|
||||||
|
2:00 PM Project review (1h)
|
||||||
|
|
||||||
|
━━━ Friday, Jan 3 ━━━
|
||||||
|
11:00 AM Client call (1h)
|
||||||
|
|
||||||
|
━━━ Monday, Jan 6 ━━━
|
||||||
|
(no events)
|
||||||
|
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
### Context Fields
|
||||||
|
|
||||||
|
- 📍 Location or meeting link
|
||||||
|
- 👥 Attendee count
|
||||||
|
- 📝 Description snippet (first ~50 chars)
|
||||||
|
|
||||||
|
### Smart Summary (Sonnet)
|
||||||
|
|
||||||
|
> "Your week is front-loaded — Thursday and Friday are packed with 6 meetings totaling 5.5 hours. Monday-Wednesday are mostly clear, good for deep work. Heads up: the Client call Friday overlaps with your blocked focus time."
|
||||||
|
|
||||||
|
## Python Helper
|
||||||
|
|
||||||
|
### Structure
|
||||||
|
|
||||||
|
```python
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Google Calendar Delegation Helper
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
gcal_delegate.py today
|
||||||
|
gcal_delegate.py tomorrow
|
||||||
|
gcal_delegate.py week
|
||||||
|
gcal_delegate.py next
|
||||||
|
gcal_delegate.py summary
|
||||||
|
"""
|
||||||
|
|
||||||
|
SCOPES = ['https://www.googleapis.com/auth/calendar.readonly']
|
||||||
|
CREDENTIALS_PATH = "~/.gmail-mcp/credentials.json"
|
||||||
|
|
||||||
|
# Core functions
|
||||||
|
def get_calendar_service() # OAuth flow, returns Calendar API client
|
||||||
|
def fetch_events(start, end) # Returns list of event dicts
|
||||||
|
def format_event(event) # Extracts: time, title, location, attendees, description
|
||||||
|
def delegate(model, prompt) # Calls Claude CLI for Sonnet-tier tasks
|
||||||
|
|
||||||
|
# Subcommand handlers
|
||||||
|
def cmd_today() # fetch_events for today, format, return JSON
|
||||||
|
def cmd_tomorrow() # fetch_events for tomorrow
|
||||||
|
def cmd_week() # fetch_events for 7 days, group by date
|
||||||
|
def cmd_next() # fetch next single event
|
||||||
|
def cmd_summary() # fetch week, delegate to Sonnet for analysis
|
||||||
|
```
|
||||||
|
|
||||||
|
### Output Format
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"tier": "haiku",
|
||||||
|
"operation": "today",
|
||||||
|
"date": "2025-01-02",
|
||||||
|
"events": [...],
|
||||||
|
"count": 3
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## OAuth Setup
|
||||||
|
|
||||||
|
### One-Time Setup
|
||||||
|
|
||||||
|
1. Go to [Google Cloud Console](https://console.cloud.google.com/) → APIs & Services
|
||||||
|
2. Enable "Google Calendar API"
|
||||||
|
3. OAuth consent screen → Add scope: `calendar.readonly`
|
||||||
|
4. Delete local token: `rm ~/.gmail-mcp/token.json`
|
||||||
|
5. Run `/gcal today` — triggers re-auth with new scope
|
||||||
|
|
||||||
|
### Scope Detection
|
||||||
|
|
||||||
|
Helper detects missing scope and prompts:
|
||||||
|
```
|
||||||
|
Calendar scope not authorized. Run:
|
||||||
|
rm ~/.gmail-mcp/token.json
|
||||||
|
Then retry /gcal today to re-authenticate.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Delegation Tiers
|
||||||
|
|
||||||
|
| Tier | Operations | Reason |
|
||||||
|
|------|------------|--------|
|
||||||
|
| Haiku | today, tomorrow, week, next | Fetch + format only |
|
||||||
|
| Sonnet | summary | Requires analysis |
|
||||||
|
| Opus | (reserved) | Strategic planning if needed |
|
||||||
|
|
||||||
|
## Policy
|
||||||
|
|
||||||
|
- Read-only operations only
|
||||||
|
- Show context (attendees, location) by default
|
||||||
|
- Summarize results, don't dump raw data
|
||||||
|
- Start with lowest capable model tier
|
||||||
|
|
||||||
|
## Implementation Checklist
|
||||||
|
|
||||||
|
- [ ] Add Calendar scope to GCP OAuth app
|
||||||
|
- [ ] Create `~/.claude/skills/gcal/SKILL.md`
|
||||||
|
- [ ] Create `~/.claude/commands/gcal.md`
|
||||||
|
- [ ] Create `~/.claude/mcp/delegation/gcal_delegate.py`
|
||||||
|
- [ ] Test OAuth re-authorization
|
||||||
|
- [ ] Test all subcommands
|
||||||
|
- [ ] Test natural language fallback
|
||||||
Reference in New Issue
Block a user