A hosted, multi-tenant Telegram MCP (Model Context Protocol) server that enables Claude to interact with Telegram on behalf of users. Built with FastAPI, Telethon, and OAuth 2.0 authentication.
- 73 Telegram Tools - Complete Telegram automation including chats, messages, contacts, groups, channels, and more
- Multi-tenant Architecture - Single deployment serves multiple users with isolated Telegram sessions
- OAuth 2.0 + PKCE - Secure authentication flow compatible with Claude Teams
- Web-based Telegram Auth - Users connect their Telegram accounts via phone/SMS verification (no CLI required)
- Encrypted Session Storage - Telegram sessions stored with AES-256 encryption
- Railway-ready - One-click deployment to Railway with PostgreSQL
Chat & Group Management (21 tools)
| Tool | Description |
|---|---|
get_chats |
Get paginated list of Telegram chats |
list_chats |
List chats with optional filtering by type |
get_chat |
Get detailed information about a specific chat |
create_group |
Create a new group chat |
invite_to_group |
Invite users to a group or channel |
create_channel |
Create a new channel or supergroup |
edit_chat_title |
Edit chat/channel title |
leave_chat |
Leave a group or channel |
get_participants |
Get participants of a group or channel |
get_admins |
Get administrators of a group or channel |
get_banned_users |
Get banned users of a group or channel |
promote_admin |
Promote a user to admin |
demote_admin |
Demote an admin |
ban_user |
Ban a user from a group or channel |
unban_user |
Unban a user from a group or channel |
get_invite_link |
Get the invite link for a group or channel |
export_chat_invite |
Export a new invite link |
import_chat_invite |
Join a chat using an invite hash |
join_chat_by_link |
Join a chat using a full invite link |
subscribe_public_channel |
Subscribe to a public channel |
get_recent_actions |
Get recent admin actions in a group or channel |
Messaging (24 tools)
| Tool | Description |
|---|---|
get_messages |
Get paginated messages from a chat |
list_messages |
List messages with search and date filtering |
send_message |
Send a message to a Telegram chat |
reply_to_message |
Reply to a specific message |
edit_message |
Edit a message |
delete_message |
Delete a message |
forward_message |
Forward a message to another chat |
pin_message |
Pin a message in a chat |
unpin_message |
Unpin a message in a chat |
mark_as_read |
Mark all messages in a chat as read |
get_message_context |
Get messages around a specific message |
get_history |
Get chat history |
get_pinned_messages |
Get all pinned messages in a chat |
search_messages |
Search for messages in a chat or globally |
get_last_interaction |
Get the last interaction with a contact |
create_poll |
Create a poll in a chat |
list_inline_buttons |
List inline keyboard buttons on a message |
press_inline_button |
Press an inline keyboard button |
send_reaction |
Add a reaction to a message |
remove_reaction |
Remove reaction from a message |
get_message_reactions |
Get all reactions on a message |
get_media_info |
Get media information from a message |
Contact Management (12 tools)
| Tool | Description |
|---|---|
list_contacts |
List all contacts |
search_contacts |
Search for contacts by name or username |
add_contact |
Add a new contact |
delete_contact |
Delete a contact |
block_user |
Block a user |
unblock_user |
Unblock a user |
import_contacts |
Import multiple contacts |
export_contacts |
Export all contacts |
get_blocked_users |
Get list of blocked users |
get_contact_ids |
Get list of all contact IDs |
get_direct_chat_by_contact |
Find direct chat with a contact |
get_contact_chats |
Get all chats involving a specific contact |
User & Profile (5 tools)
| Tool | Description |
|---|---|
get_me |
Get current user's account information |
update_profile |
Update current user's profile |
delete_profile_photo |
Delete current profile photo |
get_user_photos |
Get a user's profile photos |
get_user_status |
Get a user's online status |
Search & Discovery (4 tools)
| Tool | Description |
|---|---|
search_public_chats |
Search for public chats/channels |
resolve_username |
Resolve a username to get user/channel ID |
get_sticker_sets |
Get user's saved sticker sets |
get_bot_info |
Get information about a bot |
Privacy & Settings (6 tools)
| Tool | Description |
|---|---|
get_privacy_settings |
Get current privacy settings |
set_privacy_settings |
Set privacy settings for a specific key |
mute_chat |
Mute notifications for a chat |
unmute_chat |
Unmute notifications for a chat |
archive_chat |
Archive a chat |
unarchive_chat |
Unarchive a chat |
Drafts (3 tools)
| Tool | Description |
|---|---|
save_draft |
Save a draft message for a chat |
get_drafts |
Get all draft messages |
clear_draft |
Clear draft message for a specific chat |
-
Telegram API Credentials - Get from my.telegram.org:
- Go to "API development tools"
- Create a new application
- Note the
api_idandapi_hash
-
Railway Account - Sign up at railway.app
# Clone the repository
git clone https://github.com/your-org/telegram-hosted-mcp.git
cd telegram-hosted-mcp
# Install Railway CLI
npm install -g @railway/cli
# Login to Railway
railway login
# Create a new project
railway init
# Add PostgreSQL
railway add --plugin postgresql
# Set environment variables
railway variables set TELEGRAM_API_ID=your_api_id
railway variables set TELEGRAM_API_HASH=your_api_hash
railway variables set JWT_SECRET=$(openssl rand -hex 32)
railway variables set ENCRYPTION_KEY=$(python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())")
railway variables set BASE_URL=https://your-app.up.railway.app
railway variables set ALLOWED_REDIRECT_URIS=https://claude.ai/oauth/callback
# Deploy
railway up| Variable | Description | How to Generate |
|---|---|---|
DATABASE_URL |
PostgreSQL connection string | Auto-set by Railway (ensure it has +asyncpg) |
TELEGRAM_API_ID |
Telegram API ID | Get from my.telegram.org |
TELEGRAM_API_HASH |
Telegram API hash | Get from my.telegram.org |
JWT_SECRET |
Secret key for JWT tokens | openssl rand -hex 32 |
ENCRYPTION_KEY |
Fernet key for session encryption | See below |
BASE_URL |
Public URL of your server | Your Railway URL |
ALLOWED_REDIRECT_URIS |
OAuth redirect URIs (comma-separated) | https://claude.ai/oauth/callback |
Generate Encryption Key:
python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"Important: If Railway sets DATABASE_URL as postgresql://..., update it to postgresql+asyncpg://...
After deploying, register an OAuth client for Claude Teams:
curl -X POST https://your-app.up.railway.app/oauth/register \
-H "Content-Type: application/json" \
-d '{
"client_name": "Claude Teams",
"redirect_uris": ["https://claude.ai/oauth/callback"]
}'Response:
{
"client_id": "abc123...",
"client_secret": "secret456...",
"client_name": "Claude Teams",
"redirect_uris": ["https://claude.ai/oauth/callback"]
}Save the client_id and client_secret.
- Go to your Claude Teams admin dashboard
- Navigate to Integrations β MCP Servers
- Click Add MCP Server
- Fill in the configuration:
| Field | Value |
|---|---|
| Name | Telegram |
| Server URL | https://your-app.up.railway.app/mcp |
| Authentication | OAuth 2.0 |
| Authorization URL | https://your-app.up.railway.app/oauth/authorize |
| Token URL | https://your-app.up.railway.app/oauth/token |
| Client ID | (from Step 1) |
| Client Secret | (from Step 1) |
| Scopes | telegram:read telegram:write |
- Click Save
When a team member first uses Telegram tools:
- Claude prompts them to authorize the Telegram connection
- They click "Connect" and are redirected to your OAuth server
- After OAuth, they see a web form to enter their Telegram phone number (international format, e.g., +1234567890)
- Telegram sends an SMS code to their phone
- They enter the code on the web form
- If they have 2FA enabled, they enter their 2FA password
- Success! They're redirected back to Claude with Telegram connected
The user only needs to do this once. Their session persists across conversations.
For individual Claude users (not on a Team), use Claude Desktop with MCP support.
curl -X POST https://your-app.up.railway.app/oauth/register \
-H "Content-Type: application/json" \
-d '{
"client_name": "Claude Desktop",
"redirect_uris": ["http://localhost:3000/callback"]
}'Add to your Claude Desktop config file:
macOS: ~/.config/claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"telegram": {
"url": "https://your-app.up.railway.app/mcp",
"transport": "http",
"auth": {
"type": "oauth2",
"authorizationUrl": "https://your-app.up.railway.app/oauth/authorize",
"tokenUrl": "https://your-app.up.railway.app/oauth/token",
"clientId": "YOUR_CLIENT_ID",
"clientSecret": "YOUR_CLIENT_SECRET",
"scopes": ["telegram:read", "telegram:write"]
}
}
}
}- Restart Claude Desktop
- Start a new conversation
- Ask Claude: "List my Telegram chats"
- Claude will prompt you to authorize - click the link
- Complete the Telegram phone verification in your browser
- Return to Claude - you're connected!
Once connected, ask Claude things like:
"Show me my recent Telegram chats"
"Get the last 20 messages from the 'Family' group"
"Search my Telegram for messages about 'meeting notes'"
"What did John send me yesterday?"
"Send a message to @username saying 'Hello, how are you?'"
"Reply to the last message in 'Work Team' with 'Sounds good!'"
"Forward that message to the 'Archive' chat"
"Create a new group called 'Project Alpha' and add @user1 and @user2"
"Show me the admins in the 'Company' channel"
"Promote @newadmin to admin in 'My Group'"
"Remove the person who keeps spamming from the group"
"Create a poll in the family chat: 'What should we have for dinner?' with options Pizza, Sushi, Tacos"
"React with π to John's last message"
"Press the 'Confirm' button on that bot message"
"Archive all chats I haven't messaged in over a month"
"Mute the 'Announcements' channel"
"Show me my blocked users"
| Endpoint | Method | Description |
|---|---|---|
/.well-known/oauth-authorization-server |
GET | OAuth server metadata (RFC 8414) |
/.well-known/oauth-protected-resource |
GET | Protected resource metadata (RFC 9728) |
/oauth/register |
POST | Register new OAuth client |
/oauth/authorize |
GET | Start OAuth authorization |
/oauth/token |
POST | Exchange code for tokens |
| Endpoint | Method | Description |
|---|---|---|
/auth/telegram/start |
POST | Start phone verification |
/auth/telegram/verify |
POST | Submit SMS code |
/auth/telegram/2fa |
POST | Submit 2FA password |
| Endpoint | Method | Description |
|---|---|---|
/mcp |
POST | MCP JSON-RPC endpoint |
| Endpoint | Method | Description |
|---|---|---|
/health |
GET | Server health status |
-
Check server health:
curl https://your-app.up.railway.app/health
-
Verify OAuth metadata:
curl https://your-app.up.railway.app/.well-known/oauth-authorization-server
-
Check environment variables:
BASE_URLmust match your actual deployed URL exactlyALLOWED_REDIRECT_URISmust include the Claude callback URL
The user needs to complete Telegram authentication:
- Trigger the OAuth flow again by asking Claude to use a Telegram tool
- Complete phone/SMS verification
The encryption key must be a valid Fernet key. Generate one:
python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"Ensure DATABASE_URL includes the async driver:
postgresql+asyncpg://user:pass@host:port/db
If Railway set it as postgresql://..., update the variable.
Phone numbers must be in international format with country code:
- Correct:
+14155551234 - Wrong:
415-555-1234
Check logs:
railway logs --build # Build logs
railway logs # Runtime logs| Feature | Implementation |
|---|---|
| Session Encryption | AES-256 via Fernet (encrypts Telegram session strings) |
| Phone Privacy | Phone numbers used only during auth, never stored in plaintext |
| OAuth Security | OAuth 2.0 + PKCE prevents authorization code interception |
| Token Security | JWT tokens with configurable expiration |
| Session Isolation | Each user has their own Telegram client instance |
| Audit Logging | Metadata only - never logs message content |
# Clone repository
git clone https://github.com/your-org/telegram-hosted-mcp.git
cd telegram-hosted-mcp
# Install uv (if not installed)
curl -LsSf https://astral.sh/uv/install.sh | sh
# Install dependencies
uv sync
# Create .env file
cat > .env << EOF
DATABASE_URL=postgresql+asyncpg://postgres:postgres@localhost:5432/telegram_mcp
TELEGRAM_API_ID=your_api_id
TELEGRAM_API_HASH=your_api_hash
JWT_SECRET=$(openssl rand -hex 32)
ENCRYPTION_KEY=$(python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())")
BASE_URL=http://localhost:8000
ALLOWED_REDIRECT_URIS=http://localhost:3000/callback
EOF
# Start PostgreSQL (using Docker)
docker run -d --name postgres -e POSTGRES_PASSWORD=postgres -p 5432:5432 postgres:15
# Run migrations
alembic upgrade head
# Start server
uvicorn app.main:app --reload# Test OAuth metadata
curl http://localhost:8000/.well-known/oauth-authorization-server
# Register a test client
curl -X POST http://localhost:8000/oauth/register \
-H "Content-Type: application/json" \
-d '{"client_name": "Test", "redirect_uris": ["http://localhost:3000/callback"]}'telegram-hosted-mcp/
βββ app/
β βββ main.py # FastAPI application entry point
β βββ config.py # Environment configuration (pydantic-settings)
β βββ database.py # SQLAlchemy async engine setup
β βββ models.py # Database models (users, clients, sessions)
β βββ auth/
β β βββ oauth.py # OAuth 2.0 endpoints (authorize, token, register)
β β βββ tokens.py # JWT token creation and verification
β β βββ telegram.py # Telegram phone/SMS/2FA auth flow
β βββ mcp/
β β βββ server.py # MCP server (73 tool definitions + routing)
β β βββ middleware.py # Bearer token validation, audit logging
β β βββ tools/
β β βββ __init__.py
β β βββ chats.py # 21 chat/group management tools
β β βββ messages.py # 24 messaging tools
β β βββ contacts.py # 12 contact management tools
β β βββ users.py # 9 user/profile/discovery tools
β β βββ privacy.py # 6 privacy/settings tools
β β βββ drafts.py # 3 draft tools
β βββ telegram/
β β βββ client_pool.py # Multi-tenant Telethon client pool with LRU
β β βββ session_store.py # Fernet-encrypted session storage
β βββ templates/ # HTML templates for auth flow
β βββ phone.html # Phone number entry
β βββ code.html # SMS code entry
β βββ 2fa.html # 2FA password entry
β βββ success.html # Success page
βββ alembic/ # Database migrations
βββ Dockerfile # Container configuration
βββ pyproject.toml # Python dependencies
βββ railway.toml # Railway deployment config
βββ README.md
- Inspired by chigwell/telegram-mcp
- Built with Telethon
- MCP Protocol by Anthropic
MIT License