<div align="center"> <img src="https://capsule-render.vercel.app/api?type=waving&color=gradient&height=200§ion=header&text=Telegram%20MCP%20Server&fontSize=50&fontAlignY=35&animation=fadeIn&fontColor=FFFFFF&descAlignY=55&descAlign=62" alt="Telegram MCP Server" width="100%" /> </div>
!MCP Badge   
🤖 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
- getchats(page, pagesize): Paginated list of chats
- listchats(chattype, limit): List chats with metadata and filtering
- getchat(chatid): Detailed info about a chat
- creategroup(title, userids): Create a new group
- invitetogroup(groupid, userids): Invite users to a group or channel
- create_channel(title, about, megagroup): Create a channel or supergroup
- editchattitle(chat_id, title): Change chat/group/channel title
- deletechatphoto(chat_id): Remove chat/group/channel photo
- leavechat(chatid): Leave a group or channel
- getparticipants(chatid): List all participants
- getadmins(chatid): List all admins
- getbannedusers(chat_id): List all banned users
- promoteadmin(chatid, user_id): Promote user to admin
- demoteadmin(chatid, user_id): Demote admin to user
- banuser(chatid, user_id): Ban user
- unbanuser(chatid, user_id): Unban user
- getinvitelink(chat_id): Get invite link
- exportchatinvite(chat_id): Export invite link
- importchatinvite(hash): Join chat by invite hash
- joinchatby_link(link): Join chat by invite link
- subscribepublicchannel(channel): Subscribe to a public channel or supergroup by username or ID
Messaging
- getmessages(chatid, page, page_size): Paginated messages
- listmessages(chatid, limit, searchquery, fromdate, to_date): Filtered messages
- listtopics(chatid, limit, offsettopic, searchquery): List forum topics in supergroups
- sendmessage(chatid, message): Send a message
- replytomessage(chatid, messageid, text): Reply to a message
- editmessage(chatid, messageid, newtext): Edit your message
- deletemessage(chatid, message_id): Delete a message
- forwardmessage(fromchatid, messageid, tochatid): Forward a message
- pinmessage(chatid, message_id): Pin a message
- unpinmessage(chatid, message_id): Unpin a message
- markasread(chat_id): Mark all as read
- getmessagecontext(chatid, messageid, context_size): Context around a message
- gethistory(chatid, limit): Full chat history
- getpinnedmessages(chat_id): List pinned messages
- getlastinteraction(contact_id): Most recent message with a contact
- createpoll(chatid, question, options, multiplechoice, quizmode, publicvotes, closedate): Create a poll
- listinlinebuttons(chatid, messageid, limit): Inspect inline keyboards to discover button text/index
- pressinlinebutton(chatid, messageid, buttontext, buttonindex): Trigger inline keyboard callbacks by label or index
- sendreaction(chatid, message_id, emoji, big=False): Add a reaction to a message
- removereaction(chatid, message_id): Remove a reaction from a message
- getmessagereactions(chatid, messageid, limit=50): Get all reactions on a message
Contact Management
- list_contacts(): List all contacts
- search_contacts(query): Search contacts
- addcontact(phone, firstname, last_name): Add a contact
- deletecontact(userid): Delete a contact
- blockuser(userid): Block a user
- unblockuser(userid): Unblock a user
- import_contacts(contacts): Bulk import contacts
- export_contacts(): Export all contacts as JSON
- getblockedusers(): List blocked users
- getcontactids(): List all contact IDs
- getdirectchatbycontact(contact_query): Find direct chat with a contact
- getcontactchats(contact_id): List all chats with a contact
User & Profile
- get_me(): Get your user info
- updateprofile(firstname, last_name, about): Update your profile
- setprofilephoto(file_path): Set a profile photo from an allowed root path
- deleteprofilephoto(): Remove your profile photo
- getuserphotos(user_id, limit): Get a user's profile photos
- getuserstatus(user_id): Get a user's online status
Media
- getmediainfo(chatid, messageid): Get info about media in a message
- sendfile(chatid, file_path, caption): Send a local file from allowed roots
- downloadmedia(chatid, messageid, filepath): Save message media under allowed roots
- uploadfile(filepath): Upload a local file and return upload metadata
- sendvoice(chatid, file_path): Send
.ogg/.opusvoice note from allowed roots - sendsticker(chatid, file_path): Send
.webpsticker from allowed roots - editchatphoto(chatid, filepath): Update chat photo from allowed roots
Search & Discovery
- searchpublicchats(query, limit): Search public chats/channels/bots with a configurable result limit
- searchmessages(chatid, query, limit): Search messages in a chat
- searchglobal(query, page, pagesize): Search messages globally with pagination
- resolve_username(username): Resolve a username to ID
Stickers, GIFs, Bots
- getstickersets(): List sticker sets
- getbotinfo(bot_username): Get info about a bot
- setbotcommands(bot_username, commands): Set bot commands (bot accounts only)
Privacy, Settings, and Misc
- getprivacysettings(): Get privacy settings
- setprivacysettings(key, allowusers, disallowusers): Set privacy settings
- mutechat(chatid): Mute notifications
- unmutechat(chatid): Unmute notifications
- archivechat(chatid): Archive a chat
- unarchivechat(chatid): Unarchive a chat
- getrecentactions(chat_id): Get recent admin actions
Drafts
- savedraft(chatid, message, replytomsgid, nowebpage): Save a draft message to a chat/channel
- get_drafts(): Get all draft messages across all chats
- cleardraft(chatid): Clear/delete a draft from a specific chat
Input Validation
To improve robustness, all functions accepting chat_id or user_id parameters now include input validation. You can use any of the following formats for these IDs:
- Integer ID: The direct integer ID for a user, chat, or channel (e.g.,
123456789or-1001234567890). - String ID: The integer ID provided as a string (e.g.,
"123456789"). - Username: The public username for a user or channel (e.g.,
"@username"or"username").
The server will automatically validate the input and convert it to the correct format before making a request to Telegram. If the input is invalid, a clear error message will be returned.
File-path Tools Security Model
File-path tools are available, but disabled by default until allowed roots are configured.
Supported file-path tools:
send_file,download_media,set_profile_photo,edit_chat_photo,send_voice,send_sticker,upload_file
Security semantics (aligned with MCP filesystem server):
- Server-side allowlist via CLI positional arguments (fallback when Roots API is unsupported).
- Client-provided MCP Roots replace the server allowlist when available.
- If the client returns an empty Roots list, file-path tools are disabled (deny-all).
- All paths are resolved via realpath and must stay inside an allowed root.
- Traversal/glob-like patterns are rejected (
..,*,?,~, etc.). - Relative paths resolve against the first allowed root.
- Write tools default to
<first_root>/downloads/whenfile_pathis omitted.
Example server launch with allowlisted roots:
uv --directory /full/path/to/telegram-mcp run main.py /data/telegram /tmp/telegram-mcpGIF tools are currently limited: get_gif_search and send_gif are available, while get_saved_gifs is not implemented due to reliability limits in Telethon/Telegram API interactions.
📋 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-mcp2. Install Dependencies with uv
uv sync3. Generate a Session String
uv run session_string_generator.pyFollow 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_hereGet your API credentials at my.telegram.org/apps.
🐳 Running with Docker
If you have Docker and Docker Compose installed, you can build and run the server in a container, simplifying dependency management.
1. Build the Image
From the project root directory, build the Docker image:
docker build -t telegram-mcp:latest .2. Running the Container
You have two options:
Option A: Using Docker Compose (Recommended for Local Use)
This method uses the docker-compose.yml file and automatically reads your credentials from a .env file.
- Create
.envFile: Ensure you have a.envfile in the project root containing yourTELEGRAM_API_ID,TELEGRAM_API_HASH, andTELEGRAM_SESSION_STRING(orTELEGRAM_SESSION_NAME). Use.env.exampleas a template. - Run Compose:
docker compose up --build* Use docker compose up -d to run in detached mode (background). * Press Ctrl+C to stop the server.
Option B: Using docker run
You can run the container directly, passing credentials as environment variables.
docker run -it --rm \
-e TELEGRAM_API_ID="YOUR_API_ID" \
-e TELEGRAM_API_HASH="YOUR_API_HASH" \
-e TELEGRAM_SESSION_STRING="YOUR_SESSION_STRING" \
telegram-mcp:latest* Replace placeholders with your actual credentials. * Use -e TELEGRAM_SESSION_NAME=your_session_file_name instead of TELEGRAM_SESSION_STRING if you prefer file-based sessions (requires volume mounting, see docker-compose.yml for an example). * The -it flags are crucial for interacting with the server.
⚙️ 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",
"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 ChannelSending 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.Listing Inline Buttons
@mcp.tool()
async def list_inline_buttons(
chat_id: Union[int, str],
message_id: Optional[int] = None,
limit: int = 20,
) -> str:
"""
Discover inline keyboard layout, including button indices, callback availability, and URLs.
"""Example usage:
list_inline_buttons(chat_id="@sample_tasks_bot")This returns something like:
Buttons for message 42 (date 2025-01-01 12:00:00+00:00):
[0] text='📋 View tasks', callback=yes
[1] text='ℹ️ Help', callback=yes
[2] text='🌐 Visit site', callback=no, url=https://example.orgPressing Inline Buttons
@mcp.tool()
async def press_inline_button(
chat_id: Union[int, str],
message_id: Optional[int] = None,
button_text: Optional[str] = None,
button_index: Optional[int] = None,
) -> str:
"""
Press an inline keyboard button by label or zero-based index.
If message_id is omitted, the server searches recent messages for the latest inline keyboard.
"""Example usage:
press_inline_button(chat_id="@sample_tasks_bot", button_text="📋 View tasks")Use list_inline_buttons first if you need to inspect available buttons—pass a bogus button_text to quickly list options or call list_inline_buttons directly. Once you know the text or index, press_inline_button sends the callback, just like tapping the button in a native Telegram client.
Subscribing to Public Channels
@mcp.tool()
async def