mcp-telegram-agent

讓 AI 代理透過純 Telegram Bot API 取得完整 Telegram Bot 控制能力:豐富鍵盤、本機檔案與媒體相簿、互動元件(投票、測驗、定位、骰子),零登入風險,僅需一個 Bot Token。

Documentation

📬 mcp-telegram-agent

Give your AI agent full Telegram Bot superpowers — zero login risk, zero setup.

A Model Context Protocol (MCP) server empowering AI agents with Telegram Bot capabilities. Built with Python (SDK-compatible with both mcp 1.x FastMCP and 2.x MCPServer), designed to work seamlessly out-of-the-box with uvx and modern AI coding agents like VS Code Cline and Claude Desktop. Also includes the ultra-short command alias tg-agent.

PyPI Version Python Versions PyPI Downloads License: MIT

English · 繁體中文 · 简体中文

🔐 Stop giving AI your personal Telegram account credentials.

mcp-telegram-agent is a lightweight, zero-login-risk notification and delivery tool — just one Bot Token sends local files, reports, interactive keyboards, and rich notifications straight to your phone.


✨ Highlights

  • Pure Bot API Architecture — Zero login session risk, no SMS codes or sensitive credential files needed.
  • Dual CLI Entry Points — Call via standard mcp-telegram-agent or the compact tg-agent alias.
  • Full Keyboard Support — Effortlessly construct dynamic Inline Keyboards (with URLs or callback data) and custom Reply Keyboards.
  • Rich Media & Local Files — Deliver logs, reports, source code, images, audio, voice notes, videos, and multi-image albums (2–10 files).
  • Interactive Tooling — Launch quizzes, single/multiple-choice polls, GPS coordinates, digital contact cards, and animated dice.
  • Message Lifecycle — Broadcast bot statuses (typing, uploading) and edit or delete messages on the fly.
  • Interactive Receive — Pull or long-poll user replies (text messages, inline button clicks, poll answers) with get_updates / wait_for_reply, and acknowledge button presses with answer_callback_query.
  • Instant uvx Execution — Run directly with no virtual environments or manual package installs.

🚀 Quick Start

1️⃣ Create a Telegram Bot & Get the Token

  1. Open Telegram and search for @BotFather (verified blue check).
  2. Send /newbot and follow the prompts: choose a display name, then a username ending in bot (e.g. my_alert_bot).
  3. BotFather replies with your Bot Token, e.g. 123456789:AAHxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx. Keep it secret — anyone with this token can control your bot.
  4. Important: open your new bot's chat and press Start (or send /start). A bot cannot message a user who has never started a conversation with it.

2️⃣ Get Your Personal Chat ID

  1. Send any message (e.g. "hi") to your new bot.
  2. Open this URL in a browser, replacing <YOUR_BOT_TOKEN> with your token: https://api.telegram.org/bot<YOUR_BOT_TOKEN>/getUpdates
  3. In the JSON response, locate "chat":{"id": 123456789, ...} — that number is your Chat ID.
    • Alternative: send any message to @userinfobot and it replies with your ID.

3️⃣ Send to Groups or Channels (Optional)

  • Add the bot to the group first (via group settings → Add members). For channels, the bot must be added as an administrator to post.
  • Group IDs are negative numbers (e.g. -1001234567890). To find one:
    • Temporarily add @userinfobot to the group and forward a group message to it, or
    • Disable the bot's Group Privacy via @BotFather (/setprivacy → select your bot → Disable), post a message in the group, then check getUpdates as above.
  • Once the bot is a member of the group/channel, it can send messages there — no extra configuration needed.

4️⃣ Sending to Any Chat at Runtime

TELEGRAM_DEFAULT_CHAT_ID is only the fallback target. Every tool accepts an optional chat_id parameter, so you can simply ask your AI agent to "send this to chat ID -1001234567890":

  • chat_id omitted → the message goes to the TELEGRAM_DEFAULT_CHAT_ID from your config.
  • chat_id provided → the message goes to that user/group/channel instead.
  • Requirement: the bot must be a member of that group/channel (or the target user must have pressed Start on the bot).

5️⃣ Configuration for AI Clients

Add the configuration below to your MCP settings file (e.g., cline_mcp_settings.json or claude_desktop_config.json).

✅ Recommended Configuration (All Features Enabled)

This single block enables both sending and interactive receive — just fill in your token, chat ID and allowlists:

{
  "mcpServers": {
    "Telegram": {
      "command": "uvx",
      "args": ["mcp-telegram-agent"],
      "env": {
        "TELEGRAM_BOT_TOKEN": "YOUR_TELEGRAM_BOT_TOKEN",
        "TELEGRAM_DEFAULT_CHAT_ID": "YOUR_CHAT_ID",
        "TELEGRAM_ALLOWED_CHAT_IDS": "123456789,-1001234567890",
        "TELEGRAM_ALLOWED_USER_IDS": "123456789"
      }
    }
  }
}

💡 All four environment variables are optional. If one is missing, the server still starts normally — it returns a clear, human-readable error only when a tool call actually needs it. What each variable does:

  • TELEGRAM_BOT_TOKEN — your Bot Token; required by all sending tools.
  • TELEGRAM_DEFAULT_CHAT_ID — the default recipient used when a tool call omits chat_id.
  • TELEGRAM_ALLOWED_CHAT_IDS / TELEGRAM_ALLOWED_USER_IDS — interactive-receive allowlists so your agent can read back your replies (text messages, inline button clicks, poll answers); see step 6️⃣ for details. Omitting them disables receive: get_updates / wait_for_reply return a setup hint instead.
  • TELEGRAM_STATE_DIR (advanced, not shown above) — directory for the persisted getUpdates offset file; defaults to a mcp-telegram-agent subfolder under the system temp directory.

💡 For a credential-free trial run, omit the whole env block and fill in the pieces as the tool error messages instruct.

6️⃣ Interactive Receive (Allowlist) Behavior

The recommended configuration already fills in both allowlists, so get_updates / wait_for_reply can fetch or long-poll your replies. Behavior details:

  • TELEGRAM_ALLOWED_CHAT_IDS — comma-separated chat allowlist; when set, updates from other chats are filtered out (group IDs are negative). poll_answer events carry no chat_id, so they cannot be matched against this list.
  • TELEGRAM_ALLOWED_USER_IDS — comma-separated user allowlist; when set, updates are also filtered by user ID. An update passes if it hits any configured list (each unset list is simply ignored).
  • First-call note: the very first get_updates (no state file yet) fetches all pending updates from Telegram, and they are consumed when mark_read is true (the default). Use mark_read: false to preview without consuming them.
  • limit only caps how many matching updates are returned per call: the offset advances only past the last returned update, so the remaining matches stay queued for the next call. Updates that don't match the allowlist or types are still consumed — the offset is a single stream, so a single type or chat cannot be held back.
  • Without an allowlist, get_updates / wait_for_reply return a setup hint instead of updates. Restart the MCP server after editing env.

⚠️ Timing matters: Telegram keeps a callback query valid for only a few seconds. get_updates and wait_for_reply answer button clicks automatically right when they are received (default auto_answer: true); only if you disable it must you call answer_callback_query immediately after a click is returned — long gaps are rejected with query is too old (a platform limit, not a bug).


🧰 Available Tools

ToolDescription
send_text_messageSend Markdown/HTML text with inline buttons or custom reply keyboards.
send_local_documentSend any local file (PDF, CSV, code, logs, archives).
send_local_photoSend image files with captions and inline buttons.
send_local_videoSend video files as native players.
send_local_audio_or_voiceSend music tracks or native voice audio notes.
send_media_albumDeliver 2 to 10 photos or videos as a grouped carousel.
send_pollCreate interactive single/multiple choice polls or scored quizzes.
send_locationBroadcast map location points via coordinates.
send_contactShare phone contact cards.
send_diceSend animated dice, basketball, football, or slot machines.
send_chat_actionDisplay temporary bot statuses (typing, uploading).
edit_message_textDynamically update previously sent text and keyboards.
delete_messageClean up or remove specific messages.
get_updatesFetch pending updates once (messages, button clicks, poll answers) without waiting.
wait_for_replyLong-poll until the next user reply arrives, with a timeout.
answer_callback_queryAcknowledge a button press to stop its loading spinner; optional alert text or URL.

💬 Usage Examples

Once configured, simply talk to your AI agent in natural language — it picks the right tool automatically. Below are typical requests together with the tool calls the agent makes:

📢 Send a formatted notification

"Tell me in Telegram that the nightly build succeeded, with a button linking to https://example.com"

{
  "tool": "send_text_message",
  "arguments": {
    "text": "✅ *Nightly build succeeded!*",
    "parse_mode": "Markdown",
    "inline_buttons": [[{"text": "Dashboard", "url": "https://example.com"}]]
  }
}

📁 Share local files

"Upload logs/app.log to my Telegram and summarize the errors"

{
  "tool": "send_local_document",
  "arguments": {
    "file_path": "logs/app.log",
    "caption": "Error log summary: 3 timeout warnings"
  }
}

🖼️ Send an album

"Send the three screenshots in screenshots/ to my Telegram as one album"

{
  "tool": "send_media_album",
  "arguments": {
    "file_paths": ["screenshots/1.png", "screenshots/2.png", "screenshots/3.png"],
    "caption": "Latest UI screenshots"
  }
}

🗳️ Run a quiz in a group

"Post a quiz to chat ID -1001234567890: which port does HTTPS use? Options 80 / 443 / 8080, the answer is 443, with a short explanation"

{
  "tool": "send_poll",
  "arguments": {
    "chat_id": "-1001234567890",
    "question": "Which port does HTTPS use?",
    "options": ["80", "443", "8080"],
    "type": "quiz",
    "correct_option_id": 1,
    "explanation": "HTTPS defaults to port 443."
  }
}

♻️ Update or clean up messages

"Change that deployment notice to 'finished', then delete the old reminder (ID 120)"

{
  "tool": "edit_message_text",
  "arguments": { "message_id": 123, "text": "🚀 Deployment **finished**" }
}
{
  "tool": "delete_message",
  "arguments": { "message_id": 120 }
}

📨 Wait for a reply and confirm a button click

"Ask me a yes/no question with inline buttons, wait for my answer, and tell me what I clicked"

{
  "tool": "send_text_message",
  "arguments": {
    "text": "Deploy to production now?",
    "inline_buttons": [[{"text": "Yes", "callback_data": "deploy:yes"}, {"text": "No", "callback_data": "deploy:no"}]]
  }
}
{
  "tool": "wait_for_reply",
  "arguments": { "types": ["callback_query"], "timeout": 25 }
}
{
  "tool": "answer_callback_query",
  "arguments": { "callback_query_id": "<callback_query.id from the click>", "text": "🚀 Deploying…" }
}

💡 Requires the step 5️⃣ allowlist. Use get_updates for a one-shot, non-blocking check (add mark_read: false to peek without consuming updates) and wait_for_reply to long-poll until something arrives. Callback clicks are auto-answered the moment they are fetched; pass auto_answer: false only when you need a custom prompt, alert, or URL via answer_callback_query.

💡 When chat_id is omitted, messages go to TELEGRAM_DEFAULT_CHAT_ID; pass it explicitly to reach any user, group, or channel — see step 4️⃣ above.


🛠️ Development & Release Flow

  • Develop against main — all feature work and PRs land here.
  • Publishing happens only from the release branch: open a PR main → release, bump version in pyproject.toml within the PR, then merge — CI builds and publishes to PyPI automatically, and creates a matching GitHub Release (v* tag + auto-generated notes).
  • If the version was not bumped, the workflow skips publishing with a clear warning — docs-only PRs won't break CI.
  • Direct pushes to release are blocked by branch protection; always go through a PR.

📄 License

This project is licensed under the MIT License.