Merge pull request #2 from l1v0n1/main
Telegram MCP v2.0: Major Functionality Enhancements & Robust Error Handling
This commit is contained in:
commit
e3842dade9
9 changed files with 2360 additions and 247 deletions
11
.env.example
11
.env.example
|
|
@ -1,10 +1,9 @@
|
|||
# Your Telegram API credentials from https://my.telegram.org/apps
|
||||
# Telegram API Credentials (Required - get from https://my.telegram.org/apps)
|
||||
TELEGRAM_API_ID=123456
|
||||
TELEGRAM_API_HASH=0123456789abcdef0123456789abcdef
|
||||
|
||||
# Session name (can be any name you choose)
|
||||
# Session Management (Choose ONE)
|
||||
# Option 1: File-based session (a .session file will be created)
|
||||
TELEGRAM_SESSION_NAME=telegram_session
|
||||
|
||||
# Optional: Session string for portable authentication (leave empty if not using)
|
||||
# This will be generated by the session_string_generator.py script
|
||||
TELEGRAM_SESSION_STRING=
|
||||
# Option 2: String-based session (if you generate one, e.g., using Telethon's string session generator)
|
||||
TELEGRAM_SESSION_STRING=1231231232erfdfdffd
|
||||
|
|
|
|||
19
.gitignore
vendored
19
.gitignore
vendored
|
|
@ -85,7 +85,7 @@ ipython_config.py
|
|||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
.python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
|
|
@ -185,3 +185,20 @@ anon_new.session-journal
|
|||
|
||||
# Claude Desktop config
|
||||
claude_desktop_config.json
|
||||
.DS_Store
|
||||
|
||||
# Test files
|
||||
.cursor/
|
||||
tests/
|
||||
test.py
|
||||
extract_test_issues.py
|
||||
*.tmp
|
||||
mcp_errors.log
|
||||
telegram_test.log
|
||||
test_issues_report.md
|
||||
|
||||
# Temporary data files
|
||||
test_upload.txt
|
||||
test_voice.ogg
|
||||
sticker.webp
|
||||
two.png
|
||||
572
README.md
572
README.md
|
|
@ -1,68 +1,165 @@
|
|||
# Telegram MCP for Claude
|
||||
# Telegram MCP Server
|
||||
|
||||

|
||||
[](https://opensource.org/licenses/Apache-2.0)
|
||||
|
||||
A powerful Telegram integration for Claude via the Model Context Protocol (MCP), allowing you to interact with your Telegram account directly from Claude Desktop.
|
||||
---
|
||||
|
||||
## 🤖 MCP in Action
|
||||
|
||||
Here's a demonstration of the Telegram MCP capabilities in Claude:
|
||||
|
||||
**Basic usage example:**
|
||||
|
||||

|
||||
|
||||
## 🚀 Features
|
||||
1. **Example: Asking Claude to analyze chat history and send a response:**
|
||||
|
||||
This MCP server provides a comprehensive suite of tools for seamless Telegram interaction:
|
||||

|
||||
|
||||
### Chat Management
|
||||
- **get_chats** - Get a paginated list of your chats
|
||||
- **list_chats** - List all chats with detailed metadata and filtering options
|
||||
- **get_chat** - Get detailed information about a specific chat
|
||||
2. **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](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.
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 🚀 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** - Get messages from a specific chat with pagination
|
||||
- **list_messages** - Retrieve messages with powerful filtering (text search, date ranges)
|
||||
- **send_message** - Send messages to any chat
|
||||
- **get_message_context** - View the context around a specific message
|
||||
- **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
|
||||
- **search_contacts** - Find contacts by name, username or phone number
|
||||
- **get_direct_chat_by_contact** - Find personal chats with specific contacts
|
||||
- **get_contact_chats** - List all chats (including groups) involving a contact
|
||||
- **get_last_interaction** - View your most recent exchanges with a contact
|
||||
- **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](https://docs.telethon.dev/) for Telegram API access
|
||||
- [Telethon](https://docs.telethon.dev/)
|
||||
- [MCP Python SDK](https://modelcontextprotocol.io/docs/)
|
||||
- [UV](https://astral.sh/uv/) package manager
|
||||
- [Claude Desktop](https://claude.ai/desktop) app
|
||||
- [Claude Desktop](https://claude.ai/desktop) or [Cursor](https://cursor.so/) (or any MCP client)
|
||||
|
||||
## 🔧 Installation
|
||||
---
|
||||
|
||||
### 1. Clone the Repository
|
||||
## 🔧 Installation & Setup
|
||||
|
||||
### 1. Fork & Clone
|
||||
|
||||
```bash
|
||||
git clone https://github.com/chigwell/telegram-mcp-server
|
||||
cd telegram-mcp-server
|
||||
git clone https://github.com/chigwell/telegram-mcp.git
|
||||
cd telegram-mcp
|
||||
```
|
||||
|
||||
### 2. Generate Session String
|
||||
|
||||
For better security and portability, this project uses Telethon's StringSession. Generate your session string:
|
||||
### 2. Create a Virtual Environment
|
||||
|
||||
```bash
|
||||
python session_string_generator.py
|
||||
python3 -m venv .venv
|
||||
source .venv/bin/activate # On Windows: .venv\Scripts\activate
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
This will:
|
||||
1. Ask for your phone number
|
||||
2. Send a verification code to your Telegram app
|
||||
3. Generate a session string and add it to your `.env` file
|
||||
### 3. Generate a Session String
|
||||
|
||||
The session string allows authentication without storing SQLite session files, which helps avoid database lock issues and improves portability.
|
||||
```bash
|
||||
python3 session_string_generator.py
|
||||
```
|
||||
Follow the prompts to authenticate and update your `.env` file.
|
||||
|
||||
### 3. Set Up Your Environment
|
||||
### 4. Configure .env
|
||||
|
||||
Create a `.env` file with your Telegram credentials:
|
||||
Copy `.env.example` to `.env` and fill in your values:
|
||||
|
||||
```
|
||||
TELEGRAM_API_ID=your_api_id_here
|
||||
|
|
@ -70,89 +167,380 @@ 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](https://my.telegram.org/apps).
|
||||
|
||||
You can obtain API credentials at [my.telegram.org/apps](https://my.telegram.org/apps).
|
||||
---
|
||||
|
||||
### 4. Install Dependencies
|
||||
## ⚙️ Configuration for Claude & Cursor
|
||||
|
||||
```bash
|
||||
uv venv
|
||||
source .venv/bin/activate # On Windows: .venv\Scripts\activate
|
||||
uv add "mcp[cli]" telethon python-dotenv nest_asyncio
|
||||
```
|
||||
|
||||
### 5. Configure Claude Desktop
|
||||
|
||||
#### On macOS/Linux:
|
||||
Edit `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
||||
### MCP Configuration
|
||||
Edit your Claude desktop config (e.g. `~/Library/Application Support/Claude/claude_desktop_config.json`) or Cursor config (`~/.cursor/mcp.json`):
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"telegram-mcp": {
|
||||
"command": "/full/path/to/uv",
|
||||
"args": [
|
||||
"--directory",
|
||||
"/full/path/to/telegram-mcp-server",
|
||||
"run",
|
||||
"main.py"
|
||||
]
|
||||
}
|
||||
"mcpServers": {
|
||||
"telegram-mcp": {
|
||||
"command": "uv",
|
||||
"args": [
|
||||
"--directory",
|
||||
"/full/path/to/telegram-mcp-server",
|
||||
"run",
|
||||
"main.py"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### On Windows:
|
||||
Edit `%APPDATA%\Claude\claude_desktop_config.json` with similar configuration.
|
||||
## 📝 Tool Examples with Code & Output
|
||||
|
||||
Below are examples of the most commonly used tools with their implementation and sample output.
|
||||
|
||||
### Getting Your Chats
|
||||
|
||||
```python
|
||||
@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
|
||||
|
||||
```python
|
||||
@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:
|
||||
|
||||
```python
|
||||
@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
|
||||
|
||||
```python
|
||||
@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
|
||||
|
||||
```python
|
||||
@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:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": 123456789,
|
||||
"name": "TelegramBot",
|
||||
"type": "user",
|
||||
"username": "telegram_bot"
|
||||
},
|
||||
{
|
||||
"id": 987654321,
|
||||
"name": "Telegram News",
|
||||
"type": "user",
|
||||
"username": "telegram_news"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### Getting Direct Chats with Contacts
|
||||
|
||||
```python
|
||||
@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
|
||||
|
||||
Here are some ways to interact with Telegram through Claude:
|
||||
- "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"
|
||||
|
||||
### Basic Chat Navigation
|
||||
- "Show me my most recent chats"
|
||||
- "List my group chats with unread messages"
|
||||
- "Show detailed information about chat 123456789"
|
||||
You can use these tools via natural language in Claude, Cursor, or any MCP-compatible client.
|
||||
|
||||
### Messaging
|
||||
- "Show me the last 10 messages from chat 123456789"
|
||||
- "Send 'I'll be there in 10 minutes' to chat 123456789"
|
||||
- "Find messages containing 'meeting' in chat 123456789"
|
||||
- "Show messages from March 1-15, 2023 in chat 123456789"
|
||||
---
|
||||
|
||||
### Contact Interactions
|
||||
- "Search for contacts named 'Alex'"
|
||||
- "Find my direct chat with John"
|
||||
- "Show all chats where I interact with contact 987654321"
|
||||
- "Show my last conversation with Lisa"
|
||||
## 🧠 Error Handling & Robustness
|
||||
|
||||
### Advanced Features
|
||||
- "Show the context around message 42 in chat 123456789"
|
||||
- "List all channels I'm subscribed to"
|
||||
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
|
||||
|
||||
1. **Fork this repo:** [chigwell/telegram-mcp](https://github.com/chigwell/telegram-mcp)
|
||||
2. **Clone your fork:**
|
||||
```bash
|
||||
git clone https://github.com/<your-github-username>/telegram-mcp.git
|
||||
```
|
||||
3. **Create a new branch:**
|
||||
```bash
|
||||
git checkout -b my-feature
|
||||
```
|
||||
4. **Make your changes, add tests/docs if needed.**
|
||||
5. **Push and open a Pull Request** to [chigwell/telegram-mcp](https://github.com/chigwell/telegram-mcp) with a clear description.
|
||||
6. **Tag @chigwell or @l1v0n1** in your PR for review.
|
||||
|
||||
---
|
||||
|
||||
## 🔒 Security Considerations
|
||||
- **Never commit your `.env` or session string.**
|
||||
- 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 `.env.example` as a template and keep your actual `.env` file private.
|
||||
- Test files are automatically excluded in `.gitignore`.
|
||||
|
||||
- **Private API Keys**: Never commit your `.env` file or session files to Git repositories
|
||||
- **Session String**: The session string in your `.env` file provides full access to your Telegram account. Keep it secure.
|
||||
- **Local Processing**: All Telegram data is processed locally on your machine - no data is sent to external servers beyond Telegram's own API.
|
||||
- **Permissions**: The MCP server has the same access to Telegram as you would have with the official app, including reading and sending messages.
|
||||
---
|
||||
|
||||
## 🛠️ 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 `.venv` is created and selected.
|
||||
- **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 `.env` for valid test accounts/groups.
|
||||
|
||||
If you encounter issues:
|
||||
|
||||
1. Check Claude Desktop logs for error messages
|
||||
2. Ensure your Telegram API credentials are correct
|
||||
3. Verify that the paths in your Claude Desktop config are absolute and correct
|
||||
4. If you see database lock errors, use the session string authentication method
|
||||
5. If you need to regenerate your session string, run `python session_string_generator.py` again
|
||||
---
|
||||
|
||||
## 📄 License
|
||||
|
||||
This project is licensed under the [Apache 2.0 License](LICENSE).
|
||||
|
||||
## 🙏 Acknowledgements
|
||||
---
|
||||
|
||||
- [Telethon](https://github.com/LonamiWebs/Telethon) for the Telegram client library
|
||||
- [Model Context Protocol](https://modelcontextprotocol.io/) for the integration framework
|
||||
- [Anthropic](https://www.anthropic.com/) for Claude and the Claude Desktop app
|
||||
## 🙏 Acknowledgements
|
||||
- [Telethon](https://github.com/LonamiWebs/Telethon)
|
||||
- [Model Context Protocol](https://modelcontextprotocol.io/)
|
||||
- [Claude](https://www.anthropic.com/) and [Cursor](https://cursor.so/)
|
||||
- [chigwell/telegram-mcp](https://github.com/chigwell/telegram-mcp) (upstream)
|
||||
|
||||
---
|
||||
|
||||
**Maintained by @chigwell and @l1v0n1. PRs welcome!**
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||
|
||||
[project]
|
||||
name = "telegram-mcp"
|
||||
version = "1.5.0"
|
||||
version = "2.0.0"
|
||||
description = "Telegram integration for Claude via the Model Context Protocol"
|
||||
readme = "README.md"
|
||||
authors = [
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 170 KiB After Width: | Height: | Size: 306 KiB |
BIN
screenshots/2.png
Normal file
BIN
screenshots/2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 293 KiB |
BIN
screenshots/3.png
Normal file
BIN
screenshots/3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 278 KiB |
31
uv.lock
31
uv.lock
|
|
@ -1,10 +1,6 @@
|
|||
version = 1
|
||||
revision = 1
|
||||
<<<<<<< HEAD
|
||||
requires-python = ">=3.10"
|
||||
=======
|
||||
requires-python = ">=3.13"
|
||||
>>>>>>> 34256575c5ccd1cfa8bf8cf577bfa4fd48429904
|
||||
|
||||
[[package]]
|
||||
name = "annotated-types"
|
||||
|
|
@ -20,15 +16,10 @@ name = "anyio"
|
|||
version = "4.9.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
<<<<<<< HEAD
|
||||
{ name = "exceptiongroup", marker = "python_full_version < '3.11'" },
|
||||
{ name = "idna" },
|
||||
{ name = "sniffio" },
|
||||
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
|
||||
=======
|
||||
{ name = "idna" },
|
||||
{ name = "sniffio" },
|
||||
>>>>>>> 34256575c5ccd1cfa8bf8cf577bfa4fd48429904
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949 }
|
||||
wheels = [
|
||||
|
|
@ -77,7 +68,6 @@ wheels = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
<<<<<<< HEAD
|
||||
name = "exceptiongroup"
|
||||
version = "1.2.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
|
|
@ -87,8 +77,6 @@ wheels = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
=======
|
||||
>>>>>>> 34256575c5ccd1cfa8bf8cf577bfa4fd48429904
|
||||
name = "h11"
|
||||
version = "0.14.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
|
|
@ -237,7 +225,6 @@ dependencies = [
|
|||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b9/05/91ce14dfd5a3a99555fce436318cc0fd1f08c4daa32b3248ad63669ea8b4/pydantic_core-2.33.0.tar.gz", hash = "sha256:40eb8af662ba409c3cbf4a8150ad32ae73514cd7cb1f1a2113af39763dd616b3", size = 434080 }
|
||||
wheels = [
|
||||
<<<<<<< HEAD
|
||||
{ url = "https://files.pythonhosted.org/packages/29/43/0649ad07e66b36a3fb21442b425bd0348ac162c5e686b36471f363201535/pydantic_core-2.33.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:71dffba8fe9ddff628c68f3abd845e91b028361d43c5f8e7b3f8b91d7d85413e", size = 2042968 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/a6/975fea4774a459e495cb4be288efd8b041ac756a0a763f0b976d0861334b/pydantic_core-2.33.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:abaeec1be6ed535a5d7ffc2e6c390083c425832b20efd621562fbb5bff6dc518", size = 1860347 },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/49/7858dadad305101a077ec4d0c606b6425a2b134ea8d858458a6d287fd871/pydantic_core-2.33.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:759871f00e26ad3709efc773ac37b4d571de065f9dfb1778012908bcc36b3a73", size = 1910060 },
|
||||
|
|
@ -279,8 +266,6 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/e8/27/be7571e215ac8d321712f2433c445b03dbcd645366a18f67b334df8912bc/pydantic_core-2.33.0-cp312-cp312-win32.whl", hash = "sha256:918f2013d7eadea1d88d1a35fd4a1e16aaf90343eb446f91cb091ce7f9b431a2", size = 1908353 },
|
||||
{ url = "https://files.pythonhosted.org/packages/be/3a/be78f28732f93128bd0e3944bdd4b3970b389a1fbd44907c97291c8dcdec/pydantic_core-2.33.0-cp312-cp312-win_amd64.whl", hash = "sha256:aec79acc183865bad120b0190afac467c20b15289050648b876b07777e67ea48", size = 1955956 },
|
||||
{ url = "https://files.pythonhosted.org/packages/21/26/b8911ac74faa994694b76ee6a22875cc7a4abea3c381fdba4edc6c6bef84/pydantic_core-2.33.0-cp312-cp312-win_arm64.whl", hash = "sha256:5461934e895968655225dfa8b3be79e7e927e95d4bd6c2d40edd2fa7052e71b6", size = 1903259 },
|
||||
=======
|
||||
>>>>>>> 34256575c5ccd1cfa8bf8cf577bfa4fd48429904
|
||||
{ url = "https://files.pythonhosted.org/packages/79/20/de2ad03ce8f5b3accf2196ea9b44f31b0cd16ac6e8cfc6b21976ed45ec35/pydantic_core-2.33.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f00e8b59e1fc8f09d05594aa7d2b726f1b277ca6155fc84c0396db1b373c4555", size = 2032214 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f9/af/6817dfda9aac4958d8b516cbb94af507eb171c997ea66453d4d162ae8948/pydantic_core-2.33.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1a73be93ecef45786d7d95b0c5e9b294faf35629d03d5b145b09b81258c7cd6d", size = 1852338 },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/f3/49193a312d9c49314f2b953fb55740b7c530710977cabe7183b8ef111b7f/pydantic_core-2.33.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ff48a55be9da6930254565ff5238d71d5e9cd8c5487a191cb85df3bdb8c77365", size = 1896913 },
|
||||
|
|
@ -298,7 +283,6 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/52/54/295e38769133363d7ec4a5863a4d579f331728c71a6644ff1024ee529315/pydantic_core-2.33.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:7b79af799630af263eca9ec87db519426d8c9b3be35016eddad1832bac812d87", size = 1813331 },
|
||||
{ url = "https://files.pythonhosted.org/packages/4c/9c/0c8ea02db8d682aa1ef48938abae833c1d69bdfa6e5ec13b21734b01ae70/pydantic_core-2.33.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eabf946a4739b5237f4f56d77fa6668263bc466d06a8036c055587c130a46f7b", size = 1986653 },
|
||||
{ url = "https://files.pythonhosted.org/packages/8e/4f/3fb47d6cbc08c7e00f92300e64ba655428c05c56b8ab6723bd290bae6458/pydantic_core-2.33.0-cp313-cp313t-win_amd64.whl", hash = "sha256:8a1d581e8cdbb857b0e0e81df98603376c1a5c34dc5e54039dcc00f043df81e7", size = 1931234 },
|
||||
<<<<<<< HEAD
|
||||
{ url = "https://files.pythonhosted.org/packages/44/77/85e173b715e1a277ce934f28d877d82492df13e564fa68a01c96f36a47ad/pydantic_core-2.33.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e2762c568596332fdab56b07060c8ab8362c56cf2a339ee54e491cd503612c50", size = 2040129 },
|
||||
{ url = "https://files.pythonhosted.org/packages/33/e7/33da5f8a94bbe2191cfcd15bd6d16ecd113e67da1b8c78d3cc3478112dab/pydantic_core-2.33.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5bf637300ff35d4f59c006fff201c510b2b5e745b07125458a5389af3c0dff8c", size = 1872656 },
|
||||
{ url = "https://files.pythonhosted.org/packages/b4/7a/9600f222bea840e5b9ba1f17c0acc79b669b24542a78c42c6a10712c0aae/pydantic_core-2.33.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62c151ce3d59ed56ebd7ce9ce5986a409a85db697d25fc232f8e81f195aa39a1", size = 1903731 },
|
||||
|
|
@ -317,8 +301,6 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/23/8b/b6be91243da44a26558d9c3a9007043b3750334136c6550551e8092d6d96/pydantic_core-2.33.0-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:2c0afd34f928383e3fd25740f2050dbac9d077e7ba5adbaa2227f4d4f3c8da5c", size = 2251618 },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/c5/fbcf1977035b834f63eb542e74cd6c807177f383386175b468f0865bcac4/pydantic_core-2.33.0-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:7da333f21cd9df51d5731513a6d39319892947604924ddf2e24a4612975fb936", size = 2255374 },
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/f8/66f328e411f1c9574b13c2c28ab01f308b53688bbbe6ca8fb981e6cabc42/pydantic_core-2.33.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:4b6d77c75a57f041c5ee915ff0b0bb58eabb78728b69ed967bc5b780e8f701b8", size = 2082099 },
|
||||
=======
|
||||
>>>>>>> 34256575c5ccd1cfa8bf8cf577bfa4fd48429904
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -359,10 +341,7 @@ source = { registry = "https://pypi.org/simple" }
|
|||
dependencies = [
|
||||
{ name = "markdown-it-py" },
|
||||
{ name = "pygments" },
|
||||
<<<<<<< HEAD
|
||||
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
|
||||
=======
|
||||
>>>>>>> 34256575c5ccd1cfa8bf8cf577bfa4fd48429904
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a1/53/830aa4c3066a8ab0ae9a9955976fb770fe9c6102117c8ec4ab3ea62d89e8/rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725", size = 224078 }
|
||||
wheels = [
|
||||
|
|
@ -426,13 +405,8 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "telegram-mcp"
|
||||
<<<<<<< HEAD
|
||||
version = "1.5.0"
|
||||
version = "2.0.0"
|
||||
source = { editable = "." }
|
||||
=======
|
||||
version = "2025.3.201549"
|
||||
source = { virtual = "." }
|
||||
>>>>>>> 34256575c5ccd1cfa8bf8cf577bfa4fd48429904
|
||||
dependencies = [
|
||||
{ name = "dotenv" },
|
||||
{ name = "httpx" },
|
||||
|
|
@ -508,10 +482,7 @@ source = { registry = "https://pypi.org/simple" }
|
|||
dependencies = [
|
||||
{ name = "click" },
|
||||
{ name = "h11" },
|
||||
<<<<<<< HEAD
|
||||
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
|
||||
=======
|
||||
>>>>>>> 34256575c5ccd1cfa8bf8cf577bfa4fd48429904
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/4b/4d/938bd85e5bf2edeec766267a5015ad969730bb91e31b44021dfe8b22df6c/uvicorn-0.34.0.tar.gz", hash = "sha256:404051050cd7e905de2c9a7e61790943440b3416f49cb409f965d9dcd0fa73e9", size = 76568 }
|
||||
wheels = [
|
||||
|
|
|
|||
Loading…
Reference in a new issue