send_audio
Send an audio file (music) to a Telegram chat. Supports MP3/M4A URLs, optional caption, performer, title, and silent notification.
Instructions
Send an audio file (music) to a Telegram chat.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| chat_id | Yes | Target chat ID. | |
| audio_url | Yes | URL or file_id of the audio (MP3/M4A). | |
| caption | No | Optional caption. | |
| parse_mode | No | HTML, Markdown, MarkdownV2, or None. | HTML |
| performer | No | Performer name. | |
| title | No | Track title. | |
| disable_notification | No | Send silently. |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| ok | Yes | ||
| error | No | ||
| message_id | No | ||
| chat_id | No |
Implementation Reference
- aiogram_mcp/tools/media.py:257-313 (handler)The send_audio async function that executes the tool logic: validates chat permission, applies rate limiting, calls ctx.bot.send_audio(), and returns a SendMediaResult.
async def send_audio( chat_id: int, audio_url: str, caption: str | None = None, parse_mode: str | None = "HTML", performer: str | None = None, title: str | None = None, disable_notification: bool = False, ) -> SendMediaResult: """Send an audio file (music) to a Telegram chat. Args: chat_id: Target chat ID. audio_url: URL or file_id of the audio (MP3/M4A). caption: Optional caption. parse_mode: HTML, Markdown, MarkdownV2, or None. performer: Performer name. title: Track title. disable_notification: Send silently. """ if not ctx.is_chat_allowed(chat_id): result = SendMediaResult(ok=False, error=f"Chat {chat_id} is not allowed.") if ctx.audit_logger: ctx.audit_logger.log( "send_audio", {"chat_id": chat_id, "audio_url": audio_url}, result.ok, result.error, ) return result try: if ctx.rate_limiter: await ctx.rate_limiter.acquire() msg = await ctx.bot.send_audio( chat_id=chat_id, audio=audio_url, caption=caption, parse_mode=normalize_parse_mode(parse_mode), performer=performer, title=title, disable_notification=disable_notification, ) result = SendMediaResult(ok=True, message_id=msg.message_id, chat_id=msg.chat.id) except ValueError as exc: result = SendMediaResult(ok=False, error=str(exc)) except (TelegramBadRequest, TelegramForbiddenError) as exc: result = SendMediaResult(ok=False, error=str(exc)) if ctx.audit_logger: ctx.audit_logger.log( "send_audio", {"chat_id": chat_id, "audio_url": audio_url}, result.ok, result.error, ) return result - aiogram_mcp/tools/media.py:14-16 (schema)SendMediaResult response model (extends ToolResponse) with message_id and chat_id fields.
class SendMediaResult(ToolResponse): message_id: int | None = None chat_id: int | None = None - aiogram_mcp/models.py:8-12 (schema)ToolResponse base model with ok/error fields.
class ToolResponse(BaseModel): """Base model for all tool results. ok=True on success, ok=False with error on failure.""" ok: bool error: str | None = None - aiogram_mcp/tools/media.py:25-254 (registration)register_media_tools() function registers send_audio (and other media tools) onto the FastMCP instance via @mcp.tool decorator. Called from server.py line 102.
def register_media_tools( mcp: FastMCP, ctx: BotContext, allowed_tools: set[str] | None = None ) -> None: if allowed_tools is None or "send_document" in allowed_tools: @mcp.tool async def send_document( chat_id: int, document_url: str, caption: str | None = None, parse_mode: str | None = "HTML", disable_notification: bool = False, ) -> SendMediaResult: """Send a document/file to a Telegram chat. Args: chat_id: Target chat ID. document_url: URL or file_id of the document. caption: Optional caption. parse_mode: HTML, Markdown, MarkdownV2, or None. disable_notification: Send silently. """ if not ctx.is_chat_allowed(chat_id): result = SendMediaResult(ok=False, error=f"Chat {chat_id} is not allowed.") if ctx.audit_logger: ctx.audit_logger.log( "send_document", {"chat_id": chat_id, "document_url": document_url}, result.ok, result.error, ) return result try: if ctx.rate_limiter: await ctx.rate_limiter.acquire() msg = await ctx.bot.send_document( chat_id=chat_id, document=document_url, caption=caption, parse_mode=normalize_parse_mode(parse_mode), disable_notification=disable_notification, ) result = SendMediaResult(ok=True, message_id=msg.message_id, chat_id=msg.chat.id) except ValueError as exc: result = SendMediaResult(ok=False, error=str(exc)) except (TelegramBadRequest, TelegramForbiddenError) as exc: result = SendMediaResult(ok=False, error=str(exc)) if ctx.audit_logger: ctx.audit_logger.log( "send_document", {"chat_id": chat_id, "document_url": document_url}, result.ok, result.error, ) return result if allowed_tools is None or "send_voice" in allowed_tools: @mcp.tool async def send_voice( chat_id: int, voice_url: str, caption: str | None = None, parse_mode: str | None = "HTML", duration: int | None = None, disable_notification: bool = False, ) -> SendMediaResult: """Send a voice message to a Telegram chat. Args: chat_id: Target chat ID. voice_url: URL or file_id of the voice message (OGG with OPUS). caption: Optional caption. parse_mode: HTML, Markdown, MarkdownV2, or None. duration: Duration in seconds. disable_notification: Send silently. """ if not ctx.is_chat_allowed(chat_id): result = SendMediaResult(ok=False, error=f"Chat {chat_id} is not allowed.") if ctx.audit_logger: ctx.audit_logger.log( "send_voice", {"chat_id": chat_id, "voice_url": voice_url}, result.ok, result.error, ) return result try: if ctx.rate_limiter: await ctx.rate_limiter.acquire() msg = await ctx.bot.send_voice( chat_id=chat_id, voice=voice_url, caption=caption, parse_mode=normalize_parse_mode(parse_mode), duration=duration, disable_notification=disable_notification, ) result = SendMediaResult(ok=True, message_id=msg.message_id, chat_id=msg.chat.id) except ValueError as exc: result = SendMediaResult(ok=False, error=str(exc)) except (TelegramBadRequest, TelegramForbiddenError) as exc: result = SendMediaResult(ok=False, error=str(exc)) if ctx.audit_logger: ctx.audit_logger.log( "send_voice", {"chat_id": chat_id, "voice_url": voice_url}, result.ok, result.error, ) return result if allowed_tools is None or "send_video" in allowed_tools: @mcp.tool async def send_video( chat_id: int, video_url: str, caption: str | None = None, parse_mode: str | None = "HTML", duration: int | None = None, disable_notification: bool = False, ) -> SendMediaResult: """Send a video to a Telegram chat. Args: chat_id: Target chat ID. video_url: URL or file_id of the video. caption: Optional caption. parse_mode: HTML, Markdown, MarkdownV2, or None. duration: Duration in seconds. disable_notification: Send silently. """ if not ctx.is_chat_allowed(chat_id): result = SendMediaResult(ok=False, error=f"Chat {chat_id} is not allowed.") if ctx.audit_logger: ctx.audit_logger.log( "send_video", {"chat_id": chat_id, "video_url": video_url}, result.ok, result.error, ) return result try: if ctx.rate_limiter: await ctx.rate_limiter.acquire() msg = await ctx.bot.send_video( chat_id=chat_id, video=video_url, caption=caption, parse_mode=normalize_parse_mode(parse_mode), duration=duration, disable_notification=disable_notification, ) result = SendMediaResult(ok=True, message_id=msg.message_id, chat_id=msg.chat.id) except ValueError as exc: result = SendMediaResult(ok=False, error=str(exc)) except (TelegramBadRequest, TelegramForbiddenError) as exc: result = SendMediaResult(ok=False, error=str(exc)) if ctx.audit_logger: ctx.audit_logger.log( "send_video", {"chat_id": chat_id, "video_url": video_url}, result.ok, result.error, ) return result if allowed_tools is None or "send_animation" in allowed_tools: @mcp.tool async def send_animation( chat_id: int, animation_url: str, caption: str | None = None, parse_mode: str | None = "HTML", disable_notification: bool = False, ) -> SendMediaResult: """Send a GIF/animation to a Telegram chat. Args: chat_id: Target chat ID. animation_url: URL or file_id of the animation. caption: Optional caption. parse_mode: HTML, Markdown, MarkdownV2, or None. disable_notification: Send silently. """ if not ctx.is_chat_allowed(chat_id): result = SendMediaResult(ok=False, error=f"Chat {chat_id} is not allowed.") if ctx.audit_logger: ctx.audit_logger.log( "send_animation", {"chat_id": chat_id, "animation_url": animation_url}, result.ok, result.error, ) return result try: if ctx.rate_limiter: await ctx.rate_limiter.acquire() msg = await ctx.bot.send_animation( chat_id=chat_id, animation=animation_url, caption=caption, parse_mode=normalize_parse_mode(parse_mode), disable_notification=disable_notification, ) result = SendMediaResult(ok=True, message_id=msg.message_id, chat_id=msg.chat.id) except ValueError as exc: result = SendMediaResult(ok=False, error=str(exc)) except (TelegramBadRequest, TelegramForbiddenError) as exc: result = SendMediaResult(ok=False, error=str(exc)) if ctx.audit_logger: ctx.audit_logger.log( "send_animation", {"chat_id": chat_id, "animation_url": animation_url}, result.ok, result.error, ) return result if allowed_tools is None or "send_audio" in allowed_tools: - aiogram_mcp/permissions.py:35-35 (registration)Permission mapping: 'send_audio' is assigned PermissionLevel.MESSAGING.
"send_audio": PermissionLevel.MESSAGING,