PrefectHQ/fastmcp · error · RuntimeError
No access token available. Cannot perform OBO exchange.
Error message
No access token available. Cannot perform OBO exchange.
What it means
EntraOBOToken's __aenter__ exchanges the current request's access token for a downstream token via OBO. It reads the token from FastMCP's request-scoped context via get_access_token(); if no token is in context (None), there is nothing to exchange, so it raises RuntimeError.
Source
Thrown at fastmcp_slim/fastmcp/server/auth/providers/azure.py:851
"""Dependency that performs OBO token exchange for Microsoft Entra.
Uses azure.identity's OnBehalfOfCredential for async-native OBO,
with automatic token caching and refresh. Credentials are cached on
the AzureProvider so repeated tool calls reuse existing credentials
and benefit from the Azure SDK's internal token cache.
"""
def __init__(self, scopes: list[str]):
self.scopes = scopes
async def __aenter__(self) -> str:
_require_azure_identity("EntraOBOToken")
from fastmcp.server.dependencies import get_access_token, get_server
access_token = get_access_token()
if access_token is None:
raise RuntimeError(
"No access token available. Cannot perform OBO exchange."
)
server = get_server()
azure_provider = _find_azure_provider(server.auth)
if azure_provider is None:
raise RuntimeError(
"EntraOBOToken requires an AzureProvider as the auth provider. "
f"Current provider: {type(server.auth).__name__}"
)
credential = await azure_provider.get_obo_credential(
user_assertion=access_token.token,
)
result = await credential.get_token(*self.scopes)
return result.token
View on GitHub (pinned to 1f02114297)
Solutions
- Only use EntraOBOToken inside request-scoped code (tool/resource handlers) where an authenticated access token exists.
- Ensure the server's auth provider is configured and the client is actually sending a valid token so get_access_token() returns a token.
- For non-request contexts, obtain the user assertion explicitly and call provider.get_obo_credential(user_assertion=...) instead of relying on context.
Example fix
// before
@app.on_event("startup")
async def warm():
async with EntraOBOToken(scopes=["api"]) as t: ... # no request context
// after
@.tool
async def my_tool():
async with EntraOBOToken(scopes=["api"]) as t: ... # inside request context Defensive patterns
Strategy: try-catch
Validate before calling
from fastmcp.server.dependencies import get_access_token
def assert_request_token_available() -> bool:
return get_access_token() is not None Type guard
def has_access_token() -> bool:
from fastmcp.server.dependencies import get_access_token
t = get_access_token()
return t is not None and bool(t.token) Try / catch
try:
async with EntraOBOToken(scopes=["api"]) as t:
...
except RuntimeError as e:
if "No access token" in str(e):
raise RuntimeError("EntraOBOToken used outside an authenticated request context") from e
raise Prevention
- Only enter EntraOBOToken inside tool/resource handlers, never at startup or in background jobs
- Require auth middleware so every request carries a token
- Add a unit test asserting tools fail fast without a token context
When it happens
Trigger: Using `async with EntraOBOToken(...) as t:` in code that runs outside an authenticated MCP request — e.g. at server startup, in a background task, in a tool without auth, or when the auth middleware did not populate the context.
Common situations: Calling OBO from a non-request context (startup/shutdown hooks, scheduled jobs); running a tool while the server has no auth provider wired for the incoming request; testing tools outside the FastMCP request lifecycle.
Related errors
- Azure AD B2C does not support the On-Behalf-Of (OBO) flow. U
- OBO token exchange requires either a client_secret or a subc
- EntraOBOToken requires an AzureProvider as the auth provider
- AzureProvider requires at least one non-OIDC scope in requir
- tenant_name should be the short name without the .onmicrosof
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/d8fdb82228c5a1c6.
Report an issue: GitHub.