|
12 | 12 | ) |
13 | 13 | from itertools import chain |
14 | 14 | from typing import Any, Generic, Literal |
| 15 | +from urllib.parse import urljoin |
15 | 16 |
|
16 | 17 | import anyio |
17 | 18 | import pydantic_core |
|
21 | 22 | from pydantic_settings import BaseSettings, SettingsConfigDict |
22 | 23 | from starlette.applications import Starlette |
23 | 24 | from starlette.requests import Request |
| 25 | +from starlette.responses import Response, JSONResponse |
24 | 26 | from starlette.routing import Mount, Route |
25 | 27 |
|
26 | | -from mcp.server.auth.backend import BearerTokenBackend |
| 28 | +from mcp.server.auth.backend import BearerTokenBackend, OAUTH_WELL_KNOWN_PATH, OPENID_WELL_KNOWN_PATH |
27 | 29 | from mcp.server.fastmcp.exceptions import ResourceError |
28 | 30 | from mcp.server.fastmcp.prompts import Prompt, PromptManager |
29 | 31 | from mcp.server.fastmcp.resources import FunctionResource, Resource, ResourceManager |
@@ -499,19 +501,38 @@ async def handle_sse(request: Request) -> None: |
499 | 501 | self._mcp_server.create_initialization_options(), |
500 | 502 | ) |
501 | 503 |
|
| 504 | + import httpx |
| 505 | + async def handle_well_known(_: Request) -> Response: |
| 506 | + async with httpx.AsyncClient() as client: |
| 507 | + issuer_url = str(self.settings.issuer_url).rstrip("/") + "/" |
| 508 | + well_known_url = urljoin(issuer_url, OAUTH_WELL_KNOWN_PATH) |
| 509 | + response = await client.get(well_known_url) |
| 510 | + return JSONResponse(response.json()) |
| 511 | + |
| 512 | + routes = [ |
| 513 | + Route(self.settings.sse_path, endpoint=handle_sse), |
| 514 | + Mount(self.settings.message_path, app=sse.handle_post_message), |
| 515 | + ] |
502 | 516 | middleware = [] |
503 | 517 |
|
504 | 518 | if self.settings.authentication_enabled: |
| 519 | + # We wrap all the default routes in the auth check. |
505 | 520 | backend = BearerTokenBackend(self.settings.issuer_url) |
506 | | - middleware.append(backend.as_middleware()) |
| 521 | + |
| 522 | + routes = [ |
| 523 | + Route(f"/{OAUTH_WELL_KNOWN_PATH}", endpoint=handle_well_known), |
| 524 | + Route(f"/{OPENID_WELL_KNOWN_PATH}", endpoint=handle_well_known), |
| 525 | + Mount( |
| 526 | + "/", |
| 527 | + routes=routes, |
| 528 | + middleware=[backend.as_middleware()] |
| 529 | + ), |
| 530 | + ] |
507 | 531 |
|
508 | 532 | return Starlette( |
509 | 533 | debug=self.settings.debug, |
510 | 534 | middleware=middleware, |
511 | | - routes=[ |
512 | | - Route(self.settings.sse_path, endpoint=handle_sse), |
513 | | - Mount(self.settings.message_path, app=sse.handle_post_message), |
514 | | - ], |
| 535 | + routes=routes, |
515 | 536 | ) |
516 | 537 |
|
517 | 538 | async def list_prompts(self) -> list[MCPPrompt]: |
|
0 commit comments