Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
make consent optional, default true
  • Loading branch information
pcarleton committed May 9, 2025
commit e05f0b6f27a422cbf49ba4b0ae52c499aea4dafa
26 changes: 23 additions & 3 deletions src/mcp/server/auth/handlers/consent.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import Any
from typing import Any, Optional
from urllib.parse import urlencode

from starlette.requests import Request
Expand All @@ -9,8 +10,27 @@
from mcp.server.auth.provider import OAuthAuthorizationServerProvider


class AbstractConsentHandler(ABC):
"""Abstract base class for handling OAuth consent operations."""

@abstractmethod
async def handle(self, request: Request) -> Response:
"""Handle consent requests - both showing the form and processing submissions."""
pass

@abstractmethod
async def _show_consent_form(self, request: Request) -> Response:
"""Display the consent form to the user."""
pass

@abstractmethod
async def _process_consent(self, request: Request) -> Response:
"""Process the user's consent decision."""
pass


@dataclass
class ConsentHandler:
class ConsentHandler(AbstractConsentHandler):
provider: OAuthAuthorizationServerProvider[Any, Any, Any]

async def handle(self, request: Request) -> Response:
Expand Down Expand Up @@ -157,7 +177,7 @@ async def _show_consent_form(self, request: Request) -> HTMLResponse:
"""
return HTMLResponse(content=html_content)

async def _process_consent(self, request: Request) -> RedirectResponse:
async def _process_consent(self, request: Request) -> RedirectResponse | HTMLResponse:
form_data = await request.form()
action = form_data.get("action")

Expand Down
19 changes: 12 additions & 7 deletions src/mcp/server/auth/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,13 +115,6 @@ def create_auth_routes(
),
methods=["POST", "OPTIONS"],
),
Route(
CONSENT_PATH,
# do not allow CORS for consent endpoint;
# clients should just redirect to this
endpoint=ConsentHandler(provider).handle,
methods=["GET", "POST"],
),
]

if client_registration_options.enabled:
Expand All @@ -140,6 +133,18 @@ def create_auth_routes(
)
)

if client_registration_options.client_consent_required:
consent_path: str = client_registration_options.client_consent_path or CONSENT_PATH
routes.append(
Route(
consent_path,
# do not allow CORS for consent endpoint;
# clients should just redirect to this
endpoint=ConsentHandler(provider).handle,
methods=["GET", "POST"],
),
)

if revocation_options.enabled:
revocation_handler = RevocationHandler(provider, client_authenticator)
routes.append(
Expand Down
2 changes: 2 additions & 0 deletions src/mcp/server/auth/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ class ClientRegistrationOptions(BaseModel):
client_secret_expiry_seconds: int | None = None
valid_scopes: list[str] | None = None
default_scopes: list[str] | None = None
client_consent_required: bool = True
client_consent_path: str | None = None


class RevocationOptions(BaseModel):
Expand Down