by chigwell
Telegram MCP is an MCP server that provides paginated chat reading, message retrieval, and message sending capabilities for Telegram through Telethon integration, enabling AI models like Claude and Cursor to interact with Telegram accounts programmatically.
Telegram MCP is a server that integrates Telegram with the Model Context Protocol (MCP), allowing AI models and other MCP-compatible clients (like Claude and Cursor) to interact with a user's Telegram account. It leverages the Telethon library to provide a wide range of functionalities, essentially turning your Telegram account into a programmable interface for AI.
To use Telegram MCP, you need to:
pip install -r requirements.txt).python3 session_string_generator.py to authenticate your Telegram account..env with your Telegram API ID, API Hash, and the generated session string.docker build -t telegram-mcp:latest .) and run using Docker Compose (docker compose up --build) or docker run.telegram-mcp server, specifying the command and arguments to run main.py.Telegram MCP exposes a comprehensive suite of Telegram tools, making almost every major Telegram/Telethon feature available:
get_chats, list_chats)get_chat)create_group, create_channel)get_messages, list_messages)get_me)get_media_info)search_public_chats)search_messages)resolve_username)Removed Functionality: Tools requiring direct file path access (e.g., send_file, download_media) and GIF-related tools have been removed due to current MCP environment limitations or Telethon reliability issues.
Q: What are the requirements to run Telegram MCP? A: Python 3.10+, Telethon, MCP Python SDK, and an MCP client like Claude Desktop or Cursor.
Q: How do I get my Telegram API credentials? A: You can get your API ID and API Hash from my.telegram.org/apps.
Q: Why were some functionalities removed? A: Tools requiring direct file path access were removed due to limitations in the current MCP environment regarding file attachments and local file system paths. GIF-related tools were removed due to ongoing reliability issues with Telethon or the Telegram API.
Q: How can I troubleshoot issues?
A: Check logs in your MCP client and terminal. Detailed errors are in mcp_errors.log. Ensure your virtual environment is active. If you see database lock errors, use session string authentication. Regenerate your session string if you change your Telegram password or encounter authentication errors.
Q: Is it safe to use Telegram MCP?
A: It is crucial to never commit your .env file or session string to version control, as the session string grants full access to your Telegram account. All processing is local, and no data is sent anywhere except Telegram's API.
Here's a demonstration of the Telegram MCP capabilities in Claude:
Basic usage example:



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.
This MCP server exposes a huge suite of Telegram tools. Every major Telegram/Telethon feature is available as a tool!
Please note that tools requiring direct file path access on the server (send_file, download_media, set_profile_photo, edit_chat_photo, send_voice, send_sticker, upload_file) have been removed from main.py. This is due to limitations in the current MCP environment regarding handling file attachments and local file system paths.
Additionally, GIF-related tools (get_gif_search, get_saved_gifs, send_gif) have been removed due to ongoing issues with reliability in the Telethon library or Telegram API interactions.
git clone https://github.com/chigwell/telegram-mcp.git
cd telegram-mcp
python3 -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
pip install -r requirements.txt
python3 session_string_generator.py
Follow the prompts to authenticate and update your .env file.
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.
If you have Docker and Docker Compose installed, you can build and run the server in a container, simplifying dependency management.
From the project root directory, build the Docker image:
docker build -t telegram-mcp:latest .
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.
.env File: Ensure you have a .env file in the project root containing your TELEGRAM_API_ID, TELEGRAM_API_HASH, and TELEGRAM_SESSION_STRING (or TELEGRAM_SESSION_NAME). Use .env.example as a template.docker compose up --build
docker compose up -d to run in detached mode (background).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
-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).-it flags are crucial for interacting with the server.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"
]
}
}
}
Below are examples of the most commonly used tools with their implementation and sample output.
@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
@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.
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
@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
@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"
}
]
@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
You can use these tools via natural language in Claude, Cursor, or any MCP-compatible client.
This implementation includes comprehensive error handling:
mcp_errors.logThe code is designed to be robust against common Telegram API issues and limitations.
git clone https://github.com/<your-github-username>/telegram-mcp.git
git checkout -b my-feature
.env or session string..env.example as a template and keep your actual .env file private..gitignore.mcp_errors.log..venv is created and selected..env for valid test accounts/groups.This project is licensed under the Apache 2.0 License.
Maintained by @chigwell and @l1v0n1. PRs welcome!
Please log in to share your review and rating for this MCP.
Discover more MCP servers with similar functionality and use cases
by danny-avila
Provides a customizable ChatGPT‑like web UI that integrates dozens of AI models, agents, code execution, image generation, web search, speech capabilities, and secure multi‑user authentication, all open‑source and ready for self‑hosting.
by ahujasid
BlenderMCP integrates Blender with Claude AI via the Model Context Protocol (MCP), enabling AI-driven 3D scene creation, modeling, and manipulation. This project allows users to control Blender directly through natural language prompts, streamlining the 3D design workflow.
by pydantic
Enables building production‑grade generative AI applications using Pydantic validation, offering a FastAPI‑like developer experience.
by GLips
Figma-Context-MCP is a Model Context Protocol (MCP) server that provides Figma layout information to AI coding agents. It bridges design and development by enabling AI tools to directly access and interpret Figma design data for more accurate and efficient code generation.
by mcp-use
Easily create and interact with MCP servers using custom agents, supporting any LLM with tool calling and offering multi‑server, sandboxed, and streaming capabilities.
by sonnylazuardi
This project implements a Model Context Protocol (MCP) integration between Cursor AI and Figma, allowing Cursor to communicate with Figma for reading designs and modifying them programmatically.
by lharries
WhatsApp MCP Server is a Model Context Protocol (MCP) server for WhatsApp that allows users to search, read, and send WhatsApp messages (including media) through AI models like Claude. It connects directly to your personal WhatsApp account via the WhatsApp web multi-device API and stores messages locally in a SQLite database.
by idosal
GitMCP is a free, open-source remote Model Context Protocol (MCP) server that transforms any GitHub project into a documentation hub, enabling AI tools to access up-to-date documentation and code directly from the source to eliminate "code hallucinations."
by Klavis-AI
Klavis AI provides open-source Multi-platform Control Protocol (MCP) integrations and a hosted API for AI applications. It simplifies connecting AI to various third-party services by managing secure MCP servers and authentication.