Added Audio to FastMCP (#1130)

This commit is contained in:
xavier 2025-08-22 18:45:38 +08:00 • committed by GitHub
commit f4b2957a20
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 130 additions and 12 deletions

View file

@ -3,7 +3,7 @@
from importlib.metadata import version
from .server import Context, FastMCP
from .utilities.types import Image
from .utilities.types import Audio, Image
__version__ = version("mcp")
__all__ = ["FastMCP", "Context", "Image"]
__all__ = ["FastMCP", "Context", "Image", "Audio"]

View file

@ -21,7 +21,7 @@ from pydantic_core import PydanticUndefined
from mcp.server.fastmcp.exceptions import InvalidSignature
from mcp.server.fastmcp.utilities.logging import get_logger
from mcp.server.fastmcp.utilities.types import Image
from mcp.server.fastmcp.utilities.types import Audio, Image
from mcp.types import ContentBlock, TextContent
logger = get_logger(__name__)
@ -506,6 +506,9 @@ def _convert_to_content(
if isinstance(result, Image):
return [result.to_image_content()]
if isinstance(result, Audio):
return [result.to_audio_content()]
if isinstance(result, list | tuple):
return list(
chain.from_iterable(

View file

@ -3,7 +3,7 @@
import base64
from pathlib import Path
from mcp.types import ImageContent
from mcp.types import AudioContent, ImageContent
class Image:
@ -52,3 +52,50 @@ class Image:
raise ValueError("No image data available")
return ImageContent(type="image", data=data, mimeType=self._mime_type)
class Audio:
"""Helper class for returning audio from tools."""
def __init__(
self,
path: str | Path | None = None,
data: bytes | None = None,
format: str | None = None,
):
if not bool(path) ^ bool(data):
raise ValueError("Either path or data can be provided")
self.path = Path(path) if path else None
self.data = data
self._format = format
self._mime_type = self._get_mime_type()
def _get_mime_type(self) -> str:
"""Get MIME type from format or guess from file extension."""
if self._format:
return f"audio/{self._format.lower()}"
if self.path:
suffix = self.path.suffix.lower()
return {
".wav": "audio/wav",
".mp3": "audio/mpeg",
".ogg": "audio/ogg",
".flac": "audio/flac",
".aac": "audio/aac",
".m4a": "audio/mp4",
}.get(suffix, "application/octet-stream")
return "audio/wav" # default for raw binary data
def to_audio_content(self) -> AudioContent:
"""Convert to MCP AudioContent."""
if self.path:
with open(self.path, "rb") as f:
data = base64.b64encode(f.read()).decode()
elif self.data is not None:
data = base64.b64encode(self.data).decode()
else:
raise ValueError("No audio data available")
return AudioContent(type="audio", data=data, mimeType=self._mime_type)