Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from bundle
Telegram MTProto MCP server with userbot watcher, chat/DM parser and context builders
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
README.md
1<div align="center">2<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%" />3</div>456[](https://opensource.org/licenses/Apache-2.0)7[](https://github.com/chigwell/telegram-mcp/actions/workflows/python-lint-format.yml)8[](https://github.com/chigwell/telegram-mcp/actions/workflows/docker-build.yml)910---1112## 🤖 MCP in Action1314Here's a demonstration of the Telegram MCP capabilities in [Claude](https://docs.anthropic.com/en/docs/agents-and-tools/mcp):1516**Basic usage example:**171819201. **Example: Asking Claude to analyze chat history and send a response:**212223242. **Successfully sent message to the group:**25262728As 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.2930---3132A full-featured Telegram integration for Claude, Cursor, and any MCP-compatible client, powered by [Telethon](https://docs.telethon.dev/) and the [Model Context Protocol (MCP)](https://modelcontextprotocol.io/). This project lets you interact with your Telegram account programmatically, automating everything from messaging to group management.333435---3637## 🚀 Features & Tools3839This MCP server exposes a huge suite of Telegram tools. **Every major Telegram/Telethon feature is available as a tool!**4041### Chat & Group Management42- **get_chats(page, page_size)**: Paginated list of chats43- **list_chats(chat_type, limit)**: List chats with metadata and filtering44- **get_chat(chat_id)**: Detailed info about a chat45- **create_group(title, user_ids)**: Create a new group46- **invite_to_group(group_id, user_ids)**: Invite users to a group or channel47- **create_channel(title, about, megagroup)**: Create a channel or supergroup48- **edit_chat_title(chat_id, title)**: Change chat/group/channel title49- **delete_chat_photo(chat_id)**: Remove chat/group/channel photo50- **leave_chat(chat_id)**: Leave a group or channel51- **get_participants(chat_id)**: List all participants52- **get_admins(chat_id)**: List all admins53- **get_banned_users(chat_id)**: List all banned users54- **promote_admin(chat_id, user_id)**: Promote user to admin55- **demote_admin(chat_id, user_id)**: Demote admin to user56- **ban_user(chat_id, user_id)**: Ban user57- **unban_user(chat_id, user_id)**: Unban user58- **get_invite_link(chat_id)**: Get invite link59- **export_chat_invite(chat_id)**: Export invite link60- **import_chat_invite(hash)**: Join chat by invite hash61- **join_chat_by_link(link)**: Join chat by invite link62- **subscribe_public_channel(channel)**: Subscribe to a public channel or supergroup by username or ID6364### Messaging65- **get_messages(chat_id, page, page_size)**: Paginated messages66- **list_messages(chat_id, limit, search_query, from_date, to_date)**: Filtered messages67- **list_topics(chat_id, limit, offset_topic, search_query)**: List forum topics in supergroups68- **send_message(chat_id, message)**: Send a message69- **reply_to_message(chat_id, message_id, text)**: Reply to a message70- **edit_message(chat_id, message_id, new_text)**: Edit your message71- **delete_message(chat_id, message_id)**: Delete a message72- **forward_message(from_chat_id, message_id, to_chat_id)**: Forward a message73- **pin_message(chat_id, message_id)**: Pin a message74- **unpin_message(chat_id, message_id)**: Unpin a message75- **mark_as_read(chat_id)**: Mark all as read76- **get_message_context(chat_id, message_id, context_size)**: Context around a message77- **get_history(chat_id, limit)**: Full chat history78- **get_pinned_messages(chat_id)**: List pinned messages79- **get_last_interaction(contact_id)**: Most recent message with a contact80- **create_poll(chat_id, question, options, multiple_choice, quiz_mode, public_votes, close_date)**: Create a poll81- **list_inline_buttons(chat_id, message_id, limit)**: Inspect inline keyboards to discover button text/index82- **press_inline_button(chat_id, message_id, button_text, button_index)**: Trigger inline keyboard callbacks by label or index83- **send_reaction(chat_id, message_id, emoji, big=False)**: Add a reaction to a message84- **remove_reaction(chat_id, message_id)**: Remove a reaction from a message85- **get_message_reactions(chat_id, message_id, limit=50)**: Get all reactions on a message8687### Contact Management88- **list_contacts()**: List all contacts89- **search_contacts(query)**: Search contacts90- **add_contact(phone, first_name, last_name)**: Add a contact91- **delete_contact(user_id)**: Delete a contact92- **block_user(user_id)**: Block a user93- **unblock_user(user_id)**: Unblock a user94- **import_contacts(contacts)**: Bulk import contacts95- **export_contacts()**: Export all contacts as JSON96- **get_blocked_users()**: List blocked users97- **get_contact_ids()**: List all contact IDs98- **get_direct_chat_by_contact(contact_query)**: Find direct chat with a contact99- **get_contact_chats(contact_id)**: List all chats with a contact100101### User & Profile102- **get_me()**: Get your user info103- **update_profile(first_name, last_name, about)**: Update your profile104- **set_profile_photo(file_path)**: Set a profile photo from an allowed root path105- **delete_profile_photo()**: Remove your profile photo106- **get_user_photos(user_id, limit)**: Get a user's profile photos107- **get_user_status(user_id)**: Get a user's online status108109### Media110- **get_media_info(chat_id, message_id)**: Get info about media in a message111- **send_file(chat_id, file_path, caption)**: Send a local file from allowed roots112- **download_media(chat_id, message_id, file_path)**: Save message media under allowed roots113- **upload_file(file_path)**: Upload a local file and return upload metadata114- **send_voice(chat_id, file_path)**: Send `.ogg/.opus` voice note from allowed roots115- **send_sticker(chat_id, file_path)**: Send `.webp` sticker from allowed roots116- **edit_chat_photo(chat_id, file_path)**: Update chat photo from allowed roots117118### Search & Discovery119- **search_public_chats(query, limit)**: Search public chats/channels/bots with a configurable result limit120- **search_messages(chat_id, query, limit)**: Search messages in a chat121- **search_global(query, page, page_size)**: Search messages globally with pagination122- **resolve_username(username)**: Resolve a username to ID123124### Stickers, GIFs, Bots125- **get_sticker_sets()**: List sticker sets126- **get_bot_info(bot_username)**: Get info about a bot127- **set_bot_commands(bot_username, commands)**: Set bot commands (bot accounts only)128129### Privacy, Settings, and Misc130- **get_privacy_settings()**: Get privacy settings131- **set_privacy_settings(key, allow_users, disallow_users)**: Set privacy settings132- **mute_chat(chat_id)**: Mute notifications133- **unmute_chat(chat_id)**: Unmute notifications134- **archive_chat(chat_id)**: Archive a chat135- **unarchive_chat(chat_id)**: Unarchive a chat136- **get_recent_actions(chat_id)**: Get recent admin actions137138### Drafts139- **save_draft(chat_id, message, reply_to_msg_id, no_webpage)**: Save a draft message to a chat/channel140- **get_drafts()**: Get all draft messages across all chats141- **clear_draft(chat_id)**: Clear/delete a draft from a specific chat142143### Input Validation144145To 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:146147- **Integer ID**: The direct integer ID for a user, chat, or channel (e.g., `123456789` or `-1001234567890`).148- **String ID**: The integer ID provided as a string (e.g., `"123456789"`).149- **Username**: The public username for a user or channel (e.g., `"@username"` or `"username"`).150151The 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.152153## File-path Tools Security Model154155File-path tools are available, but **disabled by default** until allowed roots are configured.156157Supported file-path tools:158- `send_file`, `download_media`, `set_profile_photo`, `edit_chat_photo`, `send_voice`, `send_sticker`, `upload_file`159160Security semantics (aligned with MCP filesystem server):161- Server-side allowlist via CLI positional arguments (fallback when Roots API is unsupported).162- Client-provided MCP Roots replace the server allowlist when available.163- If the client returns an empty Roots list, file-path tools are disabled (deny-all).164- All paths are resolved via realpath and must stay inside an allowed root.165- Traversal/glob-like patterns are rejected (`..`, `*`, `?`, `~`, etc.).166- Relative paths resolve against the first allowed root.167- Write tools default to `<first_root>/downloads/` when `file_path` is omitted.168169Example server launch with allowlisted roots:170```bash171uv --directory /full/path/to/telegram-mcp run main.py /data/telegram /tmp/telegram-mcp172```173174GIF 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.175176---177178## 📋 Requirements179- Python 3.10+180- [Telethon](https://docs.telethon.dev/)181- [MCP Python SDK](https://modelcontextprotocol.io/docs/)182- [Claude Desktop](https://claude.ai/desktop) or [Cursor](https://cursor.so/) (or any MCP client)183184---185186## 🔧 Installation & Setup187188### 1. Fork & Clone189190```bash191git clone https://github.com/chigwell/telegram-mcp.git192cd telegram-mcp193```194195### 2. Install Dependencies with uv196197```bash198uv sync199```200201### 3. Generate a Session String202203```bash204uv run session_string_generator.py205```206Follow the prompts to authenticate and update your `.env` file.207208### 4. Configure .env209210Copy `.env.example` to `.env` and fill in your values:211212```213TELEGRAM_API_ID=your_api_id_here214TELEGRAM_API_HASH=your_api_hash_here215TELEGRAM_SESSION_NAME=anon216TELEGRAM_SESSION_STRING=your_session_string_here217```218Get your API credentials at [my.telegram.org/apps](https://my.telegram.org/apps).219220---221222## 🐳 Running with Docker223224If you have Docker and Docker Compose installed, you can build and run the server in a container, simplifying dependency management.225226### 1. Build the Image227228From the project root directory, build the Docker image:229230```bash231docker build -t telegram-mcp:latest .232```233234### 2. Running the Container235236You have two options:237238**Option A: Using Docker Compose (Recommended for Local Use)**239240This method uses the `docker-compose.yml` file and automatically reads your credentials from a `.env` file.2412421. **Create `.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.2432. **Run Compose:**244```bash245docker compose up --build246```247* Use `docker compose up -d` to run in detached mode (background).248* Press `Ctrl+C` to stop the server.249250**Option B: Using `docker run`**251252You can run the container directly, passing credentials as environment variables.253254```bash255docker run -it --rm \256-e TELEGRAM_API_ID="YOUR_API_ID" \257-e TELEGRAM_API_HASH="YOUR_API_HASH" \258-e TELEGRAM_SESSION_STRING="YOUR_SESSION_STRING" \259telegram-mcp:latest260```261* Replace placeholders with your actual credentials.262* 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).263* The `-it` flags are crucial for interacting with the server.264265---266267## ⚙️ Configuration for Claude & Cursor268269### MCP Configuration270Edit your Claude desktop config (e.g. `~/Library/Application Support/Claude/claude_desktop_config.json`) or Cursor config (`~/.cursor/mcp.json`):271272```json273{274"mcpServers": {275"telegram-mcp": {276"command": "uv",277"args": [278"--directory",279"/full/path/to/telegram-mcp",280"run",281"main.py"282]283}284}285}286```287288## 📝 Tool Examples with Code & Output289290Below are examples of the most commonly used tools with their implementation and sample output.291292### Getting Your Chats293294```python295@mcp.tool()296async def get_chats(page: int = 1, page_size: int = 20) -> str:297"""298Get a paginated list of chats.299Args:300page: Page number (1-indexed).301page_size: Number of chats per page.302"""303try:304dialogs = await client.get_dialogs()305start = (page - 1) * page_size306end = start + page_size307if start >= len(dialogs):308return "Page out of range."309chats = dialogs[start:end]310lines = []311for dialog in chats:312entity = dialog.entity313chat_id = entity.id314title = getattr(entity, "title", None) or getattr(entity, "first_name", "Unknown")315lines.append(f"Chat ID: {chat_id}, Title: {title}")316return "\n".join(lines)317except Exception as e:318logger.exception(f"get_chats failed (page={page}, page_size={page_size})")319return "An error occurred (code: GETCHATS-ERR-001). Check mcp_errors.log for details."320```321322Example output:323```324Chat ID: 123456789, Title: John Doe325Chat ID: -100987654321, Title: My Project Group326Chat ID: 111223344, Title: Jane Smith327Chat ID: -200123456789, Title: News Channel328```329330### Sending Messages331332```python333@mcp.tool()334async def send_message(chat_id: int, message: str) -> str:335"""336Send a message to a specific chat.337Args:338chat_id: The ID of the chat.339message: The message content to send.340"""341try:342entity = await client.get_entity(chat_id)343await client.send_message(entity, message)344return "Message sent successfully."345except Exception as e:346logger.exception(f"send_message failed (chat_id={chat_id})")347return "An error occurred (code: SENDMSG-ERR-001). Check mcp_errors.log for details."348```349350Example output:351```352Message sent successfully.353```354355### Listing Inline Buttons356357```python358@mcp.tool()359async def list_inline_buttons(360chat_id: Union[int, str],361message_id: Optional[int] = None,362limit: int = 20,363) -> str:364"""365Discover inline keyboard layout, including button indices, callback availability, and URLs.366"""367```368369Example usage:370```371list_inline_buttons(chat_id="@sample_tasks_bot")372```373374This returns something like:375```376Buttons for message 42 (date 2025-01-01 12:00:00+00:00):377[0] text='📋 View tasks', callback=yes378[1] text='ℹ️ Help', callback=yes379[2] text='🌐 Visit site', callback=no, url=https://example.org380```381382### Pressing Inline Buttons383384```python385@mcp.tool()386async def press_inline_button(387chat_id: Union[int, str],388message_id: Optional[int] = None,389button_text: Optional[str] = None,390button_index: Optional[int] = None,391) -> str:392"""393Press an inline keyboard button by label or zero-based index.394If message_id is omitted, the server searches recent messages for the latest inline keyboard.395"""396```397398Example usage:399```400press_inline_button(chat_id="@sample_tasks_bot", button_text="📋 View tasks")401```402403Use `list_inline_buttons` first if you need to inspect available buttons—pass a bogus `button_text`404to quickly list options or call `list_inline_buttons` directly. Once you know the text or index,405`press_inline_button` sends the callback, just like tapping the button in a native Telegram client.406407### Subscribing to Public Channels408409```python410@mcp.tool()411async def