From 3095ce5e575345a2038d2e415b37cc00dcb63cae Mon Sep 17 00:00:00 2001 From: Jeremiah Lowin <153965+jlowin@users.noreply.github.com> Date: Tue, 10 Jun 2025 09:43:48 -0400 Subject: [PATCH] Fix field validator for resource --- src/fastmcp/resources/resource.py | 21 ++++++++++++--------- tests/resources/test_resources.py | 10 ++-------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/fastmcp/resources/resource.py b/src/fastmcp/resources/resource.py index 8155ccaa4..ad25ec10d 100644 --- a/src/fastmcp/resources/resource.py +++ b/src/fastmcp/resources/resource.py @@ -14,9 +14,10 @@ from pydantic import ( ConfigDict, Field, UrlConstraints, - ValidationInfo, field_validator, + model_validator, ) +from typing_extensions import Self from fastmcp.server.dependencies import get_context from fastmcp.utilities.components import FastMCPComponent @@ -36,6 +37,7 @@ class Resource(FastMCPComponent, abc.ABC): uri: Annotated[AnyUrl, UrlConstraints(host_required=False)] = Field( default=..., description="URI of the resource" ) + name: str = Field(default="", description="Name of the resource") mime_type: str = Field( default="text/plain", description="MIME type of the resource content", @@ -68,15 +70,16 @@ class Resource(FastMCPComponent, abc.ABC): return mime_type return "text/plain" - @field_validator("name", mode="before") - @classmethod - def set_default_name(cls, name: str | None, info: ValidationInfo) -> str: + @model_validator(mode="after") + def set_default_name(self) -> Self: """Set default name from URI if not provided.""" - if name: - return name - if uri := info.data.get("uri"): - return str(uri) - raise ValueError("Either name or uri must be provided") + if self.name: + pass + elif self.uri: + self.name = str(self.uri) + else: + raise ValueError("Either name or uri must be provided") + return self @abc.abstractmethod async def read(self) -> str | bytes: diff --git a/tests/resources/test_resources.py b/tests/resources/test_resources.py index 1621d1f3e..33e76d71e 100644 --- a/tests/resources/test_resources.py +++ b/tests/resources/test_resources.py @@ -50,18 +50,12 @@ class TestResourceValidation: ) assert resource.name == "resource://my-resource" - def test_resource_name_validation(self): - """Test name validation.""" + def test_provided_name_takes_precedence_over_uri(self): + """Test that provided name takes precedence over URI.""" def dummy_func() -> str: return "data" - # Must provide either name or URI - with pytest.raises(ValueError, match="Either name or uri must be provided"): - FunctionResource( - fn=dummy_func, - ) - # Explicit name takes precedence over URI resource = FunctionResource( uri=AnyUrl("resource://uri-name"),