send_interactive_message
Send a formatted message with interactive inline keyboard buttons to engage users. Supports multiple button rows with callback data or URLs.
Instructions
Send a message with inline keyboard buttons.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| chat_id | Yes | Target chat ID. | |
| text | Yes | Message text. | |
| buttons | Yes | Rows of buttons. Each button: {"text": "Label", "callback_data": "value"} or {"text": "Label", "url": "https://..."}. | |
| parse_mode | No | HTML, Markdown, MarkdownV2, or None. | HTML |
| disable_notification | No | Send silently. |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| ok | Yes | ||
| error | No | ||
| message_id | No | ||
| chat_id | No | ||
| date | No |
Implementation Reference
- aiogram_mcp/tools/interactive.py:63-135 (handler)The main tool handler that sends a message with an inline keyboard. Checks chat permissions, builds the keyboard, sends via bot, and returns SendInteractiveResult.
@mcp.tool async def send_interactive_message( chat_id: int, text: str, buttons: list[list[dict[str, str]]], parse_mode: str | None = "HTML", disable_notification: bool = False, ) -> SendInteractiveResult: """Send a message with inline keyboard buttons. Args: chat_id: Target chat ID. text: Message text. buttons: Rows of buttons. Each button: {"text": "Label", "callback_data": "value"} or {"text": "Label", "url": "https://..."}. parse_mode: HTML, Markdown, MarkdownV2, or None. disable_notification: Send silently. """ if not ctx.is_chat_allowed(chat_id): result = SendInteractiveResult( ok=False, error=f"Chat {chat_id} is not in allowed_chat_ids.", ) if ctx.audit_logger: ctx.audit_logger.log( "send_interactive_message", {"chat_id": chat_id, "text": text}, result.ok, result.error, ) return result keyboard = _build_keyboard(buttons) if isinstance(keyboard, str): result = SendInteractiveResult(ok=False, error=keyboard) if ctx.audit_logger: ctx.audit_logger.log( "send_interactive_message", {"chat_id": chat_id, "text": text}, result.ok, result.error, ) return result try: if ctx.rate_limiter: await ctx.rate_limiter.acquire() msg = await ctx.bot.send_message( chat_id=chat_id, text=text, parse_mode=normalize_parse_mode(parse_mode), reply_markup=keyboard, disable_notification=disable_notification, ) result = SendInteractiveResult( ok=True, message_id=msg.message_id, chat_id=msg.chat.id, date=msg.date.isoformat(), ) except ValueError as exc: result = SendInteractiveResult(ok=False, error=str(exc)) except (TelegramBadRequest, TelegramForbiddenError) as exc: result = SendInteractiveResult(ok=False, error=str(exc)) if ctx.audit_logger: ctx.audit_logger.log( "send_interactive_message", {"chat_id": chat_id, "text": text}, result.ok, result.error, ) return result - Pydantic response model for the send_interactive_message tool, extending ToolResponse with message_id, chat_id, and date fields.
class SendInteractiveResult(ToolResponse): message_id: int | None = None chat_id: int | None = None date: str | None = None - Helper function that validates and constructs an InlineKeyboardMarkup from a nested list of button dicts.
def _build_keyboard( buttons: list[list[dict[str, str]]], ) -> InlineKeyboardMarkup | str: """Build InlineKeyboardMarkup from a list of button rows. Returns the markup on success, or an error string on validation failure. Each button dict must have 'text' and either 'callback_data' or 'url'. """ rows: list[list[InlineKeyboardButton]] = [] for row_idx, row in enumerate(buttons): built_row: list[InlineKeyboardButton] = [] for btn_idx, btn in enumerate(row): if "text" not in btn: return f"Button [{row_idx}][{btn_idx}] is missing required 'text' field." if "callback_data" not in btn and "url" not in btn: return ( f"Button [{row_idx}][{btn_idx}] must have 'callback_data' or 'url'." ) built_row.append( InlineKeyboardButton( text=btn["text"], callback_data=btn.get("callback_data"), url=btn.get("url"), ) ) rows.append(built_row) return InlineKeyboardMarkup(inline_keyboard=rows) - aiogram_mcp/server.py:101-101 (registration)Registration call that wires the interactive tools (including send_interactive_message) into the FastMCP server.
register_interactive_tools(self._mcp, self._ctx, allowed_tools=at) - aiogram_mcp/permissions.py:28-29 (schema)Permission mapping assigning send_interactive_message to the MESSAGING permission level.
"send_interactive_message": PermissionLevel.MESSAGING, "edit_message": PermissionLevel.MESSAGING,