mcp-server-telegram
by awenas
README.md
# mcp-server-telegram
Model Context Protocol (MCP) server for Telegram integration. Exposes a Telegram bot as three MCP tools over the standard stdio transport.
Originally built for the YengTong 7-Scenario AI Agent demo as the second messaging channel alongside email, where:
- Staff are modeled as a single Telegram group — reminders land as @mentions.
- Escalations go to a **separate manager-only chat**, never visible to staff.
- A background long-poller buffers incoming messages into a local JSON store so tool calls return synchronously with low, predictable latency.
## Tools
| Tool | Description |
|---|---|
| `telegram_send_message` | Send a text message to either the `staff_group` (for reminders) or `manager_chat` (for escalations). |
| `telegram_list_messages` | List incoming messages buffered from the staff group. Optional `since` (ISO timestamp) filter. |
| `telegram_list_group_members` | Setup/debug helper — list staff group members observed by the poller so far. |
## Setup
### 1. Create a Telegram bot
1. Open Telegram and message [@BotFather](https://t.me/BotFather).
2. Send `/newbot`, follow the prompts, and copy the bot token.
3. **Disable privacy mode** for the bot (also via BotFather → `/mybots` → select your bot → Bot Settings → Group Privacy → Turn off). Without this, the bot only sees `/command` messages in groups, not regular text — the poller won't receive staff updates.
### 2. Create the chats
1. **Staff group**: create a Telegram group, add the bot as a member, add the 5 staff personas (or real staff).
2. **Manager chat**: a separate small group or a 1:1 DM with a manager test account that has sent `/start` to the bot. This is where `telegram_send_message` with `target: "manager_chat"` sends to.
To obtain the chat IDs, send a message in each chat and call the Telegram API manually:
```bash
curl "https://api.telegram.org/bot<TOKEN>/getUpdates"
```
Look for `message.chat.id` in the response. Group chat IDs are negative numbers (e.g. `-1001234567890`).
### 3. Configure environment
```bash
cp .env.template .env
# Edit .env with your bot token and the two chat IDs.
```
### 4. Install and build
```bash
npm install
npm run build
```
### 5. Run
```bash
npm start
```
The server boots, loads (or creates) the local store, starts the long-poller, and connects over stdio. Connect it from an MCP client.
### 6. Test
```bash
npm run build && node test_mcp.mjs
```
Runs an end-to-end MCP handshake (initialize → tools/list) and verifies all three tools are registered.
## Environment variables
| Variable | Required | Default | Description |
|---|---|---|---|
| `TELEGRAM_BOT_TOKEN` | yes | — | Bot token from @BotFather |
| `TELEGRAM_STAFF_GROUP_CHAT_ID` | yes | — | Chat ID of the staff group (negative number for supergroups) |
| `TELEGRAM_MANAGER_CHAT_ID` | yes | — | Chat ID of the manager-only chat |
| `TELEGRAM_STORE_PATH` | no | `./telegram_store.json` | Path to the local JSON message/member store |
| `TELEGRAM_POLL_INTERVAL_MS` | no | `1000` | Delay between poll cycles (ms) — only relevant after errors |
| `TELEGRAM_LONG_POLL_TIMEOUT` | no | `30` | Seconds to hold each `getUpdates` request open (Telegram long-polling) |
## Operational notes
- **The poller must be running before any workflow that reads messages fires.** Messages sent to the group before the poller starts are lost unless the poller's `offset` was persisted from a previous run — on boot, the poller resumes from the last saved `offset` and Telegram redelivers any updates received while the server was down (within Telegram's 24-hour update retention window).
- **Attachments are flagged, not fetched.** `telegram_list_messages` returns `has_attachment: true` for messages with photos/files. Photo/document captions are captured in the `text` field so the workflow still sees the sender's words. The workflow's edge-case guard routes media-bearing messages to human review; this server intentionally doesn't fetch or interpret attachment content.
- **Consumption contract**: `telegram_list_messages` does **not** mark messages as consumed. The workflow is responsible for tracking its own last-run timestamp (e.g. in the tracker CSV or a workspace key) and passing it as the `since` parameter each run. For a daily 09:00 schedule, pass `since=yesterday_09:00` to get the previous day's messages. Invalid `since` values throw rather than silently returning all messages.
- **Rate limits** are handled automatically — on HTTP 429 from Telegram, the server sleeps for the `Retry-After` duration and retries up to 3 times before surfacing the error.
- **Graceful shutdown**: SIGINT/SIGTERM aborts any in-flight long-poll immediately, stops the poller, and flushes the store to disk via an atomic temp-file-then-rename write.
## Design decisions
- **No external Telegram library.** Uses `fetch` directly against `https://api.telegram.org/bot<TOKEN>/...` — keeps dependencies minimal and avoids native bindings.
- **JSON file store, not SQLite.** Demo-scale volume (under a few dozen messages) doesn't justify SQLite's native dependency cost. Swap in SQLite by replacing the `loadStore`/`saveStore` methods if needed for larger deployments.
- **Member list is observed, not enumerated.** The Telegram Bot API has no "list all group members" endpoint. `telegram_list_group_members` returns the set of users the poller has seen send messages into the staff group, which for a demo with seeded personas is the full set.
- **`target` is an enum, not a freeform chat ID.** Constrains sends to the two pre-configured chats, so workflows can't accidentally send escalations to the wrong audience by passing a wrong chat ID.
## Security notes
- **Bot token is a secret.** Keep it in `.env`, never commit it. `.env` is in `.gitignore`.
- **Sends are constrained to configured chats.** The `target` enum means tool calls can only send to `staff_group` or `manager_chat` — the chat IDs are server-side, not caller-controlled.
- **No attachment content is fetched or written to disk.** `has_attachment` is a flag only; no risk of malicious-file write-through.
This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessNo issues