Commit graph

3,473 commits

Author SHA1 Message Date
strawgate
2e1178fa84 fix: forward resource_base_url and issuer_url through KeycloakOAuthProxy
🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 22:05:29 -05:00
strawgate
f19df2dce7 fix: forward redirect_path through KeycloakOAuthProxy for custom callbacks
🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 12:11:59 -05:00
strawgate
9402d37e1b test+docs: transparent-refresh sentinel coverage and KeycloakOAuthProxy docs
🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 00:26:17 -05:00
strawgate
bfd38fc45a feat: forward valid_scopes through KeycloakOAuthProxy for offline_access DCR
🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 23:49:06 -05:00
strawgate
853097a256 refactor: replace flag with _upstream_refresh_token_never_expires override
Replaces the _zero_refresh_expiry_means_never_expires class attribute with
a proper override hook. The base class stays free of any provider-specific
knowledge; KeycloakOAuthProxy overrides the method and returns True for val==0.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-13 15:54:40 -05:00
strawgate
97649771d1 feat: add KeycloakOAuthProxy with offline token support
Keycloak returns refresh_expires_in=0 for offline_access tokens to signal
'this refresh token never expires'. Base OAuthProxy has no way to know this
is intentional rather than a malformed response, so it falls through to the
standard 1-year wall-clock fallback — which causes the FastMCP refresh token
TTL to shrink on every subsequent refresh cycle until it reaches ~0 after
one year, forcing re-authentication even though the Keycloak offline token
is still valid.

This commit:
- Adds KeycloakOAuthProxy(OAuthProxy) to providers/keycloak.py with a
  convenience __init__ that derives OIDC endpoints from realm_url
- Adds _zero_refresh_expiry_means_never_expires: bool = False class attr
  on OAuthProxy; KeycloakOAuthProxy sets it to True
- Adds refresh_token_never_expires: bool = False to UpstreamTokenSet so
  the intent is visible in stored state
- When the flag is set and val==0: marks the token as never-expiring and
  clears refresh_token_expires_at so subsequent refresh cycles always get
  a fresh full fallback-TTL FastMCP RT instead of a decaying one
- Base OAuthProxy is completely unchanged for val==0: falls through to
  the existing 1-year wall-clock fallback as before

Tests:
- test_refresh_expires_in_zero_issues_refresh_token: KeycloakOAuthProxy
  correctly issues a refresh token and marks upstream as never-expiring
- test_refresh_expires_in_zero_subsequent_refresh_does_not_shrink: TTL
  stays at ~1 year after repeated refresh cycles
- test_base_proxy_does_not_treat_zero_as_never_expires: confirms base
  OAuthProxy behaviour is unaffected

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-13 15:30:15 -05:00
strawgate
08baf0763b fix: clear stale refresh_token_expires_at for Keycloak offline tokens on subsequent refreshes
When Keycloak returns refresh_expires_in=0 (offline token, never expires) on
every token response, the previous code only guarded the initial exchange path.
On subsequent exchange_refresh_token and transparent refresh cycles, the code
would fall through to 'keep existing expiry' — inheriting the wall-clock
timestamp set at initial exchange.  After ~1 year that decayed to ~0 seconds,
issuing FastMCP RTs with 1-second TTL and forcing re-auth even though the
Keycloak offline token was still valid.

Fix: add elif val == 0 to both refresh paths that clears refresh_token_expires_at
to None.  The fallback branch then issues a fresh full fallback-TTL FastMCP RT
on every cycle, matching the 'always-valid' semantics of offline tokens.

Also improves the debug log message to distinguish 'never expires' from
'expiry not provided' so operators can see exactly what Keycloak sent.

Adds a regression test: test_refresh_expires_in_zero_subsequent_refresh_does_not_shrink

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-13 12:38:50 -05:00
strawgate
68534df4f3 Fix: remove keycloak_never_expires flag, let val<=0 fallthrough to default
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-12 22:59:24 -05:00
strawgate
07e3808e2b fix: skip fallback expiry when Keycloak sends refresh_expires_in=0
The initial auth-code exchange path had a bug where val==0 kept
refresh_expires_in as None, causing the fallback to be applied
despite the Keycloak never-expires sentinel. Now uses the same
keycloak_never_expires flag pattern as the other two code paths.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-12 22:39:35 -05:00
strawgate
5ef06f2306 fix: treat Keycloak refresh_expires_in=0 as never-expires sentinel 2026-05-12 22:39:00 -05:00
Jeremiah Lowin
ee48a0fd6e
Refine fastmcp-slim packaging (#4125) v3.3.0 v3.3.0b2
* Refine fastmcp-slim packaging

* Format install hints
2026-05-12 07:11:57 -07:00
Jeremiah Lowin
bb4894d215
Add fastmcp-slim for client-only installs (#4122) v3.3.0b1
* Add fastmcp-client workspace package

* Fix client package static checks

* Document client-only package

* Harden fastmcp-client package split

* Preserve forwarded headers in full package

* Switch to fastmcp-slim package

* Fix fastmcp-slim release edges

* Match pydantic-style slim layout

* Polish fastmcp-slim packaging
2026-05-11 17:13:21 -04:00
Sarthak Bhardwaj
8209093871
fix(http): terminate active streamable-HTTP transports before lifespan shutdown (#4118) 2026-05-10 10:58:13 -04:00
Kiran Thakkar
cf59a4511f
Fix OCI Provider issue in 3.x version. Add OCI auth provider example … (#4116)
* Fix OCI Provider issue in 3.x version. Add OCI auth provider example and test

* Fix OCI Provider issue in 3.x version. Add OCI auth provider example and test. Fixed a couple of minor issues in README.

* Rerun CI
2026-05-10 10:08:00 -04:00
Craig Ringer
89b99ecfb9
fix(proxy): fall back to live identifier for backend_* span attributes (#4109)
🤖 Generated with Claude Code (Opus 4.7) on behalf of @ringerc

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 10:07:57 -04:00
itaru2622
310314cf14
fix: cli option --no-banner is NOT passed to cli but server-spec in-correctly when cli --reload option is specified. (#4083) 2026-05-09 14:57:42 -04:00
sergeykad
28722f846a
fix: drop exc_info for expected tool failures, remove unreachable ValidationError (#4029) 2026-05-09 14:56:17 -04:00
Owen Taylor
567b832bca
fix: Don't completely hide plain mcp.tool app-only tools (#4112) 2026-05-09 14:55:20 -04:00
Shaik Mohammed Kaif
6b6db33c4a
#4084 [Issues] Windows startup crash due to UnicodeDecodeError when l… (#4092)
Co-authored-by: Jeremiah Lowin <153965+jlowin@users.noreply.github.com>
2026-05-07 15:30:56 -04:00
Jeremiah Lowin
3d476ca01e
fix: don't cache import map in dev apps bundle (#4106) 2026-05-07 11:46:13 -04:00
Sarthak Bhardwaj
aaff9243a3
fix(auth): silence authlib.jose DeprecationWarning at JWT import (#4100)
Co-authored-by: Jeremiah Lowin <153965+jlowin@users.noreply.github.com>
2026-05-07 11:27:08 -04:00
Sarthak Bhardwaj
b8597f941d
fix(tool_transform): hoist $defs to schema root when ArgTransform introduces them (#4101) 2026-05-07 11:10:52 -04:00
Jeremiah Lowin
4719f3055a
Make Docket reentrant; mounted servers enter their own lifespan (#4095) 2026-05-04 17:36:50 -04:00
Taylor Wilsdon
61e56c683e
enh: Add public API for updating OAuthProxy scopes after initialization (#4091) 2026-05-04 15:49:12 -04:00
marvin-context-protocol[bot]
074189265d
chore: Update SDK documentation (#3988)
Co-authored-by: marvin-context-protocol[bot] <225465937+marvin-context-protocol[bot]@users.noreply.github.com>
2026-05-04 15:34:27 -04:00
Chris Guidry
a037bd8cc7
Bump pydocket to 0.20.0 (#4031)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 15:34:06 -04:00
cuyua9
0b59af9489
Fix sampling from background tasks (#4068)
Co-authored-by: Jeremiah Lowin <153965+jlowin@users.noreply.github.com>
2026-05-04 12:52:35 -04:00
Ashwin Madavan
44d3f8d858
fix(ping): exit ping loop cleanly when session stream is closed (#4087) 2026-05-04 12:50:39 -04:00
Mukunda Rao Katta
20359de953
Fix #4056: keep blank query values, add token bucket regression test (#4069)
Co-authored-by: Jeremiah Lowin <153965+jlowin@users.noreply.github.com>
2026-05-04 12:44:38 -04:00
Mukunda Rao Katta
1cfd30840d
fix(openapi): keep blank values in parse_qs (refs #4056) (#4076)
Co-authored-by: Jeremiah Lowin <153965+jlowin@users.noreply.github.com>
2026-05-04 12:44:06 -04:00
Daniel Tsiang
73b7f2e44d
Add log_level parameter to FastMCP errors (#4036)
Co-authored-by: Jeremiah Lowin <153965+jlowin@users.noreply.github.com>
2026-05-04 12:38:16 -04:00
Jeremiah Lowin
51e339d8ed
docs: fix broken links in Pydantic AI guide (#4094) 2026-05-04 12:35:35 -04:00
chaoliang yan
2ffe68cfa1
fix: preserve tool decorator metadata (#4072)
Generated with Codex.

Co-authored-by: lawrence3699 <lawrence3699@users.noreply.github.com>
2026-05-04 12:22:31 -04:00
Mukunda Rao Katta
f5dea2a17c
docs(integrations): add Pydantic AI FastMCP toolset guide (#4070)
* docs(integrations): add Pydantic AI FastMCP toolset guide

* docs(integrations): add Pydantic AI to AI SDKs nav
2026-05-04 12:22:17 -04:00
Jeremiah Lowin
d0315974fa
Add UTM tracking to Horizon links (#4064) 2026-04-26 08:38:05 -04:00
Jeremiah Lowin
b96ce24e35
Refresh landing page copy (#4047) 2026-04-25 22:06:30 -04:00
Jeremiah Lowin
6f3ea0b929
Refresh landing page copy (#4043) 2026-04-25 14:46:48 -04:00
Jeremiah Lowin
a010927ea5
Add experimental_capabilities kwarg to FastMCP constructor (#4042)
Co-authored-by: Claude <noreply@anthropic.com>
2026-04-25 12:24:54 -04:00
Jeremiah Lowin
0fe01372f0
Partition ResponseCachingMiddleware cache by access token (#4041)
Co-authored-by: Claude <noreply@anthropic.com>
2026-04-25 12:09:11 -04:00
Mukunda Rao Katta
c740b6d70a
docs: add best practices for custom telemetry spans (#4001) 2026-04-25 11:43:31 -04:00
Bill Easton
4be46c6fa6
Add a sandboxed-agents deployment guide (#4027)
* Add sandboxed agents deployment guide

* Tighten sandboxed agents guide

* Refine sandboxed agents docs flow

---------

Co-authored-by: Jeremiah Lowin <153965+jlowin@users.noreply.github.com>
2026-04-25 11:40:56 -04:00
Zach Leventer
07c34acb12
Add missing return type annotation to __getattr__ (#4026) 2026-04-25 11:35:27 -04:00
Adam Azzam
e95efce988
Add UTM params to Horizon docs links (#4018)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 16:47:55 -04:00
Jeremiah Lowin
74efa32edf
Add run_in_thread opt-out for sync tools with thread affinity (#4010)
Co-authored-by: Claude <noreply@anthropic.com>
2026-04-22 10:31:44 -04:00
Jeremiah Lowin
485747353e
fix: cancel orphaned session_task when Client._disconnect times out (#4011)
Co-authored-by: Claude <noreply@anthropic.com>
2026-04-22 10:17:00 -04:00
Jeremiah Lowin
008f91d84b
fix: narrow _token_validator with isinstance for ty in AzureProvider.from_b2c (#4007)
Co-authored-by: Claude <noreply@anthropic.com>
2026-04-22 09:30:37 -04:00
Carlos Rian
eebdc8c031
feat: add AzureB2CProvider for Azure AD B2C user flows (#3995) 2026-04-22 09:24:09 -04:00
Jeremiah Lowin
2d6143c6d8
fix: honor upstream refresh token expiry in OAuthProxy (#3990) 2026-04-20 14:03:57 -04:00
dependabot[bot]
db6bc3e94c
chore(deps-dev): bump pydantic-monty from 0.0.14 to 0.0.16 (#3984)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jeremiah Lowin <153965+jlowin@users.noreply.github.com>
2026-04-20 13:26:23 -04:00
marvin-context-protocol[bot]
34f6e68826
chore: Update SDK documentation (#3969)
Co-authored-by: marvin-context-protocol[bot] <225465937+marvin-context-protocol[bot]@users.noreply.github.com>
2026-04-20 13:00:37 -04:00