mcp-gee-sweet 0.7.0.dev30__tar.gz → 0.7.0.dev32__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/PKG-INFO +1 -1
  2. mcp_gee_sweet-0.7.0.dev32/docs/qa/playwright_oauth.md +130 -0
  3. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/qa/runs/v0.8.0.md +1 -1
  4. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/qa/setup.md +11 -0
  5. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/qa/tests/docs.md +85 -85
  6. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/qa-checklist.md +170 -0
  7. mcp_gee_sweet-0.7.0.dev32/scripts/oauth_setup.py +115 -0
  8. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/src/mcp_gee_sweet/.env.template +7 -0
  9. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/.claude/commands/prep-for-pr.md +0 -0
  10. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/.dockerignore +0 -0
  11. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  12. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  13. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/.github/dependabot.yml +0 -0
  14. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/.github/workflows/ci.yml +0 -0
  15. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/.github/workflows/docs.yml +0 -0
  16. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/.github/workflows/publish-dev.yml +0 -0
  17. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/.github/workflows/release.yml +0 -0
  18. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/.gitignore +0 -0
  19. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/.pre-commit-config.yaml +0 -0
  20. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/.python-version +0 -0
  21. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/CLAUDE.md +0 -0
  22. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/CONTRIBUTING.md +0 -0
  23. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/Dockerfile +0 -0
  24. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/LICENSE +0 -0
  25. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/Makefile +0 -0
  26. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/README.md +0 -0
  27. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/SECURITY.md +0 -0
  28. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/TODO.md +0 -0
  29. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docker-compose.yml +0 -0
  30. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/auth.md +0 -0
  31. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/client-setup.md +0 -0
  32. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/configuration.md +0 -0
  33. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/decisions/decision-chart-theming.md +0 -0
  34. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/decisions/decision-composite-tools.md +0 -0
  35. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/decisions/decision-docs-formatting.md +0 -0
  36. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/decisions/decision-fork.md +0 -0
  37. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/decisions/decision-publishing.md +0 -0
  38. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/decisions/decision-testing.md +0 -0
  39. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/decisions/index.md +0 -0
  40. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/design/docs-ast-pipeline.md +0 -0
  41. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/design/effectiveformat-spike.md +0 -0
  42. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/design/index.md +0 -0
  43. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/design/markdown-support.md +0 -0
  44. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/design/phase3-theme-system.md +0 -0
  45. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/design.md +0 -0
  46. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/index.md +0 -0
  47. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/known-limitations.md +0 -0
  48. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/notes-read.md +0 -0
  49. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/qa/README.md +0 -0
  50. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/qa/fixtures/tc-d195-create-doc.md +0 -0
  51. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/qa/fixtures/tc-d196-create-doc.html +0 -0
  52. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/qa/fixtures/tc-d226-heading-table.md +0 -0
  53. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/qa/fixtures.template.md +0 -0
  54. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/qa/operations.yaml +0 -0
  55. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/qa/results/.gitkeep +0 -0
  56. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/qa/results/2026-06-02.md +0 -0
  57. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/qa/results/2026-06-14.md +0 -0
  58. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/qa/results/2026-06-17.md +0 -0
  59. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/qa/results/2026-06-18.md +0 -0
  60. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/qa/results/2026-06-21-drive-discovery.md +0 -0
  61. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/qa/results/2026-06-21-sheets-delete-clear.md +0 -0
  62. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/qa/run.md +0 -0
  63. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/qa/runs/README.md +0 -0
  64. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/qa/tests/calendar.md +0 -0
  65. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/qa/tests/drive.md +0 -0
  66. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/qa/tests/infra.md +0 -0
  67. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/qa/tests/sheets_charts.md +0 -0
  68. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/qa/tests/sheets_mgmt.md +0 -0
  69. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/qa/tests/sheets_read.md +0 -0
  70. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/qa/tests/sheets_write.md +0 -0
  71. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/roadmap.md +0 -0
  72. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/docs/tools.md +0 -0
  73. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/mkdocs.yml +0 -0
  74. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/pyproject.toml +0 -0
  75. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/src/mcp_gee_sweet/__init__.py +0 -0
  76. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/src/mcp_gee_sweet/auth.py +0 -0
  77. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/src/mcp_gee_sweet/cache.py +0 -0
  78. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/src/mcp_gee_sweet/server.py +0 -0
  79. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/src/mcp_gee_sweet/tools/__init__.py +0 -0
  80. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/src/mcp_gee_sweet/tools/cache.py +0 -0
  81. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/src/mcp_gee_sweet/tools/calendar.py +0 -0
  82. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/src/mcp_gee_sweet/tools/docs/__init__.py +0 -0
  83. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/src/mcp_gee_sweet/tools/docs/ast.py +0 -0
  84. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/src/mcp_gee_sweet/tools/docs/emitter.py +0 -0
  85. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/src/mcp_gee_sweet/tools/docs/html_parser.py +0 -0
  86. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/src/mcp_gee_sweet/tools/drive/__init__.py +0 -0
  87. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/src/mcp_gee_sweet/tools/drive/activity.py +0 -0
  88. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/src/mcp_gee_sweet/tools/drive/files.py +0 -0
  89. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/src/mcp_gee_sweet/tools/drive/sharing.py +0 -0
  90. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/src/mcp_gee_sweet/tools/drive/transfer.py +0 -0
  91. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/src/mcp_gee_sweet/tools/sheets/__init__.py +0 -0
  92. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/src/mcp_gee_sweet/tools/sheets/data.py +0 -0
  93. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/src/mcp_gee_sweet/tools/sheets/helpers.py +0 -0
  94. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/src/mcp_gee_sweet/tools/sheets/structure.py +0 -0
  95. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/tests/__init__.py +0 -0
  96. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/tests/drive/__init__.py +0 -0
  97. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/tests/drive/test_activity.py +0 -0
  98. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/tests/drive/test_files.py +0 -0
  99. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/tests/drive/test_sharing.py +0 -0
  100. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/tests/drive/test_transfer.py +0 -0
  101. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/tests/sheets/__init__.py +0 -0
  102. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/tests/sheets/test_data.py +0 -0
  103. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/tests/sheets/test_helpers.py +0 -0
  104. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/tests/sheets/test_structure.py +0 -0
  105. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/tests/test_auth.py +0 -0
  106. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/tests/test_cache.py +0 -0
  107. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/tests/test_calendar.py +0 -0
  108. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/tests/test_docs.py +0 -0
  109. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/tests/test_server.py +0 -0
  110. {mcp_gee_sweet-0.7.0.dev30 → mcp_gee_sweet-0.7.0.dev32}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcp-gee-sweet
3
- Version: 0.7.0.dev30
3
+ Version: 0.7.0.dev32
4
4
  Summary: MCP server for Google Workspace — Sheets, Drive, Docs, and Calendar. 60+ tools for AI clients.
5
5
  Project-URL: Homepage, https://github.com/khuisman/mcp-gee-sweet
6
6
  Project-URL: Bug Tracker, https://github.com/khuisman/mcp-gee-sweet/issues
@@ -0,0 +1,130 @@
1
+ # Playwright OAuth Automation
2
+
3
+ How to acquire or refresh an OAuth token for QA runs without manually clicking through Google's consent screen.
4
+
5
+ ---
6
+
7
+ ## When you need this
8
+
9
+ - Fresh environment with no `token.json`
10
+ - `token.json` expired and cannot be refreshed (e.g. revoked, scopes changed)
11
+ - Running `⚠️ requires-oauth` test cases in an automated QA session
12
+
13
+ If `token.json` already exists and is valid, you don't need any of this — the server refreshes the access token automatically.
14
+
15
+ ---
16
+
17
+ ## Approach A — Playwright-assisted browser flow
18
+
19
+ Use `oauth_setup.py` to start the consent flow without auto-launching a browser, then let the Playwright MCP complete it.
20
+
21
+ ### Prerequisites
22
+
23
+ Add to `src/mcp_gee_sweet/.env`:
24
+
25
+ ```
26
+ GOOGLE_TEST_EMAIL=your-test-account@gmail.com
27
+ GOOGLE_TEST_PASSWORD=your-test-account-password
28
+ ```
29
+
30
+ > **Security note:** these are used only by the Playwright step below and are never sent to the MCP server. Keep them in `.env` (gitignored).
31
+
32
+ ### Step 1 — Start the OAuth server
33
+
34
+ In a terminal:
35
+
36
+ ```bash
37
+ uv run python scripts/oauth_setup.py
38
+ ```
39
+
40
+ The script prints an `OAUTH_URL: https://accounts.google.com/o/oauth2/auth?...` line and waits for the callback.
41
+
42
+ ### Step 2 — Conductor prompt (Playwright MCP)
43
+
44
+ Paste this into a Claude Code session that has the Playwright MCP connected:
45
+
46
+ ```
47
+ I need to complete a Google OAuth consent flow. Do the following:
48
+
49
+ 1. Read GOOGLE_TEST_EMAIL and GOOGLE_TEST_PASSWORD from src/mcp_gee_sweet/.env
50
+ 2. Navigate to the OAUTH_URL printed by the oauth_setup.py script (running in a terminal)
51
+ 3. On the Google sign-in page, enter the email address and click Next
52
+ 4. Enter the password and click Next
53
+ 5. On the consent screen, click "Allow" (or "Continue") to grant all requested scopes
54
+ 6. Wait for the "Authorization complete" message on the page
55
+
56
+ Report what you see at each step and flag immediately if Google shows a CAPTCHA or
57
+ "This browser or app may not be secure" warning — those require manual intervention.
58
+ ```
59
+
60
+ ### Step 3 — Verify
61
+
62
+ Once the script exits, confirm `token.json` was updated:
63
+
64
+ ```bash
65
+ python3 -c "import json; d=json.load(open('token.json')); print('refresh_token present:', bool(d.get('refresh_token')))"
66
+ ```
67
+
68
+ ### Known limitations
69
+
70
+ **Page load: ✅ confirmed working.** Google's sign-in page (`accounts.google.com/signin`) renders correctly in Playwright — the "Email or phone" field is present and accessible.
71
+
72
+ The remaining risk is at the credential-entry step. Google may apply bot detection after password entry:
73
+
74
+ | Symptom | Cause | Resolution |
75
+ |---|---|---|
76
+ | "This browser or app may not be secure" | Google flagged the automated browser post-login | Switch to Approach B (manual) then save refresh token |
77
+ | CAPTCHA or phone verification prompt | Account triggered fraud detection | Use a test-only Google account with 2FA disabled and a trusted browser profile |
78
+ | Redirect loop or 400 error | `redirect_uri` mismatch | Ensure `credentials.json` has `http://localhost` in authorized redirect URIs |
79
+
80
+ If Playwright is blocked after email/password entry, fall back to a one-time manual browser flow (paste the URL into Chrome) and then use Approach B for all future runs.
81
+
82
+ ---
83
+
84
+ ## Approach B — Refresh token injection (CI / headless)
85
+
86
+ Run the browser flow **once** (manually or via Playwright), then store the refresh token as an environment secret. All future runs reconstruct `token.json` without any browser.
87
+
88
+ ### Step 1 — Extract the refresh token (one time)
89
+
90
+ After a successful OAuth flow:
91
+
92
+ ```bash
93
+ python3 -c "import json; d=json.load(open('token.json')); print(d['refresh_token'])"
94
+ ```
95
+
96
+ Store the output as:
97
+ - `GOOGLE_OAUTH_REFRESH_TOKEN` (secret)
98
+ - `GOOGLE_OAUTH_CLIENT_ID` (from `credentials.json → installed.client_id`)
99
+ - `GOOGLE_OAUTH_CLIENT_SECRET` (from `credentials.json → installed.client_secret`)
100
+
101
+ ### Step 2 — Reconstruct token.json on each run
102
+
103
+ ```bash
104
+ uv run python scripts/oauth_setup.py --from-refresh-token
105
+ ```
106
+
107
+ This writes a minimal `token.json` with the refresh token. The MCP server exchanges it for a fresh access token on first use (same as a normal expired-token refresh — no browser needed).
108
+
109
+ ### In CI (GitHub Actions example)
110
+
111
+ ```yaml
112
+ - name: Restore OAuth token
113
+ env:
114
+ GOOGLE_OAUTH_REFRESH_TOKEN: ${{ secrets.GOOGLE_OAUTH_REFRESH_TOKEN }}
115
+ GOOGLE_OAUTH_CLIENT_ID: ${{ secrets.GOOGLE_OAUTH_CLIENT_ID }}
116
+ GOOGLE_OAUTH_CLIENT_SECRET: ${{ secrets.GOOGLE_OAUTH_CLIENT_SECRET }}
117
+ run: uv run python scripts/oauth_setup.py --from-refresh-token
118
+ ```
119
+
120
+ ---
121
+
122
+ ## Which approach to use
123
+
124
+ | Scenario | Approach |
125
+ |---|---|
126
+ | First-ever token acquisition on a new machine | A (Playwright or manual) |
127
+ | QA run after token expiry — Playwright available | A |
128
+ | QA run after token expiry — no browser possible | B (if refresh token stored) |
129
+ | CI pipeline | B |
130
+ | Playwright blocked by Google | Manual browser + B for future |
@@ -27,7 +27,7 @@ Run each suite using the conductor prompt in `docs/qa/run.md`. Check off when th
27
27
  - [ ] **Sheets Management** — `tests/sheets_mgmt.md` (TC-S01–TC-S24)
28
28
  - [ ] **Sheets Charts** — `tests/sheets_charts.md` (TC-C01–TC-C12)
29
29
  - [ ] **Drive + Docs** — `tests/drive.md` (TC-D01–TC-D166)
30
- - [ ] **Docs Tools** — `tests/docs.md` (TC-D152–TC-D226)
30
+ - [ ] **Docs Tools** — `tests/docs.md` (TC-DOC01–TC-DOC79)
31
31
  - [ ] **Calendar** — `tests/calendar.md` (TC-CAL01–TC-CAL43)
32
32
 
33
33
  ---
@@ -118,6 +118,17 @@ Add the returned IDs to `.env` as `TEST_CALENDAR_ID` and `TEST_EVENT_ID`.
118
118
 
119
119
  ---
120
120
 
121
+ ## OAuth token setup (for `⚠️ requires-oauth` tests)
122
+
123
+ Tests tagged `⚠️ requires-oauth` need a valid OAuth token (`token.json`). If you're running with a service account or ADC, these tests will be skipped or will fail — they require personal Drive access.
124
+
125
+ To acquire or refresh a token without manually clicking through Google's consent screen, see **[`docs/qa/playwright_oauth.md`](playwright_oauth.md)**. Two paths are documented:
126
+
127
+ - **Playwright-assisted**: run `scripts/oauth_setup.py`, let Claude Code + Playwright MCP complete the browser flow
128
+ - **Refresh token injection**: one-time manual flow, then store the refresh token as an env secret for all future runs (CI-friendly)
129
+
130
+ ---
131
+
121
132
  ## Resetting fixtures
122
133
 
123
134
  Re-run the Step 3 seed prompt against your existing IDs to restore known state. No need to create new files.