From ec74c0293d6cfb40d7f8ee37fdc16455bd68a0e1 Mon Sep 17 00:00:00 2001 From: Jeremiah Lowin <153965+jlowin@users.noreply.github.com> Date: Mon, 2 Dec 2024 12:47:36 -0500 Subject: [PATCH 1/2] Remove BaseURL reference and use AnyURL --- src/fastmcp/resources/base.py | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/src/fastmcp/resources/base.py b/src/fastmcp/resources/base.py index bde9b38..11f9029 100644 --- a/src/fastmcp/resources/base.py +++ b/src/fastmcp/resources/base.py @@ -1,34 +1,16 @@ """Base classes and interfaces for FastMCP resources.""" import abc -from typing import Annotated, Union +from typing import Union from pydantic import ( AnyUrl, BaseModel, - BeforeValidator, ConfigDict, Field, - FileUrl, ValidationInfo, field_validator, ) -from pydantic.networks import _BaseUrl # TODO: remove this once pydantic is updated - - -def maybe_cast_str_to_any_url(x) -> AnyUrl: - if isinstance(x, FileUrl): - return x - elif isinstance(x, AnyUrl): - return x - elif isinstance(x, str): - if x.startswith("file://"): - return FileUrl(x) - return AnyUrl(x) - raise ValueError(f"Expected str or AnyUrl, got {type(x)}") - - -LaxAnyUrl = Annotated[_BaseUrl | str, BeforeValidator(maybe_cast_str_to_any_url)] class Resource(BaseModel, abc.ABC): @@ -36,7 +18,8 @@ class Resource(BaseModel, abc.ABC): model_config = ConfigDict(validate_default=True) - uri: LaxAnyUrl = Field(default=..., description="URI of the resource") + # uri: Annotated[AnyUrl, BeforeValidator(maybe_cast_str_to_any_url)] = Field( + uri: AnyUrl = Field(default=..., description="URI of the resource") name: str | None = Field(description="Name of the resource", default=None) description: str | None = Field( description="Description of the resource", default=None @@ -47,6 +30,12 @@ class Resource(BaseModel, abc.ABC): pattern=r"^[a-zA-Z0-9]+/[a-zA-Z0-9\-+.]+$", ) + @field_validator("uri", mode="before") + def validate_uri(cls, uri: AnyUrl | str) -> AnyUrl: + if isinstance(uri, str): + return AnyUrl(uri) + return uri + @field_validator("name", mode="before") @classmethod def set_default_name(cls, name: str | None, info: ValidationInfo) -> str: From 73b06f81d3d0de3e4f81c0f62a08aca5f13b11d8 Mon Sep 17 00:00:00 2001 From: Jeremiah Lowin <153965+jlowin@users.noreply.github.com> Date: Mon, 2 Dec 2024 12:55:20 -0500 Subject: [PATCH 2/2] Use FileURL where possible --- src/fastmcp/resources/base.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/fastmcp/resources/base.py b/src/fastmcp/resources/base.py index 11f9029..28b0834 100644 --- a/src/fastmcp/resources/base.py +++ b/src/fastmcp/resources/base.py @@ -8,6 +8,7 @@ BaseModel, ConfigDict, Field, + FileUrl, ValidationInfo, field_validator, ) @@ -33,6 +34,9 @@ class Resource(BaseModel, abc.ABC): @field_validator("uri", mode="before") def validate_uri(cls, uri: AnyUrl | str) -> AnyUrl: if isinstance(uri, str): + # AnyUrl doesn't support triple-slashes, but files do ("file:///absolute/path") + if uri.startswith("file://"): + return FileUrl(uri) return AnyUrl(uri) return uri