Skip to content

Commit cf7b48b

Browse files
committed
refactor: update types and comments
1 parent e5c06bb commit cf7b48b

File tree

3 files changed

+80
-14
lines changed

3 files changed

+80
-14
lines changed

mcpauth/exceptions.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
from enum import Enum
22
from pydantic import BaseModel
3-
from typing import Optional, List, Dict, Union
3+
from typing import Any, Optional, List, Dict, Union
44
from .types import Record
55

6-
ExceptionCause = Optional[Union[Record, Exception]]
6+
ExceptionCause = Optional[Union[Record, Exception, BaseModel]]
77

88

99
class MCPAuthException(Exception):
@@ -63,7 +63,7 @@ class MCPAuthAuthServerException(MCPAuthException):
6363
Exception thrown when there is an issue with the remote authorization server.
6464
"""
6565

66-
def __init__(self, code: AuthServerExceptionCode, cause: Optional[Record] = None):
66+
def __init__(self, code: AuthServerExceptionCode, cause: ExceptionCause = None):
6767
super().__init__(
6868
code.value,
6969
auth_server_exception_description.get(
@@ -96,7 +96,7 @@ class BearerAuthExceptionCode(str, Enum):
9696

9797

9898
class MCPAuthBearerAuthExceptionDetails(BaseModel):
99-
cause: Optional[Record] = None
99+
cause: Any = None
100100
uri: Optional[str] = None
101101
missing_scopes: Optional[List[str]] = None
102102
expected: Optional[Union[str, Record]] = None
@@ -156,7 +156,7 @@ class MCPAuthJwtVerificationException(MCPAuthException):
156156
"""
157157

158158
def __init__(
159-
self, code: MCPAuthJwtVerificationExceptionCode, cause: Optional[Record] = None
159+
self, code: MCPAuthJwtVerificationExceptionCode, cause: ExceptionCause = None
160160
):
161161
super().__init__(
162162
code.value,

mcpauth/utils/fetch_server_config.py

Lines changed: 70 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@
88
from ..types import Record
99
from ..models.oauth import AuthorizationServerMetadata
1010
from ..models.auth_server import AuthServerConfig, AuthServerType
11-
from ..exceptions import MCPAuthConfigException
11+
from ..exceptions import (
12+
AuthServerExceptionCode,
13+
MCPAuthAuthServerException,
14+
MCPAuthConfigException,
15+
)
1216

1317

1418
class ServerMetadataPaths(str, Enum):
@@ -22,6 +26,11 @@ class ServerMetadataPaths(str, Enum):
2226

2327

2428
def smart_join(*args: str) -> str:
29+
"""
30+
Joins multiple path components into a single path string, regardless of leading or trailing
31+
slashes.
32+
"""
33+
2534
return Path("/".join(arg.strip("/") for arg in args)).as_posix()
2635

2736

@@ -42,6 +51,23 @@ async def fetch_server_config_by_well_known_url(
4251
type: AuthServerType,
4352
transpile_data: Optional[Callable[[Record], Record]] = None,
4453
) -> AuthServerConfig:
54+
"""
55+
Fetches the server configuration from the provided well-known URL and validates it against the
56+
MCP specification.
57+
58+
If the server metadata does not conform to the expected schema, but you are sure that it is
59+
compatible, you can provide a `transpile_data` function to transform the metadata into the
60+
expected format.
61+
62+
:param well_known_url: The well-known URL to fetch the server configuration from.
63+
:param type: The type of the authorization server (OAuth or OIDC).
64+
:param transpile_data: Optional function to transform the fetched data into the expected
65+
format.
66+
:return AuthServerConfig: An instance of `AuthServerConfig` containing the server metadata.
67+
:raises MCPAuthConfigException: If there is an error fetching the server metadata.
68+
:raises MCPAuthAuthServerException: If the server metadata is invalid or malformed.
69+
"""
70+
4571
try:
4672
async with aiohttp.ClientSession() as session:
4773
async with session.get(well_known_url) as response:
@@ -52,9 +78,8 @@ async def fetch_server_config_by_well_known_url(
5278
metadata=AuthorizationServerMetadata(**transpiled_data), type=type
5379
)
5480
except pydantic.ValidationError as e:
55-
raise MCPAuthConfigException(
56-
"invalid_server_metadata",
57-
f"Invalid server metadata from {well_known_url}: {str(e)}",
81+
raise MCPAuthAuthServerException(
82+
AuthServerExceptionCode.INVALID_SERVER_METADATA,
5883
cause=e,
5984
) from e
6085
except Exception as e:
@@ -70,6 +95,47 @@ async def fetch_server_config(
7095
type: AuthServerType,
7196
transpile_data: Optional[Callable[[Record], Record]] = None,
7297
) -> AuthServerConfig:
98+
"""
99+
Fetches the server configuration according to the issuer and authorization server type.
100+
101+
This function automatically determines the well-known URL based on the server type, as OAuth
102+
and OpenID Connect servers have different conventions for their metadata endpoints.
103+
104+
See Also:
105+
- `fetchServerConfigByWellKnownUrl` for the underlying implementation.
106+
- https://www.rfc-editor.org/rfc/rfc8414 for the OAuth 2.0 Authorization Server Metadata
107+
specification.
108+
- https://openid.net/specs/openid-connect-discovery-1_0.html for the OpenID Connect Discovery
109+
specification.
110+
111+
Example:
112+
```python
113+
from mcpauth.utils import fetch_server_config, AuthServerType
114+
115+
# Fetch OAuth server config. This will fetch the metadata from
116+
# `https://auth.logto.io/.well-known/oauth-authorization-server/oauth`
117+
oauth_config = await fetch_server_config(
118+
issuer="https://auth.logto.io/oauth",
119+
type=AuthServerType.OAUTH
120+
)
121+
122+
# Fetch OIDC server config. This will fetch the metadata from
123+
# `https://auth.logto.io/oidc/.well-known/openid-configuration`
124+
oidc_config = await fetch_server_config(
125+
issuer="https://auth.logto.io/oidc",
126+
type=AuthServerType.OIDC
127+
)
128+
```
129+
130+
:param issuer: The issuer URL of the authorization server.
131+
:param type: The type of the authorization server (OAuth or OIDC).
132+
:param transpile_data: Optional function to transform the fetched data into the expected
133+
format.
134+
:return AuthServerConfig: An instance of `AuthServerConfig` containing the server metadata.
135+
:raises MCPAuthConfigException: If there is an error fetching the server metadata.
136+
:raises MCPAuthAuthServerException: If the server metadata is invalid or malformed.
137+
"""
138+
73139
well_known_url = (
74140
get_oauth_well_known_url(issuer)
75141
if type == AuthServerType.OAUTH

mcpauth/utils/fetch_server_config_test.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from aiohttp.web_response import Response
44

55
from mcpauth.models.auth_server import AuthServerType
6-
from mcpauth.exceptions import MCPAuthConfigException
6+
from mcpauth.exceptions import MCPAuthAuthServerException, MCPAuthConfigException
77
from mcpauth.types import Record
88
from mcpauth.utils.fetch_server_config import (
99
ServerMetadataPaths,
@@ -44,12 +44,12 @@ async def test_fetch_server_config_by_well_known_url_invalid_metadata(
4444
"example.com", ServerMetadataPaths.OAUTH.value, "GET", response={}
4545
)
4646

47-
with pytest.raises(MCPAuthConfigException) as exc_info:
47+
with pytest.raises(MCPAuthAuthServerException) as exc_info:
4848
await fetch_server_config_by_well_known_url(
4949
sample_well_known_url, AuthServerType.OAUTH
5050
)
5151

52-
assert "Invalid server metadata" in str(exc_info.value)
52+
assert "The server metadata is invalid or malformed" in str(exc_info.value)
5353

5454
async def test_fetch_server_config_by_well_known_url_malformed_metadata(
5555
self, aresponses: ResponsesMockServer
@@ -67,12 +67,12 @@ async def test_fetch_server_config_by_well_known_url_malformed_metadata(
6767
"example.com", ServerMetadataPaths.OAUTH.value, "GET", sample_response
6868
)
6969

70-
with pytest.raises(MCPAuthConfigException) as exc_info:
70+
with pytest.raises(MCPAuthAuthServerException) as exc_info:
7171
await fetch_server_config_by_well_known_url(
7272
sample_well_known_url, AuthServerType.OAUTH
7373
)
7474

75-
assert "Invalid server metadata" in str(exc_info.value)
75+
assert "The server metadata is invalid or malformed" in str(exc_info.value)
7676

7777
async def test_fetch_server_config_by_well_known_url_success_with_transpile(
7878
self, aresponses: ResponsesMockServer

0 commit comments

Comments
 (0)