loading…
Search for a command to run...
loading…
Telegram API integration for accessing user data, managing dialogs (chats, channels, groups), retrieving messages, sending messages and handling read status.
Telegram API integration for accessing user data, managing dialogs (chats, channels, groups), retrieving messages, sending messages and handling read status.
License: Apache 2.0
Python Lint & Format Check
Docker Build & Compose Validation
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!
with_about=True enriches output with each chat's description (slower)..ogg/.opus voice note from allowed roots.webp sticker from allowed rootsAll tools accept an optional account parameter to target a specific account. In multi-account mode:
get_chats, list_messages) query all accounts when account is omitted, returning results prefixed with [label].send_message, mark_as_read) require an explicit account value.account parameter is optional everywhere.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:
123456789 or -1001234567890)."123456789")."@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 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_fileSecurity semantics (aligned with MCP filesystem server):
.., *, ?, ~, etc.).<first_root>/downloads/ when file_path is omitted.Example server launch with allowlisted roots:
uv --directory /full/path/to/telegram-mcp run main.py /data/telegram /tmp/telegram-mcp
GIF 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.
git clone https://github.com/chigwell/telegram-mcp.git
cd telegram-mcp
uv sync
uv run session_string_generator.py
Follow the prompts to authenticate. You'll be asked for an optional account label (e.g., work, personal) — leave empty for a single-account setup.
Copy .env.example to .env and fill in your values.
Single account:
TELEGRAM_API_ID=your_api_id_here
TELEGRAM_API_HASH=your_api_hash_here
TELEGRAM_SESSION_STRING=your_session_string_here
Multiple accounts:
TELEGRAM_API_ID=your_api_id_here
TELEGRAM_API_HASH=your_api_hash_here
TELEGRAM_SESSION_STRING_WORK=session_string_for_work_account
TELEGRAM_SESSION_STRING_PERSONAL=session_string_for_personal_account
Add _<LABEL> suffix to TELEGRAM_SESSION_STRING for each account. Labels become account identifiers used in tool calls. A shared TELEGRAM_API_ID and TELEGRAM_API_HASH are used for all accounts.
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_STRING_WORK="..." -e TELEGRAM_SESSION_STRING_PERSONAL="..." instead.-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()
@with_account(readonly=True)
async def get_chats(account: str = None, page: int = 1, page_size: int = 20) -> str:
"""
Get a paginated list of chats.
Args:
account: Account label (optional in single-account mode).
page: Page number (1-indexed).
page_size: Number of chats per page.
"""
try:
cl = get_client(account)
dialogs = await cl.get_dialogs()
...
Example output (multi-account, no account specified):
[work]
Chat ID: -100987654321, Title: My Project Group
Chat ID: 111223344, Title: Jane Smith
[personal]
Chat ID: 123456789, Title: John Doe
Chat ID: -200123456789, Title: News Channel
@mcp.tool()
@with_account(readonly=False)
async def send_message(chat_id: Union[int, str], message: str, account: str = None) -> str:
"""
Send a message to a specific chat.
Args:
chat_id: The ID of the chat.
message: The message content to send.
account: Account label (required in multi-account mode).
"""
try:
cl = get_client(account)
entity = await resolve_entity(chat_id, cl)
await cl.send_message(entity, message)
return "Message sent successfully."
...
Example output:
Message sent successfully.
@mcp.tool()
@with_account(readonly=True)
async def list_inline_buttons(
chat_id: Union[int, str],
message_id: Optional[int] = None,
limit: int = 20,
account: str = None,
) -> 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.org
@mcp.tool()
@with_account(readonly=False)
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,
account: str = 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.
@mcp.tool()
@with_account(readonly=False)
async def subscribe_public_channel(channel: Union[int, str], account: str = None) -> str:
"""
Join a public channel or supergroup by username (e.g., "@examplechannel") or ID.
"""
Example usage:
subscribe_public_channel(channel="@daily_updates_feed")
If the account is already a participant, the tool reports that instead of failing, making it safe to run repeatedly in workflows that need idempotent joins.
The get_invite_link function is particularly robust with multiple fallback methods:
@mcp.tool()
@with_account(readonly=True)
async def get_invite_link(chat_id: int, account: str = None) -> str:
"""
Get the invite link for a group or channel.
"""
try:
cl = get_client(account)
entity = await resolve_entity(chat_id, cl)
# Tries ExportChatInviteRequest, then export_chat_invite_link,
# then GetFullChatRequest as fallbacks
...
Example output:
https://t.me/+AbCdEfGhIjKlMnOp
@mcp.tool()
@with_account(readonly=False)
async def join_chat_by_link(link: str, account: str = None) -> str:
"""
Join a chat by invite link.
"""
try:
cl = get_client(account)
# Extracts hash, checks membership, and joins via ImportChatInviteRequest
...
Example output:
Successfully joined chat: Developer Community
@mcp.tool()
@with_account(readonly=True)
async def search_public_chats(query: str, limit: int = 20, account: str = None) -> str:
"""
Search for public chats, channels, or bots by username or title.
"""
try:
cl = get_client(account)
result = await cl(functions.contacts.SearchRequest(q=query, limit=limit))
entities = [format_entity(e) for e in result.chats + result.users]
return json.dumps(entities, indent=2)
...
Example output:
[
{
"id": 123456789,
"name": "TelegramBot",
"type": "user",
"username": "telegram_bot"
},
{
"id": 987654321,
"name": "Telegram News",
"type": "user",
"username": "telegram_news"
}
]
@mcp.tool()
@with_account(readonly=True)
async def get_direct_chat_by_contact(contact_query: str, account: str = None) -> 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:
cl = get_client(account)
result = await cl(functions.contacts.GetContactsRequest(hash=0))
# Searches contacts, then matches against dialogs
...
Example output:
Chat ID: 123456789, Contact: John Smith, Username: @johnsmith, Unread: 3
Multi-account examples:
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!
Add this to claude_desktop_config.json and restart Claude Desktop.
{
"mcpServers": {
"chigwell-telegram-mcp": {
"command": "npx",
"args": []
}
}
}Read, send and search emails from Claude
Send, search and summarize Slack messages
No-code MCP client for team chat platforms, such as Slack, Microsoft Teams, and Discord.
A community discord server dedicated to MCP by [Frank Fiegel](https://github.com/punkpeye)