Background
The long-running sample src/hello_claude_token_refresh.py defines an AnthropicIdentity(Anthropic) subclass that overrides the �uth_token property as a workaround for the SDK only accepting a static �uth_token: str. The plain Anthropic client's captured Entra ID token would otherwise start failing with 401 after ~1 hour, which is why the shim exists.
What changed upstream
Anthropic Python SDK v0.98.0 (May 2026) added a public credentials= constructor parameter on Anthropic / AsyncAnthropic that takes an AccessTokenProvider (a callable returning an AccessToken(token, expires_at)). The SDK wraps it in an internal TokenCache that:
- Calls the provider lazily (no token fetched at client construction).
- Caches the token and reuses it across requests.
- On a
401 response, invalidates the cache and retries the request once with a freshly minted token (_should_retry override in _client.py).
This is exactly the capability the AnthropicIdentity shim was emulating. auth_token itself is still a static str (so anthropics/anthropic-sdk-python#1496 remains open for the narrower "make auth_token accept a Callable" ask), but the more general credentials= path solves the long-running use case cleanly.
Verified against src/anthropic/lib/credentials/_types.py and src/anthropic/_client.py at tag v0.109.1.
Proposed change
- Bump
anthropic>=0.109.1 in requirements.txt.
- Rewrite src/hello_claude_token_refresh.py to:
- Delete the
AnthropicIdentity(Anthropic) subclass.
- Build a tiny
AccessTokenProvider from DefaultAzureCredential that returns AccessToken(token=t.token, expires_at=t.expires_on) (expires_on is unix seconds, same format the SDK expects).
- Pass it to
Anthropic(credentials=provider, base_url=...).
- Update the long-running code snippet in the README's collapsed SDK call shape section to match. The one-shot
hello_claude.py and its snippet are unchanged.
Out of scope
- No change to
src/hello_claude.py (the short-lived script path keeps using auth_token=token — still the simplest pattern for one-shot runs).
- No change to
src/check_claude_quota.py, src/hello_claude_apikey.py, or the verifier scripts.
- No change to RBAC, infra, or hooks.
Background
The long-running sample src/hello_claude_token_refresh.py defines an AnthropicIdentity(Anthropic) subclass that overrides the �uth_token property as a workaround for the SDK only accepting a static �uth_token: str. The plain Anthropic client's captured Entra ID token would otherwise start failing with 401 after ~1 hour, which is why the shim exists.
What changed upstream
Anthropic Python SDK v0.98.0 (May 2026) added a public
credentials=constructor parameter onAnthropic/AsyncAnthropicthat takes anAccessTokenProvider(a callable returning anAccessToken(token, expires_at)). The SDK wraps it in an internalTokenCachethat:401response, invalidates the cache and retries the request once with a freshly minted token (_should_retryoverride in_client.py).This is exactly the capability the
AnthropicIdentityshim was emulating.auth_tokenitself is still a staticstr(so anthropics/anthropic-sdk-python#1496 remains open for the narrower "makeauth_tokenaccept a Callable" ask), but the more generalcredentials=path solves the long-running use case cleanly.Verified against
src/anthropic/lib/credentials/_types.pyandsrc/anthropic/_client.pyat tagv0.109.1.Proposed change
anthropic>=0.109.1in requirements.txt.AnthropicIdentity(Anthropic)subclass.AccessTokenProviderfromDefaultAzureCredentialthat returnsAccessToken(token=t.token, expires_at=t.expires_on)(expires_onis unix seconds, same format the SDK expects).Anthropic(credentials=provider, base_url=...).hello_claude.pyand its snippet are unchanged.Out of scope
src/hello_claude.py(the short-lived script path keeps usingauth_token=token— still the simplest pattern for one-shot runs).src/check_claude_quota.py,src/hello_claude_apikey.py, or the verifier scripts.