fix: allow MIME type parameters in resource validation (RFC 2045) (#1755)
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
parent
b7cc25493c
commit
8ac11ec604
2 changed files with 71 additions and 1 deletions
|
|
@ -28,7 +28,7 @@ class Resource(BaseModel, abc.ABC):
|
|||
mime_type: str = Field(
|
||||
default="text/plain",
|
||||
description="MIME type of the resource content",
|
||||
pattern=r"^[a-zA-Z0-9]+/[a-zA-Z0-9\-+.]+$",
|
||||
pattern=r"^[a-zA-Z0-9]+/[a-zA-Z0-9\-+.]+(;\s*[a-zA-Z0-9\-_.]+=[a-zA-Z0-9\-_.]+)*$",
|
||||
)
|
||||
icons: list[Icon] | None = Field(default=None, description="Optional list of icons for this resource")
|
||||
annotations: Annotations | None = Field(default=None, description="Optional annotations for the resource")
|
||||
|
|
|
|||
70
tests/issues/test_1754_mime_type_parameters.py
Normal file
70
tests/issues/test_1754_mime_type_parameters.py
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
"""Test for GitHub issue #1754: MIME type validation rejects valid RFC 2045 parameters.
|
||||
|
||||
The MIME type validation regex was too restrictive and rejected valid MIME types
|
||||
with parameters like 'text/html;profile=mcp-app' which are valid per RFC 2045.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from pydantic import AnyUrl
|
||||
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
from mcp.shared.memory import (
|
||||
create_connected_server_and_client_session as client_session,
|
||||
)
|
||||
|
||||
pytestmark = pytest.mark.anyio
|
||||
|
||||
|
||||
async def test_mime_type_with_parameters():
|
||||
"""Test that MIME types with parameters are accepted (RFC 2045)."""
|
||||
mcp = FastMCP("test")
|
||||
|
||||
# This should NOT raise a validation error
|
||||
@mcp.resource("ui://widget", mime_type="text/html;profile=mcp-app")
|
||||
def widget() -> str:
|
||||
raise NotImplementedError()
|
||||
|
||||
resources = await mcp.list_resources()
|
||||
assert len(resources) == 1
|
||||
assert resources[0].mimeType == "text/html;profile=mcp-app"
|
||||
|
||||
|
||||
async def test_mime_type_with_parameters_and_space():
|
||||
"""Test MIME type with space after semicolon."""
|
||||
mcp = FastMCP("test")
|
||||
|
||||
@mcp.resource("data://json", mime_type="application/json; charset=utf-8")
|
||||
def data() -> str:
|
||||
raise NotImplementedError()
|
||||
|
||||
resources = await mcp.list_resources()
|
||||
assert len(resources) == 1
|
||||
assert resources[0].mimeType == "application/json; charset=utf-8"
|
||||
|
||||
|
||||
async def test_mime_type_with_multiple_parameters():
|
||||
"""Test MIME type with multiple parameters."""
|
||||
mcp = FastMCP("test")
|
||||
|
||||
@mcp.resource("data://multi", mime_type="text/plain; charset=utf-8; format=fixed")
|
||||
def data() -> str:
|
||||
raise NotImplementedError()
|
||||
|
||||
resources = await mcp.list_resources()
|
||||
assert len(resources) == 1
|
||||
assert resources[0].mimeType == "text/plain; charset=utf-8; format=fixed"
|
||||
|
||||
|
||||
async def test_mime_type_preserved_in_read_resource():
|
||||
"""Test that MIME type with parameters is preserved when reading resource."""
|
||||
mcp = FastMCP("test")
|
||||
|
||||
@mcp.resource("ui://my-widget", mime_type="text/html;profile=mcp-app")
|
||||
def my_widget() -> str:
|
||||
return "<html><body>Hello MCP-UI</body></html>"
|
||||
|
||||
async with client_session(mcp._mcp_server) as client:
|
||||
# Read the resource
|
||||
result = await client.read_resource(AnyUrl("ui://my-widget"))
|
||||
assert len(result.contents) == 1
|
||||
assert result.contents[0].mimeType == "text/html;profile=mcp-app"
|
||||
Loading…
Reference in a new issue