fastmcp 2.11.0__py3-none-any.whl → 2.11.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fastmcp/server/auth/__init__.py +2 -1
- fastmcp/server/auth/auth.py +122 -41
- fastmcp/server/auth/providers/jwt.py +1 -1
- fastmcp/server/auth/providers/workos.py +27 -46
- fastmcp/server/http.py +32 -73
- fastmcp/utilities/components.py +6 -1
- fastmcp/utilities/openapi.py +3 -7
- fastmcp/utilities/types.py +6 -1
- {fastmcp-2.11.0.dist-info → fastmcp-2.11.1.dist-info}/METADATA +1 -1
- {fastmcp-2.11.0.dist-info → fastmcp-2.11.1.dist-info}/RECORD +13 -13
- {fastmcp-2.11.0.dist-info → fastmcp-2.11.1.dist-info}/WHEEL +0 -0
- {fastmcp-2.11.0.dist-info → fastmcp-2.11.1.dist-info}/entry_points.txt +0 -0
- {fastmcp-2.11.0.dist-info → fastmcp-2.11.1.dist-info}/licenses/LICENSE +0 -0
fastmcp/server/auth/__init__.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from .auth import OAuthProvider, TokenVerifier
|
|
1
|
+
from .auth import OAuthProvider, TokenVerifier, RemoteAuthProvider
|
|
2
2
|
from .providers.jwt import JWTVerifier, StaticTokenVerifier
|
|
3
3
|
|
|
4
4
|
|
|
@@ -7,6 +7,7 @@ __all__ = [
|
|
|
7
7
|
"TokenVerifier",
|
|
8
8
|
"JWTVerifier",
|
|
9
9
|
"StaticTokenVerifier",
|
|
10
|
+
"RemoteAuthProvider",
|
|
10
11
|
]
|
|
11
12
|
|
|
12
13
|
|
fastmcp/server/auth/auth.py
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from typing import TYPE_CHECKING
|
|
4
|
-
|
|
5
3
|
from mcp.server.auth.provider import (
|
|
6
4
|
AccessToken,
|
|
7
5
|
AuthorizationCode,
|
|
@@ -11,6 +9,10 @@ from mcp.server.auth.provider import (
|
|
|
11
9
|
from mcp.server.auth.provider import (
|
|
12
10
|
TokenVerifier as TokenVerifierProtocol,
|
|
13
11
|
)
|
|
12
|
+
from mcp.server.auth.routes import (
|
|
13
|
+
create_auth_routes,
|
|
14
|
+
create_protected_resource_routes,
|
|
15
|
+
)
|
|
14
16
|
from mcp.server.auth.settings import (
|
|
15
17
|
ClientRegistrationOptions,
|
|
16
18
|
RevocationOptions,
|
|
@@ -18,11 +20,8 @@ from mcp.server.auth.settings import (
|
|
|
18
20
|
from pydantic import AnyHttpUrl
|
|
19
21
|
from starlette.routing import Route
|
|
20
22
|
|
|
21
|
-
if TYPE_CHECKING:
|
|
22
|
-
pass
|
|
23
|
-
|
|
24
23
|
|
|
25
|
-
class AuthProvider:
|
|
24
|
+
class AuthProvider(TokenVerifierProtocol):
|
|
26
25
|
"""Base class for all FastMCP authentication providers.
|
|
27
26
|
|
|
28
27
|
This class provides a unified interface for all authentication providers,
|
|
@@ -31,9 +30,18 @@ class AuthProvider:
|
|
|
31
30
|
custom authentication routes.
|
|
32
31
|
"""
|
|
33
32
|
|
|
34
|
-
def __init__(self,
|
|
35
|
-
"""
|
|
36
|
-
|
|
33
|
+
def __init__(self, resource_server_url: AnyHttpUrl | str | None = None):
|
|
34
|
+
"""
|
|
35
|
+
Initialize the auth provider.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
resource_server_url: The URL of this resource server. This is used
|
|
39
|
+
for RFC 8707 resource indicators, including creating the WWW-Authenticate
|
|
40
|
+
header.
|
|
41
|
+
"""
|
|
42
|
+
if isinstance(resource_server_url, str):
|
|
43
|
+
resource_server_url = AnyHttpUrl(resource_server_url)
|
|
44
|
+
self.resource_server_url = resource_server_url
|
|
37
45
|
|
|
38
46
|
async def verify_token(self, token: str) -> AccessToken | None:
|
|
39
47
|
"""Verify a bearer token and return access info if valid.
|
|
@@ -48,22 +56,34 @@ class AuthProvider:
|
|
|
48
56
|
"""
|
|
49
57
|
raise NotImplementedError("Subclasses must implement verify_token")
|
|
50
58
|
|
|
51
|
-
def
|
|
52
|
-
"""
|
|
59
|
+
def get_routes(self) -> list[Route]:
|
|
60
|
+
"""Get the routes for this authentication provider.
|
|
53
61
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
62
|
+
Each provider is responsible for creating whatever routes it needs:
|
|
63
|
+
- TokenVerifier: typically no routes (default implementation)
|
|
64
|
+
- RemoteAuthProvider: protected resource metadata routes
|
|
65
|
+
- OAuthProvider: full OAuth authorization server routes
|
|
66
|
+
- Custom providers: whatever routes they need
|
|
59
67
|
|
|
60
68
|
Returns:
|
|
61
|
-
List of routes
|
|
69
|
+
List of routes for this provider
|
|
62
70
|
"""
|
|
63
|
-
return
|
|
71
|
+
return []
|
|
72
|
+
|
|
73
|
+
def get_resource_metadata_url(self) -> AnyHttpUrl | None:
|
|
74
|
+
"""Get the resource metadata URL for RFC 9728 compliance."""
|
|
75
|
+
if self.resource_server_url is None:
|
|
76
|
+
return None
|
|
64
77
|
|
|
78
|
+
# Add .well-known path for RFC 9728 compliance
|
|
79
|
+
resource_metadata_url = AnyHttpUrl(
|
|
80
|
+
str(self.resource_server_url).rstrip("/")
|
|
81
|
+
+ "/.well-known/oauth-protected-resource"
|
|
82
|
+
)
|
|
83
|
+
return resource_metadata_url
|
|
65
84
|
|
|
66
|
-
|
|
85
|
+
|
|
86
|
+
class TokenVerifier(AuthProvider):
|
|
67
87
|
"""Base class for token verifiers (Resource Servers).
|
|
68
88
|
|
|
69
89
|
This class provides token verification capability without OAuth server functionality.
|
|
@@ -79,26 +99,71 @@ class TokenVerifier(AuthProvider, TokenVerifierProtocol):
|
|
|
79
99
|
Initialize the token verifier.
|
|
80
100
|
|
|
81
101
|
Args:
|
|
82
|
-
resource_server_url: The URL of this resource server
|
|
102
|
+
resource_server_url: The URL of this resource server. This is used
|
|
103
|
+
for RFC 8707 resource indicators, including creating the WWW-Authenticate
|
|
104
|
+
header.
|
|
83
105
|
required_scopes: Scopes that are required for all requests
|
|
84
106
|
"""
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
# Handle our own resource_server_url and required_scopes
|
|
89
|
-
self.resource_server_url: AnyHttpUrl | None
|
|
90
|
-
if resource_server_url is None:
|
|
91
|
-
self.resource_server_url = None
|
|
92
|
-
elif isinstance(resource_server_url, str):
|
|
93
|
-
self.resource_server_url = AnyHttpUrl(resource_server_url)
|
|
94
|
-
else:
|
|
95
|
-
self.resource_server_url = resource_server_url
|
|
107
|
+
super().__init__(resource_server_url=resource_server_url)
|
|
108
|
+
self.required_scopes = required_scopes or []
|
|
96
109
|
|
|
97
110
|
async def verify_token(self, token: str) -> AccessToken | None:
|
|
98
111
|
"""Verify a bearer token and return access info if valid."""
|
|
99
112
|
raise NotImplementedError("Subclasses must implement verify_token")
|
|
100
113
|
|
|
101
114
|
|
|
115
|
+
class RemoteAuthProvider(AuthProvider):
|
|
116
|
+
"""Authentication provider for resource servers that verify tokens from known authorization servers.
|
|
117
|
+
|
|
118
|
+
This provider composes a TokenVerifier with authorization server metadata to create
|
|
119
|
+
standardized OAuth 2.0 Protected Resource endpoints (RFC 9728). Perfect for:
|
|
120
|
+
- JWT verification with known issuers
|
|
121
|
+
- Remote token introspection services
|
|
122
|
+
- Any resource server that knows where its tokens come from
|
|
123
|
+
|
|
124
|
+
Use this when you have token verification logic and want to advertise
|
|
125
|
+
the authorization servers that issue valid tokens.
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
def __init__(
|
|
129
|
+
self,
|
|
130
|
+
token_verifier: TokenVerifier,
|
|
131
|
+
authorization_servers: list[AnyHttpUrl],
|
|
132
|
+
resource_server_url: AnyHttpUrl | str,
|
|
133
|
+
):
|
|
134
|
+
"""Initialize the remote auth provider.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
token_verifier: TokenVerifier instance for token validation
|
|
138
|
+
authorization_servers: List of authorization servers that issue valid tokens
|
|
139
|
+
resource_server_url: URL of this resource server. This is used
|
|
140
|
+
for RFC 8707 resource indicators, including creating the WWW-Authenticate
|
|
141
|
+
header.
|
|
142
|
+
"""
|
|
143
|
+
super().__init__(resource_server_url=resource_server_url)
|
|
144
|
+
self.token_verifier = token_verifier
|
|
145
|
+
self.authorization_servers = authorization_servers
|
|
146
|
+
|
|
147
|
+
async def verify_token(self, token: str) -> AccessToken | None:
|
|
148
|
+
"""Verify token using the configured token verifier."""
|
|
149
|
+
return await self.token_verifier.verify_token(token)
|
|
150
|
+
|
|
151
|
+
def get_routes(self) -> list[Route]:
|
|
152
|
+
"""Get OAuth routes for this provider.
|
|
153
|
+
|
|
154
|
+
By default, returns only the standardized OAuth 2.0 Protected Resource routes.
|
|
155
|
+
Subclasses can override this method to add additional routes by calling
|
|
156
|
+
super().get_routes() and extending the returned list.
|
|
157
|
+
"""
|
|
158
|
+
assert self.resource_server_url is not None
|
|
159
|
+
|
|
160
|
+
return create_protected_resource_routes(
|
|
161
|
+
resource_url=self.resource_server_url,
|
|
162
|
+
authorization_servers=self.authorization_servers,
|
|
163
|
+
scopes_supported=self.token_verifier.required_scopes,
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
|
|
102
167
|
class OAuthProvider(
|
|
103
168
|
AuthProvider,
|
|
104
169
|
OAuthAuthorizationServerProvider[AuthorizationCode, RefreshToken, AccessToken],
|
|
@@ -181,17 +246,33 @@ class OAuthProvider(
|
|
|
181
246
|
"""
|
|
182
247
|
return await self.load_access_token(token)
|
|
183
248
|
|
|
184
|
-
def
|
|
185
|
-
"""
|
|
249
|
+
def get_routes(self) -> list[Route]:
|
|
250
|
+
"""Get OAuth authorization server routes and optional protected resource routes.
|
|
186
251
|
|
|
187
|
-
This method
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
Args:
|
|
192
|
-
routes: List of standard OAuth routes from create_auth_routes
|
|
252
|
+
This method creates the full set of OAuth routes including:
|
|
253
|
+
- Standard OAuth authorization server routes (/.well-known/oauth-authorization-server, /authorize, /token, etc.)
|
|
254
|
+
- Optional protected resource routes if resource_server_url is configured
|
|
193
255
|
|
|
194
256
|
Returns:
|
|
195
|
-
List of routes
|
|
257
|
+
List of OAuth routes
|
|
196
258
|
"""
|
|
197
|
-
|
|
259
|
+
|
|
260
|
+
# Create standard OAuth authorization server routes
|
|
261
|
+
oauth_routes = create_auth_routes(
|
|
262
|
+
provider=self,
|
|
263
|
+
issuer_url=self.issuer_url,
|
|
264
|
+
service_documentation_url=self.service_documentation_url,
|
|
265
|
+
client_registration_options=self.client_registration_options,
|
|
266
|
+
revocation_options=self.revocation_options,
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
# Add protected resource routes if this server is also acting as a resource server
|
|
270
|
+
if self.resource_server_url:
|
|
271
|
+
protected_routes = create_protected_resource_routes(
|
|
272
|
+
resource_url=self.resource_server_url,
|
|
273
|
+
authorization_servers=[self.issuer_url],
|
|
274
|
+
scopes_supported=self.required_scopes,
|
|
275
|
+
)
|
|
276
|
+
oauth_routes.extend(protected_routes)
|
|
277
|
+
|
|
278
|
+
return oauth_routes
|
|
@@ -16,7 +16,7 @@ from pydantic import AnyHttpUrl, SecretStr
|
|
|
16
16
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
17
17
|
from typing_extensions import TypedDict
|
|
18
18
|
|
|
19
|
-
from fastmcp.server.auth
|
|
19
|
+
from fastmcp.server.auth import TokenVerifier
|
|
20
20
|
from fastmcp.server.auth.registry import register_provider
|
|
21
21
|
from fastmcp.utilities.logging import get_logger
|
|
22
22
|
from fastmcp.utilities.types import NotSet, NotSetT
|
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import httpx
|
|
4
|
-
from mcp.server.auth.provider import (
|
|
5
|
-
AccessToken,
|
|
6
|
-
)
|
|
7
4
|
from pydantic import AnyHttpUrl
|
|
8
5
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
9
6
|
from starlette.responses import JSONResponse
|
|
10
|
-
from starlette.routing import
|
|
7
|
+
from starlette.routing import Route
|
|
11
8
|
|
|
12
|
-
from fastmcp.server.auth
|
|
9
|
+
from fastmcp.server.auth import RemoteAuthProvider, TokenVerifier
|
|
13
10
|
from fastmcp.server.auth.providers.jwt import JWTVerifier
|
|
14
11
|
from fastmcp.server.auth.registry import register_provider
|
|
15
12
|
from fastmcp.utilities.logging import get_logger
|
|
@@ -31,10 +28,10 @@ class AuthKitProviderSettings(BaseSettings):
|
|
|
31
28
|
|
|
32
29
|
|
|
33
30
|
@register_provider("AUTHKIT")
|
|
34
|
-
class AuthKitProvider(
|
|
35
|
-
"""
|
|
31
|
+
class AuthKitProvider(RemoteAuthProvider):
|
|
32
|
+
"""AuthKit metadata provider for DCR (Dynamic Client Registration).
|
|
36
33
|
|
|
37
|
-
This provider implements
|
|
34
|
+
This provider implements AuthKit integration using metadata forwarding
|
|
38
35
|
instead of OAuth proxying. This is the recommended approach for WorkOS DCR
|
|
39
36
|
as it allows WorkOS to handle the OAuth flow directly while FastMCP acts
|
|
40
37
|
as a resource server.
|
|
@@ -56,7 +53,7 @@ class AuthKitProvider(AuthProvider):
|
|
|
56
53
|
```python
|
|
57
54
|
from fastmcp.server.auth.providers.workos import AuthKitProvider
|
|
58
55
|
|
|
59
|
-
# Create
|
|
56
|
+
# Create AuthKit metadata provider (JWT verifier created automatically)
|
|
60
57
|
workos_auth = AuthKitProvider(
|
|
61
58
|
authkit_domain="https://your-workos-domain.authkit.app",
|
|
62
59
|
base_url="https://your-fastmcp-server.com",
|
|
@@ -75,16 +72,14 @@ class AuthKitProvider(AuthProvider):
|
|
|
75
72
|
required_scopes: list[str] | None | NotSetT = NotSet,
|
|
76
73
|
token_verifier: TokenVerifier | None = None,
|
|
77
74
|
):
|
|
78
|
-
"""Initialize
|
|
75
|
+
"""Initialize AuthKit metadata provider.
|
|
79
76
|
|
|
80
77
|
Args:
|
|
81
|
-
authkit_domain: Your
|
|
78
|
+
authkit_domain: Your AuthKit domain (e.g., "https://your-app.authkit.app")
|
|
82
79
|
base_url: Public URL of this FastMCP server
|
|
83
80
|
required_scopes: Optional list of scopes to require for all requests
|
|
84
|
-
token_verifier: Optional token verifier. If None, creates JWT verifier for
|
|
81
|
+
token_verifier: Optional token verifier. If None, creates JWT verifier for AuthKit
|
|
85
82
|
"""
|
|
86
|
-
super().__init__()
|
|
87
|
-
|
|
88
83
|
settings = AuthKitProviderSettings.model_validate(
|
|
89
84
|
{
|
|
90
85
|
k: v
|
|
@@ -109,19 +104,21 @@ class AuthKitProvider(AuthProvider):
|
|
|
109
104
|
required_scopes=settings.required_scopes,
|
|
110
105
|
)
|
|
111
106
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
107
|
+
# Initialize RemoteAuthProvider with AuthKit as the authorization server
|
|
108
|
+
super().__init__(
|
|
109
|
+
token_verifier=token_verifier,
|
|
110
|
+
authorization_servers=[AnyHttpUrl(self.authkit_domain)],
|
|
111
|
+
resource_server_url=self.base_url,
|
|
112
|
+
)
|
|
117
113
|
|
|
118
|
-
def
|
|
119
|
-
"""
|
|
114
|
+
def get_routes(self) -> list[Route]:
|
|
115
|
+
"""Get OAuth routes including AuthKit authorization server metadata forwarding.
|
|
120
116
|
|
|
121
|
-
This
|
|
122
|
-
|
|
123
|
-
- /.well-known/oauth-protected-resource (returns FastMCP resource info)
|
|
117
|
+
This returns the standard protected resource routes plus an authorization server
|
|
118
|
+
metadata endpoint that forwards AuthKit's OAuth metadata to clients.
|
|
124
119
|
"""
|
|
120
|
+
# Get the standard protected resource routes from RemoteAuthProvider
|
|
121
|
+
routes = super().get_routes()
|
|
125
122
|
|
|
126
123
|
async def oauth_authorization_server_metadata(request):
|
|
127
124
|
"""Forward AuthKit OAuth authorization server metadata with FastMCP customizations."""
|
|
@@ -142,29 +139,13 @@ class AuthKitProvider(AuthProvider):
|
|
|
142
139
|
status_code=500,
|
|
143
140
|
)
|
|
144
141
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
"bearer_methods_supported": ["header"],
|
|
152
|
-
}
|
|
142
|
+
# Add AuthKit authorization server metadata forwarding
|
|
143
|
+
routes.append(
|
|
144
|
+
Route(
|
|
145
|
+
"/.well-known/oauth-authorization-server",
|
|
146
|
+
endpoint=oauth_authorization_server_metadata,
|
|
147
|
+
methods=["GET"],
|
|
153
148
|
)
|
|
154
|
-
|
|
155
|
-
routes.extend(
|
|
156
|
-
[
|
|
157
|
-
Route(
|
|
158
|
-
"/.well-known/oauth-authorization-server",
|
|
159
|
-
endpoint=oauth_authorization_server_metadata,
|
|
160
|
-
methods=["GET"],
|
|
161
|
-
),
|
|
162
|
-
Route(
|
|
163
|
-
"/.well-known/oauth-protected-resource",
|
|
164
|
-
endpoint=oauth_protected_resource_metadata,
|
|
165
|
-
methods=["GET"],
|
|
166
|
-
),
|
|
167
|
-
]
|
|
168
149
|
)
|
|
169
150
|
|
|
170
151
|
return routes
|
fastmcp/server/http.py
CHANGED
|
@@ -11,12 +11,10 @@ from mcp.server.auth.middleware.bearer_auth import (
|
|
|
11
11
|
RequireAuthMiddleware,
|
|
12
12
|
)
|
|
13
13
|
from mcp.server.auth.provider import TokenVerifier as TokenVerifierProtocol
|
|
14
|
-
from mcp.server.auth.routes import create_auth_routes
|
|
15
14
|
from mcp.server.lowlevel.server import LifespanResultT
|
|
16
15
|
from mcp.server.sse import SseServerTransport
|
|
17
16
|
from mcp.server.streamable_http import EventStore
|
|
18
17
|
from mcp.server.streamable_http_manager import StreamableHTTPSessionManager
|
|
19
|
-
from pydantic import AnyHttpUrl
|
|
20
18
|
from starlette.applications import Starlette
|
|
21
19
|
from starlette.middleware import Middleware
|
|
22
20
|
from starlette.middleware.authentication import AuthenticationMiddleware
|
|
@@ -25,7 +23,7 @@ from starlette.responses import Response
|
|
|
25
23
|
from starlette.routing import BaseRoute, Mount, Route
|
|
26
24
|
from starlette.types import Lifespan, Receive, Scope, Send
|
|
27
25
|
|
|
28
|
-
from fastmcp.server.auth.auth import AuthProvider
|
|
26
|
+
from fastmcp.server.auth.auth import AuthProvider
|
|
29
27
|
from fastmcp.utilities.logging import get_logger
|
|
30
28
|
|
|
31
29
|
if TYPE_CHECKING:
|
|
@@ -71,51 +69,6 @@ class RequestContextMiddleware:
|
|
|
71
69
|
await self.app(scope, receive, send)
|
|
72
70
|
|
|
73
71
|
|
|
74
|
-
def setup_auth_middleware_and_routes(
|
|
75
|
-
auth: AuthProvider,
|
|
76
|
-
) -> tuple[list[Middleware], list[Route], list[str]]:
|
|
77
|
-
"""Set up authentication middleware and routes if auth is enabled.
|
|
78
|
-
|
|
79
|
-
Args:
|
|
80
|
-
auth: An AuthProvider for authentication (TokenVerifier or OAuthProvider)
|
|
81
|
-
|
|
82
|
-
Returns:
|
|
83
|
-
Tuple of (middleware, auth_routes, required_scopes)
|
|
84
|
-
"""
|
|
85
|
-
middleware: list[Middleware] = [
|
|
86
|
-
Middleware(
|
|
87
|
-
AuthenticationMiddleware,
|
|
88
|
-
backend=BearerAuthBackend(cast(TokenVerifierProtocol, auth)),
|
|
89
|
-
),
|
|
90
|
-
Middleware(AuthContextMiddleware),
|
|
91
|
-
]
|
|
92
|
-
|
|
93
|
-
auth_routes: list[Route] = []
|
|
94
|
-
required_scopes: list[str] = auth.required_scopes or []
|
|
95
|
-
|
|
96
|
-
# Check if it's an OAuthProvider (has OAuth server capability)
|
|
97
|
-
if isinstance(auth, OAuthProvider):
|
|
98
|
-
# OAuthProvider: create standard OAuth routes first
|
|
99
|
-
standard_routes = list(
|
|
100
|
-
create_auth_routes(
|
|
101
|
-
provider=auth,
|
|
102
|
-
issuer_url=auth.issuer_url,
|
|
103
|
-
service_documentation_url=auth.service_documentation_url,
|
|
104
|
-
client_registration_options=auth.client_registration_options,
|
|
105
|
-
revocation_options=auth.revocation_options,
|
|
106
|
-
)
|
|
107
|
-
)
|
|
108
|
-
|
|
109
|
-
# Allow provider to customize routes (e.g., for proxy behavior or metadata endpoints)
|
|
110
|
-
auth_routes = auth.customize_auth_routes(standard_routes)
|
|
111
|
-
else:
|
|
112
|
-
# Simple AuthProvider or TokenVerifier: start with empty routes
|
|
113
|
-
# Allow provider to add custom routes (e.g., metadata endpoints)
|
|
114
|
-
auth_routes = auth.customize_auth_routes([])
|
|
115
|
-
|
|
116
|
-
return middleware, auth_routes, required_scopes
|
|
117
|
-
|
|
118
|
-
|
|
119
72
|
def create_base_app(
|
|
120
73
|
routes: list[BaseRoute],
|
|
121
74
|
middleware: list[Middleware],
|
|
@@ -183,24 +136,27 @@ def create_sse_app(
|
|
|
183
136
|
)
|
|
184
137
|
return Response()
|
|
185
138
|
|
|
186
|
-
#
|
|
139
|
+
# Set up auth if enabled
|
|
187
140
|
if auth:
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
141
|
+
# Create auth middleware
|
|
142
|
+
auth_middleware = [
|
|
143
|
+
Middleware(
|
|
144
|
+
AuthenticationMiddleware,
|
|
145
|
+
backend=BearerAuthBackend(auth),
|
|
146
|
+
),
|
|
147
|
+
Middleware(AuthContextMiddleware),
|
|
148
|
+
]
|
|
149
|
+
|
|
150
|
+
# Get auth routes and scopes
|
|
151
|
+
auth_routes = auth.get_routes()
|
|
152
|
+
required_scopes = getattr(auth, "required_scopes", None) or []
|
|
153
|
+
|
|
154
|
+
# Get resource metadata URL for WWW-Authenticate header
|
|
155
|
+
resource_metadata_url = auth.get_resource_metadata_url()
|
|
191
156
|
|
|
192
157
|
server_routes.extend(auth_routes)
|
|
193
158
|
server_middleware.extend(auth_middleware)
|
|
194
159
|
|
|
195
|
-
# Determine resource_metadata_url for TokenVerifier
|
|
196
|
-
resource_metadata_url = None
|
|
197
|
-
if isinstance(auth, TokenVerifier) and auth.resource_server_url:
|
|
198
|
-
# Add .well-known path for RFC 9728 compliance
|
|
199
|
-
resource_metadata_url = AnyHttpUrl(
|
|
200
|
-
str(auth.resource_server_url).rstrip("/")
|
|
201
|
-
+ "/.well-known/oauth-protected-resource"
|
|
202
|
-
)
|
|
203
|
-
|
|
204
160
|
# Auth is enabled, wrap endpoints with RequireAuthMiddleware
|
|
205
161
|
server_routes.append(
|
|
206
162
|
Route(
|
|
@@ -328,22 +284,25 @@ def create_streamable_http_app(
|
|
|
328
284
|
|
|
329
285
|
# Add StreamableHTTP routes with or without auth
|
|
330
286
|
if auth:
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
287
|
+
# Create auth middleware
|
|
288
|
+
auth_middleware = [
|
|
289
|
+
Middleware(
|
|
290
|
+
AuthenticationMiddleware,
|
|
291
|
+
backend=BearerAuthBackend(cast(TokenVerifierProtocol, auth)),
|
|
292
|
+
),
|
|
293
|
+
Middleware(AuthContextMiddleware),
|
|
294
|
+
]
|
|
295
|
+
|
|
296
|
+
# Get auth routes and scopes
|
|
297
|
+
auth_routes = auth.get_routes()
|
|
298
|
+
required_scopes = getattr(auth, "required_scopes", None) or []
|
|
299
|
+
|
|
300
|
+
# Get resource metadata URL for WWW-Authenticate header
|
|
301
|
+
resource_metadata_url = auth.get_resource_metadata_url()
|
|
334
302
|
|
|
335
303
|
server_routes.extend(auth_routes)
|
|
336
304
|
server_middleware.extend(auth_middleware)
|
|
337
305
|
|
|
338
|
-
# Determine resource_metadata_url for TokenVerifier
|
|
339
|
-
resource_metadata_url = None
|
|
340
|
-
if isinstance(auth, TokenVerifier) and auth.resource_server_url:
|
|
341
|
-
# Add .well-known path for RFC 9728 compliance
|
|
342
|
-
resource_metadata_url = AnyHttpUrl(
|
|
343
|
-
str(auth.resource_server_url).rstrip("/")
|
|
344
|
-
+ "/.well-known/oauth-protected-resource"
|
|
345
|
-
)
|
|
346
|
-
|
|
347
306
|
# Auth is enabled, wrap endpoint with RequireAuthMiddleware
|
|
348
307
|
server_routes.append(
|
|
349
308
|
Mount(
|
fastmcp/utilities/components.py
CHANGED
|
@@ -92,7 +92,12 @@ class FastMCPComponent(FastMCPBaseModel):
|
|
|
92
92
|
return meta or None
|
|
93
93
|
|
|
94
94
|
def with_key(self, key: str) -> Self:
|
|
95
|
-
|
|
95
|
+
# `model_copy` has an `update` parameter but it doesn't work for certain private attributes
|
|
96
|
+
# https://github.com/pydantic/pydantic/issues/12116
|
|
97
|
+
# So we manually set the private attribute here instead
|
|
98
|
+
copy = self.model_copy()
|
|
99
|
+
copy._key = key
|
|
100
|
+
return copy
|
|
96
101
|
|
|
97
102
|
def __eq__(self, other: object) -> bool:
|
|
98
103
|
if type(self) is not type(other):
|
fastmcp/utilities/openapi.py
CHANGED
|
@@ -82,13 +82,9 @@ def format_array_parameter(
|
|
|
82
82
|
return values
|
|
83
83
|
else:
|
|
84
84
|
# For path parameters, fallback to string representation without Python syntax
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
.replace("]", "")
|
|
89
|
-
.replace("'", "")
|
|
90
|
-
.replace('"', "")
|
|
91
|
-
)
|
|
85
|
+
# Use str.translate() for efficient character removal
|
|
86
|
+
translation_table = str.maketrans("", "", "[]'\"")
|
|
87
|
+
str_value = str(values).translate(translation_table)
|
|
92
88
|
return str_value
|
|
93
89
|
|
|
94
90
|
|
fastmcp/utilities/types.py
CHANGED
|
@@ -101,7 +101,12 @@ def get_cached_typeadapter(cls: T) -> TypeAdapter[T]:
|
|
|
101
101
|
new_func.__module__ = cls.__module__
|
|
102
102
|
new_func.__qualname__ = getattr(cls, "__qualname__", cls.__name__)
|
|
103
103
|
new_func.__annotations__ = processed_hints
|
|
104
|
-
|
|
104
|
+
|
|
105
|
+
if inspect.ismethod(cls):
|
|
106
|
+
new_method = types.MethodType(new_func, cls.__self__)
|
|
107
|
+
return TypeAdapter(new_method)
|
|
108
|
+
else:
|
|
109
|
+
return TypeAdapter(new_func)
|
|
105
110
|
|
|
106
111
|
return TypeAdapter(cls)
|
|
107
112
|
|
|
@@ -65,19 +65,19 @@ fastmcp/server/__init__.py,sha256=bMD4aQD4yJqLz7-mudoNsyeV8UgQfRAg3PRwPvwTEds,11
|
|
|
65
65
|
fastmcp/server/context.py,sha256=ooVwF4RoIbLB2S043E5HtK_dCyAWQkDm1YWCkqfUPzs,22638
|
|
66
66
|
fastmcp/server/dependencies.py,sha256=gfb1l3KD2m3h7BpN8lgKWrHY3_OJdDrlnJXCvzmU5YM,2487
|
|
67
67
|
fastmcp/server/elicitation.py,sha256=jZIHjV4NjhYbT-w8pBArwd0vNzP8OYwzmsnWDdk6Bd0,6136
|
|
68
|
-
fastmcp/server/http.py,sha256=
|
|
68
|
+
fastmcp/server/http.py,sha256=idZGs4kRm_eLMscMrlEBIBpX21aiuTlvon9Fizmjswo,11648
|
|
69
69
|
fastmcp/server/low_level.py,sha256=LNmc_nU_wx-fRG8OEHdLPKopZpovcrWlyAxJzKss3TA,1239
|
|
70
70
|
fastmcp/server/openapi.py,sha256=-7-pKwQ1hT-UV9OnLlWrjbbXXRfZld8YJqa4Duybhtw,42102
|
|
71
71
|
fastmcp/server/proxy.py,sha256=4eHW2Vgwe7zvd0g-ozsvYRyIoengyGyhlyMcSPhcaIU,24975
|
|
72
72
|
fastmcp/server/server.py,sha256=hmNiwYFnWhERY-WhxAvS9jIvXOx8_g8ylX-Ju91Y4qk,87597
|
|
73
|
-
fastmcp/server/auth/__init__.py,sha256=
|
|
74
|
-
fastmcp/server/auth/auth.py,sha256=
|
|
73
|
+
fastmcp/server/auth/__init__.py,sha256=ldv13Dsxr2r-VdUV6af6P5qa3wMPQ-MP_yKxsWdlb-4,550
|
|
74
|
+
fastmcp/server/auth/auth.py,sha256=FMkCcht4BMEqKx-SwvfzVC5l7X2moiIzS_ZevaHBq08,10536
|
|
75
75
|
fastmcp/server/auth/registry.py,sha256=4ftVbbuyAi-8zBiJL9-dYIKLU_EOpxY-pFdzHMrmjV8,1306
|
|
76
76
|
fastmcp/server/auth/providers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
77
77
|
fastmcp/server/auth/providers/bearer.py,sha256=iu4pUj7TF5pT1wPuAGzDuM6lt5WtzenBN3c0otUleQY,923
|
|
78
78
|
fastmcp/server/auth/providers/in_memory.py,sha256=tlUwbUZK5stUC_wjezcDs61PxjaHjykXbd8aH0cnW6k,14359
|
|
79
|
-
fastmcp/server/auth/providers/jwt.py,sha256=
|
|
80
|
-
fastmcp/server/auth/providers/workos.py,sha256=
|
|
79
|
+
fastmcp/server/auth/providers/jwt.py,sha256=7pAH8kuudl_lbY2skeVDlLkxFMhs5-3axpAoGVVp_MU,18882
|
|
80
|
+
fastmcp/server/auth/providers/workos.py,sha256=jedSq4uhttDxYDYSZFrb4u_q1X9JeHgvKupJPpu0tSM,5590
|
|
81
81
|
fastmcp/server/middleware/__init__.py,sha256=vh5C9ubN6q-y5QND32P4mQ4zDT89C7XYK39yqhELNAk,155
|
|
82
82
|
fastmcp/server/middleware/error_handling.py,sha256=SoDatr9i3T2qSIUbSEGWrOnu4WPPyMDymnsF5GR_BiE,7572
|
|
83
83
|
fastmcp/server/middleware/logging.py,sha256=UIAoafnKRGWpQa7OX5nzChep-9EhKdyTDBmmcRcEVdo,6239
|
|
@@ -90,7 +90,7 @@ fastmcp/tools/tool_manager.py,sha256=Pr6BI7IInvhkTw2S4ENWj8qMewdYi2CpzbOBiJIk40c
|
|
|
90
90
|
fastmcp/tools/tool_transform.py,sha256=f8Bt79qdmOM8Zietm7lXb5_5DlJLu02ovGmQBQJ6orw,36671
|
|
91
91
|
fastmcp/utilities/__init__.py,sha256=-imJ8S-rXmbXMWeDamldP-dHDqAPg_wwmPVz-LNX14E,31
|
|
92
92
|
fastmcp/utilities/cli.py,sha256=TXuSyALFAGJwi7tWEBwBmaGhYZBdF1aG6dLgl3zjM1w,3272
|
|
93
|
-
fastmcp/utilities/components.py,sha256=
|
|
93
|
+
fastmcp/utilities/components.py,sha256=wmo1XZBqf_tcOMMN1a-r8d1pT7g--Zmf75f5-vXx5FA,5333
|
|
94
94
|
fastmcp/utilities/exceptions.py,sha256=7Z9j5IzM5rT27BC1Mcn8tkS-bjqCYqMKwb2MMTaxJYU,1350
|
|
95
95
|
fastmcp/utilities/http.py,sha256=1ns1ymBS-WSxbZjGP6JYjSO52Wa_ls4j4WbnXiupoa4,245
|
|
96
96
|
fastmcp/utilities/inspect.py,sha256=XNA0dfYM5G-FVbJaVJO8loSUUCNypyLA-QjqTOneJyU,10833
|
|
@@ -98,11 +98,11 @@ fastmcp/utilities/json_schema.py,sha256=Nk6qQKtp0-MlMbRQmx8ps8SJ7SW9CpuWhSes1VAS
|
|
|
98
98
|
fastmcp/utilities/json_schema_type.py,sha256=fSG-af3OPGgOhuhY_xb0-JsTu5tqi275zXlUw4ItjNo,22287
|
|
99
99
|
fastmcp/utilities/logging.py,sha256=1y7oNmy8WrR0NsfNVw1LPoKu92OFdmzIO65syOKi_BI,1388
|
|
100
100
|
fastmcp/utilities/mcp_config.py,sha256=zzs4VWHqG0eWEnEUwVve7mef_JFThwfvqBYt7nx3jXc,871
|
|
101
|
-
fastmcp/utilities/openapi.py,sha256=
|
|
101
|
+
fastmcp/utilities/openapi.py,sha256=Jcxu0s9VdA8RSkx1iyoTQwvsny4YCusuuJahyy-TvE8,63300
|
|
102
102
|
fastmcp/utilities/tests.py,sha256=9FVLmGYfUjqfn0pPH33awlTgvg-JCNbnZnraJHNSYTg,6156
|
|
103
|
-
fastmcp/utilities/types.py,sha256=
|
|
104
|
-
fastmcp-2.11.
|
|
105
|
-
fastmcp-2.11.
|
|
106
|
-
fastmcp-2.11.
|
|
107
|
-
fastmcp-2.11.
|
|
108
|
-
fastmcp-2.11.
|
|
103
|
+
fastmcp/utilities/types.py,sha256=zrF8Oc_L_BdzGj0pqU5ZalM02BisC0_D-d8kcRGccS0,13984
|
|
104
|
+
fastmcp-2.11.1.dist-info/METADATA,sha256=7kstQeYl88_tx9oEhjN98bGmGWskG478FOFlvfCuMiA,17839
|
|
105
|
+
fastmcp-2.11.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
106
|
+
fastmcp-2.11.1.dist-info/entry_points.txt,sha256=ff8bMtKX1JvXyurMibAacMSKbJEPmac9ffAKU9mLnM8,44
|
|
107
|
+
fastmcp-2.11.1.dist-info/licenses/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
|
|
108
|
+
fastmcp-2.11.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|