T
Telegram
community
communication
An MCP server that provides paginated chat reading, message retrieval, and message sending capabilities for Telegram through Telethon integration.
Telegram MCP Server
๐ค MCP in Action
Here's a demonstration of the Telegram MCP capabilities in Claude:
Basic usage example:

- Example: Asking Claude to analyze chat history and send a response:

- Successfully sent message to the group:

As you can see, the AI can seamlessly interact with your Telegram account, retrieving and displaying your chats, messages, and other data in a natural way.
A full-featured Telegram integration for Claude, Cursor, and any MCP-compatible client, powered by Telethon and the Model Context Protocol (MCP). This project lets you interact with your Telegram account programmatically, automating everything from messaging to group management.
๐ Features & Tools
This MCP server exposes a huge suite of Telegram tools. Every major Telegram/Telethon feature is available as a tool!
Chat & Group Management
- get_chats(page, page_size): Paginated list of chats
- list_chats(chat_type, limit): List chats with metadata and filtering
- get_chat(chat_id): Detailed info about a chat
- create_group(title, user_ids): Create a new group
- create_channel(title, about, megagroup): Create a channel or supergroup
- edit_chat_title(chat_id, title): Change chat/group/channel title
- edit_chat_photo(chat_id, file_path): Set chat/group/channel photo
- delete_chat_photo(chat_id): Remove chat/group/channel photo
- leave_chat(chat_id): Leave a group or channel
- get_participants(chat_id): List all participants
- get_admins(chat_id): List all admins
- get_banned_users(chat_id): List all banned users
- promote_admin(chat_id, user_id): Promote user to admin
- demote_admin(chat_id, user_id): Demote admin to user
- ban_user(chat_id, user_id): Ban user
- unban_user(chat_id, user_id): Unban user
- get_invite_link(chat_id): Get invite link
- export_chat_invite(chat_id): Export invite link
- import_chat_invite(hash): Join chat by invite hash
- join_chat_by_link(link): Join chat by invite link
Messaging
- get_messages(chat_id, page, page_size): Paginated messages
- list_messages(chat_id, limit, search_query, from_date, to_date): Filtered messages
- send_message(chat_id, message): Send a message
- reply_to_message(chat_id, message_id, text): Reply to a message
- edit_message(chat_id, message_id, new_text): Edit your message
- delete_message(chat_id, message_id): Delete a message
- forward_message(from_chat_id, message_id, to_chat_id): Forward a message
- pin_message(chat_id, message_id): Pin a message
- unpin_message(chat_id, message_id): Unpin a message
- mark_as_read(chat_id): Mark all as read
- get_message_context(chat_id, message_id, context_size): Context around a message
- get_history(chat_id, limit): Full chat history
- get_pinned_messages(chat_id): List pinned messages
Contact Management
- list_contacts(): List all contacts
- search_contacts(query): Search contacts
- add_contact(phone, first_name, last_name): Add a contact
- delete_contact(user_id): Delete a contact
- block_user(user_id): Block a user
- unblock_user(user_id): Unblock a user
- import_contacts(contacts): Bulk import contacts
- export_contacts(): Export all contacts as JSON
- get_blocked_users(): List blocked users
- get_contact_ids(): List all contact IDs
- get_direct_chat_by_contact(contact_query): Find direct chat with a contact
- get_contact_chats(contact_id): List all chats with a contact
- get_last_interaction(contact_id): Most recent message with a contact
User & Profile
- get_me(): Get your user info
- update_profile(first_name, last_name, about): Update your profile
- set_profile_photo(file_path): Set your profile photo
- delete_profile_photo(): Remove your profile photo
- get_user_photos(user_id, limit): Get a user's profile photos
- get_user_status(user_id): Get a user's online status
Media
- send_file(chat_id, file_path, caption): Send a file
- send_voice(chat_id, file_path): Send a voice message
- download_media(chat_id, message_id, file_path): Download media
- upload_file(file_path): Upload a file to Telegram servers
- get_media_info(chat_id, message_id): Get info about media in a message
Search & Discovery
- search_public_chats(query): Search public chats/channels/bots
- search_messages(chat_id, query, limit): Search messages in a chat
- resolve_username(username): Resolve a username to ID
Stickers, GIFs, Bots
- get_sticker_sets(): List sticker sets
- send_sticker(chat_id, file_path): Send a sticker
- get_gif_search(query, limit): Search for GIFs
- send_gif(chat_id, gif_id): Send a GIF
- get_bot_info(bot_username): Get info about a bot
- set_bot_commands(bot_username, commands): Set bot commands (bot accounts only)
Privacy, Settings, and Misc
- get_privacy_settings(): Get privacy settings
- set_privacy_settings(key, allow_users, disallow_users): Set privacy settings
- mute_chat(chat_id): Mute notifications
- unmute_chat(chat_id): Unmute notifications
- archive_chat(chat_id): Archive a chat
- unarchive_chat(chat_id): Unarchive a chat
- get_recent_actions(chat_id): Get recent admin actions
๐ Requirements
- Python 3.10+
- Telethon
- MCP Python SDK
- Claude Desktop or Cursor (or any MCP client)
๐ง Installation & Setup
1. Fork & Clone
git clone https://github.com/chigwell/telegram-mcp.git cd telegram-mcp
2. Create a Virtual Environment
python3 -m venv .venv source .venv/bin/activate # On Windows: .venv\Scripts\activate pip install -r requirements.txt
3. Generate a Session String
python3 session_string_generator.py
Follow the prompts to authenticate and update your
.env
file.4. Configure .env
Copy
.env.example
to .env
and fill in your values:TELEGRAM_API_ID=your_api_id_here TELEGRAM_API_HASH=your_api_hash_here TELEGRAM_SESSION_NAME=anon TELEGRAM_SESSION_STRING=your_session_string_here
Get your API credentials at my.telegram.org/apps.
โ๏ธ Configuration for Claude & Cursor
MCP Configuration
Edit your Claude desktop config (e.g.
~/Library/Application Support/Claude/claude_desktop_config.json
) or Cursor config (~/.cursor/mcp.json
):{ "mcpServers": { "telegram-mcp": { "command": "uv", "args": [ "--directory", "/full/path/to/telegram-mcp-server", "run", "main.py" ] } } }
๐ Tool Examples with Code & Output
Below are examples of the most commonly used tools with their implementation and sample output.
Getting Your Chats
@mcp.tool() async def get_chats(page: int = 1, page_size: int = 20) -> str: """ Get a paginated list of chats. Args: page: Page number (1-indexed). page_size: Number of chats per page. """ try: dialogs = await client.get_dialogs() start = (page - 1) * page_size end = start + page_size if start >= len(dialogs): return "Page out of range." chats = dialogs[start:end] lines = [] for dialog in chats: entity = dialog.entity chat_id = entity.id title = getattr(entity, "title", None) or getattr(entity, "first_name", "Unknown") lines.append(f"Chat ID: {chat_id}, Title: {title}") return "\n".join(lines) except Exception as e: logger.exception(f"get_chats failed (page={page}, page_size={page_size})") return "An error occurred (code: GETCHATS-ERR-001). Check mcp_errors.log for details."
Example output:
Chat ID: 123456789, Title: John Doe Chat ID: -100987654321, Title: My Project Group Chat ID: 111223344, Title: Jane Smith Chat ID: -200123456789, Title: News Channel
Sending Messages
@mcp.tool() async def send_message(chat_id: int, message: str) -> str: """ Send a message to a specific chat. Args: chat_id: The ID of the chat. message: The message content to send. """ try: entity = await client.get_entity(chat_id) await client.send_message(entity, message) return "Message sent successfully." except Exception as e: logger.exception(f"send_message failed (chat_id={chat_id})") return "An error occurred (code: SENDMSG-ERR-001). Check mcp_errors.log for details."
Example output:
Message sent successfully.
Getting Chat Invite Links
The
get_invite_link
function is particularly robust with multiple fallback methods:@mcp.tool() async def get_invite_link(chat_id: int) -> str: """ Get the invite link for a group or channel. """ try: entity = await client.get_entity(chat_id) # Try using ExportChatInviteRequest first try: from telethon.tl import functions result = await client(functions.messages.ExportChatInviteRequest( peer=entity )) return result.link except AttributeError: # If the function doesn't exist in the current Telethon version logger.warning("ExportChatInviteRequest not available, using alternative method") except Exception as e1: # If that fails, log and try alternative approach logger.warning(f"ExportChatInviteRequest failed: {e1}") # Alternative approach using client.export_chat_invite_link try: invite_link = await client.export_chat_invite_link(entity) return invite_link except Exception as e2: logger.warning(f"export_chat_invite_link failed: {e2}") # Last resort: Try directly fetching chat info try: if isinstance(entity, (Chat, Channel)): full_chat = await client(functions.messages.GetFullChatRequest( chat_id=entity.id )) if hasattr(full_chat, 'full_chat') and hasattr(full_chat.full_chat, 'invite_link'): return full_chat.full_chat.invite_link or "No invite link available." except Exception as e3: logger.warning(f"GetFullChatRequest failed: {e3}") return "Could not retrieve invite link for this chat." except Exception as e: logger.exception(f"get_invite_link failed (chat_id={chat_id})") return f"Error getting invite link: {e}"
Example output:
https://t.me/+AbCdEfGhIjKlMnOp
Joining Chats via Invite Links
@mcp.tool() async def join_chat_by_link(link: str) -> str: """ Join a chat by invite link. """ try: # Extract the hash from the invite link if '/' in link: hash_part = link.split('/')[-1] if hash_part.startswith('+'): hash_part = hash_part[1:] # Remove the '+' if present else: hash_part = link # Try checking the invite before joining try: # Try to check invite info first (will often fail if not a member) invite_info = await client(functions.messages.CheckChatInviteRequest(hash=hash_part)) if hasattr(invite_info, 'chat') and invite_info.chat: # If we got chat info, we're already a member chat_title = getattr(invite_info.chat, 'title', 'Unknown Chat') return f"You are already a member of this chat: {chat_title}" except Exception: # This often fails if not a member - just continue pass # Join the chat using the hash result = await client(functions.messages.ImportChatInviteRequest(hash=hash_part)) if result and hasattr(result, 'chats') and result.chats: chat_title = getattr(result.chats[0], 'title', 'Unknown Chat') return f"Successfully joined chat: {chat_title}" return f"Joined chat via invite hash." except Exception as e: err_str = str(e).lower() if "expired" in err_str: return "The invite hash has expired and is no longer valid." elif "invalid" in err_str: return "The invite hash is invalid or malformed." elif "already" in err_str and "participant" in err_str: return "You are already a member of this chat." logger.exception(f"join_chat_by_link failed (link={link})") return f"Error joining chat: {e}"
Example output:
Successfully joined chat: Developer Community
Searching Public Chats
@mcp.tool() async def search_public_chats(query: str) -> str: """ Search for public chats, channels, or bots by username or title. """ try: result = await client(functions.contacts.SearchRequest(q=query, limit=20)) return json.dumps([format_entity(u) for u in result.users], indent=2) except Exception as e: return f"Error searching public chats: {e}"
Example output:
[ { "id": 123456789, "name": "TelegramBot", "type": "user", "username": "telegram_bot" }, { "id": 987654321, "name": "Telegram News", "type": "user", "username": "telegram_news" } ]
Getting Direct Chats with Contacts
@mcp.tool() async def get_direct_chat_by_contact(contact_query: str) -> str: """ Find a direct chat with a specific contact by name, username, or phone. Args: contact_query: Name, username, or phone number to search for. """ try: # Fetch all contacts using the correct Telethon method result = await client(functions.contacts.GetContactsRequest(hash=0)) contacts = result.users found_contacts = [] for contact in contacts: if not contact: continue name = f"{getattr(contact, 'first_name', '')} {getattr(contact, 'last_name', '')}".strip() username = getattr(contact, 'username', '') phone = getattr(contact, 'phone', '') if (contact_query.lower() in name.lower() or (username and contact_query.lower() in username.lower()) or (phone and contact_query in phone)): found_contacts.append(contact) if not found_contacts: return f"No contacts found matching '{contact_query}'." # If we found contacts, look for direct chats with them results = [] dialogs = await client.get_dialogs() for contact in found_contacts: contact_name = f"{getattr(contact, 'first_name', '')} {getattr(contact, 'last_name', '')}".strip() for dialog in dialogs: if isinstance(dialog.entity, User) and dialog.entity.id == contact.id: chat_info = f"Chat ID: {dialog.entity.id}, Contact: {contact_name}" if getattr(contact, 'username', ''): chat_info += f", Username: @{contact.username}" if dialog.unread_count: chat_info += f", Unread: {dialog.unread_count}" results.append(chat_info) break if not results: return f"Found contacts matching '{contact_query}', but no direct chats with them." return "\n".join(results) except Exception as e: return f"Error searching for direct chat: {e}"
Example output:
Chat ID: 123456789, Contact: John Smith, Username: @johnsmith, Unread: 3
๐ฎ Usage Examples
- "Show my recent chats"
- "Send 'Hello world' to chat 123456789"
- "Add contact with phone +1234567890, name John Doe"
- "Create a group 'Project Team' with users 111, 222, 333"
- "Download the media from message 42 in chat 123456789"
- "Mute notifications for chat 123456789"
- "Promote user 111 to admin in group 123456789"
- "Search for public channels about 'news'"
- "Join the Telegram group with invite link https://t.me/+AbCdEfGhIjK"
- "Send a sticker to my Saved Messages"
- "Get all my sticker sets"
You can use these tools via natural language in Claude, Cursor, or any MCP-compatible client.
๐ง Error Handling & Robustness
This implementation includes comprehensive error handling:
- Session management: Works with both file-based and string-based sessions
- Error reporting: Detailed errors logged to
mcp_errors.log
- Graceful degradation: Multiple fallback approaches for critical functions
- User-friendly messages: Clear, actionable error messages instead of technical errors
- Account type detection: Functions that require bot accounts detect and notify when used with user accounts
- Invite link processing: Handles various link formats and already-member cases
The code is designed to be robust against common Telegram API issues and limitations.
๐ ๏ธ Contribution Guide
- Fork this repo: chigwell/telegram-mcp
- Clone your fork:
git clone https://github.com/<your-github-username>/telegram-mcp.git
- Create a new branch:
git checkout -b my-feature
- Make your changes, add tests/docs if needed.
- Push and open a Pull Request to chigwell/telegram-mcp with a clear description.
- Tag @chigwell or @l1v0n1 in your PR for review.
๐ Security Considerations
- Never commit your
or session string..env
- The session string gives full access to your Telegram accountโkeep it safe!
- All processing is local; no data is sent anywhere except Telegram's API.
- Use
as a template and keep your actual.env.example
file private..env
- Test files are automatically excluded in
..gitignore
๐ ๏ธ Troubleshooting
- Check logs in your MCP client (Claude/Cursor) and the terminal for errors.
- Detailed error logs can be found in
.mcp_errors.log
- Interpreter errors? Make sure your
is created and selected..venv
- Database lock? Use session string authentication, not file-based sessions.
- iCloud/Dropbox issues? Move your project to a local path without spaces if you see odd errors.
- Regenerate session string if you change your Telegram password or see auth errors.
- Bot-only functions will show clear messages when used with regular user accounts.
- Test script failures? Check test configuration in
for valid test accounts/groups..env
๐ License
This project is licensed under the Apache 2.0 License.
๐ Acknowledgements
- Telethon
- Model Context Protocol
- Claude and Cursor
- chigwell/telegram-mcp (upstream)
Star History
Related Servers
D
Discord
community
A MCP server to connect to Discord guilds through a bot and read and write messages in channels
View DetailsR
RabbitMQ
community
The MCP server that interacts with RabbitMQ to publish and consume messages.
View DetailsT
Telegram-Client
community
A Telegram API bridge that manages user data, dialogs, messages, drafts, read status, and more for seamless interactions.
View Details