Skip to content

feat(oauth): make proactive token refresh window configurable (--oauth-refresh-leeway)#57

Merged
shigechika merged 2 commits into
mainfrom
feat/oauth-refresh-leeway
May 7, 2026
Merged

feat(oauth): make proactive token refresh window configurable (--oauth-refresh-leeway)#57
shigechika merged 2 commits into
mainfrom
feat/oauth-refresh-leeway

Conversation

@shigechika
Copy link
Copy Markdown
Owner

Fixes #56

Summary

  • New flag --oauth-refresh-leeway SECONDS (default 60, env var MCP_OAUTH_REFRESH_LEEWAY)
  • Replaces the hard-coded 60 s leeway in ensure_token() cache-validity check
  • Negative values rejected at parse time
  • Default behaviour preserved — all 360 existing tests pass

Why

mcp-stdio already does proactive token refresh — ensure_token() treats a
cached access token as expired when its real expiry is within 60 s. This is
fine for most ASes, but a real-world deployment (mcp-remote#252)
issues refresh tokens that outlive the access token by only ~1 s. Under those
conditions, even mcp-stdio's 60 s window is too generous: the refresh races
the AS revocation. Make the window tunable.

A related upstream bug is typescript-sdk#1954
adaptOAuthProvider.token() returns expired tokens with no leeway check at
all. mcp-stdio is structurally immune to that side; this PR is purely about
exposing configurability of the leeway we already have.

Scope

  • src/mcp_stdio/oauth.pyensure_token() gains refresh_leeway: float = 60.0
  • src/mcp_stdio/cli.py — new flag + env var + _non_negative_float argparse type
  • relay.py (the 401-reactive refresh path) — untouched, leeway is proactive only

Test plan

  • pytest tests/ -v — 367 tests pass (+7 new)
    • 3 new in TestEnsureToken: leeway=0 / leeway=300 / default leeway=60
    • 4 new in TestMain: default / flag / env var / negative-rejection
  • mcp-stdio --help shows the new flag
  • mcp-stdio --oauth-refresh-leeway -1 ... exits with argparse error

🤖 Generated with Claude Code

shigechika and others added 2 commits May 7, 2026 11:08
The 60 s leeway in ensure_token() was hard-coded; tune via the new
--oauth-refresh-leeway SECONDS flag (or MCP_OAUTH_REFRESH_LEEWAY env
var). Default 60 s preserves existing behaviour. Negative values are
rejected at parse time.

Use cases (cf. mcp-remote#252):

- ASes whose refresh token lifetime is barely longer than the access
  token's — the default 60 s margin can race expiry; raise leeway for
  clock-skew tolerance.
- Deployments with extremely short access tokens — drop leeway towards
  0 to keep cache hits useful.

Cross-linked in WORKAROUNDS.md alongside typescript-sdk#1954, which
documents the upstream side of the bug (no leeway check at all).

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
- Replace ineffective patch.dict('os.environ', {}, clear=False) with
  monkeypatch.delenv for proper teardown
- Pass env var value as string to argparse so invalid values surface as
  argparse errors instead of a raw Python ValueError on startup
- Add test for invalid env var value (covers the new error path)

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
@shigechika shigechika merged commit 10737ca into main May 7, 2026
6 checks passed
@shigechika shigechika deleted the feat/oauth-refresh-leeway branch May 7, 2026 11:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(oauth): make proactive token refresh window configurable (--oauth-refresh-leeway)

1 participant