diff --git a/main.py b/main.py index ad5d8f8..ce01cdf 100644 --- a/main.py +++ b/main.py @@ -14,6 +14,7 @@ import nest_asyncio from dotenv import load_dotenv from mcp.server.fastmcp import FastMCP +from mcp.types import ToolAnnotations from pythonjsonlogger import jsonlogger from telethon import TelegramClient, functions, utils from telethon.sessions import StringSession @@ -316,7 +317,7 @@ def get_sender_name(message) -> str: return "Unknown" -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, readOnlyHint=True)) async def get_chats(page: int = 1, page_size: int = 20) -> str: """ Get a paginated list of chats. @@ -342,7 +343,7 @@ async def get_chats(page: int = 1, page_size: int = 20) -> str: return log_and_format_error("get_chats", e) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, readOnlyHint=True)) @validate_id("chat_id") async def get_messages(chat_id: Union[int, str], page: int = 1, page_size: int = 20) -> str: """ @@ -374,7 +375,7 @@ async def get_messages(chat_id: Union[int, str], page: int = 1, page_size: int = ) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, destructiveHint=True)) @validate_id("chat_id") async def send_message(chat_id: Union[int, str], message: str) -> str: """ @@ -391,7 +392,9 @@ async def send_message(chat_id: Union[int, str], message: str) -> str: return log_and_format_error("send_message", e, chat_id=chat_id) -@mcp.tool() +@mcp.tool( + annotations=ToolAnnotations(openWorldHint=True, destructiveHint=True, idempotentHint=True) +) @validate_id("channel") async def subscribe_public_channel(channel: Union[int, str]) -> str: """ @@ -411,7 +414,7 @@ async def subscribe_public_channel(channel: Union[int, str]) -> str: return log_and_format_error("subscribe_public_channel", e, channel=channel) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, readOnlyHint=True)) @validate_id("chat_id") async def list_inline_buttons( chat_id: Union[int, str], message_id: Optional[Union[int, str]] = None, limit: int = 20 @@ -475,7 +478,7 @@ async def list_inline_buttons( ) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, destructiveHint=True)) @validate_id("chat_id") async def press_inline_button( chat_id: Union[int, str], @@ -590,7 +593,7 @@ async def press_inline_button( ) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, readOnlyHint=True)) async def list_contacts() -> str: """ List all contacts in your Telegram account. @@ -616,7 +619,7 @@ async def list_contacts() -> str: return log_and_format_error("list_contacts", e) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, readOnlyHint=True)) async def search_contacts(query: str) -> str: """ Search for contacts by name, username, or phone number using Telethon's SearchRequest. @@ -644,7 +647,7 @@ async def search_contacts(query: str) -> str: return log_and_format_error("search_contacts", e, query=query) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, readOnlyHint=True)) async def get_contact_ids() -> str: """ Get all contact IDs in your Telegram account. @@ -658,7 +661,7 @@ async def get_contact_ids() -> str: return log_and_format_error("get_contact_ids", e) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, readOnlyHint=True)) @validate_id("chat_id") async def list_messages( chat_id: Union[int, str], @@ -781,7 +784,7 @@ async def list_messages( return log_and_format_error("list_messages", e, chat_id=chat_id) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, readOnlyHint=True)) async def list_topics( chat_id: int, limit: int = 200, @@ -868,7 +871,7 @@ async def list_topics( ) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, readOnlyHint=True)) async def list_chats(chat_type: str = None, limit: int = 20) -> str: """ List available chats with metadata. @@ -929,7 +932,7 @@ async def list_chats(chat_type: str = None, limit: int = 20) -> str: return log_and_format_error("list_chats", e, chat_type=chat_type, limit=limit) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, readOnlyHint=True)) @validate_id("chat_id") async def get_chat(chat_id: Union[int, str]) -> str: """ @@ -1010,7 +1013,7 @@ async def get_chat(chat_id: Union[int, str]) -> str: return log_and_format_error("get_chat", e, chat_id=chat_id) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, readOnlyHint=True)) async def get_direct_chat_by_contact(contact_query: str) -> str: """ Find a direct chat with a specific contact by name, username, or phone. @@ -1065,7 +1068,7 @@ async def get_direct_chat_by_contact(contact_query: str) -> str: return log_and_format_error("get_direct_chat_by_contact", e, contact_query=contact_query) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, readOnlyHint=True)) @validate_id("contact_id") async def get_contact_chats(contact_id: Union[int, str]) -> str: """ @@ -1118,7 +1121,7 @@ async def get_contact_chats(contact_id: Union[int, str]) -> str: return log_and_format_error("get_contact_chats", e, contact_id=contact_id) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, readOnlyHint=True)) @validate_id("contact_id") async def get_last_interaction(contact_id: Union[int, str]) -> str: """ @@ -1155,7 +1158,7 @@ async def get_last_interaction(contact_id: Union[int, str]) -> str: return log_and_format_error("get_last_interaction", e, contact_id=contact_id) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, readOnlyHint=True)) @validate_id("chat_id") async def get_message_context( chat_id: Union[int, str], message_id: int, context_size: int = 3 @@ -1222,7 +1225,9 @@ async def get_message_context( ) -@mcp.tool() +@mcp.tool( + annotations=ToolAnnotations(openWorldHint=True, destructiveHint=True, idempotentHint=True) +) async def add_contact(phone: str, first_name: str, last_name: str = "") -> str: """ Add a new contact to your Telegram account. @@ -1278,7 +1283,9 @@ async def add_contact(phone: str, first_name: str, last_name: str = "") -> str: return log_and_format_error("add_contact", e, phone=phone) -@mcp.tool() +@mcp.tool( + annotations=ToolAnnotations(openWorldHint=True, destructiveHint=True, idempotentHint=True) +) @validate_id("user_id") async def delete_contact(user_id: Union[int, str]) -> str: """ @@ -1294,7 +1301,9 @@ async def delete_contact(user_id: Union[int, str]) -> str: return log_and_format_error("delete_contact", e, user_id=user_id) -@mcp.tool() +@mcp.tool( + annotations=ToolAnnotations(openWorldHint=True, destructiveHint=True, idempotentHint=True) +) @validate_id("user_id") async def block_user(user_id: Union[int, str]) -> str: """ @@ -1310,7 +1319,9 @@ async def block_user(user_id: Union[int, str]) -> str: return log_and_format_error("block_user", e, user_id=user_id) -@mcp.tool() +@mcp.tool( + annotations=ToolAnnotations(openWorldHint=True, destructiveHint=True, idempotentHint=True) +) @validate_id("user_id") async def unblock_user(user_id: Union[int, str]) -> str: """ @@ -1326,7 +1337,7 @@ async def unblock_user(user_id: Union[int, str]) -> str: return log_and_format_error("unblock_user", e, user_id=user_id) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, readOnlyHint=True)) async def get_me() -> str: """ Get your own user information. @@ -1338,7 +1349,7 @@ async def get_me() -> str: return log_and_format_error("get_me", e) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, destructiveHint=True)) @validate_id("user_ids") async def create_group(title: str, user_ids: List[Union[int, str]]) -> str: """ @@ -1397,7 +1408,9 @@ async def create_group(title: str, user_ids: List[Union[int, str]]) -> str: return log_and_format_error("create_group", e, title=title, user_ids=user_ids) -@mcp.tool() +@mcp.tool( + annotations=ToolAnnotations(openWorldHint=True, destructiveHint=True, idempotentHint=True) +) @validate_id("group_id", "user_ids") async def invite_to_group(group_id: Union[int, str], user_ids: List[Union[int, str]]) -> str: """ @@ -1447,7 +1460,9 @@ async def invite_to_group(group_id: Union[int, str], user_ids: List[Union[int, s return log_and_format_error("invite_to_group", e, group_id=group_id, user_ids=user_ids) -@mcp.tool() +@mcp.tool( + annotations=ToolAnnotations(openWorldHint=True, destructiveHint=True, idempotentHint=True) +) @validate_id("chat_id") async def leave_chat(chat_id: Union[int, str]) -> str: """ @@ -1528,7 +1543,7 @@ async def leave_chat(chat_id: Union[int, str]) -> str: return log_and_format_error("leave_chat", e, chat_id=chat_id) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, readOnlyHint=True)) @validate_id("chat_id") async def get_participants(chat_id: Union[int, str]) -> str: """ @@ -1547,7 +1562,7 @@ async def get_participants(chat_id: Union[int, str]) -> str: return log_and_format_error("get_participants", e, chat_id=chat_id) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, destructiveHint=True)) @validate_id("chat_id") async def send_file(chat_id: Union[int, str], file_path: str, caption: str = None) -> str: """ @@ -1571,7 +1586,7 @@ async def send_file(chat_id: Union[int, str], file_path: str, caption: str = Non ) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, readOnlyHint=True)) @validate_id("chat_id") async def download_media(chat_id: Union[int, str], message_id: int, file_path: str) -> str: """ @@ -1604,7 +1619,9 @@ async def download_media(chat_id: Union[int, str], message_id: int, file_path: s ) -@mcp.tool() +@mcp.tool( + annotations=ToolAnnotations(openWorldHint=True, destructiveHint=True, idempotentHint=True) +) async def update_profile(first_name: str = None, last_name: str = None, about: str = None) -> str: """ Update your profile information (name, bio). @@ -1622,7 +1639,9 @@ async def update_profile(first_name: str = None, last_name: str = None, about: s ) -@mcp.tool() +@mcp.tool( + annotations=ToolAnnotations(openWorldHint=True, destructiveHint=True, idempotentHint=True) +) async def set_profile_photo(file_path: str) -> str: """ Set a new profile photo. @@ -1636,7 +1655,9 @@ async def set_profile_photo(file_path: str) -> str: return log_and_format_error("set_profile_photo", e, file_path=file_path) -@mcp.tool() +@mcp.tool( + annotations=ToolAnnotations(openWorldHint=True, destructiveHint=True, idempotentHint=True) +) async def delete_profile_photo() -> str: """ Delete your current profile photo. @@ -1653,7 +1674,7 @@ async def delete_profile_photo() -> str: return log_and_format_error("delete_profile_photo", e) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, readOnlyHint=True)) async def get_privacy_settings() -> str: """ Get your privacy settings for last seen status. @@ -1677,7 +1698,9 @@ async def get_privacy_settings() -> str: return log_and_format_error("get_privacy_settings", e) -@mcp.tool() +@mcp.tool( + annotations=ToolAnnotations(openWorldHint=True, destructiveHint=True, idempotentHint=True) +) @validate_id("allow_users", "disallow_users") async def set_privacy_settings( key: str, @@ -1774,7 +1797,7 @@ async def set_privacy_settings( return log_and_format_error("set_privacy_settings", e, key=key) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, destructiveHint=True)) async def import_contacts(contacts: list) -> str: """ Import a list of contacts. Each contact should be a dict with phone, first_name, last_name. @@ -1795,7 +1818,7 @@ async def import_contacts(contacts: list) -> str: return log_and_format_error("import_contacts", e, contacts=contacts) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, readOnlyHint=True)) async def export_contacts() -> str: """ Export all contacts as a JSON string. @@ -1808,7 +1831,7 @@ async def export_contacts() -> str: return log_and_format_error("export_contacts", e) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, readOnlyHint=True)) async def get_blocked_users() -> str: """ Get a list of blocked users. @@ -1820,7 +1843,7 @@ async def get_blocked_users() -> str: return log_and_format_error("get_blocked_users", e) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, destructiveHint=True)) async def create_channel(title: str, about: str = "", megagroup: bool = False) -> str: """ Create a new channel or supergroup. @@ -1836,7 +1859,9 @@ async def create_channel(title: str, about: str = "", megagroup: bool = False) - ) -@mcp.tool() +@mcp.tool( + annotations=ToolAnnotations(openWorldHint=True, destructiveHint=True, idempotentHint=True) +) @validate_id("chat_id") async def edit_chat_title(chat_id: Union[int, str], title: str) -> str: """ @@ -1856,7 +1881,9 @@ async def edit_chat_title(chat_id: Union[int, str], title: str) -> str: return log_and_format_error("edit_chat_title", e, chat_id=chat_id, title=title) -@mcp.tool() +@mcp.tool( + annotations=ToolAnnotations(openWorldHint=True, destructiveHint=True, idempotentHint=True) +) @validate_id("chat_id") async def edit_chat_photo(chat_id: Union[int, str], file_path: str) -> str: """ @@ -1890,7 +1917,9 @@ async def edit_chat_photo(chat_id: Union[int, str], file_path: str) -> str: return log_and_format_error("edit_chat_photo", e, chat_id=chat_id, file_path=file_path) -@mcp.tool() +@mcp.tool( + annotations=ToolAnnotations(openWorldHint=True, destructiveHint=True, idempotentHint=True) +) @validate_id("chat_id") async def delete_chat_photo(chat_id: Union[int, str]) -> str: """ @@ -1919,7 +1948,9 @@ async def delete_chat_photo(chat_id: Union[int, str]) -> str: return log_and_format_error("delete_chat_photo", e, chat_id=chat_id) -@mcp.tool() +@mcp.tool( + annotations=ToolAnnotations(openWorldHint=True, destructiveHint=True, idempotentHint=True) +) @validate_id("group_id", "user_id") async def promote_admin( group_id: Union[int, str], user_id: Union[int, str], rights: dict = None @@ -1986,7 +2017,9 @@ async def promote_admin( return log_and_format_error("promote_admin", e, group_id=group_id, user_id=user_id) -@mcp.tool() +@mcp.tool( + annotations=ToolAnnotations(openWorldHint=True, destructiveHint=True, idempotentHint=True) +) @validate_id("group_id", "user_id") async def demote_admin(group_id: Union[int, str], user_id: Union[int, str]) -> str: """ @@ -2035,7 +2068,9 @@ async def demote_admin(group_id: Union[int, str], user_id: Union[int, str]) -> s return log_and_format_error("demote_admin", e, group_id=group_id, user_id=user_id) -@mcp.tool() +@mcp.tool( + annotations=ToolAnnotations(openWorldHint=True, destructiveHint=True, idempotentHint=True) +) @validate_id("chat_id", "user_id") async def ban_user(chat_id: Union[int, str], user_id: Union[int, str]) -> str: """ @@ -2082,7 +2117,9 @@ async def ban_user(chat_id: Union[int, str], user_id: Union[int, str]) -> str: return log_and_format_error("ban_user", e, chat_id=chat_id, user_id=user_id) -@mcp.tool() +@mcp.tool( + annotations=ToolAnnotations(openWorldHint=True, destructiveHint=True, idempotentHint=True) +) @validate_id("chat_id", "user_id") async def unban_user(chat_id: Union[int, str], user_id: Union[int, str]) -> str: """ @@ -2129,7 +2166,7 @@ async def unban_user(chat_id: Union[int, str], user_id: Union[int, str]) -> str: return log_and_format_error("unban_user", e, chat_id=chat_id, user_id=user_id) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, readOnlyHint=True)) @validate_id("chat_id") async def get_admins(chat_id: Union[int, str]) -> str: """ @@ -2148,7 +2185,7 @@ async def get_admins(chat_id: Union[int, str]) -> str: return log_and_format_error("get_admins", e, chat_id=chat_id) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, readOnlyHint=True)) @validate_id("chat_id") async def get_banned_users(chat_id: Union[int, str]) -> str: """ @@ -2169,7 +2206,7 @@ async def get_banned_users(chat_id: Union[int, str]) -> str: return log_and_format_error("get_banned_users", e, chat_id=chat_id) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, readOnlyHint=True)) @validate_id("chat_id") async def get_invite_link(chat_id: Union[int, str]) -> str: """ @@ -2213,7 +2250,9 @@ async def get_invite_link(chat_id: Union[int, str]) -> str: return log_and_format_error("get_invite_link", e, chat_id=chat_id) -@mcp.tool() +@mcp.tool( + annotations=ToolAnnotations(openWorldHint=True, destructiveHint=True, idempotentHint=True) +) async def join_chat_by_link(link: str) -> str: """ Join a chat by invite link. @@ -2257,7 +2296,7 @@ async def join_chat_by_link(link: str) -> str: return f"Error joining chat: {e}" -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, readOnlyHint=True)) @validate_id("chat_id") async def export_chat_invite(chat_id: Union[int, str]) -> str: """ @@ -2292,7 +2331,9 @@ async def export_chat_invite(chat_id: Union[int, str]) -> str: return log_and_format_error("export_chat_invite", e, chat_id=chat_id) -@mcp.tool() +@mcp.tool( + annotations=ToolAnnotations(openWorldHint=True, destructiveHint=True, idempotentHint=True) +) async def import_chat_invite(hash: str) -> str: """ Import a chat invite by hash. @@ -2349,7 +2390,7 @@ async def import_chat_invite(hash: str) -> str: return log_and_format_error("import_chat_invite", e, hash=hash) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, destructiveHint=True)) @validate_id("chat_id") async def send_voice(chat_id: Union[int, str], file_path: str) -> str: """ @@ -2383,7 +2424,7 @@ async def send_voice(chat_id: Union[int, str], file_path: str) -> str: return log_and_format_error("send_voice", e, chat_id=chat_id, file_path=file_path) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, destructiveHint=True)) @validate_id("from_chat_id", "to_chat_id") async def forward_message( from_chat_id: Union[int, str], message_id: int, to_chat_id: Union[int, str] @@ -2406,7 +2447,9 @@ async def forward_message( ) -@mcp.tool() +@mcp.tool( + annotations=ToolAnnotations(openWorldHint=True, destructiveHint=True, idempotentHint=True) +) @validate_id("chat_id") async def edit_message(chat_id: Union[int, str], message_id: int, new_text: str) -> str: """ @@ -2422,7 +2465,9 @@ async def edit_message(chat_id: Union[int, str], message_id: int, new_text: str) ) -@mcp.tool() +@mcp.tool( + annotations=ToolAnnotations(openWorldHint=True, destructiveHint=True, idempotentHint=True) +) @validate_id("chat_id") async def delete_message(chat_id: Union[int, str], message_id: int) -> str: """ @@ -2436,7 +2481,9 @@ async def delete_message(chat_id: Union[int, str], message_id: int) -> str: return log_and_format_error("delete_message", e, chat_id=chat_id, message_id=message_id) -@mcp.tool() +@mcp.tool( + annotations=ToolAnnotations(openWorldHint=True, destructiveHint=True, idempotentHint=True) +) @validate_id("chat_id") async def pin_message(chat_id: Union[int, str], message_id: int) -> str: """ @@ -2450,7 +2497,9 @@ async def pin_message(chat_id: Union[int, str], message_id: int) -> str: return log_and_format_error("pin_message", e, chat_id=chat_id, message_id=message_id) -@mcp.tool() +@mcp.tool( + annotations=ToolAnnotations(openWorldHint=True, destructiveHint=True, idempotentHint=True) +) @validate_id("chat_id") async def unpin_message(chat_id: Union[int, str], message_id: int) -> str: """ @@ -2464,7 +2513,9 @@ async def unpin_message(chat_id: Union[int, str], message_id: int) -> str: return log_and_format_error("unpin_message", e, chat_id=chat_id, message_id=message_id) -@mcp.tool() +@mcp.tool( + annotations=ToolAnnotations(openWorldHint=True, destructiveHint=True, idempotentHint=True) +) @validate_id("chat_id") async def mark_as_read(chat_id: Union[int, str]) -> str: """ @@ -2478,7 +2529,7 @@ async def mark_as_read(chat_id: Union[int, str]) -> str: return log_and_format_error("mark_as_read", e, chat_id=chat_id) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, destructiveHint=True)) @validate_id("chat_id") async def reply_to_message(chat_id: Union[int, str], message_id: int, text: str) -> str: """ @@ -2494,7 +2545,7 @@ async def reply_to_message(chat_id: Union[int, str], message_id: int, text: str) ) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, readOnlyHint=True)) @validate_id("chat_id") async def get_media_info(chat_id: Union[int, str], message_id: int) -> str: """ @@ -2516,7 +2567,7 @@ async def get_media_info(chat_id: Union[int, str], message_id: int) -> str: return log_and_format_error("get_media_info", e, chat_id=chat_id, message_id=message_id) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, readOnlyHint=True)) async def search_public_chats(query: str) -> str: """ Search for public chats, channels, or bots by username or title. @@ -2528,7 +2579,7 @@ async def search_public_chats(query: str) -> str: return log_and_format_error("search_public_chats", e, query=query) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, readOnlyHint=True)) @validate_id("chat_id") async def search_messages(chat_id: Union[int, str], query: str, limit: int = 20) -> str: """ @@ -2554,7 +2605,7 @@ async def search_messages(chat_id: Union[int, str], query: str, limit: int = 20) ) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, readOnlyHint=True)) async def resolve_username(username: str) -> str: """ Resolve a username to a user or chat ID. @@ -2566,7 +2617,9 @@ async def resolve_username(username: str) -> str: return log_and_format_error("resolve_username", e, username=username) -@mcp.tool() +@mcp.tool( + annotations=ToolAnnotations(openWorldHint=True, destructiveHint=True, idempotentHint=True) +) @validate_id("chat_id") async def mute_chat(chat_id: Union[int, str]) -> str: """ @@ -2605,7 +2658,9 @@ async def mute_chat(chat_id: Union[int, str]) -> str: return log_and_format_error("mute_chat", e, chat_id=chat_id) -@mcp.tool() +@mcp.tool( + annotations=ToolAnnotations(openWorldHint=True, destructiveHint=True, idempotentHint=True) +) @validate_id("chat_id") async def unmute_chat(chat_id: Union[int, str]) -> str: """ @@ -2644,7 +2699,9 @@ async def unmute_chat(chat_id: Union[int, str]) -> str: return log_and_format_error("unmute_chat", e, chat_id=chat_id) -@mcp.tool() +@mcp.tool( + annotations=ToolAnnotations(openWorldHint=True, destructiveHint=True, idempotentHint=True) +) @validate_id("chat_id") async def archive_chat(chat_id: Union[int, str]) -> str: """ @@ -2661,7 +2718,9 @@ async def archive_chat(chat_id: Union[int, str]) -> str: return log_and_format_error("archive_chat", e, chat_id=chat_id) -@mcp.tool() +@mcp.tool( + annotations=ToolAnnotations(openWorldHint=True, destructiveHint=True, idempotentHint=True) +) @validate_id("chat_id") async def unarchive_chat(chat_id: Union[int, str]) -> str: """ @@ -2678,7 +2737,7 @@ async def unarchive_chat(chat_id: Union[int, str]) -> str: return log_and_format_error("unarchive_chat", e, chat_id=chat_id) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, readOnlyHint=True)) async def get_sticker_sets() -> str: """ Get all sticker sets. @@ -2690,7 +2749,7 @@ async def get_sticker_sets() -> str: return log_and_format_error("get_sticker_sets", e) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, destructiveHint=True)) @validate_id("chat_id") async def send_sticker(chat_id: Union[int, str], file_path: str) -> str: """ @@ -2715,7 +2774,7 @@ async def send_sticker(chat_id: Union[int, str], file_path: str) -> str: return log_and_format_error("send_sticker", e, chat_id=chat_id, file_path=file_path) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, readOnlyHint=True)) async def get_gif_search(query: str, limit: int = 10) -> str: """ Search for GIFs by query. Returns a list of Telegram document IDs (not file paths). @@ -2771,7 +2830,7 @@ async def get_gif_search(query: str, limit: int = 10) -> str: return log_and_format_error("get_gif_search", e, query=query, limit=limit) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, destructiveHint=True)) @validate_id("chat_id") async def send_gif(chat_id: Union[int, str], gif_id: int) -> str: """ @@ -2791,7 +2850,7 @@ async def send_gif(chat_id: Union[int, str], gif_id: int) -> str: return log_and_format_error("send_gif", e, chat_id=chat_id, gif_id=gif_id) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, readOnlyHint=True)) async def get_bot_info(bot_username: str) -> str: """ Get information about a bot by username. @@ -2827,7 +2886,9 @@ async def get_bot_info(bot_username: str) -> str: return log_and_format_error("get_bot_info", e, bot_username=bot_username) -@mcp.tool() +@mcp.tool( + annotations=ToolAnnotations(openWorldHint=True, destructiveHint=True, idempotentHint=True) +) async def set_bot_commands(bot_username: str, commands: list) -> str: """ Set bot commands for a bot you own. @@ -2874,7 +2935,7 @@ async def set_bot_commands(bot_username: str, commands: list) -> str: return log_and_format_error("set_bot_commands", e, bot_username=bot_username) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, readOnlyHint=True)) @validate_id("chat_id") async def get_history(chat_id: Union[int, str], limit: int = 100) -> str: """ @@ -2898,7 +2959,7 @@ async def get_history(chat_id: Union[int, str], limit: int = 100) -> str: return log_and_format_error("get_history", e, chat_id=chat_id, limit=limit) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, readOnlyHint=True)) @validate_id("user_id") async def get_user_photos(user_id: Union[int, str], limit: int = 10) -> str: """ @@ -2914,7 +2975,7 @@ async def get_user_photos(user_id: Union[int, str], limit: int = 10) -> str: return log_and_format_error("get_user_photos", e, user_id=user_id, limit=limit) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, readOnlyHint=True)) @validate_id("user_id") async def get_user_status(user_id: Union[int, str]) -> str: """ @@ -2927,7 +2988,7 @@ async def get_user_status(user_id: Union[int, str]) -> str: return log_and_format_error("get_user_status", e, user_id=user_id) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, readOnlyHint=True)) @validate_id("chat_id") async def get_recent_actions(chat_id: Union[int, str]) -> str: """ @@ -2956,7 +3017,7 @@ async def get_recent_actions(chat_id: Union[int, str]) -> str: return log_and_format_error("get_recent_actions", e, chat_id=chat_id) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, readOnlyHint=True)) @validate_id("chat_id") async def get_pinned_messages(chat_id: Union[int, str]) -> str: """ @@ -2995,7 +3056,7 @@ async def get_pinned_messages(chat_id: Union[int, str]) -> str: return log_and_format_error("get_pinned_messages", e, chat_id=chat_id) -@mcp.tool() +@mcp.tool(annotations=ToolAnnotations(openWorldHint=True, destructiveHint=True)) async def create_poll( chat_id: int, question: str,