Skip to content

Commit df42d3f

Browse files
Merge branch 'modelcontextprotocol:main' into fix/resource-metadata-path
2 parents 320f58c + 95b44fb commit df42d3f

File tree

1 file changed

+80
-0
lines changed

1 file changed

+80
-0
lines changed

tests/client/test_auth.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import httpx
88
import pytest
9+
from inline_snapshot import Is, snapshot
910
from pydantic import AnyHttpUrl, AnyUrl
1011

1112
from mcp.client.auth import OAuthClientProvider, PKCEParameters
@@ -590,3 +591,82 @@ async def test_auth_flow_with_valid_tokens(self, oauth_provider, mock_storage, v
590591
await auth_flow.asend(response)
591592
except StopAsyncIteration:
592593
pass # Expected
594+
595+
596+
@pytest.mark.parametrize(
597+
(
598+
"issuer_url",
599+
"service_documentation_url",
600+
"authorization_endpoint",
601+
"token_endpoint",
602+
"registration_endpoint",
603+
"revocation_endpoint",
604+
),
605+
(
606+
# Pydantic's AnyUrl incorrectly adds trailing slash to base URLs
607+
# This is being fixed in https://github.com/pydantic/pydantic-core/pull/1719 (Pydantic 2.12+)
608+
pytest.param(
609+
"https://auth.example.com",
610+
"https://auth.example.com/docs",
611+
"https://auth.example.com/authorize",
612+
"https://auth.example.com/token",
613+
"https://auth.example.com/register",
614+
"https://auth.example.com/revoke",
615+
id="simple-url",
616+
marks=pytest.mark.xfail(
617+
reason="Pydantic AnyUrl adds trailing slash to base URLs - fixed in Pydantic 2.12+"
618+
),
619+
),
620+
pytest.param(
621+
"https://auth.example.com/",
622+
"https://auth.example.com/docs",
623+
"https://auth.example.com/authorize",
624+
"https://auth.example.com/token",
625+
"https://auth.example.com/register",
626+
"https://auth.example.com/revoke",
627+
id="with-trailing-slash",
628+
),
629+
pytest.param(
630+
"https://auth.example.com/v1/mcp",
631+
"https://auth.example.com/v1/mcp/docs",
632+
"https://auth.example.com/v1/mcp/authorize",
633+
"https://auth.example.com/v1/mcp/token",
634+
"https://auth.example.com/v1/mcp/register",
635+
"https://auth.example.com/v1/mcp/revoke",
636+
id="with-path-param",
637+
),
638+
),
639+
)
640+
def test_build_metadata(
641+
issuer_url: str,
642+
service_documentation_url: str,
643+
authorization_endpoint: str,
644+
token_endpoint: str,
645+
registration_endpoint: str,
646+
revocation_endpoint: str,
647+
):
648+
from mcp.server.auth.routes import build_metadata
649+
from mcp.server.auth.settings import ClientRegistrationOptions, RevocationOptions
650+
651+
metadata = build_metadata(
652+
issuer_url=AnyHttpUrl(issuer_url),
653+
service_documentation_url=AnyHttpUrl(service_documentation_url),
654+
client_registration_options=ClientRegistrationOptions(enabled=True, valid_scopes=["read", "write", "admin"]),
655+
revocation_options=RevocationOptions(enabled=True),
656+
)
657+
658+
assert metadata.model_dump(exclude_defaults=True, mode="json") == snapshot(
659+
{
660+
"issuer": Is(issuer_url),
661+
"authorization_endpoint": Is(authorization_endpoint),
662+
"token_endpoint": Is(token_endpoint),
663+
"registration_endpoint": Is(registration_endpoint),
664+
"scopes_supported": ["read", "write", "admin"],
665+
"grant_types_supported": ["authorization_code", "refresh_token"],
666+
"token_endpoint_auth_methods_supported": ["client_secret_post"],
667+
"service_documentation": Is(service_documentation_url),
668+
"revocation_endpoint": Is(revocation_endpoint),
669+
"revocation_endpoint_auth_methods_supported": ["client_secret_post"],
670+
"code_challenge_methods_supported": ["S256"],
671+
}
672+
)

0 commit comments

Comments
 (0)