Skip to main content
Glama
Moreti2002

mcp-telegram

by Moreti2002
README.md
# mcp-telegram

An [MCP](https://modelcontextprotocol.io) server that lets an LLM send messages,
photos and documents to Telegram through a bot, as well as retrieve incoming updates.

Point any MCP-capable client (Claude Desktop, Claude Code, Antigravity, or your own agent) at
this server and the model gains tools: `get_me`, `get_updates`, `get_file`, `send_message`,
`send_photo` and `send_document`. The model decides *what* to say or process; the server
handles *how* it communicates with Telegram.

## Why the Bot API

This server talks to the [Telegram Bot API](https://core.telegram.org/bots/api).
That means you create a bot, the bot sends messages, and users receive them.

- No phone-number login, no `api_id`/`api_hash`, no session files.
- The bot can only message chats it already shares with a user (the user must
  start the bot first) or channels/groups it belongs to.

If you instead need to send messages *as your own user account* to arbitrary
people, that requires the MTProto client API (Telethon/Pyrogram) and account
authentication — a deliberately different, heavier tool that is out of scope
here.

## Architecture

Three small layers, each with one responsibility:

```
src/mcp_telegram/
├── config.py     # load & validate settings from the environment (fail-fast)
├── telegram.py   # thin async client over the Bot API (httpx, no heavy SDK)
├── server.py     # FastMCP server: tool definitions + shared client lifespan
└── __main__.py   # entry point (stdio transport)
```

The MCP layer never builds HTTP requests and the HTTP layer never knows about
MCP, so each can be tested and changed on its own. A single `httpx.AsyncClient`
is created once per server run via the FastMCP lifespan and reused across calls.

## Requirements

- Python 3.10+
- A Telegram bot token from [@BotFather](https://t.me/BotFather)

## Setup

### 1. Create the bot and get a token

1. Open [@BotFather](https://t.me/BotFather) in Telegram and send `/newbot`.
2. Follow the prompts and copy the token it gives you
   (looks like `123456789:AA...`).

### 2. Find your chat id

The bot can only message a chat it knows. The simplest path:

1. Send any message to your new bot in Telegram.
2. Ask [@userinfobot](https://t.me/userinfobot) for your numeric id, **or** call:
   ```bash
   curl "https://api.telegram.org/bot<YOUR_TOKEN>/getUpdates"
   ```
   and read `result[].message.chat.id` from the JSON.

Set that value as `TELEGRAM_DEFAULT_CHAT_ID` so the model can send without
specifying a chat every time.

### 3. Install

```bash
# with uv (recommended)
uv venv
uv pip install -e .

# or with pip
python -m venv .venv && source .venv/bin/activate
pip install -e .
```

### 4. Configure

Copy the example env file and fill it in:

```bash
cp .env.example .env
# edit .env: set TELEGRAM_BOT_TOKEN (and ideally TELEGRAM_DEFAULT_CHAT_ID)
```

| Variable                   | Required | Default                      | Description                                        |
| -------------------------- | :------: | ---------------------------- | -------------------------------------------------- |
| `TELEGRAM_BOT_TOKEN`       |    yes   | —                            | Token from @BotFather.                             |
| `TELEGRAM_DEFAULT_CHAT_ID` |    no    | —                            | Chat used when a tool call omits `chat_id`.        |
| `TELEGRAM_API_BASE`        |    no    | `https://api.telegram.org`   | Override the API host (e.g. a local Bot API server). |
| `TELEGRAM_TIMEOUT`         |    no    | `30`                         | Per-request timeout in seconds.                    |

## Running

The server speaks MCP over stdio, so you normally don't launch it by hand — your
MCP client does. To sanity-check that it starts:

```bash
TELEGRAM_BOT_TOKEN=... mcp-telegram
```

It will wait for an MCP client on stdin. Press `Ctrl+C` to stop.

## Connecting a client

### Claude Desktop

Add this to your `claude_desktop_config.json`
(`~/Library/Application Support/Claude/` on macOS,
`%APPDATA%\Claude\` on Windows):

```json
{
  "mcpServers": {
    "telegram": {
      "command": "mcp-telegram",
      "env": {
        "TELEGRAM_BOT_TOKEN": "123456789:AA...",
        "TELEGRAM_DEFAULT_CHAT_ID": "123456789"
      }
    }
  }
}
```

If `mcp-telegram` is not on your PATH, use the venv's absolute path or run it
through `uv`:

```json
{
  "mcpServers": {
    "telegram": {
      "command": "uv",
      "args": ["run", "--directory", "/absolute/path/to/mcp-telegram", "mcp-telegram"],
      "env": { "TELEGRAM_BOT_TOKEN": "123456789:AA..." }
    }
  }
}
```

### Claude Code

```bash
claude mcp add telegram \
  --env TELEGRAM_BOT_TOKEN=123456789:AA... \
  --env TELEGRAM_DEFAULT_CHAT_ID=123456789 \
  -- mcp-telegram
```

Restart the client after editing its config, then ask the model something like
*"send me a Telegram message saying the build passed"*.

## Tools

| Tool            | What it does                                              |
| --------------- | -------------------------------------------------------- |
| `get_me`        | Return the bot's identity — use it to verify the token.  |
| `get_updates`   | Fetch incoming messages and updates (supports offset & polling). |
| `get_file`      | Get file info and download URL for voice/photos/docs.    |
| `send_message`  | Send a text message (optional Markdown/HTML formatting). |
| `send_photo`    | Send a photo by URL, local path, or Telegram `file_id`.  |
| `send_document` | Send a file by URL, local path, or Telegram `file_id`.   |

Every send tool accepts an optional `chat_id`; when omitted it falls back to
`TELEGRAM_DEFAULT_CHAT_ID`. `parse_mode` is optional and defaults to plain text —
only set `"MarkdownV2"`, `"HTML"` or `"Markdown"` when you need formatting, and
make sure the payload is escaped for that mode.

## Development

```bash
uv pip install -e ".[dev]"
pytest
```

Tests mock the Telegram HTTP API with `respx`, so they run offline and never
touch a real bot.

## License

MIT — see [LICENSE](LICENSE).

TDQS

A4/5.0

Scored across 4 tools

Disambiguation5/5

Each tool has a distinct purpose: get_me verifies bot identity, send_message sends text, send_photo sends images, send_document sends files. No ambiguity.

Naming Consistency5/5

All tools follow a consistent verb_noun pattern: get_me, send_document, send_message, send_photo, using snake_case throughout.

Tool Count4/5

Four tools is a small but reasonable set for a basic Telegram bot, covering identity and three message types. Could be expanded but not inappropriate.

Completeness2/5

The tool surface lacks many common Telegram operations like send_audio, send_video, forward_message, delete_message, limiting the bot's capabilities for a full-featured server.

Maintenance

ActivityMaintained
ResponsivenessNo issues