Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update URL validation to allow file and other nonstandard schemas #207

Merged
merged 1 commit into from
Feb 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
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.",
Expand All @@ -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",
Expand All @@ -35,8 +35,7 @@ async def list_resources() -> list[types.Resource]:
]

@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:
Expand Down
24 changes: 16 additions & 8 deletions src/mcp/types.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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."""
Expand Down Expand Up @@ -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.
Expand All @@ -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."""
Expand Down Expand Up @@ -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.
Expand All @@ -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")

Expand All @@ -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.
Expand Down