|
10 | 10 | from mcp.server.fastmcp import Context, FastMCP
|
11 | 11 | from mcp.server.fastmcp.prompts.base import Message, UserMessage
|
12 | 12 | from mcp.server.fastmcp.resources import FileResource, FunctionResource
|
13 |
| -from mcp.server.fastmcp.utilities.types import Image |
| 13 | +from mcp.server.fastmcp.utilities.types import Audio, Image |
14 | 14 | from mcp.shared.exceptions import McpError
|
15 | 15 | from mcp.shared.memory import (
|
16 | 16 | create_connected_server_and_client_session as client_session,
|
@@ -194,6 +194,10 @@ def image_tool_fn(path: str) -> Image:
|
194 | 194 | return Image(path)
|
195 | 195 |
|
196 | 196 |
|
| 197 | +def audio_tool_fn(path: str) -> Audio: |
| 198 | + return Audio(path) |
| 199 | + |
| 200 | + |
197 | 201 | def mixed_content_tool_fn() -> list[ContentBlock]:
|
198 | 202 | return [
|
199 | 203 | TextContent(type="text", text="Hello"),
|
@@ -299,6 +303,27 @@ async def test_tool_image_helper(self, tmp_path: Path):
|
299 | 303 | # Check structured content - Image return type should NOT have structured output
|
300 | 304 | assert result.structuredContent is None
|
301 | 305 |
|
| 306 | + @pytest.mark.anyio |
| 307 | + async def test_tool_audio_helper(self, tmp_path: Path): |
| 308 | + # Create a test audio |
| 309 | + audio_path = tmp_path / "test.wav" |
| 310 | + audio_path.write_bytes(b"fake wav data") |
| 311 | + |
| 312 | + mcp = FastMCP() |
| 313 | + mcp.add_tool(audio_tool_fn) |
| 314 | + async with client_session(mcp._mcp_server) as client: |
| 315 | + result = await client.call_tool("audio_tool_fn", {"path": str(audio_path)}) |
| 316 | + assert len(result.content) == 1 |
| 317 | + content = result.content[0] |
| 318 | + assert isinstance(content, AudioContent) |
| 319 | + assert content.type == "audio" |
| 320 | + assert content.mimeType == "audio/wav" |
| 321 | + # Verify base64 encoding |
| 322 | + decoded = base64.b64decode(content.data) |
| 323 | + assert decoded == b"fake wav data" |
| 324 | + # Check structured content - Image return type should NOT have structured output |
| 325 | + assert result.structuredContent is None |
| 326 | + |
302 | 327 | @pytest.mark.anyio
|
303 | 328 | async def test_tool_mixed_content(self):
|
304 | 329 | mcp = FastMCP()
|
@@ -371,6 +396,47 @@ def mixed_list_fn() -> list:
|
371 | 396 | # Check structured content - untyped list with Image objects should NOT have structured output
|
372 | 397 | assert result.structuredContent is None
|
373 | 398 |
|
| 399 | + @pytest.mark.anyio |
| 400 | + async def test_tool_mixed_list_with_audio(self, tmp_path: Path): |
| 401 | + """Test that lists containing Audio objects and other types are handled |
| 402 | + correctly""" |
| 403 | + # Create a test audio |
| 404 | + audio_path = tmp_path / "test.wav" |
| 405 | + audio_path.write_bytes(b"test audio data") |
| 406 | + |
| 407 | + def mixed_list_fn() -> list: |
| 408 | + return [ |
| 409 | + "text message", |
| 410 | + Audio(audio_path), |
| 411 | + {"key": "value"}, |
| 412 | + TextContent(type="text", text="direct content"), |
| 413 | + ] |
| 414 | + |
| 415 | + mcp = FastMCP() |
| 416 | + mcp.add_tool(mixed_list_fn) |
| 417 | + async with client_session(mcp._mcp_server) as client: |
| 418 | + result = await client.call_tool("mixed_list_fn", {}) |
| 419 | + assert len(result.content) == 4 |
| 420 | + # Check text conversion |
| 421 | + content1 = result.content[0] |
| 422 | + assert isinstance(content1, TextContent) |
| 423 | + assert content1.text == "text message" |
| 424 | + # Check audio conversion |
| 425 | + content2 = result.content[1] |
| 426 | + assert isinstance(content2, AudioContent) |
| 427 | + assert content2.mimeType == "audio/wav" |
| 428 | + assert base64.b64decode(content2.data) == b"test audio data" |
| 429 | + # Check dict conversion |
| 430 | + content3 = result.content[2] |
| 431 | + assert isinstance(content3, TextContent) |
| 432 | + assert '"key": "value"' in content3.text |
| 433 | + # Check direct TextContent |
| 434 | + content4 = result.content[3] |
| 435 | + assert isinstance(content4, TextContent) |
| 436 | + assert content4.text == "direct content" |
| 437 | + # Check structured content - untyped list with Audio objects should NOT have structured output |
| 438 | + assert result.structuredContent is None |
| 439 | + |
374 | 440 | @pytest.mark.anyio
|
375 | 441 | async def test_tool_structured_output_basemodel(self):
|
376 | 442 | """Test tool with structured output returning BaseModel"""
|
|
0 commit comments