Merge pull request #207 from hmwildermuth/main

Update URL validation to allow file and other nonstandard schemas
This commit is contained in:
Justin Spahr-Summers 2025-02-13 23:05:59 +00:00 • committed by GitHub
commit 5480f67cf9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
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.