Repository navigation
Conversation
|
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:
Maintainers: reopen, remove |
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 plainTokenVerifier): 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 onmain(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.
AuthContextMiddlewaresetsauth_context_varon 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-requestrequest_ctxfrom the message metadata - applyauth_context_varfrom the attached request's scope for the duration of the handler, and reset it in thefinallynext to therequest_ctxreset. 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-AuthenticatedUserscope user, an attached object without a scope, and no metadata at all each leave the context empty.server.pyis covered. Locallytests/server/auth,tests/server/lowlevel,tests/server/fastmcp(318 tests) andtests/issuespass; 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 checkare clean andpyrightreports no errors on the changed file.