镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

[v1.x] Fix refreshed access tokens not reaching tool handlers - #3794

Closed
Ptraaah wants to merge 3 commits into
modelcontextprotocol:v1.xfrom
Ptraaah:fix-1250-refreshed-token-v1x
Closed

Ptraaah wants to merge 3 commits into
modelcontextprotocol:v1.xfrom
Ptraaah:fix-1250-refreshed-token-v1x

Conversation

@Ptraaah

@Ptraaah Ptraaah commented Oct 31, 2026 •

Copy link
Copy Markdown

Fixes #1250

The bug. On the v1 line, a client that refreshes its access token mid-session keeps sending the new token and the token verifier accepts it, but tools calling get_access_token() still receive the token the session was opened with. I reproduced it on this branch (Streamable HTTP with a plain TokenVerifier): initialize and call a tool presenting token A, then present token B in the same session - the tool still sees A. For what it's worth, the same scenario already works on main (v2), where the dispatcher propagates each message's sender context into the handler; this PR is for v1.x, which is what the reports in #1250 are running (1.16 / 1.19).

Root cause. AuthContextMiddleware sets auth_context_var on each HTTP request's task, but handlers run in the session's long-lived task, whose context still holds the session-opening request's user. The per-request data needed is already being carried: both HTTP transports attach the current Starlette request to the message metadata, and its scope holds the user authenticated for that request.

The change (+17 lines in src/mcp/server/lowlevel/server.py). In _handle_request - the same place that already re-applies the per-request request_ctx from the message metadata - apply auth_context_var from the attached request's scope for the duration of the handler, and reset it in the finally next to the request_ctx reset. No public API changes. Transports that attach no request (stdio) are untouched, and because the value is applied per request at dispatch, parallel sessions cannot observe each other's tokens. This is the layer #1998 aimed at (thanks @bgaidioz, the repro repo helped pin this down); that draft has been sitting untouched since February and predates the current shape of this function, so this is a fresh version of it with fuller tests.

Tests.

  • tests/issues/test_1250_refreshed_access_token.py - end-to-end over Streamable HTTP, in process: a refreshed token is what the tool sees in the same session, and two parallel sessions, held in flight at the same time on a barrier so they genuinely overlap, each see only their own current token.
  • tests/server/lowlevel/test_auth_context_from_request.py - dispatch level: the scope user is applied for the handler and reset afterwards; no user in scope, a non-AuthenticatedUser scope user, an attached object without a scope, and no metadata at all each leave the context empty.
  • Without the fix, 3 of these fail (both end-to-end tests and the scope-user unit test); with it, all pass, and every line/branch added in server.py is covered. Locally tests/server/auth, tests/server/lowlevel, tests/server/fastmcp (318 tests) and tests/issues pass; the handful of failures/errors in the socket-based transport tests reproduce identically on the pristine branch in my sandbox, so they look environmental rather than related. ruff format/ruff check are clean and pyright reports no errors on the changed file.

@github-actions github-actions Bot added the missing-issue-link Auto-closed: PR needs a linked issue assigned to its author (see CONTRIBUTING.md) label Oct 31, 2026
@github-actions

Copy link
Copy Markdown
Contributor

This PR has been closed automatically. This repo only keeps pull requests open when they come from a maintainer, or from a contributor a maintainer has assigned to the linked issue, and you aren't currently assigned to #1250.

If a maintainer assigns you to #1250, this PR reopens on its own and there's nothing more you need to do here. Assignment is a maintainer call based on capacity; comments that only ask to be assigned don't factor in. What does help is engaging on the issue itself by confirming the repro, explaining why it matters for your use case, or describing the approach you'd take.

You're welcome to keep pushing commits here (just avoid force-pushing, since GitHub can't reopen a rewritten branch), but that on its own won't get the PR reviewed or the issue assigned, and realistically most auto-closed PRs stay closed. There's no need to open a new PR either way.

CONTRIBUTING.md has the full reasoning, but in short:

  • We're a small team with very little capacity to review community PRs right now.
  • Many recent PRs are AI-generated with little human review, and reviewing one carefully still costs a maintainer as much time as it ever did. A well-described issue is usually more useful to us than the code.

Maintainers: reopen, remove missing-issue-link, or add bypass-issue-check to override.

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

Labels

missing-issue-link Auto-closed: PR needs a linked issue assigned to its author (see CONTRIBUTING.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant