Skip to content

Google OAuth setup (Calendar + Gmail)

Consolidated runbook for connecting Neo to Google Calendar and/or Gmail via their official preview MCP servers. Both plugins share one Google Cloud OAuth client — this doc covers provisioning it once and enabling either or both integrations.

For the plugin-specific walkthroughs (which Neo runs interactively as a skill), see:

  • templates/plugins/calendar/README.md + templates/plugins/calendar/skills/connect-calendar/SKILL.md
  • templates/plugins/gmail/README.md

This doc is the standalone reference for doing it by hand (or for the owner to follow directly, without going through Neo's interactive skill) and for verifying the auth plumbing underneath both plugins.

Architecture (what actually happens)

Both templates/plugins/calendar/plugin.json and templates/plugins/gmail/plugin.json declare an mcpServers entry of type http pointing at Google's hosted MCP endpoint, with an oauth block:

jsonc
"oauth": {
  "clientId": "${GOOGLE_CLIENT_ID}",
  "clientSecretEnv": "GOOGLE_CLIENT_SECRET",
  "scopes": ["..."],
  "pkce": true,
  "autoAuth": true
}
  • The config shape is validated by src/mcp/types.ts (clientId, clientSecretEnv, scopes, pkce, autoAuth — all optional, zod schema at src/mcp/types.ts:48-55).
  • clientSecretEnv is a level of indirection: the manifest never carries a literal secret, only the name of an env var Neo reads at connect time (src/mcp/auth-provider.ts:105-106). Both plugins point at the same GOOGLE_CLIENT_ID/GOOGLE_CLIENT_SECRET pair, so one Cloud Console OAuth client covers both — you do not provision two.
  • Token exchange runs through NeoOAuthClientProvider (src/mcp/auth-provider.ts) using PKCE + a local loopback HTTP server (src/auth/loopback/callback-server.ts, port chosen by src/auth/loopback/port-allocator.ts, fallback 3118).
  • Tokens are stored per-server key, not per-plugin (src/mcp/auth-provider.ts:53,71 getMcpServerKey(serverName, serverConfig)). Calendar's server is named gcal, Gmail's is named gmail — connecting both against the same OAuth client produces two independent token records under ~/.neo/mcp/oauth/, each refreshed independently. Revoking/expiring one does not affect the other.

Verified during Wave A chip 7 review: the plugin manifests, the zod schema they validate against, and the loopback fallback port all agree — no plumbing bugs found. Nothing in this path was changed; this doc documents existing, working infrastructure.

Prerequisites

  • A Google account (personal Gmail account works — no Workspace/org membership required).
  • Neo installed and runnable (neo on PATH or via the repo's dev entrypoint).
  • 10–15 minutes for the Cloud Console setup (one-time).

Step 1 — Create a Google Cloud project

  1. Go to https://console.cloud.google.com.
  2. Click the project selector (top bar, next to "Google Cloud") → New Project.
  3. Name it anything memorable (e.g. "Neo Personal Agent") → Create.
  4. Wait for Cloud Console to switch to the new project (a few seconds).

Step 2 — Enable the APIs you need

With the new project selected, go to APIs & Services → Library:

  • Search "Google Calendar API"Enable (only if you want the Calendar plugin).
  • Search "Gmail API"Enable (only if you want the Gmail plugin).

You can enable both now even if you only plan to use one today — enabling an unused API has no cost or side effect.

Go to APIs & Services → OAuth consent screen:

  1. User Type: choose External for a personal Google account (choose Internal only if this is a Google Workspace org account and you want to restrict access to that org).
  2. Fill in an app name (e.g. "Neo") and a support email.
  3. On the Scopes page, click Add or Remove Scopes and add whichever of these you'll use:
    • https://www.googleapis.com/auth/calendar.readonly
    • https://www.googleapis.com/auth/calendar.events
    • https://www.googleapis.com/auth/gmail.readonly
    • https://www.googleapis.com/auth/gmail.compose
  4. On the Test users page, add the exact Google account email you'll authenticate with. Required — while the app is in "Testing" status (the normal, permanent state for personal use), only listed test users can complete the OAuth flow.
  5. Save through the remaining pages.

The app can stay in "Testing" status indefinitely for personal/single-owner use — Google does not require publishing/verification unless you extend access beyond the test-user list.

Step 4 — Create the OAuth client

Go to APIs & Services → Credentials → Create Credentials → OAuth client ID:

  1. Application type: Desktop app is recommended — Google handles redirect URIs automatically and Neo's loopback server just works. If you choose Web application instead, you must manually add Neo's redirect URI (http://127.0.0.1:<port>/callback — run /calendar:auth or /gmail:auth first to see the exact port Neo picked) under "Authorized redirect URIs" before the flow will succeed.
  2. Name it (e.g. "Neo Desktop Client") → Create.
  3. Google shows client_id and client_secret in a dialog. Copy both now — the secret is not shown again after you close the dialog (you can always generate a new secret later if lost, from the credential's detail page).

Step 5 — Add credentials to ~/.neo/.env

Create or edit ~/.neo/.env and add:

GOOGLE_CLIENT_ID=your_client_id_here
GOOGLE_CLIENT_SECRET=your_client_secret_here

No quotes around the values. These are the exact env var names both plugins' manifests reference (${GOOGLE_CLIENT_ID} interpolation + clientSecretEnv: "GOOGLE_CLIENT_SECRET").

Step 6 — Install the plugin(s)

/plugins install calendar
/plugins install gmail

(Install either or both — independent.) This copies the template to ~/.neo/plugins/<name>/ and adds it to enabledPlugins in ~/.neo/settings.json.

Step 7 — Restart Neo, then complete the OAuth ceremony

Restart Neo so it picks up the new .env values, then run:

/calendar:auth

and/or

/gmail:auth

Each command:

  1. Prints the redirect URI Neo will use (only relevant if you chose "Web application" in Step 4 — add it to Cloud Console if so).
  2. Opens a browser window at Google's authorization URL (or prints it, if the browser can't be opened automatically — paste it manually).
  3. Waits for the browser to redirect back to Neo's local loopback callback.
  4. Exchanges the authorization code for access + refresh tokens.
  5. Saves the tokens to Neo's MCP auth store (~/.neo/mcp/oauth/), keyed per-server (gcal / gmail independently).

Step 8 — Verify

  • Calendar: ask Neo "What's on my calendar today?" — a real event list means success.
  • Gmail: ask Neo "Any unread email from this week?" — real thread data means success.

Trigger commands (exact, for the owner)

/plugins install calendar
/plugins install gmail
/calendar:auth
/gmail:auth

Success verification queries:

What's on my calendar today?
What Gmail labels do I have?

Troubleshooting

See the Troubleshooting sections in templates/plugins/calendar/README.md and templates/plugins/calendar/skills/connect-calendar/SKILL.md (covers 401/403/redirect_uri_mismatch/"unverified app" warnings/preview-availability in detail) — identical failure modes apply to Gmail with /gmail:auth in place of /calendar:auth.

Permissions

Both connectors' tools are namespaced (mcp-gcal-*, mcp-gmail-* — check the exact tool names via /mcp status gcal / /mcp status gmail after first connect) and can be allow/deny-scoped in ~/.neo/config/permissions.json — see ../plugins.md and the per-plugin README's "Permissions" section for the wildcard pattern.

Status as of Wave A chip 7 (2026-07-07)

No live OAuth round-trip has been performed in this dev environment — no Google Cloud project, OAuth client, or GOOGLE_CLIENT_ID/ GOOGLE_CLIENT_SECRET exists here. This doc and the exact steps above are what the owner needs to run the live E2E themselves; see scratch/wave-a-ledger.md's "Handoff items for the owner" section for the tracked follow-up.