Skip to content

Commit b905460

Browse files
committed
fix: tests for audio, init
1 parent 40294d5 commit b905460

File tree

2 files changed

+41
-74
lines changed

2 files changed

+41
-74
lines changed

src/mcp/server/fastmcp/utilities/types.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,9 @@ def __init__(
6363
data: bytes | None = None,
6464
format: str | None = None,
6565
):
66-
if path is None and data is None:
67-
raise ValueError("Either path or data must be provided")
68-
if path is not None and data is not None:
69-
raise ValueError("Only one of path or data can be provided")
70-
66+
if not bool(path) ^ bool(data):
67+
raise ValueError("Either path or data can be provided")
68+
7169
self.path = Path(path) if path else None
7270
self.data = data
7371
self._format = format

tests/server/fastmcp/test_server.py

Lines changed: 38 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -324,38 +324,38 @@ async def test_tool_audio_helper(self, tmp_path: Path):
324324
# Check structured content - Image return type should NOT have structured output
325325
assert result.structuredContent is None
326326

327-
@pytest.mark.anyio
328-
async def test_tool_audio_suffix_detection(self, tmp_path: Path):
329-
# Test different audio file extensions
330-
test_cases = [
327+
@pytest.mark.parametrize(
328+
"filename,expected_mime_type",
329+
[
331330
("test.wav", "audio/wav"),
332331
("test.mp3", "audio/mpeg"),
333332
("test.ogg", "audio/ogg"),
334333
("test.flac", "audio/flac"),
335334
("test.aac", "audio/aac"),
336335
("test.m4a", "audio/mp4"),
337336
("test.unknown", "application/octet-stream"), # Unknown extension fallback
338-
]
339-
337+
],
338+
)
339+
@pytest.mark.anyio
340+
async def test_tool_audio_suffix_detection(self, tmp_path: Path, filename: str, expected_mime_type: str):
341+
"""Test that Audio helper correctly detects MIME types from file suffixes"""
340342
mcp = FastMCP()
341343
mcp.add_tool(audio_tool_fn)
342-
async with client_session(mcp._mcp_server) as client:
343-
for filename, expected_mime_type in test_cases:
344-
# Create a test audio file with the specific extension
345-
audio_path = tmp_path / filename
346-
audio_path.write_bytes(b"fake audio data")
344+
345+
# Create a test audio file with the specific extension
346+
audio_path = tmp_path / filename
347+
audio_path.write_bytes(b"fake audio data")
347348

348-
result = await client.call_tool("audio_tool_fn", {"path": str(audio_path)})
349-
assert len(result.content) == 1
350-
content = result.content[0]
351-
assert isinstance(content, AudioContent)
352-
assert content.type == "audio"
353-
assert content.mimeType == expected_mime_type, (
354-
f"Expected {expected_mime_type} for {filename}, got {content.mimeType}"
355-
)
356-
# Verify base64 encoding
357-
decoded = base64.b64decode(content.data)
358-
assert decoded == b"fake audio data"
349+
async with client_session(mcp._mcp_server) as client:
350+
result = await client.call_tool("audio_tool_fn", {"path": str(audio_path)})
351+
assert len(result.content) == 1
352+
content = result.content[0]
353+
assert isinstance(content, AudioContent)
354+
assert content.type == "audio"
355+
assert content.mimeType == expected_mime_type
356+
# Verify base64 encoding
357+
decoded = base64.b64decode(content.data)
358+
assert decoded == b"fake audio data"
359359

360360
@pytest.mark.anyio
361361
async def test_tool_mixed_content(self):
@@ -389,17 +389,22 @@ async def test_tool_mixed_content(self):
389389
assert structured_result[i][key] == value
390390

391391
@pytest.mark.anyio
392-
async def test_tool_mixed_list_with_image(self, tmp_path: Path):
392+
async def test_tool_mixed_list_with_audio_and_image(self, tmp_path: Path):
393393
"""Test that lists containing Image objects and other types are handled
394394
correctly"""
395395
# Create a test image
396396
image_path = tmp_path / "test.png"
397397
image_path.write_bytes(b"test image data")
398398

399+
# Create a test audio
400+
audio_path = tmp_path / "test.wav"
401+
audio_path.write_bytes(b"test audio data")
402+
399403
def mixed_list_fn() -> list:
400404
return [
401405
"text message",
402406
Image(image_path),
407+
Audio(audio_path),
403408
{"key": "value"},
404409
TextContent(type="text", text="direct content"),
405410
]
@@ -418,56 +423,20 @@ def mixed_list_fn() -> list:
418423
assert isinstance(content2, ImageContent)
419424
assert content2.mimeType == "image/png"
420425
assert base64.b64decode(content2.data) == b"test image data"
421-
# Check dict conversion
422-
content3 = result.content[2]
423-
assert isinstance(content3, TextContent)
424-
assert '"key": "value"' in content3.text
425-
# Check direct TextContent
426-
content4 = result.content[3]
427-
assert isinstance(content4, TextContent)
428-
assert content4.text == "direct content"
429-
# Check structured content - untyped list with Image objects should NOT have structured output
430-
assert result.structuredContent is None
431-
432-
@pytest.mark.anyio
433-
async def test_tool_mixed_list_with_audio(self, tmp_path: Path):
434-
"""Test that lists containing Audio objects and other types are handled
435-
correctly"""
436-
# Create a test audio
437-
audio_path = tmp_path / "test.wav"
438-
audio_path.write_bytes(b"test audio data")
439-
440-
def mixed_list_fn() -> list:
441-
return [
442-
"text message",
443-
Audio(audio_path),
444-
{"key": "value"},
445-
TextContent(type="text", text="direct content"),
446-
]
447-
448-
mcp = FastMCP()
449-
mcp.add_tool(mixed_list_fn)
450-
async with client_session(mcp._mcp_server) as client:
451-
result = await client.call_tool("mixed_list_fn", {})
452-
assert len(result.content) == 4
453-
# Check text conversion
454-
content1 = result.content[0]
455-
assert isinstance(content1, TextContent)
456-
assert content1.text == "text message"
457426
# Check audio conversion
458-
content2 = result.content[1]
459-
assert isinstance(content2, AudioContent)
460-
assert content2.mimeType == "audio/wav"
461-
assert base64.b64decode(content2.data) == b"test audio data"
462-
# Check dict conversion
463427
content3 = result.content[2]
464-
assert isinstance(content3, TextContent)
465-
assert '"key": "value"' in content3.text
466-
# Check direct TextContent
428+
assert isinstance(content3, AudioContent)
429+
assert content3.mimeType == "audio/wav"
430+
assert base64.b64decode(content3.data) == b"test audio data"
431+
# Check dict conversion
467432
content4 = result.content[3]
468433
assert isinstance(content4, TextContent)
469-
assert content4.text == "direct content"
470-
# Check structured content - untyped list with Audio objects should NOT have structured output
434+
assert '"key": "value"' in content4.text
435+
# Check direct TextContent
436+
content5 = result.content[4]
437+
assert isinstance(content5, TextContent)
438+
assert content5.text == "direct content"
439+
# Check structured content - untyped list with Image objects should NOT have structured output
471440
assert result.structuredContent is None
472441

473442
@pytest.mark.anyio

0 commit comments

Comments
 (0)