feat: add gmail_reauth MCP tools for SSH-compatible token refresh #7
No reviewers
Labels
No labels
domain:backend
domain:devops
domain:frontend
status:approved
status:in-progress
status:needs-fix
status:qa
type:bug
type:devops
type:feature
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
forgejo_admin/gmail-mcp!7
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "6-gmail-reauth-tool"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
gmail_reauth_startgenerates the Google consent URL with all scopes;gmail_reauth_completeaccepts the redirect URL or bare auth code, exchanges for tokens, saves to disk, and clears the client cacheChanges
src/gmail_mcp/tools/reauth.py: new module withgmail_reauth_start,gmail_reauth_complete, and_extract_codehelpersrc/gmail_mcp/tools/__init__.py: registerreauthmodule inregister_all_tools()tests/test_reauth.py: 11 tests covering URL generation, code exchange, cache clearing, bare code handling, and error pathsTest Plan
uv run pytest tests/ -vuv run ruff check src/ tests/gmail_reauth_start, open URL, copy redirect, callgmail_reauth_complete, verifygmail_get_profileworksReview Checklist
Related
plan-pal-e-mail-- Phase 2c sender registry re-auth flowReview: LGTM
Files reviewed: 3 (1 modified, 2 new)
Findings
No issues found. The implementation is clean:
@mcp.tool()decorator,Annotated[..., Field(...)]parameters,_error_responseexception handling,json.dumpsreturnsAuthMixin.get_auth_url(),exchange_code(),_load_credentials(),_save_token()without modifying the SDK_clients.pop(alias, None)ensures subsequent tool calls get a freshGmailClient-- no MCP restart needed_extract_codehandles both full callback URLs and bare auth codes, with clear error on missing code parameter__init__.pyTest results
PR #7 Review
DOMAIN REVIEW
Tech stack: Python / MCP server (FastMCP) / gmail-sdk / pytest. Domain checklist: Python/PEP compliance, OWASP awareness, test coverage.
Code quality: The implementation is clean and follows the established patterns in this codebase well. Both tools use the same
@mcp.tool()decorator,Annotated[..., Field(...)]parameter style,try/except -> _error_responseerror handling, andjson.dumps(..., indent=2)return pattern seen inmessages.py,threads.py, etc. The_extract_codehelper is well-factored with clear docstring and handles both full-URL and bare-code inputs.PEP compliance: Module docstrings (PEP 257),
from __future__ import annotations(PEP 604 union syntax), type hints on all functions (PEP 484). Clean.SDK coupling observation: The code uses
AuthMixin._load_credentials()andAuthMixin._save_token()-- both underscore-prefixed "private" methods on the SDK. However, reviewing the SDK source at/home/ldraney/gmail-sdk/src/gmail_sdk/auth.py, these are@staticmethodmethods with clear signatures and the SDK's ownauthorize()method uses them the same way. Theget_auth_url()andexchange_code()methods are public. Since this MCP server and the SDK share the same owner/maintainer, this coupling is acceptable -- but worth noting that SDK changes to_load_credentialsor_save_tokensignatures would break this tool without a compile-time guard.Input validation: The
callback_urlparameter is validated through_extract_code()which properly raisesValueErrorwhen a URL has nocodeparameter. Bare strings are passed through with.strip(), which is the correct behavior since Google will reject invalid codes at the exchange step. No injection risk -- the code is passed to Google's token endpoint viahttpx.post, not interpolated into SQL or shell commands.Error handling: Both tools catch all exceptions and route through
_error_response(), consistent with the rest of the codebase. The_extract_codeValueError is caught by the outertry/exceptingmail_reauth_complete.Cache invalidation:
_clients.pop(alias, None)correctly clears the cached client so the next tool call gets a freshGmailClientwith the new token. This is the right approach.BLOCKERS
None.
"test-client-id","test-secret").AuthMixin, no duplicated auth logic.NITS
_extract_codeedge case -- URL with scheme but no query: If someone passeshttp://localhost:8090(no query string at all),parsed.schemeis truthy butparsed.queryis empty string (falsy), so the function falls through to the bare-code branch and returns"http://localhost:8090". This would fail at Google's token endpoint anyway, but a more descriptive error could be raised here. Very minor -- Google's error message will surface through_error_response.Unused
aliasingmail_reauth_start: Thealias = resolve_account(account)call validates the account and the alias is used in the response JSON, so this is fine. Just noting it reads slightly likealiasmight be unused at first glance -- the code is correct.Test imports inside
withblocks: The tests importgmail_reauth_start/gmail_reauth_completeinside thewithcontext manager blocks. This works because the modules are already registered at import time, but it is slightly unusual compared to top-of-file imports. This appears to be a pattern choice to ensure the patches are active during import-time side effects, which is valid for this@mcp.tool()decorator pattern.SOP COMPLIANCE
6-gmail-reauth-toolmatches issue #6plan-pal-e-mailplan slugPROCESS OBSERVATIONS
__init__.pyto register the new module (1 line). The registration is alphabetically ordered, consistent with the existing pattern.ruff check,ruff format --check, andpyteston PRs. The 11 new tests will run in CI.VERDICT: APPROVED