Skip to main content
Glama
README.md
# telegram-mcp šŸ¤–āœ‰ļø

**MCP (Model Context Protocol) server for Telegram Bot API integration.**  
Enables AI agents to send messages, photos, documents, polls, progress updates, and receive user responses via Telegram — all with persistent storage, rate limiting, and robust error handling.

---

## Features

- āœ… **10 MCP Tools** — send_message, send_photo, send_document, send_progress, send_todo_update, send_error_alert, ask_user, wait_for_response, create_poll, send_daily_report
- āœ… **Bidirectional Communication** — AI agents can pause, ask questions, wait for Telegram replies, and resume workflows based on user responses
- āœ… **Multi-Chat Support** — Configure one or many Telegram chats/groups/channels
- āœ… **MarkdownV2 Formatting** — Rich text with bold, italic, code blocks, links, tables, progress bars
- āœ… **SQLite Persistence** — Conversation history, pending response tracking, message logs
- āœ… **Rate Limiting** — Token-bucket algorithm respects Telegram's API limits
- āœ… **Retry with Exponential Backoff** — Auto-retries failed API calls
- āœ… **Comprehensive Logging** — Structured logs with levels (debug/info/warn/error)
- āœ… **Type-Safe** — Full TypeScript with Zod schemas for all tool inputs
- āœ… **Docker Support** — Multi-stage Dockerfile + docker-compose
- āœ… **Environment Config** — All settings via environment variables with `.env.example`
- āœ… **Polling-Based Updates** — No webhook needed; polls Telegram for new messages

---

## Architecture

```
ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”     stdio      ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│  AI Agent /      │ ◄──────────► │     telegram-mcp          │
│  MCP Client      │               │                          │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜               │  ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”   │
                                   │  │  MCP Tools Layer  │   │
                                   │  │  (Zod schemas)    │   │
                                   │  ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜   │
                                   │           │               │
                                   │  ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā–¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”   │
                                   │  │  Telegram Client   │   │
                                   │  │  (retry + rate     │   │
                                   │  │   limiting)        │   │
                                   │  ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜   │
                                   │           │               │
                                   │  ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā–¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”   │
                                   │  │  Update Poller     │   │
                                   │  │  (getUpdates loop) │   │
                                   │  ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜   │
                                   │           │               │
                                   │  ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā–¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”   │
                                   │  │  SQLite Storage    │   │
                                   │  │  (better-sqlite3)  │   │
                                   │  ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜   │
                                   ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
                                              │
                                    Telegram Bot API
                                              │
                                         Telegram
                                        (Users/Chats)
```

---

## Prerequisites

- **Node.js** ≄ 22 (or Docker)
- A **Telegram Bot Token** from [@BotFather](https://t.me/botfather)
- The **Chat ID** of your target Telegram chat(s) (use [@userinfobot](https://t.me/userinfobot) to find)

---

## Installation

### Option 1: npm / pnpm (standalone)

```bash
# Clone or create the project directory
cd telegram-mcp

# Install dependencies
npm install

# Build TypeScript
npm run build

# Copy and edit environment file
cp .env.example .env
# Edit .env with your BOT_TOKEN and CHAT_ID
```

### Option 2: Docker

```bash
# Build the image
docker build -t telegram-mcp .

# Or use docker-compose
cp .env.example .env
# Edit .env with your BOT_TOKEN and CHAT_ID
docker compose up -d
```

---

## Configuration

All configuration is via environment variables (defined in `.env`):

| Variable | Required | Default | Description |
|---|---|---|---|
| `BOT_TOKEN` | āœ… | — | Telegram Bot API token from @BotFather |
| `CHAT_ID` | āœ… | — | Comma-separated chat IDs (e.g., `123456789,-987654321`) |
| `OWNER_ID` | āŒ | — | Bot owner's Telegram user ID |
| `DB_PATH` | āŒ | `./data/telegram-mcp.db` | SQLite database file path |
| `LOG_LEVEL` | āŒ | `info` | Log level: `debug`, `info`, `warn`, `error` |
| `POLL_INTERVAL_MS` | āŒ | `2000` | Polling interval for message updates (ms) |
| `RESPONSE_TIMEOUT_SEC` | āŒ | `300` | Default timeout for `wait_for_response` (seconds) |
| `RESPONSE_POLL_INTERVAL_MS` | āŒ | `500` | Poll interval when waiting for a response (ms) |
| `RATE_LIMIT_RPS` | āŒ | `30` | Telegram API rate limit (requests per second) |
| `MAX_RETRIES` | āŒ | `3` | Maximum retries for failed API calls |

---

## Usage

### Running the Server

```bash
# Development mode (with hot reload)
npm run dev

# Production mode
npm start

# With Docker
docker compose up
```

The server starts an MCP service over **stdio**, which is automatically used by MCP-compatible clients.  
Configure your AI agent or MCP client to launch:

```bash
node /path/to/telegram-mcp/dist/index.js
```

### MCP Client Configuration

If using Claude Desktop or another MCP client, add to your `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "telegram": {
      "command": "node",
      "args": ["/absolute/path/to/telegram-mcp/dist/index.js"],
      "env": {
        "BOT_TOKEN": "your_bot_token",
        "CHAT_ID": "your_chat_id"
      }
    }
  }
}
```

---

## Tools

### `send_message`

Send a text message to one or all configured chats.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `text` | string | āœ… | Message content (MarkdownV2) |
| `chat_id` | string | āŒ | Specific chat ID; omit to broadcast |
| `parse_mode` | enum | āŒ | `MarkdownV2` (default), `HTML`, `Markdown` |
| `disable_web_page_preview` | boolean | āŒ | Disable link previews |
| `disable_notification` | boolean | āŒ | Send silently |

### `send_photo`

Send a photo (by URL or Telegram file_id).

| Parameter | Type | Required | Description |
|---|---|---|---|
| `photo` | string | āœ… | URL or file_id |
| `caption` | string | āŒ | Optional caption |
| `chat_id` | string | āŒ | Specific chat ID |
| `parse_mode` | enum | āŒ | `MarkdownV2`, `HTML` |

### `send_document`

Send a document (by URL or file_id).

| Parameter | Type | Required | Description |
|---|---|---|---|
| `document` | string | āœ… | URL or file_id |
| `caption` | string | āŒ | Optional caption |
| `chat_id` | string | āŒ | Specific chat ID |
| `filename` | string | āŒ | Display name |

### `send_progress`

Send a visual progress bar.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `title` | string | āœ… | Progress title |
| `current` | number | āœ… | Current value |
| `total` | number | āœ… | Total value |
| `status_text` | string | āŒ | Optional status |
| `chat_id` | string | āŒ | Specific chat ID |

### `send_todo_update`

Notify about a todo item status change.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `action` | enum | āœ… | `created`, `updated`, `completed`, `deleted` |
| `title` | string | āœ… | Todo title |
| `description` | string | āŒ | Optional description |
| `status` | string | āŒ | e.g., `in progress`, `done` |
| `priority` | enum | āŒ | `low`, `medium`, `high`, `urgent` |
| `chat_id` | string | āŒ | Specific chat ID |

### `send_error_alert`

Send a structured error notification.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `title` | string | āœ… | Error title |
| `description` | string | āœ… | Error details |
| `stack` | string | āŒ | Stack trace |
| `metadata` | object | āŒ | Key-value pairs |
| `chat_id` | string | āŒ | Specific chat ID |

### `create_poll`

Create and send a poll.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `question` | string | āœ… | Poll question |
| `options` | string[] | āœ… | 2–10 options |
| `is_anonymous` | boolean | āŒ | Default: true |
| `allows_multiple_answers` | boolean | āŒ | Default: false |
| `type` | enum | āŒ | `regular`, `quiz` |
| `correct_option_id` | number | āŒ | For quiz polls |
| `explanation` | string | āŒ | For quiz polls |
| `open_period` | number | āŒ | Auto-close seconds |
| `chat_id` | string | āŒ | Specific chat ID |

### `send_daily_report`

Send a structured daily summary.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `date` | string | āŒ | ISO date (defaults to today) |
| `title` | string | āœ… | Report title |
| `sections` | array | āœ… | `[{heading, content}]` |
| `footer` | string | āŒ | Optional footer |
| `chat_id` | string | āŒ | Specific chat ID |

### `ask_user`

Send a question and create a pending response slot. Returns a `response_id`.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `question` | string | āœ… | Question text |
| `chat_id` | string | āŒ | Specific chat ID |
| `timeout_seconds` | number | āŒ | Override default timeout |

**Returns:** `response_id` string (pass to `wait_for_response`)

### `wait_for_response`

Block until the user replies to a previously asked question.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `response_id` | string | āœ… | ID from `ask_user` |
| `timeout_seconds` | number | āŒ | Timeout override |
| `chat_id` | string | āŒ | Narrow chat scope |

**Returns:**
```json
{
  "received": true,
  "text": "User's reply message",
  "messageId": 42
}
```

On timeout:
```json
{
  "received": false,
  "error": "Timeout: No response received within the allowed time."
}
```

---

## Two-Phase Agent Workflow (ask → wait → resume)

The most powerful feature is the ability for an AI agent to **pause execution**, **ask a question** via Telegram, **wait** for the user's reply, and **resume** the workflow based on the response.

### Flow

```
Agent                    telegram-mcp              Telegram User
  │                          │                        │
  │── ask_user({question}) ──►─────────── message ────►│
  │◀── { response_id } ──────│                        │
  │                          │                        │
  │── wait_for_response({    │                        │
  │     response_id }) ──────►                        │
  │                          │                        │
  │                          │◄──── reply ────────────│
  │◀── { received:true,     │                        │
  │      text:"..." } ──────│                        │
  │                          │                        │
  │ (resumes logic based    │                        │
  │  on user's response)    │                        │
```

### Example Agent Script

```typescript
// Phase 1: Ask the user
const { response_id } = await mcp.callTool("ask_user", {
  question: "Which report should I generate? (daily/weekly/monthly)"
});

// Phase 2: Wait for their response (blocks up to 5 minutes)
const result = await mcp.callTool("wait_for_response", {
  response_id: response_id,
  timeout_seconds: 300
});

if (result.received) {
  const userChoice = result.text.toLowerCase();
  if (userChoice.includes("daily")) {
    await generateDailyReport();
  } else if (userChoice.includes("weekly")) {
    await generateWeeklyReport();
  }
  await mcp.callTool("send_message", {
    text: `āœ… Generating ${userChoice} report...`
  });
} else {
  await mcp.callTool("send_error_alert", {
    title: "No Response",
    description: "User did not respond within the timeout period."
  });
}
```

---

## Development

```bash
# Install dependencies
npm install

# Run in development mode (with hot reload)
npm run dev

# Run tests
npm test

# Run tests in watch mode
npm run test:watch

# Build for production
npm run build

# Lint (if configured)
npm run lint
```

### Project Structure

```
telegram-mcp/
ā”œā”€ā”€ src/
│   ā”œā”€ā”€ index.ts                 # Entry point — starts MCP server + polling
│   ā”œā”€ā”€ config.ts                # Environment variable validation (Zod)
│   ā”œā”€ā”€ logger.ts                # Structured logging
│   ā”œā”€ā”€ types.ts                 # TypeScript interfaces
│   ā”œā”€ā”€ telegram/
│   │   ā”œā”€ā”€ client.ts            # Telegram Bot API client (retry, rate limit)
│   │   └── formatter.ts         # MarkdownV2 formatting utilities
│   ā”œā”€ā”€ storage/
│   │   └── db.ts                # SQLite database (conversations, pending)
│   ā”œā”€ā”€ handlers/
│   │   └── updates.ts           # Telegram update polling
│   └── mcp/
│       └── tools.ts             # All MCP tool definitions + response handler
ā”œā”€ā”€ tests/
│   └── telegram-mcp.test.ts     # Vitest unit tests
ā”œā”€ā”€ Dockerfile                   # Multi-stage Docker build
ā”œā”€ā”€ docker-compose.yml           # Docker Compose setup
ā”œā”€ā”€ .env.example                 # Environment template
ā”œā”€ā”€ vitest.config.ts             # Test configuration
ā”œā”€ā”€ tsconfig.json                # TypeScript configuration
└── package.json
```

---

## Testing

```bash
npm test
```

Tests cover:
- MarkdownV2 escaping and formatting
- Structured message builders (errors, todos, reports, polls)
- Database CRUD operations (in-memory SQLite)
- Config validation logic
- Response ID generation

---

## Error Handling & Reliability

- **Exponential backoff** — Failed Telegram API calls retry with `1s → 2s → 4s` delays (configurable)
- **Rate limiting** — Token-bucket algorithm prevents hitting Telegram's rate limits
- **Graceful shutdown** — Cleanly closes database and stops polling on SIGINT/SIGTERM
- **Uncaught exception handling** — Logs fatal errors before exiting
- **Input validation** — All tool inputs validated with Zod schemas

---

## Database

The server uses **SQLite** (via `better-sqlite3`) for persistence:

- **conversations** — Full chat history (user + agent messages)
- **pending_responses** — Active `ask_user` / `wait_for_response` requests
- **messages_log** — Auditable log of all sent/received messages

The database file is stored at `DB_PATH` (default: `./data/telegram-mcp.db`).

---

## Docker

### Build & Run

```bash
# Build
docker build -t telegram-mcp .

# Run with .env file
docker run --env-file .env -v telegram-mcp-data:/app/data telegram-mcp

# Or use docker-compose
docker compose up -d
```

### Multi-Stage Build

The Dockerfile uses a multi-stage build:
1. **Builder** stage — Installs dev dependencies, compiles TypeScript
2. **Production** stage — Minimal image with only runtime dependencies

---

## Security

- **Bot Token** — Keep your `BOT_TOKEN` secret. Never commit it to version control.
- **Chat IDs** — Restrict which chats the bot listens to via `CHAT_ID`.
- **Input Validation** — All tool inputs are validated with Zod schemas.
- **No Webhooks** — Uses polling instead, avoiding the need for a public HTTPS endpoint.

---

## License

MIT

---

## Contributing

Contributions are welcome! Please open an issue or PR for:

- Additional Telegram API features
- Improved formatting utilities
- Enhanced response matching (e.g., reply chains)
- Webhook support
- Multi-language support