mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-26 15:34:18 +02:00
Fix SSE endpoint accepting POST requests instead of returning 405 (#1953)
This commit is contained in:
parent
27dbb9eabe
commit
d9fe373358
6 changed files with 48 additions and 65 deletions
|
|
@ -3,10 +3,7 @@ from __future__ import annotations
|
|||
from typing import Any
|
||||
|
||||
from mcp.server.auth.middleware.auth_context import AuthContextMiddleware
|
||||
from mcp.server.auth.middleware.bearer_auth import (
|
||||
BearerAuthBackend,
|
||||
RequireAuthMiddleware,
|
||||
)
|
||||
from mcp.server.auth.middleware.bearer_auth import BearerAuthBackend
|
||||
from mcp.server.auth.provider import (
|
||||
AccessToken as _SDKAccessToken,
|
||||
)
|
||||
|
|
@ -81,7 +78,6 @@ class AuthProvider(TokenVerifierProtocol):
|
|||
def get_routes(
|
||||
self,
|
||||
mcp_path: str | None = None,
|
||||
mcp_endpoint: Any | None = None,
|
||||
) -> list[Route]:
|
||||
"""Get the routes for this authentication provider.
|
||||
|
||||
|
|
@ -93,30 +89,13 @@ class AuthProvider(TokenVerifierProtocol):
|
|||
|
||||
Args:
|
||||
mcp_path: The path where the MCP endpoint is mounted (e.g., "/mcp")
|
||||
mcp_endpoint: The MCP endpoint handler to protect with auth
|
||||
This is used to advertise the resource URL in metadata, but the
|
||||
provider does not create the actual MCP endpoint route.
|
||||
|
||||
Returns:
|
||||
List of routes for this provider, including protected MCP endpoints if provided
|
||||
List of routes for this provider (excluding the MCP endpoint itself)
|
||||
"""
|
||||
|
||||
routes = []
|
||||
|
||||
# Add protected MCP endpoint if provided
|
||||
if mcp_path and mcp_endpoint:
|
||||
resource_metadata_url = self._get_resource_url(
|
||||
"/.well-known/oauth-protected-resource"
|
||||
)
|
||||
|
||||
routes.append(
|
||||
Route(
|
||||
mcp_path,
|
||||
endpoint=RequireAuthMiddleware(
|
||||
mcp_endpoint, self.required_scopes, resource_metadata_url
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
return routes
|
||||
return []
|
||||
|
||||
def get_middleware(self) -> list:
|
||||
"""Get HTTP application-level middleware for this auth provider.
|
||||
|
|
@ -225,14 +204,13 @@ class RemoteAuthProvider(AuthProvider):
|
|||
def get_routes(
|
||||
self,
|
||||
mcp_path: str | None = None,
|
||||
mcp_endpoint: Any | None = None,
|
||||
) -> list[Route]:
|
||||
"""Get OAuth routes for this provider.
|
||||
|
||||
Creates protected resource metadata routes and optionally wraps MCP endpoints with auth.
|
||||
Creates protected resource metadata routes.
|
||||
"""
|
||||
# Start with base routes (protected MCP endpoint)
|
||||
routes = super().get_routes(mcp_path, mcp_endpoint)
|
||||
# Start with base routes
|
||||
routes = super().get_routes(mcp_path)
|
||||
|
||||
# Get the resource URL based on the MCP path
|
||||
resource_url = self._get_resource_url(mcp_path)
|
||||
|
|
@ -326,14 +304,12 @@ class OAuthProvider(
|
|||
def get_routes(
|
||||
self,
|
||||
mcp_path: str | None = None,
|
||||
mcp_endpoint: Any | None = None,
|
||||
) -> list[Route]:
|
||||
"""Get OAuth authorization server routes and optional protected resource routes.
|
||||
|
||||
This method creates the full set of OAuth routes including:
|
||||
- Standard OAuth authorization server routes (/.well-known/oauth-authorization-server, /authorize, /token, etc.)
|
||||
- Optional protected resource routes
|
||||
- Protected MCP endpoints if provided
|
||||
|
||||
Returns:
|
||||
List of OAuth routes
|
||||
|
|
@ -366,7 +342,7 @@ class OAuthProvider(
|
|||
)
|
||||
oauth_routes.extend(protected_routes)
|
||||
|
||||
# Add protected MCP endpoint from base class
|
||||
oauth_routes.extend(super().get_routes(mcp_path, mcp_endpoint))
|
||||
# Add base routes
|
||||
oauth_routes.extend(super().get_routes(mcp_path))
|
||||
|
||||
return oauth_routes
|
||||
|
|
|
|||
|
|
@ -873,7 +873,6 @@ class OAuthProxy(OAuthProvider):
|
|||
def get_routes(
|
||||
self,
|
||||
mcp_path: str | None = None,
|
||||
mcp_endpoint: Any | None = None,
|
||||
) -> list[Route]:
|
||||
"""Get OAuth routes with custom proxy token handler.
|
||||
|
||||
|
|
@ -882,10 +881,10 @@ class OAuthProxy(OAuthProvider):
|
|||
|
||||
Args:
|
||||
mcp_path: The path where the MCP endpoint is mounted (e.g., "/mcp")
|
||||
mcp_endpoint: The MCP endpoint handler to protect with auth
|
||||
This is used to advertise the resource URL in metadata.
|
||||
"""
|
||||
# Get standard OAuth routes from parent class
|
||||
routes = super().get_routes(mcp_path, mcp_endpoint)
|
||||
routes = super().get_routes(mcp_path)
|
||||
custom_routes = []
|
||||
token_route_found = False
|
||||
|
||||
|
|
|
|||
|
|
@ -7,8 +7,6 @@ for seamless MCP client authentication.
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
from pydantic import AnyHttpUrl
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
|
|
@ -127,7 +125,6 @@ class DescopeProvider(RemoteAuthProvider):
|
|||
def get_routes(
|
||||
self,
|
||||
mcp_path: str | None = None,
|
||||
mcp_endpoint: Any | None = None,
|
||||
) -> list[Route]:
|
||||
"""Get OAuth routes including Descope authorization server metadata forwarding.
|
||||
|
||||
|
|
@ -136,10 +133,10 @@ class DescopeProvider(RemoteAuthProvider):
|
|||
|
||||
Args:
|
||||
mcp_path: The path where the MCP endpoint is mounted (e.g., "/mcp")
|
||||
mcp_endpoint: The MCP endpoint handler to protect with auth
|
||||
This is used to advertise the resource URL in metadata.
|
||||
"""
|
||||
# Get the standard protected resource routes from RemoteAuthProvider
|
||||
routes = super().get_routes(mcp_path, mcp_endpoint)
|
||||
routes = super().get_routes(mcp_path)
|
||||
|
||||
async def oauth_authorization_server_metadata(request):
|
||||
"""Forward Descope OAuth authorization server metadata with FastMCP customizations."""
|
||||
|
|
|
|||
|
|
@ -7,8 +7,6 @@ authentication for seamless MCP client authentication.
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
from pydantic import AnyHttpUrl
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
|
|
@ -135,7 +133,6 @@ class ScalekitProvider(RemoteAuthProvider):
|
|||
def get_routes(
|
||||
self,
|
||||
mcp_path: str | None = None,
|
||||
mcp_endpoint: Any | None = None,
|
||||
) -> list[Route]:
|
||||
"""Get OAuth routes including Scalekit authorization server metadata forwarding.
|
||||
|
||||
|
|
@ -144,10 +141,10 @@ class ScalekitProvider(RemoteAuthProvider):
|
|||
|
||||
Args:
|
||||
mcp_path: The path where the MCP endpoint is mounted (e.g., "/mcp")
|
||||
mcp_endpoint: The MCP endpoint handler to protect with auth
|
||||
This is used to advertise the resource URL in metadata.
|
||||
"""
|
||||
# Get the standard protected resource routes from RemoteAuthProvider
|
||||
routes = super().get_routes(mcp_path, mcp_endpoint)
|
||||
routes = super().get_routes(mcp_path)
|
||||
|
||||
async def oauth_authorization_server_metadata(request):
|
||||
"""Forward Scalekit OAuth authorization server metadata with FastMCP customizations."""
|
||||
|
|
|
|||
|
|
@ -10,8 +10,6 @@ Choose based on your WorkOS setup and authentication requirements.
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
from pydantic import AnyHttpUrl, SecretStr, field_validator
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
|
|
@ -364,7 +362,6 @@ class AuthKitProvider(RemoteAuthProvider):
|
|||
def get_routes(
|
||||
self,
|
||||
mcp_path: str | None = None,
|
||||
mcp_endpoint: Any | None = None,
|
||||
) -> list[Route]:
|
||||
"""Get OAuth routes including AuthKit authorization server metadata forwarding.
|
||||
|
||||
|
|
@ -373,10 +370,10 @@ class AuthKitProvider(RemoteAuthProvider):
|
|||
|
||||
Args:
|
||||
mcp_path: The path where the MCP endpoint is mounted (e.g., "/mcp")
|
||||
mcp_endpoint: The MCP endpoint handler to protect with auth
|
||||
This is used to advertise the resource URL in metadata.
|
||||
"""
|
||||
# Get the standard protected resource routes from RemoteAuthProvider
|
||||
routes = super().get_routes(mcp_path, mcp_endpoint)
|
||||
routes = super().get_routes(mcp_path)
|
||||
|
||||
async def oauth_authorization_server_metadata(request):
|
||||
"""Forward AuthKit OAuth authorization server metadata with FastMCP customizations."""
|
||||
|
|
|
|||
|
|
@ -167,16 +167,25 @@ def create_sse_app(
|
|||
# Get auth middleware from the provider
|
||||
auth_middleware = auth.get_middleware()
|
||||
|
||||
# Get auth routes including protected MCP endpoint
|
||||
auth_routes = auth.get_routes(
|
||||
mcp_path=sse_path,
|
||||
mcp_endpoint=handle_sse,
|
||||
)
|
||||
|
||||
# Get auth provider's own routes (OAuth endpoints, metadata, etc)
|
||||
auth_routes = auth.get_routes(mcp_path=sse_path)
|
||||
server_routes.extend(auth_routes)
|
||||
server_middleware.extend(auth_middleware)
|
||||
|
||||
# Manually wrap the SSE message endpoint with RequireAuthMiddleware
|
||||
# Create protected SSE endpoint route with GET method only
|
||||
server_routes.append(
|
||||
Route(
|
||||
sse_path,
|
||||
endpoint=RequireAuthMiddleware(
|
||||
handle_sse,
|
||||
auth.required_scopes,
|
||||
auth._get_resource_url("/.well-known/oauth-protected-resource"),
|
||||
),
|
||||
methods=["GET"],
|
||||
)
|
||||
)
|
||||
|
||||
# Wrap the SSE message endpoint with RequireAuthMiddleware
|
||||
server_routes.append(
|
||||
Mount(
|
||||
message_path,
|
||||
|
|
@ -274,14 +283,22 @@ def create_streamable_http_app(
|
|||
# Get auth middleware from the provider
|
||||
auth_middleware = auth.get_middleware()
|
||||
|
||||
# Get auth routes including protected MCP endpoint
|
||||
auth_routes = auth.get_routes(
|
||||
mcp_path=streamable_http_path,
|
||||
mcp_endpoint=streamable_http_app,
|
||||
)
|
||||
|
||||
# Get auth provider's own routes (OAuth endpoints, metadata, etc)
|
||||
auth_routes = auth.get_routes(mcp_path=streamable_http_path)
|
||||
server_routes.extend(auth_routes)
|
||||
server_middleware.extend(auth_middleware)
|
||||
|
||||
# Create protected HTTP endpoint route
|
||||
server_routes.append(
|
||||
Route(
|
||||
streamable_http_path,
|
||||
endpoint=RequireAuthMiddleware(
|
||||
streamable_http_app,
|
||||
auth.required_scopes,
|
||||
auth._get_resource_url("/.well-known/oauth-protected-resource"),
|
||||
),
|
||||
)
|
||||
)
|
||||
else:
|
||||
# No auth required
|
||||
server_routes.append(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue