Run pre-commit and fix typing issues

This commit is contained in:
Goro 2025-06-16 07:12:41 +02:00
commit 57e0b03711
6 changed files with 48 additions and 24 deletions

View file

@ -1,7 +1,9 @@
import aiohttp
from fastmcp.server import FastMCP
from fastmcp.utilities.types import File
def create_server():
mcp = FastMCP(name="File Demo", instructions="Get files from the server or URL.")
@ -11,9 +13,11 @@ def create_server():
Get a test file from the server. If the path is not provided, it defaults to 'requirements.txt'.
"""
return File(path=path)
@mcp.tool()
async def get_test_pdf_from_url(url: str = "https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf") -> File:
async def get_test_pdf_from_url(
url: str = "https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf",
) -> File:
"""
Get a test PDF file from a URL. If the URL is not provided, it defaults to a sample PDF.
"""
@ -21,7 +25,9 @@ def create_server():
async with session.get(url) as response:
pdf_data = await response.read()
return File(data=pdf_data, format="pdf")
return mcp
if __name__ == "__main__":
create_server().run(transport="sse", host="0.0.0.0", port=8001, path="/sse")
create_server().run(transport="sse", host="0.0.0.0", port=8001, path="/sse")

View file

@ -18,8 +18,8 @@ from fastmcp.utilities.json_schema import compress_schema
from fastmcp.utilities.logging import get_logger
from fastmcp.utilities.types import (
Audio,
Image,
File,
Image,
MCPContent,
find_kwarg_by_type,
get_cached_typeadapter,

View file

@ -2,22 +2,22 @@
import base64
import inspect
import mimetypes
from collections.abc import Callable
from functools import lru_cache
from pathlib import Path
from types import UnionType
from typing import Annotated, TypeAlias, TypeVar, Union, get_args, get_origin
import mimetypes
from mcp.types import (
Annotations,
AudioContent,
BlobResourceContents,
EmbeddedResource,
ImageContent,
TextContent,
BlobResourceContents
)
from pydantic import BaseModel, ConfigDict, TypeAdapter
from pydantic import AnyUrl, BaseModel, ConfigDict, TypeAdapter, UrlConstraints
T = TypeVar("T")
@ -239,7 +239,7 @@ class File:
mime_type, _ = mimetypes.guess_type(self.path)
if mime_type:
return mime_type
return "application/octet-stream"
def to_resource_content(
@ -250,23 +250,28 @@ class File:
if self.path:
with open(self.path, "rb") as f:
data = base64.b64encode(f.read()).decode()
uri=self.path.resolve().as_uri()
uri_str = self.path.resolve().as_uri()
elif self.data is not None:
data = base64.b64encode(self.data).decode()
uri=self.path or (self._name and f"file:///{self._name}.{self._mime_type.split('/')[1]}") or f"file:///resource.{self._mime_type.split('/')[1]}"
if self._name:
uri_str = f"file:///{self._name}.{self._mime_type.split('/')[1]}"
else:
uri_str = f"file:///resource.{self._mime_type.split('/')[1]}"
else:
raise ValueError("No resource data available")
UriType = Annotated[AnyUrl, UrlConstraints(host_required=False)]
uri = TypeAdapter(UriType).validate_python(uri_str)
resource = BlobResourceContents(
blob=data,
mimeType=mime_type or self._mime_type,
uri=uri,
)
blob=data,
mimeType=mime_type or self._mime_type,
uri=uri,
)
return EmbeddedResource(
type="resource",
resource=resource,
annotations=annotations or self.annotations,
)
)

View file

@ -11,11 +11,11 @@ import pytest
from mcp import McpError
from mcp.types import (
AudioContent,
BlobResourceContents,
EmbeddedResource,
ImageContent,
TextContent,
TextResourceContents,
BlobResourceContents,
)
from pydantic import AnyUrl, Field
@ -62,7 +62,14 @@ def tool_server():
return [
TextContent(type="text", text="Hello"),
ImageContent(type="image", data="abc", mimeType="application/octet-stream"),
EmbeddedResource(type="resource", resource=BlobResourceContents(blob="abc", mimeType="application/octet-stream", uri=AnyUrl("abc"))),
EmbeddedResource(
type="resource",
resource=BlobResourceContents(
blob=base64.b64encode(b"abc").decode(),
mimeType="application/octet-stream",
uri=AnyUrl("file:///test.bin"),
),
),
]
@mcp.tool

View file

@ -113,7 +113,7 @@ class TestToolFromFunction:
result = await tool.run({"data": "test.wav"})
assert tool.parameters["properties"]["data"]["type"] == "string"
assert isinstance(result[0], AudioContent)
async def test_tool_with_file_return(self):
def file_tool(data: bytes) -> File:
return File(data=data, format="octet-stream")
@ -620,7 +620,7 @@ class TestConvertResultToContent:
text_content_count = sum(isinstance(item, TextContent) for item in result)
embedded_content_count = sum(
isinstance(item, EmbeddedResource) and item.type == "resource"
isinstance(item, EmbeddedResource) and item.type == "resource"
for item in result
)
@ -631,7 +631,8 @@ class TestConvertResultToContent:
assert text_item.text == '{\n "a": 1\n}'
embedded_item = next(
item for item in result
item
for item in result
if isinstance(item, EmbeddedResource) and item.type == "resource"
)
resource = embedded_item.resource

View file

@ -3,6 +3,7 @@ from types import EllipsisType
from typing import Annotated, Any
import pytest
from mcp.types import BlobResourceContents
from fastmcp.utilities.types import (
Audio,
@ -345,7 +346,9 @@ class TestFile:
def test_get_mime_type_from_path(self, tmp_path):
"""Test MIME type detection from file extension."""
file_path = tmp_path / "test.txt"
file_path.write_text("test content") # Need to write content for MIME type detection
file_path.write_text(
"test content"
) # Need to write content for MIME type detection
file = File(path=file_path)
# The MIME type should be detected from the .txt extension
assert file._mime_type == "text/plain"
@ -363,7 +366,8 @@ class TestFile:
assert resource.resource.mimeType == "text/plain"
# Convert both to strings for comparison
assert str(resource.resource.uri) == file_path.resolve().as_uri()
assert resource.resource.blob == base64.b64encode(test_data).decode()
if isinstance(resource.resource, BlobResourceContents):
assert resource.resource.blob == base64.b64encode(test_data).decode()
def test_to_resource_content_with_data(self):
"""Test conversion to ResourceContent with data."""
@ -375,7 +379,8 @@ class TestFile:
assert resource.resource.mimeType == "application/pdf"
# Convert URI to string for comparison
assert str(resource.resource.uri) == "file:///resource.pdf"
assert resource.resource.blob == base64.b64encode(test_data).decode()
if isinstance(resource.resource, BlobResourceContents):
assert resource.resource.blob == base64.b64encode(test_data).decode()
def test_to_resource_content_error(self, monkeypatch):
"""Test error case in to_resource_content."""