Google accepts shorthand scopes like "email" in authorization requests but
returns full URIs like "https://www.googleapis.com/auth/userinfo.email" in
token responses. The verifier now normalizes shorthands at initialization so
the subset check works regardless of which form was used. GoogleProvider also
now exposes valid_scopes for controlling which scopes clients can request
beyond the required minimum.
Co-authored-by: Claude <noreply@anthropic.com>
When OIDCProxy has verify_id_token=True and the IdP issues the same JWT
for both access_token and id_token, the value-equality check
`verification_token != upstream_token_set.access_token` evaluated to
False, skipping the scope patch entirely. This left AccessToken.scopes
empty, causing RequireAuthMiddleware to return 403 insufficient_scope.
Replace the value-equality check with an intent-based virtual method
`_uses_alternate_verification()` that OIDCProxy overrides to return
`self._verify_id_token`. The base OAuthProxy returns False (preserving
existing behavior for non-OIDC providers).
Fixes#3461
Co-authored-by: voidborne-d <voidborne-d@users.noreply.github.com>
* Block HS* JWT verification with public keys/JWKS
🤖 Generated with GPT-5.2-Codex
* Fix ruff format violations
🤖 Generated with Claude Code
* Handle bytes public_key in HS* algorithm PEM check
* Fix get_* returning None when latest version is disabled (#3421)
When a visibility transform disabled the highest version of a component,
get_tool/get_resource/get_resource_template/get_prompt returned None
instead of falling back to the next-highest enabled version. The list_*
path already worked correctly because deduplication runs after visibility
filtering. The get_* path now falls back to listing all versions and
picking the highest enabled one when the top version is disabled.
* Apply auth checks in version fallback paths
The fallback code in get_tool, get_resource, get_resource_template, and
get_prompt bypassed auth filtering when falling back to older versions
after the highest version was disabled. This could expose auth-protected
older versions to unauthorized users.
* Bind Cognito verifier audience to client ID
🤖 Generated with GPT-5.2-Codex
* Fix ty error: narrow return type of AWSCognitoProvider.get_token_verifier
🤖 Generated with Claude Code
* Cap client auto-pagination pages
🤖 Generated with GPT-5.2-Codex
* Raise on pagination limit instead of returning partial data
Add max_pages kwarg (default 250) to list_tools/list_resources/
list_resource_templates/list_prompts so users can control the bound.
Message.content now accepts ImageContent and AudioContent in addition to
TextContent and EmbeddedResource, matching MCP's ContentBlock type. This
fixes ProxyPrompt.render() silently JSON-serializing image/audio content
instead of preserving it.
🤖 Generated with Claude Code
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Drop form-action from the default Content Security Policy on the OAuth
consent page. Chrome enforces form-action across the entire redirect
chain, which breaks flows where an HTTPS callback internally redirects
to a custom scheme (e.g. claude://, cursor://). Since the form posts
to itself and all redirects are server-controlled, form-action adds
no security value here.
Also forward the consent_csp_policy parameter through all concrete
OAuth providers (Auth0, Azure, Google, GitHub, Discord, WorkOS, AWS
Cognito, OCI) so users can override the CSP without accessing private
attributes.
* Replace vendored DI with uncalled-for
FastMCP vendored a minimal DI engine extracted from Docket (~164 lines)
with try/except fallback patterns everywhere. The `uncalled-for` package
is a clean, typed extraction of this same system, and since Docket will
also depend on it (chrisguidry/docket#353), `uncalled_for.Dependency`
becomes the single canonical base class.
This deletes the `_vendor/docket_di/` directory, replaces all the
try/except import patterns with direct `uncalled_for` imports, and
updates the `Dependency.execution` → `current_execution` ContextVar
references to match the Docket branch. The `Progress` class now
delegates to an internal impl and returns `self` from `__aenter__`
(matching Docket's pattern) so that ty's generic resolution works
without `type: ignore` suppressions.
Temporarily points pydocket at the `use-uncalled-for` branch so both
sides can be validated together in CI.
🤖 Generated with Claude Code
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Re-export Dependency from fastmcp.dependencies
Internal code like azure.py should import from the fastmcp namespace
rather than reaching into uncalled_for directly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Import Dependency from fastmcp namespace in tests
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Add generic type parameters to Dependency subclasses
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Mention uncalled-for in DI docs
The DI engine now comes from uncalled-for, so the docs should credit
it alongside Docket. Also updates the Docket docs link to docket.lol.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Point docket dependency at main
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Bump uncalled-for pin to >=0.2.0
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Fix uncalled-for imports for 0.2.0 API changes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Support Shared() dependencies without docket
Enters a SharedContext at server lifetime so that Shared() dependencies
from uncalled-for resolve once and are cached across tool/resource/prompt
calls. When running with docket, the Worker already handles this; this
covers the non-docket path and direct call_tool() usage.
Also re-exports Shared from fastmcp.dependencies.
Closes#3251
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Bump docket lockfile to latest main
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Remove duplicate test classes from rebase conflict resolution
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Point docket dependency at pydocket>=0.18.0 release
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Pair SharedContext __aenter__ with __aexit__ in Context lifecycle
The old `_ensure_shared_context` on the server called `__aenter__()` on a
lazy `SharedContext` but never `__aexit__()`, leaking the exit stack and
its resources. Moved the SharedContext management into Context's own
enter/exit so it's properly paired: when docket is available the lifespan
handles it, otherwise Context creates and cleans up a per-request one.
Updated Shared() tests to use Client (which runs the lifespan) rather
than calling server methods directly, since cross-request sharing
requires a lifespan.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Hoist SharedContext import to module level
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Adds the PropelAuthProvider which delegates to the IntrospectionTokenVerifier
and optionally does an additional resource check.
Adds an example server and client which makes an authenticated request
and gets information from the token.
Updates the documentation (but only for v3 as this isn't in v2).
Increase performance test threshold from 100ms to 1.0s. Windows CI runners
are slower than Linux, and the 100ms threshold was too tight. The test's
intent is to catch obvious regressions (e.g., accidentally re-introducing
code generation), not to precisely benchmark.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add MultiAuth for composing multiple token verification sources
🤖 Generated with Claude Code
https://claude.ai/code/session_01WwKYDCqjM2FqYwY5ZNVvjb
* Fix ruff lint/format in MultiAuth tests
🤖 Generated with Claude Code
https://claude.ai/code/session_01WwKYDCqjM2FqYwY5ZNVvjb
* Fix MultiAuth well-known route delegation and empty scopes handling
🤖 Generated with Claude Code
https://claude.ai/code/session_01WwKYDCqjM2FqYwY5ZNVvjb
* Harden MultiAuth: exception resilience, mcp_path propagation, test coverage
- verify_token now catches exceptions from individual sources and
continues to the next, so one broken verifier can't take down the
whole chain
- set_mcp_path propagates to verifiers, not just the server
- Fix jwks_url→jwks_uri typo in class docstring
- Add tests for raising verifiers, valid-token HTTP acceptance,
and set_mcp_path propagation
* Clean up MultiAuth: precompute sources, deduplicate test helpers
* Fix version badges to 3.1.0
---------
Co-authored-by: Claude <noreply@anthropic.com>
* feat: Add search transforms for tool discovery
RegexSearchTransform and BM25SearchTransform collapse large tool
catalogs into a search interface so LLMs discover tools on demand
instead of receiving the full listing.
* chore: Update SDK documentation
* fix: call_tool recursion guard, atomic BM25 rebuild, hash includes descriptions
* Extract CatalogTransform base class for catalog-aware transforms
Transforms that replace list_tools() with synthetic components (like
search) need to read the real catalog at call time without triggering
their own replacement logic. CatalogTransform handles the re-entrant
bypass via per-instance ContextVar, exposing transform_tools() as the
subclass hook and get_tool_catalog() for catalog access.
* Add search transform examples for regex and BM25
* Add README for search transform examples
* Polish search example clients with rich output
* Remove hardcoded tool counts from search example subtitles
* Clarify that review bot feedback should be evaluated on its merits
* Expand search transform docs with proper hierarchy
---------
Co-authored-by: marvin-context-protocol[bot] <225465937+marvin-context-protocol[bot]@users.noreply.github.com>
* Fix ty 0.0.19 type errors
🤖 Generated with Claude Code
* Fix ruff formatting in sampling/run.py
🤖 Generated with Claude Code
https://claude.ai/code/session_01GWzbyF1vHvVeS4yJ5bhScf
---------
Co-authored-by: Claude <noreply@anthropic.com>
* Fix background Context request correlation
* Make OptionalCurrentContext type-safe
Refactor OptionalCurrentContext to wrap CurrentContext instead of overriding __aenter__ with a wider return type. Adds a background-task origin_request_id round-trip test and applies ruff formatting.
* Fix ty ignore syntax in OpenAPI provider
* Fix flaky rate limiting and ping timing tests
* Assert rate limit error message in flaky test fix
* Catch only ToolError in rate limiting test
* Fix non-serializable state lost between middleware and tools
Inherit _request_state dict from parent Context in __aenter__ so
middleware and tool contexts share the same in-memory state.
Closes#3228
* chore: Update SDK documentation
---------
Co-authored-by: marvin-context-protocol[bot] <225465937+marvin-context-protocol[bot]@users.noreply.github.com>
* Update repository references from jlowin/fastmcp to prefecthq/fastmcp
* Retrigger CI after repo transfer
* chore: Update SDK documentation
* Only run deep triage on bug issues for jlowin
---------
Co-authored-by: marvin-context-protocol[bot] <225465937+marvin-context-protocol[bot]@users.noreply.github.com>
* Add consent binding cookie to prevent confused deputy attacks (GHSA-rww4-4w9c-7733)
The OAuthProxy's consent page verified user intent but didn't bind the
consenting browser to the IdP callback. An attacker could intercept the
upstream authorization URL after consent and send it to a victim, whose
browser would complete the flow without having the consent cookie.
This adds a signed consent binding cookie set during consent approval
(both manual and auto-approve paths) and verified in the IdP callback
handler. A different browser won't have this cookie and gets a 403.
* Use startswith for URL assertion in consent binding test
* Store consent bindings as per-transaction map to support parallel flows
* Only accept __Host- consent binding cookie on HTTPS
* chore: Update SDK documentation
---------
Co-authored-by: marvin-context-protocol[bot] <225465937+marvin-context-protocol[bot]@users.noreply.github.com>
* fix: restore request context in StatefulProxyClient handlers
StatefulProxyClient reuses sessions across requests, so its receive-loop
task inherits a stale request_ctx ContextVar from the first request.
Server-initiated messages (elicitation, sampling, etc.) that depend on
related_request_id routing get sent to a closed stream and hang forever.
Closes#3169
* chore: Update SDK documentation
---------
Co-authored-by: marvin-context-protocol[bot] <225465937+marvin-context-protocol[bot]@users.noreply.github.com>