You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Framework does not handle refreshed tokens correctly #1250
I use python sdk with Keycloak as external authz provider. I allowed my mcp client (in my test scenario it is latest VSC) to get AT and RT (using offline_access scope). I have implemented a TokenVerifier that is doing validation of the MCP keycloak JWT. And then in some MCP tool, i use get_access_token() from the framework to use the JWT. I log both values and i can clearly see that it works fine initially, but after the token was refreshed on client-side, i still get the old accesst token from get_access_token() function. Not sure if you intended to support token refresh in a clean way. But IMHO that is a bug that should be fixed soon.
I ran into a similar situation when I tried to implement token refresh on our MCP server (after adding load_refresh_token and exchange_refresh_token to our OAuthAuthorizationServerProvider). I could see the new token being passed to auth_context_var.reset(token) in auth_context.py, but get_access_token was still returning the old token when the client issued a new call.
I’m mentioning this because it seems to confirm that the framework isn’t propagating the refreshed token consistently, which may be the root of what’s being reported here.
I don’t have a minimal code snippet handy, but I’d be happy to try and put one together when I’m back working on this part of the code.
Reacted by Felix Weinberger, Conrad Frame and Grayson Honan
I ran into a similar situation when I tried to implement token refresh on our MCP server (after adding load_refresh_token and exchange_refresh_token to our OAuthAuthorizationServerProvider). I could see the new token being passed to auth_context_var.reset(token) in auth_context.py, but get_access_token was still returning the old token when the client issued a new call.
I’m mentioning this because it seems to confirm that the framework isn’t propagating the refreshed token consistently, which may be the root of what’s being reported here.
I don’t have a minimal code snippet handy, but I’d be happy to try and put one together when I’m back working on this part of the code.
I ran into a similar situation when I tried to implement token refresh on our MCP server (after adding load_refresh_token and exchange_refresh_token to our OAuthAuthorizationServerProvider). I could see the new token being passed to auth_context_var.reset(token) in auth_context.py, but get_access_token was still returning the old token when the client issued a new call.
I’m mentioning this because it seems to confirm that the framework isn’t propagating the refreshed token consistently, which may be the root of what’s being reported here.
I don’t have a minimal code snippet handy, but I’d be happy to try and put one together when I’m back working on this part of the code.
I ran into a similar situation when I tried to implement token refresh on our MCP server (after adding load_refresh_token and exchange_refresh_token to our OAuthAuthorizationServerProvider). I could see the new token being passed to auth_context_var.reset(token) in auth_context.py, but get_access_token was still returning the old token when the client issued a new call.
I’m mentioning this because it seems to confirm that the framework isn’t propagating the refreshed token consistently, which may be the root of what’s being reported here.
I don’t have a minimal code snippet handy, but I’d be happy to try and put one together when I’m back working on this part of the code.
Hi, do we have any updates on this bug? We also face the same bug where get_access_token() returns the old token. Weirdly, the TokenVerifier will receive the new token, but it won't be accessible during processing the MCP request unless the session is refreshed.
This GitHub issue was referenced in FastMCP because it describes the same problem (PrefectHQ/fastmcp#1863). It’s also related to the implementation that uses a context variable. The fix is here: PrefectHQ/fastmcp#2505
I added a regression test that fails as one would expect without the patch (the old token is seen on the server side). CI failed on coverage for the new branch in _handle_request. I did some efforts for that, added a small, low‑level unit test to cover it, but it’s fairly close to internals and I’m not sure this is the preferred approach.
This isn't a direct port of the patch to fastmcp. Setting the context var felt reasonable to me.
That fixes the demo repro and our own MCP server when running against keycloak.
This may be addressed as part of the broader auth rework in #1743. Keeping as a standalone issue for now in case it can be fixed independently before the rework lands.
I dug into this and can confirm the bug on the v1 line. Small repro against a Streamable HTTP server with a plain token verifier: initialize and call a tool presenting token A, then present token B in the same session (same client, like after a refresh) - get_access_token() inside the tool still returns token A.
One thing worth flagging: on current main (v2) the same scenario already returns the refreshed token, because the new dispatcher propagates each message's sender context into the handler. So this is live on v1.x, which matches the reports here (1.16, 1.19).
Where I would fix it on v1.x: AuthContextMiddleware sets auth_context_var per HTTP request, but handlers run in the session's long-lived task, so they keep seeing the context of whichever request opened the session. _handle_request in the lowlevel server already re-applies the per-request request_ctx from the message metadata for that exact reason, and the HTTP transports attach the current request to that metadata - its scope carries this request's authenticated user. So I would apply auth_context_var from that scope for the duration of the handler and reset it afterwards, same pattern, no public API change. Both HTTP transports attach the request, so both get it; stdio has no request attached and stays untouched. And since the value is applied per request at dispatch, parallel sessions cannot see each other's tokens.
I know #1998 took a similar route a while back (thanks @bgaidioz, the repro repo helped pin this down) but it has been sitting in draft, so I will put up a fresh PR against v1.x with regression tests, unless you would rather fold this into the #1743 rework instead.
Quick follow-up on this: I opened PR #3794 with the fix, but it was closed automatically within a couple of minutes because I am not assigned to this issue. The change itself is ready and tested locally (new regression tests fail without the fix and pass with it, details are in the PR). Could a maintainer assign me to this issue so I can reopen it? Happy to adjust the approach first if you would rather see it done differently.
Activity
felixweinberger commentedon Oct 3, 2025
Hi @pematth could you provide a code snippet that demonstrates the specific issue you're getting that we can run to debug?
bgaidioz commentedon Oct 3, 2025
I ran into a similar situation when I tried to implement token refresh on our MCP server (after adding
load_refresh_tokenandexchange_refresh_tokento ourOAuthAuthorizationServerProvider). I could see the new token being passed toauth_context_var.reset(token)inauth_context.py, butget_access_tokenwas still returning the old token when the client issued a new call.I’m mentioning this because it seems to confirm that the framework isn’t propagating the refreshed token consistently, which may be the root of what’s being reported here.
I don’t have a minimal code snippet handy, but I’d be happy to try and put one together when I’m back working on this part of the code.
felixweinberger commentedon Oct 3, 2025
That'd be great thank you!
bgaidioz commentedon Oct 6, 2025
I pushed code to https://github.com/raw-labs/python-sdk-issue-1250: a minimal OAuth implementation that issues 30-second tokens.
When used with Claude Code:
get_access_token.felixweinberger commentedon Oct 6, 2025
Thanks for providing the repro!
pematth commentedon Oct 7, 2025
Hi, sorry for not responding, i was on holiday. I guess you are fine with the code provided by Benjamin. Otherwise please let me know.
lucheng-hugh commentedon Oct 28, 2025
Hi, do we have any updates on this bug? We also face the same bug where
get_access_token()returns the old token. Weirdly, the TokenVerifier will receive the new token, but it won't be accessible during processing the MCP request unless the session is refreshed.I can confirm the bug still exists in 1.16.0
4 remaining items
bgaidioz commentedon Dec 3, 2025
This GitHub issue was referenced in FastMCP because it describes the same problem (PrefectHQ/fastmcp#1863). It’s also related to the implementation that uses a context variable. The fix is here: PrefectHQ/fastmcp#2505
Set auth context per request in streamable HTTP
Set auth context per request in streamable HTTP
bgaidioz commentedon Feb 5, 2026
Hello.
Here's a patch that I believe fixes the issue.
I added a regression test that fails as one would expect without the patch (the old token is seen on the server side). CI failed on coverage for the new branch in
_handle_request. I did some efforts for that, added a small, low‑level unit test to cover it, but it’s fairly close to internals and I’m not sure this is the preferred approach.This isn't a direct port of the patch to fastmcp. Setting the context var felt reasonable to me.
That fixes the demo repro and our own MCP server when running against keycloak.
I hope this helps.
Set auth context per request in streamable HTTP
grahonan commentedon Feb 12, 2026
I also ran into this and would love to see it fixed soon.
maxisbey commentedon Feb 19, 2026
This may be addressed as part of the broader auth rework in #1743. Keeping as a standalone issue for now in case it can be fixed independently before the rework lands.
Ptraaah commentedon Oct 31, 2026
I dug into this and can confirm the bug on the v1 line. Small repro against a Streamable HTTP server with a plain token verifier: initialize and call a tool presenting token A, then present token B in the same session (same client, like after a refresh) -
get_access_token()inside the tool still returns token A.One thing worth flagging: on current
main(v2) the same scenario already returns the refreshed token, because the new dispatcher propagates each message's sender context into the handler. So this is live on v1.x, which matches the reports here (1.16, 1.19).Where I would fix it on v1.x:
AuthContextMiddlewaresetsauth_context_varper HTTP request, but handlers run in the session's long-lived task, so they keep seeing the context of whichever request opened the session._handle_requestin the lowlevel server already re-applies the per-requestrequest_ctxfrom the message metadata for that exact reason, and the HTTP transports attach the current request to that metadata - its scope carries this request's authenticated user. So I would applyauth_context_varfrom that scope for the duration of the handler and reset it afterwards, same pattern, no public API change. Both HTTP transports attach the request, so both get it; stdio has no request attached and stays untouched. And since the value is applied per request at dispatch, parallel sessions cannot see each other's tokens.I know #1998 took a similar route a while back (thanks @bgaidioz, the repro repo helped pin this down) but it has been sitting in draft, so I will put up a fresh PR against v1.x with regression tests, unless you would rather fold this into the #1743 rework instead.
Apply the current request's auth context while handling a request (mo…
Add regression tests for refreshed access tokens (modelcontextprotoco…
Add dispatch-level tests for auth context from the request scope (mod…
Ptraaah commentedon Oct 31, 2026
Quick follow-up on this: I opened PR #3794 with the fix, but it was closed automatically within a couple of minutes because I am not assigned to this issue. The change itself is ready and tested locally (new regression tests fail without the fix and pass with it, details are in the PR). Could a maintainer assign me to this issue so I can reopen it? Happy to adjust the approach first if you would rather see it done differently.