Skip to content

fix(email): add OAuth XOAUTH2 support to MCP email server#5472

Open
rudra496 wants to merge 3 commits into
odysseus-dev:devfrom
rudra496:fix/4992-gmail-oauth-agent-tool
Open

fix(email): add OAuth XOAUTH2 support to MCP email server#5472
rudra496 wants to merge 3 commits into
odysseus-dev:devfrom
rudra496:fix/4992-gmail-oauth-agent-tool

Conversation

@rudra496

Copy link
Copy Markdown
Contributor

Summary

The agent's MCP email tools couldn't access Gmail Workspace accounts set up via OAuth. The UI routes (routes/email_helpers.py) handle OAuth correctly, but the MCP email server (mcp_servers/email_server.py) completely ignored the OAuth layer:

  1. _read_accounts_from_db() — the SQL query didn't select the oauth_provider, oauth_access_token, oauth_refresh_token, or oauth_token_expiry columns, so OAuth data was never loaded.
  2. _imap_connect() — always used conn.login(user, password) with no XOAUTH2 path.
  3. _smtp_connect() — same: always conn.login(user, password), no XOAUTH2.
  4. _smtp_ready() — required smtp_password, rejecting OAuth accounts that don't have one.

Fix (all in mcp_servers/email_server.py):

  • Added OAuth columns to the SQL SELECT with column-existence guards (same pattern as the existing owner and smtp_security handling for older DBs).
  • Load oauth_provider, oauth_access_token, oauth_refresh_token, oauth_token_expiry into cfg in _load_config() (decrypted via src.secret_storage).
  • _imap_connect(): when oauth_provider == "google", use conn.authenticate("XOAUTH2", ...) with the token from _get_valid_google_token() — reusing the same refresh logic as the UI routes.
  • _smtp_connect(): same XOAUTH2 path via conn.auth("XOAUTH2", ...).
  • _smtp_ready(): OAuth accounts only need smtp_host + smtp_user (no password required).

The XOAUTH2 helpers (_get_valid_google_token, _xoauth2_bytes, _xoauth2_raw) are imported from routes.email_helpers so the MCP server and UI share the same token-refresh and auth logic — no duplication.

Target branch

  • This PR targets dev, not main.

Linked Issue

Fixes #4992

Type of Change

  • Bug fix (non-breaking — fixes a confirmed issue)
  • New feature (non-breaking — adds new behaviour)
  • Breaking change (changes or removes existing behaviour)
  • Refactor / cleanup (behaviour unchanged)
  • Documentation only
  • CI / tooling / configuration

Checklist

  • I searched open issues and open PRs — this is not a duplicate.
  • This PR targets dev
  • My changes are limited to the scope described above — no unrelated refactors or whitespace changes mixed in.
  • I ran python -m pytest tests/test_email_oauth.py tests/test_email_smtp_security.py tests/test_email_send_only_no_inbox.py tests/test_email_imap_timeout.py -v — all 39 tests pass.

How to Test

  1. python -m pytest tests/test_email_oauth.py -v — 28 OAuth tests pass, including test_smtp_ready_true_for_oauth_account_without_password, test_imap_connect_uses_xoauth2_for_oauth_accounts, and test_get_valid_google_token_refreshes_when_expired.
  2. python -m pytest tests/test_email_smtp_security.py tests/test_email_send_only_no_inbox.py tests/test_email_imap_timeout.py -v — 11 connection/security tests pass.
  3. python -m py_compile mcp_servers/email_server.py — syntax check passes.
  4. Manual: connect a Gmail Workspace account via OAuth (Settings → Integrations), then ask the agent to list emails — the MCP email tool now connects via XOAUTH2 instead of failing with an auth error.

Visual / UI changes — REQUIRED if you touched anything that renders

Not applicable: backend MCP email server only.

  • I am not an LLM agent submitting a bulk PR. If you are, please open an issue describing the problem first — bulk auto-generated PRs that don't match the project's visual style are closed on sight, even when the underlying fix is correct.

Copilot AI review requested due to automatic review settings July 12, 2026 03:16

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions github-actions Bot added the ready for review Description complete — ready for maintainer review label Jul 12, 2026
@alain-sarti

Copy link
Copy Markdown

Thanks for jumping on this — I filed #4992 and had started on the same fix, so great to see it moving. The diagnosis matches what I found, and the IMAP/SMTP XOAUTH2 branches and _smtp_ready change all look right.

Two things I'd suggest folding in before merge:

1. Refreshed tokens don't get back into the cached config.
_load_config() caches each account's cfg in _ACCOUNT_CACHE for the life of the (long-lived) stdio process. _get_valid_google_token() decides whether to refresh by reading cfg["oauth_token_expiry"], and on refresh it persists the new token to the DB but never updates the passed-in cfg — so _load_config() keeps handing back the stale cached copy. Net effect: the first call works, but once that access token expires (~1h), every subsequent IMAP/SMTP connect sees "expired" and re-hits oauth2.googleapis.com/token. Correct, but it hammers the token endpoint per-operation and is fragile under Google's rate limits. Simplest fix: write the new token + expiry back into cfg in place after a refresh.

2. The changed MCP code isn't covered by any test.
The suites referenced here import from routes.email_helpers / routes.email_routes only (e.g. test_smtp_ready_* do from routes.email_routes import _smtp_ready, not the MCP server's). So none of the actually-changed branches in mcp_servers/email_server.py are exercised. A small test importing mcp_servers.email_server directly — _load_config loading oauth_*, _smtp_ready for OAuth-without-password, and the XOAUTH2 paths in _imap_connect/_smtp_connect — would lock it in.

I have a branch with the cache handling and these MCP tests already written — happy to share the patch or open them as a follow-up, whichever's easier. Thanks again for driving this. 🙏

@rudra496

Copy link
Copy Markdown
Contributor Author

Thanks for the thorough review, Alain — both points are spot on.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready for review Description complete — ready for maintainer review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Gmail Workspace with OAuth not accessible by agent

4 participants