Skip to content

Hal Google MCP Integration

Connect Hal (opencode) to Gmail + Google Calendar via Google's official remote MCP servers.


Architecture

opencode (Hal) ──MCP──> gmailmcp.googleapis.com/mcp/v1   (official Google)
                     └──> calendarmcp.googleapis.com/mcp/v1
                              ▲
                          OAuth 2.0 (Desktop app client, one-time consent)
                          tokens cached in ~/.local/share/opencode/mcp-auth.json
  • No daemon, no cron, no new processes — pure config + one-time OAuth.
  • Works exactly like existing memory, desktop, shopify MCP servers in opencode.
  • Session memory persists via Memory MCP — context survives across sessions.

Google Cloud Setup (one-time, ~10 min, in browser — your account)

1. Google Cloud Project

2. Enable 4 APIs

In APIs & Services → Library, enable:

gmail.googleapis.com
gmailmcp.googleapis.com
calendarmcp.googleapis.com
calendar.googleapis.com
Or via gcloud:
gcloud services enable gmail.googleapis.com gmailmcp.googleapis.com calendarmcp.googleapis.com calendar.googleapis.com --project=YOUR_PROJECT_ID

  • APIs & Services → OAuth consent screen
  • User Type: External (personal Gmail)
  • App name: Hal (or whatever)
  • Scopes (add manually under "Data access"):
  • https://www.googleapis.com/auth/gmail.readonly
  • https://www.googleapis.com/auth/gmail.compose
  • https://www.googleapis.com/auth/calendar.calendarlist.readonly
  • https://www.googleapis.com/auth/calendar.events.freebusy
  • https://www.googleapis.com/auth/calendar.events.readonly
  • Publish the app: after saving, click Publish App → status becomes In production. No formal Google verification is required for personal single-user use — the consent screen will just show an "unverified app" warning once, which is harmless.
  • Why publish: In Testing mode Google expires refresh tokens after ~7 days (forces weekly re-auth). Publishing makes the refresh token effectively indefinite. No test-user setup needed.

  • Save.

4. OAuth Client ID (Desktop App)

  • APIs & Services → Credentials → Create Credentials → OAuth client ID
  • Application type: Desktop app
  • Name: Hal opencode
  • Create → copy Client ID and Client Secret.

Important: Do NOT commit these to the config. Use environment variables.


opencode.json Configuration

Backup the existing config first (per your version-vs-in-place rule), then add two type: "remote" entries:

{
  "mcp": {
    "gmail": {
      "type": "remote",
      "url": "https://gmailmcp.googleapis.com/mcp/v1",
      "enabled": true,
      "oauth": {
        "clientId": "{env:GOOGLE_MCP_CLIENT_ID}",
        "clientSecret": "{env:GOOGLE_MCP_CLIENT_SECRET}"
      }
    },
    "calendar": {
      "type": "remote",
      "url": "https://calendarmcp.googleapis.com/mcp/v1",
      "enabled": true,
      "oauth": {
        "clientId": "{env:GOOGLE_MCP_CLIENT_ID}",
        "clientSecret": "{env:GOOGLE_MCP_CLIENT_SECRET}"
      }
    }
  }
}

Store credentials in a .env file (sourced in your shell profile) or export them:

# ~/.profile or ~/.config/fish/conf.d/hal-google.fish
export GOOGLE_MCP_CLIENT_ID="your-client-id"
export GOOGLE_MCP_CLIENT_SECRET="your-client-secret"

Restart opencode to pick up env vars.


Authenticate (once, then tokens auto-cached)

opencode mcp auth gmail
opencode mcp auth calendar

Each opens a browser consent screen. Approve. Tokens stored in ~/.local/share/opencode/mcp-auth.json (0o600).


Verify

# List available tools on each server
opencode mcp list

# Quick smoke tests (in an opencode session)
# "what's in my inbox today?"
# "what's on my calendar this week?"
# "draft a reply to the latest email from X"
# "create a calendar event for tomorrow 2pm: 'Review PRs'"

Caveats & Trade-offs

Aspect Detail
Token lifetime Access token ~1 hour (auto-refreshed silently by opencode). Refresh token indefinite once the app is published — only invalidated by 6 months inactivity, a Gmail password change, or manual revocation. Re-auth only if one of those happens.
Scope limits Official servers: Gmail = read + compose/send (gmail.readonly, gmail.compose). Calendar = read + create events. Full delete/archive or deeper write = custom server needed later.
Developer Preview Both MCP servers are Google "Developer Preview" — fine for personal use, APIs may change.
No daemon Nothing runs when opencode is closed → no reminders/scheduling (per your preference).
Security Tokens in mcp-auth.json (0o600). Credentials via env vars. Your own Google Cloud project = your data stays in your Google account. The one-time "unverified app" warning is expected until/unless you go through formal Google verification.

Alternatives (if needed later)

Option When
Custom Python MCP (like desktop-mcp) Want full scopes, shared single OAuth client, stdlib-only, max control. More code + maintenance.
klodr/gmail-mcp (hardened) Want recipient pairing, audit log, whitelisted send destinations. Still same weekly re-auth.
Google Workspace (paid) Only needed if you ever want an Internal-only app (no consent screen at all). For personal Gmail, publishing the External app already removes the 7-day expiry — Workspace is optional.

  • [[OpenCode Desktop MCP]]
  • [[OpenCode Canva Design Skill]]
  • [[OpenCode MCP Node Requirement]]
  • [[JBS LEMO Shopify Storefront]]
  • [[OpenCode Voice Input]]