Update URL validation to allow file and other nonstandard schemas

This commit is contained in:
Henry Wildermuth
2025-02-13 14:21:50 -08:00
parent faf3a9d7d6
commit b53e090299
2 changed files with 19 additions and 12 deletions

View File

@@ -2,7 +2,7 @@ import anyio
import click
import mcp.types as types
from mcp.server.lowlevel import Server
from pydantic import AnyUrl
from pydantic import FileUrl
SAMPLE_RESOURCES = {
"greeting": "Hello! This is a sample text resource.",
@@ -26,7 +26,7 @@ def main(port: int, transport: str) -> int:
async def list_resources() -> list[types.Resource]:
return [
types.Resource(
uri=AnyUrl(f"file:///{name}.txt"),
uri=FileUrl(f"file:///{name}.txt"),
name=name,
description=f"A sample text resource named {name}",
mimeType="text/plain",
@@ -35,8 +35,7 @@ def main(port: int, transport: str) -> int:
]
@app.read_resource()
async def read_resource(uri: AnyUrl) -> str | bytes:
assert uri.path is not None
async def read_resource(uri: FileUrl) -> str | bytes:
name = uri.path.replace(".txt", "").lstrip("/")
if name not in SAMPLE_RESOURCES:

View File

@@ -1,7 +1,15 @@
from typing import Annotated, Any, Callable, Generic, Literal, TypeAlias, TypeVar
from typing import (
Annotated,
Any,
Callable,
Generic,
Literal,
TypeAlias,
TypeVar,
)
from pydantic import BaseModel, ConfigDict, Field, FileUrl, RootModel
from pydantic.networks import AnyUrl
from pydantic.networks import AnyUrl, UrlConstraints
"""
Model Context Protocol bindings for Python
@@ -353,7 +361,7 @@ class Annotations(BaseModel):
class Resource(BaseModel):
"""A known resource that the server is capable of reading."""
uri: AnyUrl
uri: Annotated[AnyUrl, UrlConstraints(host_required=False)]
"""The URI of this resource."""
name: str
"""A human-readable name for this resource."""
@@ -415,7 +423,7 @@ class ListResourceTemplatesResult(PaginatedResult):
class ReadResourceRequestParams(RequestParams):
"""Parameters for reading a resource."""
uri: AnyUrl
uri: Annotated[AnyUrl, UrlConstraints(host_required=False)]
"""
The URI of the resource to read. The URI can use any protocol; it is up to the
server how to interpret it.
@@ -433,7 +441,7 @@ class ReadResourceRequest(Request):
class ResourceContents(BaseModel):
"""The contents of a specific resource or sub-resource."""
uri: AnyUrl
uri: Annotated[AnyUrl, UrlConstraints(host_required=False)]
"""The URI of this resource."""
mimeType: str | None = None
"""The MIME type of this resource, if known."""
@@ -476,7 +484,7 @@ class ResourceListChangedNotification(Notification):
class SubscribeRequestParams(RequestParams):
"""Parameters for subscribing to a resource."""
uri: AnyUrl
uri: Annotated[AnyUrl, UrlConstraints(host_required=False)]
"""
The URI of the resource to subscribe to. The URI can use any protocol; it is up to
the server how to interpret it.
@@ -497,7 +505,7 @@ class SubscribeRequest(Request):
class UnsubscribeRequestParams(RequestParams):
"""Parameters for unsubscribing from a resource."""
uri: AnyUrl
uri: Annotated[AnyUrl, UrlConstraints(host_required=False)]
"""The URI of the resource to unsubscribe from."""
model_config = ConfigDict(extra="allow")
@@ -515,7 +523,7 @@ class UnsubscribeRequest(Request):
class ResourceUpdatedNotificationParams(NotificationParams):
"""Parameters for resource update notifications."""
uri: AnyUrl
uri: Annotated[AnyUrl, UrlConstraints(host_required=False)]
"""
The URI of the resource that has been updated. This might be a sub-resource of the
one that the client actually subscribed to.