Spaces:
Running
Running
Goro commited on
Commit ·
135c70a
1
Parent(s): 12a3d14
Add missing tests
Browse files
src/fastmcp/utilities/types.py
CHANGED
|
@@ -234,7 +234,11 @@ class File:
|
|
| 234 |
def _get_mime_type(self) -> str:
|
| 235 |
"""Get MIME type from format or guess from file extension."""
|
| 236 |
if self._format:
|
| 237 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 238 |
|
| 239 |
if self.path:
|
| 240 |
mime_type, _ = mimetypes.guess_type(self.path)
|
|
|
|
| 234 |
def _get_mime_type(self) -> str:
|
| 235 |
"""Get MIME type from format or guess from file extension."""
|
| 236 |
if self._format:
|
| 237 |
+
fmt = self._format.lower()
|
| 238 |
+
# Map common text formats to text/plain
|
| 239 |
+
if fmt in {"plain", "txt", "text"}:
|
| 240 |
+
return "text/plain"
|
| 241 |
+
return f"application/{fmt}"
|
| 242 |
|
| 243 |
if self.path:
|
| 244 |
mime_type, _ = mimetypes.guess_type(self.path)
|
tests/server/test_server_interactions.py
CHANGED
|
@@ -99,6 +99,11 @@ def tool_server():
|
|
| 99 |
TextContent(type="text", text="direct content"),
|
| 100 |
]
|
| 101 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 102 |
return mcp
|
| 103 |
|
| 104 |
|
|
@@ -110,7 +115,7 @@ class TestTools:
|
|
| 110 |
|
| 111 |
async def test_list_tools(self, tool_server: FastMCP):
|
| 112 |
async with Client(tool_server) as client:
|
| 113 |
-
assert len(await client.list_tools()) ==
|
| 114 |
|
| 115 |
async def test_call_tool(self, tool_server: FastMCP):
|
| 116 |
async with Client(tool_server) as client:
|
|
@@ -151,6 +156,17 @@ class TestTools:
|
|
| 151 |
result = await client.call_tool("list_tool", {})
|
| 152 |
assert result[0].text == '[\n "x",\n 2\n]' # type: ignore[attr-defined]
|
| 153 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 154 |
|
| 155 |
class TestToolTags:
|
| 156 |
def create_server(self, include_tags=None, exclude_tags=None):
|
|
|
|
| 99 |
TextContent(type="text", text="direct content"),
|
| 100 |
]
|
| 101 |
|
| 102 |
+
@mcp.tool
|
| 103 |
+
def file_text_tool() -> File:
|
| 104 |
+
# Return a File with text data and text/plain format
|
| 105 |
+
return File(data=b"hello world", format="plain")
|
| 106 |
+
|
| 107 |
return mcp
|
| 108 |
|
| 109 |
|
|
|
|
| 115 |
|
| 116 |
async def test_list_tools(self, tool_server: FastMCP):
|
| 117 |
async with Client(tool_server) as client:
|
| 118 |
+
assert len(await client.list_tools()) == 11
|
| 119 |
|
| 120 |
async def test_call_tool(self, tool_server: FastMCP):
|
| 121 |
async with Client(tool_server) as client:
|
|
|
|
| 156 |
result = await client.call_tool("list_tool", {})
|
| 157 |
assert result[0].text == '[\n "x",\n 2\n]' # type: ignore[attr-defined]
|
| 158 |
|
| 159 |
+
async def test_file_text_tool(self, tool_server: FastMCP):
|
| 160 |
+
async with Client(tool_server) as client:
|
| 161 |
+
result = await client.call_tool("file_text_tool", {})
|
| 162 |
+
assert len(result) == 1
|
| 163 |
+
embedded = result[0]
|
| 164 |
+
assert isinstance(embedded, EmbeddedResource)
|
| 165 |
+
resource = embedded.resource
|
| 166 |
+
assert isinstance(resource, TextResourceContents)
|
| 167 |
+
assert resource.mimeType == "text/plain"
|
| 168 |
+
assert resource.text == "hello world"
|
| 169 |
+
|
| 170 |
|
| 171 |
class TestToolTags:
|
| 172 |
def create_server(self, include_tags=None, exclude_tags=None):
|
tests/tools/test_tool.py
CHANGED
|
@@ -502,6 +502,19 @@ class TestConvertResultToContent:
|
|
| 502 |
# Convert URI to string for startswith check
|
| 503 |
assert str(resource.uri).startswith("file:///resource.octet-stream")
|
| 504 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 505 |
def test_basic_type_result(self):
|
| 506 |
"""Test that a basic type is converted to TextContent."""
|
| 507 |
result = _convert_to_content(123)
|
|
|
|
| 502 |
# Convert URI to string for startswith check
|
| 503 |
assert str(resource.uri).startswith("file:///resource.octet-stream")
|
| 504 |
|
| 505 |
+
def test_file_object_text_result(self):
|
| 506 |
+
"""Test that a File object with text data is converted to EmbeddedResource with TextResourceContents."""
|
| 507 |
+
file_obj = File(data=b"sometext", format="plain")
|
| 508 |
+
result = _convert_to_content(file_obj)
|
| 509 |
+
assert isinstance(result, list)
|
| 510 |
+
assert len(result) == 1
|
| 511 |
+
assert isinstance(result[0], EmbeddedResource)
|
| 512 |
+
assert result[0].type == "resource"
|
| 513 |
+
resource = result[0].resource
|
| 514 |
+
assert isinstance(resource, TextResourceContents)
|
| 515 |
+
assert resource.mimeType == "text/plain"
|
| 516 |
+
assert resource.text == "sometext"
|
| 517 |
+
|
| 518 |
def test_basic_type_result(self):
|
| 519 |
"""Test that a basic type is converted to TextContent."""
|
| 520 |
result = _convert_to_content(123)
|
tests/utilities/test_types.py
CHANGED
|
@@ -3,7 +3,7 @@ from types import EllipsisType
|
|
| 3 |
from typing import Annotated, Any
|
| 4 |
|
| 5 |
import pytest
|
| 6 |
-
from mcp.types import BlobResourceContents
|
| 7 |
|
| 8 |
from fastmcp.utilities.types import (
|
| 9 |
Audio,
|
|
@@ -382,6 +382,17 @@ class TestFile:
|
|
| 382 |
if isinstance(resource.resource, BlobResourceContents):
|
| 383 |
assert resource.resource.blob == base64.b64encode(test_data).decode()
|
| 384 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 385 |
def test_to_resource_content_error(self, monkeypatch):
|
| 386 |
"""Test error case in to_resource_content."""
|
| 387 |
file = File(data=b"test")
|
|
|
|
| 3 |
from typing import Annotated, Any
|
| 4 |
|
| 5 |
import pytest
|
| 6 |
+
from mcp.types import BlobResourceContents, TextResourceContents
|
| 7 |
|
| 8 |
from fastmcp.utilities.types import (
|
| 9 |
Audio,
|
|
|
|
| 382 |
if isinstance(resource.resource, BlobResourceContents):
|
| 383 |
assert resource.resource.blob == base64.b64encode(test_data).decode()
|
| 384 |
|
| 385 |
+
def test_to_resource_content_with_text_data(self):
|
| 386 |
+
"""Test conversion to ResourceContent with text data (TextResourceContents)."""
|
| 387 |
+
test_data = b"hello world"
|
| 388 |
+
file = File(data=test_data, format="plain")
|
| 389 |
+
resource = file.to_resource_content()
|
| 390 |
+
assert resource.type == "resource"
|
| 391 |
+
# Should be TextResourceContents for text/plain
|
| 392 |
+
assert isinstance(resource.resource, TextResourceContents)
|
| 393 |
+
assert resource.resource.mimeType == "text/plain"
|
| 394 |
+
assert resource.resource.text == "hello world"
|
| 395 |
+
|
| 396 |
def test_to_resource_content_error(self, monkeypatch):
|
| 397 |
"""Test error case in to_resource_content."""
|
| 398 |
file = File(data=b"test")
|