Commit graph

676 commits

Author SHA1 Message Date
Bill Easton
eec52b1f02
fix: materialize generators before result conversion, handle bytes gracefully (#3830)
- Detect async/sync generators after tool execution and materialize
  into lists before the result conversion pipeline processes them
- Generator materialization runs inside timeout scope so slow generators
  respect the configured timeout
- Handle bytes return types: UTF-8 bytes as text, non-UTF-8 as base64
- Suppress output_schema for bytes return types (can't be structured JSON)
- Catch UnicodeDecodeError alongside PydanticSerializationError in
  convert_result for robustness

Fixes #3829

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 11:23:51 -04:00
Chris Guidry
db9e2685fb
Bump pydocket>=0.19.0, drop fakeredis pin (#3822)
pydocket 0.19.0 fixes the fakeredis 2.35.0 FakeConnection rename
internally, so we no longer need to carry the fakeredis ceiling
ourselves. Removes the direct fakeredis[lua]<2.35.0 dependency from the
tasks extra entirely — it's just a transitive of pydocket now.

🤖 Generated with Claude Code

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 13:41:18 -04:00
Jeremiah Lowin
faf5f86e09
Version-check is_docket_available() to avoid transitive pydocket crash (#3807) 2026-04-09 21:52:21 -04:00
Jeremiah Lowin
6592aaa70e
fix: accept both client_id and identifier_uri as Azure audience (#3797) 2026-04-08 20:25:42 -04:00
Jeremiah Lowin
f5be772403
fix: bump ty to >=0.0.29 and suppress new false positives (#3790) 2026-04-07 19:49:09 -04:00
Jeremiah Lowin
2b9d3eee0f
fix: use identifier_uri as audience for Azure token validation (#3787)
* fix: use identifier_uri as audience for Azure token validation

* fix test expectations for identifier_uri audience
2026-04-07 18:37:52 -04:00
Jeremiah Lowin
e1ea133fb7
fix: Cognito token verification checks client_id instead of aud (#3786)
* fix: Cognito token verification checks client_id instead of aud

* fix: Cognito token verification checks client_id instead of aud

* handle list audience values in Cognito client_id check
2026-04-07 18:09:43 -04:00
Fatia Kusuma Dewi
5587cb7c43
changeable allowed_client_redirect_uris (#3772) 2026-04-06 20:58:09 -04:00
Kakarlamudi Venkata Dhanush
c642d6a5cf
fix: propagate upstream_claims in load_access_token (#3750) 2026-04-06 19:55:26 -04:00
Kakarlamudi Venkata Dhanush
07e950ec09
fix: FastAPI TestClient compatibility and lifespan re-initialization (#3736) 2026-04-06 19:53:27 -04:00
Rishav Mitra
99eaeb8af4
fix: substitute server variable defaults when building base URL from OpenAPI spec (#3770)
* fix: resolve OpenAPI 3.x server variables in _create_default_client

When an OpenAPI spec defines server variables (e.g. `https://{region}.api.example.com/v1`),
the default values are now substituted before constructing the httpx client base URL.
Previously, the URL was used as-is, causing all requests to fail for specs that use
server variable templating.

Fixes #1681

* fix: use str.replace instead of format_map for server variable substitution

format_map applies Python string formatting rules, so variable names
like {api.version} would be treated as attribute access and raise errors.
Literal token replacement handles all valid OpenAPI variable names safely.
2026-04-06 19:48:30 -04:00
Jeremiah Lowin
4bbc4eec3b
fix: ResponseLimitingMiddleware no longer breaks outputSchema tools (#3756) 2026-04-05 10:36:38 -04:00
Chris Guidry
d41bcb2c9e
Unify background task context forwarding, fix concurrent dependency bugs (#3710)
* Unify background task context forwarding and fix concurrent dependency bugs

We've been getting a steady trickle of edge-case reports around background tasks
and contextual dependencies over the last few months (#3654, #3656, #3569). Each
one pointed at a different symptom, but they all traced back to the same area:
the way context is negotiated between the "frontend" server and Docket workers
was grown piecemeal, with each new piece of context (access tokens, HTTP headers,
origin request IDs) getting its own Redis key, its own restore function, and its
own ContextVar. This made it hard to reason about what state was available where,
and the shared-instance Dependency pattern made concurrent tasks stomp on each
other's cleanup state.

This takes a step back and reworks the whole thing as a single unified system:

- Dependency subclasses (_CurrentContext, Progress, _CurrentAccessToken, etc.)
  are now stateless factories — __aenter__ returns a fresh per-invocation
  object, so concurrent tasks never share mutable state. Fixes #3654, #3656.

- The three individual context-snapshot Redis keys (access_token, http_headers,
  origin_request_id) are collapsed into a single TaskContextSnapshot stored as
  one JSON key per task. The three _restore_task_* functions and two ContextVars
  they populated are gone.

- Sync functions like get_http_request() and get_access_token() now find the
  snapshot transparently in background tasks via a 3-tier sync fallback:
  ContextVar (set by _CurrentContext for functions with deps) → in-memory dict
  (same-process workers) → sync Redis GET (out-of-process workers). No function
  wrapping needed.

- The _wrap_for_task_http_headers hack is deleted. FunctionTool registers its
  raw function with Docket so Docket sees and resolves ALL dependencies,
  including Docket-native ones like Retry and Timeout.

- ProxyTool.from_mcp_tool() now propagates execution.taskSupport metadata from
  remote tools. Fixes #3569.

- Removed redundant _current_docket/_current_worker ContextVar management from
  Context.__aenter__/__aexit__ (they're only set in the lifespan now).

Closes #3654
Closes #3656
Closes #3569

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Address code review feedback

- _OptionalCurrentContext: guard __aexit__ against cleaning up contexts it
  didn't create (check is_background_task before delegating)
- Narrow except clauses in snapshot loading (OSError, JSONDecodeError, etc.
  instead of bare Exception)
- Fix docstrings on register_with_docket for resources/prompts/templates
- Simplify Progress: read ExecutionProgress directly from current_execution
  instead of creating and manually entering a DocketProgress wrapper

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Use pop-on-access transfer buffer instead of bounded LRU cache for snapshots

The in-memory snapshot dict is a transfer mechanism, not a cache. Entries go
in at submission and come out at the worker's first access. Using pop instead
of get means the dict only holds entries during the brief submission-to-execution
window, bounded by task concurrency (~10) rather than a 10,000-entry LRU limit.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Drop in-memory transfer buffer, use sync Redis for all backends

Instead of maintaining an in-memory dict to bridge the async/sync gap, use
a sync Redis client directly. For memory:// backends (fakeredis), shares the
same FakeServer instance via docket._redis.get_memory_server() so data written
by the async Docket client is visible to sync reads. For real Redis, creates a
standard sync connection. No in-process state to manage at all.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Move snapshot operations to TaskContextSnapshot methods

capture(), from_json(), to_json(), save() are now classmethod/instance methods
on the dataclass instead of free functions. Deduplicates JSON parsing that was
copy-pasted between the async and sync load paths.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Trim implementation details from register_with_docket docstrings

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Clarify docket lookup comment in submit_to_docket

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Restore docket/worker ContextVar bridge in Context.__aenter__

Servers that own the Docket (the parent) re-set _current_docket/_current_worker
from their instance attributes when entering a Context. Mounted children skip
this (their _docket is None), so they inherit the parent's value. This is needed
for ASGI deployments where ContextVars set during the lifespan don't propagate
to request handlers.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Key snapshot cache by task_id to prevent cross-task context leakage

Docket workers may reuse the same asyncio context for sequential tasks.
The ContextVar cache now stores (task_id, snapshot) tuples so stale entries
from previous tasks are automatically ignored.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 10:48:27 -04:00
AIKAWA Shigechika
5be249ada3
fix(google): use sub (user ID) for client_id instead of aud (app ID) (#3722) 2026-03-31 10:33:58 -04:00
Mostafa Kamal
57a7f121d4
Add Clerk OAuth provider (#3677) 2026-03-29 11:01:22 -04:00
Jeremiah Lowin
8c10bb8881
fix: remove stale ty:ignore directives for ty 0.0.26 (#3684) 2026-03-28 20:02:40 -04:00
Miguel Miranda Dias
5879119de5
fix: HTTP request headers not accessible in background task workers (#3631)
Co-authored-by: Jeremiah Lowin <153965+jlowin@users.noreply.github.com>
2026-03-27 21:48:33 -04:00
Jeremiah Lowin
b9ea53618d
fix: resolve CurrentFastMCP/ctx.fastmcp to child server in mounted background tasks (#3651) 2026-03-27 10:24:17 -04:00
Miguel Miranda Dias
59a126a0b6
fix(server): preserve mounted tool task metadata (#3632)
* fix(server): preserve mounted tool task metadata

* fix(server): move task execution metadata to base tool

* cleanup: remove stale import, tighten execution metadata guard

---------

Co-authored-by: Jeremiah Lowin <153965+jlowin@users.noreply.github.com>
2026-03-27 09:15:33 -04:00
Jeremy Simon
492db9972f
fix: resolve EntraOBOToken dependency injection through MultiAuth (#3609)
Co-authored-by: Jeremiah Lowin <153965+jlowin@users.noreply.github.com>
2026-03-25 10:58:19 -04:00
AIKAWA Shigechika
c3f0223bbb
fix(google): replace deprecated /oauth2/v1/tokeninfo with /oauth2/v3/userinfo (#3603)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Jeremiah Lowin <153965+jlowin@users.noreply.github.com>
2026-03-25 10:39:34 -04:00
Jeremiah Lowin
c397e68d39
Update ty ignore comments for 0.0.25 compatibility (#3614) 2026-03-24 20:26:26 -04:00
Rushabh Doshi
204e566227
Fix loopback redirect URI port matching per RFC 8252 §7.3 (#3589)
🤖 Generated with Claude Code

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 19:35:02 -04:00
Jeremiah Lowin
52feff6878
Transparently refresh upstream token in OAuthProxy.load_access_token() (#3584)
* Transparently refresh upstream token in OAuthProxy.load_access_token()

When upstream token validation fails during load_access_token, attempt
to refresh using the stored refresh token before returning None. This
prevents premature 401s that force clients into expensive full re-auth
flows when the upstream token expires.

Co-authored-by: Claude <noreply@anthropic.com>

* Gate transparent refresh on token expiry, add advisory lock

Only attempt upstream refresh when the token is actually expired, not
on any validation failure (scope mismatch, revocation, etc.). Add
per-token advisory lock to prevent concurrent async tasks from racing
to refresh the same upstream token.

* Re-check expiry inside lock, reload from storage after refresh failure

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-22 10:43:44 -04:00
Bill Easton
196e2b48c5
fix: increase sleep duration in proxy cache tests (#3567) 2026-03-21 11:38:58 -04:00
Jeremiah Lowin
269c9c9f46
Extract TokenCache utility, add caching to GitHubTokenVerifier (#3547)
* feat: add TokenCache utility and caching to GitHubTokenVerifier

Extract the caching machinery from IntrospectionTokenVerifier into a
shared TokenCache class in fastmcp.utilities.token_cache, then wire
it into both IntrospectionTokenVerifier and GitHubTokenVerifier.

* Remove dead constant, validate negative cache params

* Fix overwrite eviction bug, skip cache on scope lookup failure
2026-03-18 15:26:09 -04:00
Jeremiah Lowin
360c9c31e5
fix: resolve Pyright "Module is not callable" on @tool, @resource, @prompt decorators (#3540)
* pin pydantic-monty to 0.0.8

* rename tool/prompt/resource base modules to avoid decorator name shadow

* add sys.modules shims for old submodule import paths

* preserve original module paths in deprecation warnings

* clarify when sys.modules shims can be removed
2026-03-17 18:11:42 -04:00
Jeremiah Lowin
f9ed06176a
fix: route ResourcesAsTools/PromptsAsTools through server middleware (#3495)
* fix: enforce auth/visibility in ResourcesAsTools and PromptsAsTools for non-FastMCP providers

🤖 Co-authored-by: Claude <noreply@anthropic.com>

* fix: honor stdio auth bypass and correct transform ordering in provider wrappers

Co-authored-by: Claude <noreply@anthropic.com>

* fix: move context/dependencies imports into function to break circular import

* fix: route ResourcesAsTools/PromptsAsTools through ctx.fastmcp

Instead of manually reimplementing auth, visibility, and session
transforms in the transform layer, tool functions now call
ctx.fastmcp.read_resource() / ctx.fastmcp.render_prompt() which
routes through the server's full middleware chain. This matches
the pattern CodeMode uses with ctx.fastmcp.call_tool().

The isinstance(provider, FastMCP) branching is removed entirely.

* feat: add _scope parameter for provider-scoped listing

AggregateProvider can now filter which child providers to query when
listing components. ResourcesAsTools and PromptsAsTools use this to
scope listings to their configured provider while still routing
through ctx.fastmcp for full middleware coverage.

The scope matching walks wrapped providers, so a
WrappedProvider(Namespace, inner=MyProvider) matches if MyProvider
is in the scope list.

* test: add coverage for ResourcesAsTools scoped to a sub-server

* fix: delegate to super() when _scope is None, add AggregateProvider to scope matching

* simplify: remove _scope machinery, route everything through ctx.fastmcp

Reverts the _scope parameter from Provider/AggregateProvider/Server.
ResourcesAsTools and PromptsAsTools now simply route through
ctx.fastmcp for all operations. Apply to a FastMCP server instance
for proper auth/visibility/middleware coverage.

Tests rewritten to use FastMCP server directly instead of raw providers.

* warn when ResourcesAsTools/PromptsAsTools is applied to a non-FastMCP provider

* docs: explain that ResourcesAsTools/PromptsAsTools should wrap a FastMCP server

* raise TypeError instead of warning when applied to non-FastMCP provider

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-15 16:35:54 -04:00
Jeremiah Lowin
bb37c3bb6b
fix: reject refresh tokens used as Bearer access tokens (#3524) 2026-03-15 15:21:30 -04:00
Jeremiah Lowin
8fdb3cc27c
fix: CSRF double-submit cookie check in consent flow (#3519)
* Upgrade examples/testing_demo lockfile, drops diskcache (CVE-2025-69872)

* fix: add CSRF double-submit cookie check to consent flow (GHSA-rww4-4w9c-7733)

* fix: preserve CSRF state across concurrent flows, fix test isolation

* fix: reject non-__Host consent-state cookie on HTTPS
2026-03-15 14:22:01 -04:00
Jeremiah Lowin
32dfe50f39
Treat refresh_expires_in=0 as missing, fall back to 30-day default (#3514)
Keycloak returns refresh_expires_in=0 for offline tokens (offline_access scope),
meaning "no fixed time-based expiry". The truthiness check on this value caused
the proxy to skip issuing a PROXY_RT, forcing browser re-auth every hour.

Closes #3509

🤖 Generated with Claude Code

Co-authored-by: Marvin Context Protocol <41898282+Marvin Context Protocol@users.noreply.github.com>
Co-authored-by: Jeremiah Lowin <jlowin@users.noreply.github.com>
2026-03-15 11:49:50 -04:00
Jeremiah Lowin
ea529f6a49
feat: make upstream_client_secret optional in OAuthProxy (#3486)
* feat: make upstream_client_secret optional in OAuthProxy

Extract _create_upstream_oauth_client() factory method for subclass
override. Cookie signing falls back to JWT key material when no secret.

* fix: include client_id in revocation requests for public clients

* fix: use factory method for revocation auth
2026-03-15 11:14:56 -04:00
Jeremiah Lowin
9be42d9013
perf: reduce PBKDF2 iterations in tests, fix warnings and timeouts (#3504)
- Use 10 PBKDF2 iterations in test_mode (vs 1M in production) for
  JWT key derivation — cuts auth test setup from ~2.5s to <0.1s
- Add timeout(15) to subprocess-spawning tests (TestKeepAlive,
  test_mcp_config) that exceed 5s under parallel CI load
- Remove pytestmark filterwarnings overrides in tests/deprecated/
  that were leaking DeprecationWarning to test output
- Fix deprecated add_tool_transformation() usage in test_authorization
- Document new settings in settings.mdx
2026-03-14 16:42:06 -04:00
Jeremiah Lowin
71ba030380
fix: reject empty/OIDC-only required_scopes in AzureProvider (#3503)
🤖 Generated with Claude Code
2026-03-14 16:35:43 -04:00
Jeremiah Lowin
ca8069cb86
Enforce per-tool auth checks in SamplingTool.from_callable_tool wrapper (#3494)
Co-authored-by: Claude <noreply@anthropic.com>
2026-03-14 16:14:50 -04:00
Jeremiah Lowin
32f1118a11
Expose minimum_check_interval, reduce task pickup latency (#3500)
* perf: expose minimum_check_interval, reduce task pickup latency

The Docket Worker polls for new tasks every minimum_check_interval
(previously hardcoded to 250ms in pydocket). Expose this setting so
users can tune it, default to 50ms, and override to 10ms in tests.

This cuts average task pickup latency from ~125ms to ~5ms per task.

* perf: reduce task test overhead and eliminate cross-test contamination

- Expose minimum_check_interval setting (default 50ms, 10ms in tests)
  to reduce Docket Worker task pickup latency
- Isolate fakeredis per test via unique memory:// URLs to prevent
  stale _async_blocking tasks from contaminating subsequent tests
- Make client disconnect timeout configurable (default 5s, 1s in tests)
- Add --durations=50 to CI for passive performance regression detection
- Remove 15s timeout band-aids from task test conftest files
- Add explicit @pytest.mark.timeout(10) to cancellation tests
- Fix deprecated FastMCP.as_proxy() usage in test_task_proxy.py
2026-03-14 16:08:28 -04:00
Jeremiah Lowin
139d2d8f96
Propagate x-fastmcp-wrap-result in tool result _meta (#3490)
* Propagate x-fastmcp-wrap-result flag in tool result _meta

🤖 Generated with Claude Code

Co-authored-by: Claude <noreply@anthropic.com>

* Skip listTools round-trip when _meta has x-fastmcp-wrap-result

🤖 Generated with Claude Code

Co-authored-by: Claude <noreply@anthropic.com>

* Use namespaced meta key: {"fastmcp": {"wrap_result": true}}

🤖 Generated with Claude Code

Co-authored-by: Claude <noreply@anthropic.com>

* Clean up _parse_call_tool_result: hoist cast import, document local CallToolResult import, extract fastmcp_meta

🤖 Generated with Claude Code

Co-authored-by: Claude <noreply@anthropic.com>

* Merge _meta in tasks result handler instead of overwriting

🤖 Generated with Claude Code

* Preserve type validation in meta-based unwrap path

🤖 Generated with Claude Code

* Fix type validation for wrapped task results, guard non-dict meta

🤖 Generated with Claude Code

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-14 11:09:46 -04:00
d 🔹
68e76fea2e
fix: forward custom_route endpoints from mounted servers (#3462)
* fix: forward custom_route endpoints from mounted servers

When a child server with custom HTTP routes (registered via
@server.custom_route()) is mounted onto a parent, the routes were
silently dropped because _get_additional_http_routes() only returned
self._additional_http_routes without recursing into mounted providers.

This caused 404s for endpoints like /readyz health checks that worked
in v2 but broke in v3 (regression).

The fix updates _get_additional_http_routes() to traverse providers,
unwrap _WrappedProvider layers (from namespace transforms), find
FastMCPProvider instances, and recursively collect their server's
custom routes.

Fixes #3457

* fix: narrow type annotation from BaseRoute to Route

All items in _additional_http_routes are Route objects (created via
Route(...) in custom_route()). Using list[Route] instead of
list[BaseRoute] fixes the ty type checker failure where .path is
accessed on BaseRoute which doesn't have that attribute.

Removes unused BaseRoute imports from both server.py and transport.py.

* fix: revert route type to list[BaseRoute] to fix ty errors

The previous commit narrowed _additional_http_routes from list[BaseRoute]
to list[Route], which broke:
- component_manager appending Mount objects (Mount is BaseRoute, not Route)
- tests assigning list[BaseRoute] variables (generics are invariant)

Revert to list[BaseRoute] and use isinstance(r, Route) guards in tests
for type-safe .path access.

* fix: remove unused import and fix import grouping

- Remove unused `Route` import from server.py
- Fix import grouping in test_advanced.py (ruff check)

* Address review: move imports to module root, type Provider, add collision note

* fix: sort imports in transport.py

---------

Co-authored-by: voidborne-d <voidborne-d@users.noreply.github.com>
Co-authored-by: d 🔹 <258577966+voidborne-d@users.noreply.github.com>
Co-authored-by: Jeremiah Lowin <153965+jlowin@users.noreply.github.com>
2026-03-13 20:37:22 -04:00
Jeremiah Lowin
24d7aefe28
fix: shield lifespan teardown from cancellation (#3480)
* fix: shield lifespan teardown from cancellation

Co-authored-by: Claude <noreply@anthropic.com>

* fix: stabilize flaky task and timeout tests under parallel execution

Co-authored-by: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-13 19:47:42 -04:00
Jeremiah Lowin
e41e1fec10
fix: resolve ty 0.0.23 type-checking errors and bump pin (#3481) 2026-03-13 19:45:14 -04:00
Jeremiah Lowin
a52036ed0f
Cache component lists in ProxyProvider (#3479)
* fix: cache component lists in ProxyProvider to avoid redundant backend connections

Every call_tool through a proxy was triggering _list_tools() to resolve
the tool by name, opening a full MCP session just for the lookup, then
opening a second session for the actual execution. This caches component
lists on the ProxyProvider with a configurable TTL (default 300s),
cutting backend handshakes in half for repeated calls.

* docs: document component caching and session reuse for proxy providers

* fix: add sleep in cache TTL test for Windows clock resolution

* docs: clarify cache scope and dynamic backend guidance
2026-03-13 19:44:45 -04:00
Jeremiah Lowin
59a6fb3839
fix: normalize Google scope shorthands and surface valid_scopes (#3477)
* fix: normalize Google scope shorthands and surface valid_scopes

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>

* remove unused GOOGLE_SCOPE_ALIASES_REVERSE

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-13 18:42:16 -04:00
Jeremiah Lowin
e1338e06a2 Set readOnlyHint=True on ResourcesAsTools generated tools 2026-03-13 17:32:21 -05:00
d 🔹
33c3acfc87
fix: use intent-based flag for OIDC scope patch in load_access_token (#3465)
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>
2026-03-13 17:59:03 -04:00
Jeremiah Lowin
5ed14650ab
Block insecure HS* JWT verification with JWKS/public keys (#3430)
* 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
2026-03-07 12:20:48 -05:00
Jeremiah Lowin
bafd5419fa
Redact sensitive headers in OpenAPI provider debug logging (#3436)
* Redact sensitive headers in OpenAPI provider debug logging (#3427)

* Use safe-header allowlist instead of sensitive-header denylist for redaction
2026-03-07 12:10:05 -05:00
Jeremiah Lowin
ceb8ff18c6
Fix $ref output schema object detection regression (#3420)
* Fix  output schema object detection

🤖 Generated with GPT-5.2-Codex

* Fix ty invalid-type-alias-type error

Co-authored-by: Jeremiah Lowin <jlowin@users.noreply.github.com>
🤖 Generated with Claude Code

* Fix $ref resolution to handle JSON Pointer escaping and nested paths

---------

Co-authored-by: Marvin Context Protocol <41898282+Marvin Context Protocol@users.noreply.github.com>
Co-authored-by: Jeremiah Lowin <jlowin@users.noreply.github.com>
2026-03-07 12:09:56 -05:00
Jeremiah Lowin
6637bcebe1
Fix server lifespan overlap teardown (#3415)
* Fix server lifespan overlap teardown

🤖 Generated with GPT-5.2-Codex

* Clear lifespan state when non-owner session is last to exit
2026-03-07 12:09:50 -05:00
Jeremiah Lowin
9f8347dbaf
Fix get_* returning None when latest version is disabled (#3439)
* 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.
2026-03-07 11:55:10 -05:00
Jeremiah Lowin
901d8cdd60
Block HS* algorithms when JWTVerifier is configured with JWKS (#3419)
* Block HS* algorithms with JWKS in JWT verifier

🤖 Generated with GPT-5.2-Codex

* Fix ruff format: remove extra blank line in test_supabase.py

🤖 Generated with Claude Code

Co-authored-by: Jeremiah Lowin <jlowin@users.noreply.github.com>

---------

Co-authored-by: Marvin Context Protocol <41898282+Marvin Context Protocol@users.noreply.github.com>
Co-authored-by: Jeremiah Lowin <jlowin@users.noreply.github.com>
2026-03-07 11:41:04 -05:00