1010from  mcp .server .fastmcp  import  Context , FastMCP 
1111from  mcp .server .fastmcp .prompts .base  import  Message , UserMessage 
1212from  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 
1414from  mcp .server .session  import  ServerSession 
1515from  mcp .shared .exceptions  import  McpError 
1616from  mcp .shared .memory  import  (
@@ -195,6 +195,10 @@ def image_tool_fn(path: str) -> Image:
195195    return  Image (path )
196196
197197
198+ def  audio_tool_fn (path : str ) ->  Audio :
199+     return  Audio (path )
200+ 
201+ 
198202def  mixed_content_tool_fn () ->  list [ContentBlock ]:
199203    return  [
200204        TextContent (type = "text" , text = "Hello" ),
@@ -300,6 +304,60 @@ async def test_tool_image_helper(self, tmp_path: Path):
300304            # Check structured content - Image return type should NOT have structured output 
301305            assert  result .structuredContent  is  None 
302306
307+     @pytest .mark .anyio  
308+     async  def  test_tool_audio_helper (self , tmp_path : Path ):
309+         # Create a test audio 
310+         audio_path  =  tmp_path  /  "test.wav" 
311+         audio_path .write_bytes (b"fake wav data" )
312+ 
313+         mcp  =  FastMCP ()
314+         mcp .add_tool (audio_tool_fn )
315+         async  with  client_session (mcp ._mcp_server ) as  client :
316+             result  =  await  client .call_tool ("audio_tool_fn" , {"path" : str (audio_path )})
317+             assert  len (result .content ) ==  1 
318+             content  =  result .content [0 ]
319+             assert  isinstance (content , AudioContent )
320+             assert  content .type  ==  "audio" 
321+             assert  content .mimeType  ==  "audio/wav" 
322+             # Verify base64 encoding 
323+             decoded  =  base64 .b64decode (content .data )
324+             assert  decoded  ==  b"fake wav data" 
325+             # Check structured content - Image return type should NOT have structured output 
326+             assert  result .structuredContent  is  None 
327+ 
328+     @pytest .mark .parametrize ( 
329+         "filename,expected_mime_type" , 
330+         [ 
331+             ("test.wav" , "audio/wav" ), 
332+             ("test.mp3" , "audio/mpeg" ), 
333+             ("test.ogg" , "audio/ogg" ), 
334+             ("test.flac" , "audio/flac" ), 
335+             ("test.aac" , "audio/aac" ), 
336+             ("test.m4a" , "audio/mp4" ), 
337+             ("test.unknown" , "application/octet-stream" ),  # Unknown extension fallback  
338+         ], 
339+     ) 
340+     @pytest .mark .anyio  
341+     async  def  test_tool_audio_suffix_detection (self , tmp_path : Path , filename : str , expected_mime_type : str ):
342+         """Test that Audio helper correctly detects MIME types from file suffixes""" 
343+         mcp  =  FastMCP ()
344+         mcp .add_tool (audio_tool_fn )
345+ 
346+         # Create a test audio file with the specific extension 
347+         audio_path  =  tmp_path  /  filename 
348+         audio_path .write_bytes (b"fake audio data" )
349+ 
350+         async  with  client_session (mcp ._mcp_server ) as  client :
351+             result  =  await  client .call_tool ("audio_tool_fn" , {"path" : str (audio_path )})
352+             assert  len (result .content ) ==  1 
353+             content  =  result .content [0 ]
354+             assert  isinstance (content , AudioContent )
355+             assert  content .type  ==  "audio" 
356+             assert  content .mimeType  ==  expected_mime_type 
357+             # Verify base64 encoding 
358+             decoded  =  base64 .b64decode (content .data )
359+             assert  decoded  ==  b"fake audio data" 
360+ 
303361    @pytest .mark .anyio  
304362    async  def  test_tool_mixed_content (self ):
305363        mcp  =  FastMCP ()
@@ -332,19 +390,24 @@ async def test_tool_mixed_content(self):
332390                    assert  structured_result [i ][key ] ==  value 
333391
334392    @pytest .mark .anyio  
335-     async  def  test_tool_mixed_list_with_image (self , tmp_path : Path ):
393+     async  def  test_tool_mixed_list_with_audio_and_image (self , tmp_path : Path ):
336394        """Test that lists containing Image objects and other types are handled 
337395        correctly""" 
338396        # Create a test image 
339397        image_path  =  tmp_path  /  "test.png" 
340398        image_path .write_bytes (b"test image data" )
341399
400+         # Create a test audio 
401+         audio_path  =  tmp_path  /  "test.wav" 
402+         audio_path .write_bytes (b"test audio data" )
403+ 
342404        # TODO(Marcelo): It seems if we add the proper type hint, it generates an invalid JSON schema. 
343405        # We need to fix this. 
344406        def  mixed_list_fn () ->  list :  # type: ignore 
345407            return  [  # type: ignore 
346408                "text message" ,
347409                Image (image_path ),
410+                 Audio (audio_path ),
348411                {"key" : "value" },
349412                TextContent (type = "text" , text = "direct content" ),
350413            ]
@@ -353,7 +416,7 @@ def mixed_list_fn() -> list:  # type: ignore
353416        mcp .add_tool (mixed_list_fn )  # type: ignore 
354417        async  with  client_session (mcp ._mcp_server ) as  client :
355418            result  =  await  client .call_tool ("mixed_list_fn" , {})
356-             assert  len (result .content ) ==  4 
419+             assert  len (result .content ) ==  5 
357420            # Check text conversion 
358421            content1  =  result .content [0 ]
359422            assert  isinstance (content1 , TextContent )
@@ -363,14 +426,19 @@ def mixed_list_fn() -> list:  # type: ignore
363426            assert  isinstance (content2 , ImageContent )
364427            assert  content2 .mimeType  ==  "image/png" 
365428            assert  base64 .b64decode (content2 .data ) ==  b"test image data" 
366-             # Check dict  conversion 
429+             # Check audio  conversion 
367430            content3  =  result .content [2 ]
368-             assert  isinstance (content3 , TextContent )
369-             assert  '"key": "value"'  in  content3 .text 
370-             # Check direct TextContent 
431+             assert  isinstance (content3 , AudioContent )
432+             assert  content3 .mimeType  ==  "audio/wav" 
433+             assert  base64 .b64decode (content3 .data ) ==  b"test audio data" 
434+             # Check dict conversion 
371435            content4  =  result .content [3 ]
372436            assert  isinstance (content4 , TextContent )
373-             assert  content4 .text  ==  "direct content" 
437+             assert  '"key": "value"'  in  content4 .text 
438+             # Check direct TextContent 
439+             content5  =  result .content [4 ]
440+             assert  isinstance (content5 , TextContent )
441+             assert  content5 .text  ==  "direct content" 
374442            # Check structured content - untyped list with Image objects should NOT have structured output 
375443            assert  result .structuredContent  is  None 
376444
0 commit comments