fix: change Resource URI fields from AnyUrl to str (#1863)

This commit is contained in:
Felix Weinberger 2026-01-16 08:58:57 +00:00 • committed by GitHub
commit cfb2909631
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 343 additions and 179 deletions

View file

@ -279,24 +279,24 @@ class ClientSession(
types.ListResourceTemplatesResult,
)
async def read_resource(self, uri: AnyUrl) -> types.ReadResourceResult:
async def read_resource(self, uri: str | AnyUrl) -> types.ReadResourceResult:
"""Send a resources/read request."""
return await self.send_request(
types.ClientRequest(types.ReadResourceRequest(params=types.ReadResourceRequestParams(uri=uri))),
types.ClientRequest(types.ReadResourceRequest(params=types.ReadResourceRequestParams(uri=str(uri)))),
types.ReadResourceResult,
)
async def subscribe_resource(self, uri: AnyUrl) -> types.EmptyResult:
async def subscribe_resource(self, uri: str | AnyUrl) -> types.EmptyResult:
"""Send a resources/subscribe request."""
return await self.send_request( # pragma: no cover
types.ClientRequest(types.SubscribeRequest(params=types.SubscribeRequestParams(uri=uri))),
types.ClientRequest(types.SubscribeRequest(params=types.SubscribeRequestParams(uri=str(uri)))),
types.EmptyResult,
)
async def unsubscribe_resource(self, uri: AnyUrl) -> types.EmptyResult:
async def unsubscribe_resource(self, uri: str | AnyUrl) -> types.EmptyResult:
"""Send a resources/unsubscribe request."""
return await self.send_request( # pragma: no cover
types.ClientRequest(types.UnsubscribeRequest(params=types.UnsubscribeRequestParams(uri=uri))),
types.ClientRequest(types.UnsubscribeRequest(params=types.UnsubscribeRequestParams(uri=str(uri)))),
types.EmptyResult,
)

View file

@ -1,14 +1,12 @@
"""Base classes and interfaces for FastMCP resources."""
import abc
from typing import Annotated, Any
from typing import Any
from pydantic import (
AnyUrl,
BaseModel,
ConfigDict,
Field,
UrlConstraints,
ValidationInfo,
field_validator,
)
@ -21,7 +19,7 @@ class Resource(BaseModel, abc.ABC):
model_config = ConfigDict(validate_default=True)
uri: Annotated[AnyUrl, UrlConstraints(host_required=False)] = Field(default=..., description="URI of the resource")
uri: str = Field(default=..., description="URI of the resource")
name: str | None = Field(description="Name of the resource", default=None)
title: str | None = Field(description="Human-readable title of the resource", default=None)
description: str | None = Field(description="Description of the resource", default=None)

View file

@ -11,7 +11,7 @@ import anyio.to_thread
import httpx
import pydantic
import pydantic_core
from pydantic import AnyUrl, Field, ValidationInfo, validate_call
from pydantic import Field, ValidationInfo, validate_call
from mcp.server.fastmcp.resources.base import Resource
from mcp.types import Annotations, Icon
@ -94,7 +94,7 @@ class FunctionResource(Resource):
fn = validate_call(fn)
return cls(
uri=AnyUrl(uri),
uri=uri,
name=func_name,
title=title,
description=description or fn.__doc__ or "",

View file

@ -79,7 +79,6 @@ from typing import Any, Generic, TypeAlias, cast
import anyio
import jsonschema
from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream
from pydantic import AnyUrl
from typing_extensions import TypeVar
import mcp.types as types
@ -337,7 +336,7 @@ class Server(Generic[LifespanResultT, RequestT]):
def read_resource(self):
def decorator(
func: Callable[[AnyUrl], Awaitable[str | bytes | Iterable[ReadResourceContents]]],
func: Callable[[str], Awaitable[str | bytes | Iterable[ReadResourceContents]]],
):
logger.debug("Registering handler for ReadResourceRequest")
@ -412,7 +411,7 @@ class Server(Generic[LifespanResultT, RequestT]):
return decorator
def subscribe_resource(self): # pragma: no cover
def decorator(func: Callable[[AnyUrl], Awaitable[None]]):
def decorator(func: Callable[[str], Awaitable[None]]):
logger.debug("Registering handler for SubscribeRequest")
async def handler(req: types.SubscribeRequest):
@ -425,7 +424,7 @@ class Server(Generic[LifespanResultT, RequestT]):
return decorator
def unsubscribe_resource(self): # pragma: no cover
def decorator(func: Callable[[AnyUrl], Awaitable[None]]):
def decorator(func: Callable[[str], Awaitable[None]]):
logger.debug("Registering handler for UnsubscribeRequest")
async def handler(req: types.UnsubscribeRequest):

View file

@ -227,12 +227,12 @@ class ServerSession(
related_request_id,
)
async def send_resource_updated(self, uri: AnyUrl) -> None: # pragma: no cover
async def send_resource_updated(self, uri: str | AnyUrl) -> None: # pragma: no cover
"""Send a resource updated notification."""
await self.send_notification(
types.ServerNotification(
types.ResourceUpdatedNotification(
params=types.ResourceUpdatedNotificationParams(uri=uri),
params=types.ResourceUpdatedNotificationParams(uri=str(uri)),
)
)
)

View file

@ -5,7 +5,6 @@ from datetime import datetime
from typing import Annotated, Any, Final, Generic, Literal, TypeAlias, TypeVar
from pydantic import BaseModel, ConfigDict, Field, FileUrl, RootModel
from pydantic.networks import AnyUrl, UrlConstraints
LATEST_PROTOCOL_VERSION = "2025-11-25"
@ -756,7 +755,7 @@ class Annotations(BaseModel):
class Resource(BaseMetadata):
"""A known resource that the server is capable of reading."""
uri: Annotated[AnyUrl, UrlConstraints(host_required=False)]
uri: str
"""The URI of this resource."""
description: str | None = None
"""A description of what this resource represents."""
@ -827,7 +826,7 @@ class ListResourceTemplatesResult(PaginatedResult):
class ReadResourceRequestParams(RequestParams):
"""Parameters for reading a resource."""
uri: Annotated[AnyUrl, UrlConstraints(host_required=False)]
uri: str
"""
The URI of the resource to read. The URI can use any protocol; it is up to the
server how to interpret it.
@ -845,7 +844,7 @@ class ReadResourceRequest(Request[ReadResourceRequestParams, Literal["resources/
class ResourceContents(BaseModel):
"""The contents of a specific resource or sub-resource."""
uri: Annotated[AnyUrl, UrlConstraints(host_required=False)]
uri: str
"""The URI of this resource."""
mimeType: str | None = None
"""The MIME type of this resource, if known."""
@ -895,7 +894,7 @@ class ResourceListChangedNotification(
class SubscribeRequestParams(RequestParams):
"""Parameters for subscribing to a resource."""
uri: Annotated[AnyUrl, UrlConstraints(host_required=False)]
uri: str
"""
The URI of the resource to subscribe to. The URI can use any protocol; it is up to
the server how to interpret it.
@ -916,7 +915,7 @@ class SubscribeRequest(Request[SubscribeRequestParams, Literal["resources/subscr
class UnsubscribeRequestParams(RequestParams):
"""Parameters for unsubscribing from a resource."""
uri: Annotated[AnyUrl, UrlConstraints(host_required=False)]
uri: str
"""The URI of the resource to unsubscribe from."""
model_config = ConfigDict(extra="allow")
@ -934,7 +933,7 @@ class UnsubscribeRequest(Request[UnsubscribeRequestParams, Literal["resources/un
class ResourceUpdatedNotificationParams(NotificationParams):
"""Parameters for resource update notifications."""
uri: Annotated[AnyUrl, UrlConstraints(host_required=False)]
uri: str
"""
The URI of the resource that has been updated. This might be a sub-resource of the
one that the client actually subscribed to.