Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 147 additions & 0 deletions docs/superpowers/plans/2026-05-20-openid-scope-fix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# Add `openid` to OAuth SCOPES Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Append `"openid"` to `SCOPES` in `src/multi_google_mcp/config.py` so `oauthlib` stops aborting `multi-google-mcp-auth add` when Google implicitly grants the `openid` scope.

**Architecture:** One-line edit to a config list. No new tests (the OAuth flow is not exercised in the test suite; verification happens against live Google APIs). After editing, the locally installed `multi-google-mcp-auth` console script is reinstalled with `uv tool install --reinstall .` so it picks up the new code, then the auth flow is driven manually against the `personal` account to confirm the fix.

**Tech Stack:** Python 3.11+, `uv`, `google-auth-oauthlib`, `oauthlib`, `mcp` SDK.

---

## File Structure

- Modify: `src/multi_google_mcp/config.py:9-14` — add `"openid"` to the existing `SCOPES` list.

That's all. No new files, no other modifications.

---

### Task 1: Add `openid` to SCOPES and verify static checks

**Files:**
- Modify: `src/multi_google_mcp/config.py:9-14`

- [ ] **Step 1: Make the edit**

Open `src/multi_google_mcp/config.py` and change the `SCOPES` list from:

```python
SCOPES = [
"https://www.googleapis.com/auth/gmail.modify",
"https://www.googleapis.com/auth/calendar",
"https://www.googleapis.com/auth/drive",
"https://www.googleapis.com/auth/userinfo.email",
]
```

to:

```python
SCOPES = [
"https://www.googleapis.com/auth/gmail.modify",
"https://www.googleapis.com/auth/calendar",
"https://www.googleapis.com/auth/drive",
"https://www.googleapis.com/auth/userinfo.email",
"openid",
]
```

- [ ] **Step 2: Run the existing test suite**

Run: `uv run pytest`
Expected: PASS — all existing tests still pass. The added scope is consumed only by the auth CLI's `InstalledAppFlow` and is not exercised in tests (`accounts.py` references `config.SCOPES` but the test fixtures stub out the OAuth flow entirely).

- [ ] **Step 3: Run the linter**

Run: `uv run ruff check .`
Expected: PASS — `All checks passed!`.

- [ ] **Step 4: Run the type-checker**

Run: `uv run mypy`
Expected: PASS — no new errors.

- [ ] **Step 5: Commit**

```bash
git add src/multi_google_mcp/config.py
git commit -m "$(cat <<'EOF'
fix: add openid to OAuth SCOPES to unblock auth flow

Google implicitly grants the openid scope whenever userinfo.email (or
any identity-bearing scope) is requested. oauthlib raises the scope
mismatch as an exception via its Warning-to-error path, aborting
multi-google-mcp-auth add before any token is written.

Adding "openid" to the requested set keeps the granted set aligned and
silences oauthlib without changing actual API access.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EOF
)"
```

---

### Task 2: Reinstall the console scripts so the installed CLI picks up the new code

**Files:** None modified.

- [ ] **Step 1: Reinstall**

Run: `uv tool install --reinstall .`
Expected: output ends with something like `Installed 2 executables: multi-google-mcp, multi-google-mcp-auth`. No errors.

- [ ] **Step 2: Sanity-check the installed binary picks up the new scope list**

Run:
```bash
which multi-google-mcp-auth
uv tool run --from . python -c "from multi_google_mcp.config import SCOPES; print('openid' in SCOPES, len(SCOPES))"
```
Expected: path is printed; second line prints `True 5`.

- [ ] **Step 3: No commit**

Reinstall produces no repo changes — nothing to commit.

---

### Task 3: Verify the fix end-to-end against the `personal` account

**Files:** None modified.

- [ ] **Step 1: Run the auth flow**

Run: `multi-google-mcp-auth add personal`
Expected behavior:
- Prints a `Please visit this URL` line and opens a browser tab to Google's consent screen.
- User completes consent in the browser.
- Process prints something like `Saved account 'personal' to ~/.config/multi-google-mcp/accounts/personal.json` (or whatever success line `auth_cli.py` emits) and exits 0.
- The previous failure mode — an `oauthlib` exception about scope mismatch / Warning before the token file is written — does NOT occur.

Note: this step is interactive (browser OAuth consent). If running in a non-interactive context, hand off to the user with a clear "please complete the OAuth consent in your browser" prompt.

- [ ] **Step 2: Confirm the token file was (re)written**

Run: `ls -la ~/.config/multi-google-mcp/accounts/personal.json && stat -f '%Sm' ~/.config/multi-google-mcp/accounts/personal.json`
Expected: file exists, permissions are `-rw-------` (mode 0o600 per `accounts._atomic_write_json`), modification timestamp is from the last few seconds.

- [ ] **Step 3: Confirm the new scope set is in the saved token**

Run: `python -c "import json; d=json.load(open('${HOME}/.config/multi-google-mcp/accounts/personal.json')); print(sorted(d.get('scopes', [])))"`
Expected: list includes `"openid"` alongside the other four scopes.

- [ ] **Step 4: No commit**

Verification produces no repo changes — nothing to commit.

---

## Notes on what is *not* in this plan

- No new unit test. The `SCOPES` list is data, not behavior; the OAuth flow it feeds is not under test in this repo (test fixtures mock `googleapiclient.discovery.build` and never construct an `InstalledAppFlow`). Per the test-driven-development skill's config-only exception, no test is added.
- No update to CLAUDE.md's commentary about the SCOPES list — out of scope for this fix.
- No revisit of other OAuth-flow ergonomics (token rotation, scope subset upgrades, etc.).
68 changes: 68 additions & 0 deletions docs/superpowers/specs/2026-05-20-openid-scope-fix-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Add `openid` to OAuth SCOPES to unblock auth flow

## Goal

Fix `multi-google-mcp-auth add <label>` aborting when Google returns `openid` in
the granted scope set. Add `"openid"` to `config.SCOPES` so the requested set
matches what Google returns, and `oauthlib`'s scope-change check stops firing.

## Background

Google's OAuth implementation implicitly grants `openid` whenever
`userinfo.email` (or other identity-bearing scopes) is requested. `oauthlib`'s
`InstalledAppFlow` treats any divergence between requested and granted scopes as
a `Warning`, which `oauthlib` raises as an exception, aborting the flow before
the token is persisted. The repro is `multi-google-mcp-auth add personal`
against a fresh OAuth client — it fails consistently.

## Change

Single-line edit to `src/multi_google_mcp/config.py`: append `"openid"` to the
`SCOPES` list.

```python
SCOPES = [
"https://www.googleapis.com/auth/gmail.modify",
"https://www.googleapis.com/auth/calendar",
"https://www.googleapis.com/auth/drive",
"https://www.googleapis.com/auth/userinfo.email",
"openid",
]
```

No code changes elsewhere. `accounts.py` consumes `SCOPES` directly from
`config`; nothing else cares about the list contents.

## Why not a test

The OAuth flow only runs against live Google APIs — `auth_cli.py` calls
`google_auth_oauthlib.flow.InstalledAppFlow.from_client_secrets_file(...)` and
drives a local browser callback. The existing test suite mocks
`googleapiclient.discovery.build` and never exercises the auth flow. There is no
seam for a unit test to catch this regression, and the scope-list contents are
trivially inspectable. Per the `superpowers:test-driven-development` config-only
exception, no new test is added.

## Verification

1. `uv tool install --reinstall .` — reinstall the console script so the
already-installed `multi-google-mcp-auth` picks up the updated `SCOPES`.
2. `multi-google-mcp-auth add personal` — completes without `oauthlib` raising
on scope mismatch, prints success, writes
`~/.config/multi-google-mcp/accounts/personal.json`.

## Caveats

- CLAUDE.md notes: changing `SCOPES` requires every existing account to be
re-added. The `personal` account is re-added as the verify step. No other
accounts exist locally (confirmed by user), so nothing else to handle.
- This is a one-way fix — `openid` is a stable, standardized OIDC scope and is
effectively always granted alongside `userinfo.email`. No regression risk for
callers that don't care about identity tokens (we don't use the ID token; the
scope is purely there to keep `oauthlib` quiet).

## Non-goals

- No update to CLAUDE.md's scope-list commentary in this change.
- No revisit of the broader OAuth flow or scope set.
- No swap to a different OAuth library or relaxed `oauthlib` warning policy.
1 change: 1 addition & 0 deletions src/multi_google_mcp/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"https://www.googleapis.com/auth/calendar",
"https://www.googleapis.com/auth/drive",
"https://www.googleapis.com/auth/userinfo.email",
"openid",
]

# Cap on Drive payload size (read or upload) so a 1 GB PDF can't hang the MCP
Expand Down
Loading