telegram-notifier-mcp 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -36
- package/build/index.js +96 -2
- package/package.json +14 -1
package/README.md
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
# Telegram Notifier MCP Server
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
An [MCP](https://modelcontextprotocol.io/) server that lets an LLM send messages and files to a user via a Telegram bot, and read incoming messages. No external HTTP or Telegram libraries — just the native `fetch` API and the official MCP SDK.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Quick Start
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- A Telegram bot token (from [@BotFather](https://t.me/BotFather))
|
|
9
|
-
- Your Telegram chat ID
|
|
10
|
-
|
|
11
|
-
## Setup
|
|
7
|
+
No cloning or building required — just add the config to your MCP client.
|
|
12
8
|
|
|
13
9
|
### 1. Create a Telegram Bot
|
|
14
10
|
|
|
@@ -27,29 +23,9 @@ A one-way notification [MCP](https://modelcontextprotocol.io/) server that lets
|
|
|
27
23
|
|
|
28
24
|
> **Tip:** For group chats, add the bot to the group, send a message, and check the same URL. Group chat IDs are negative numbers (e.g., `-1001234567890`).
|
|
29
25
|
|
|
30
|
-
### 3.
|
|
31
|
-
|
|
32
|
-
```bash
|
|
33
|
-
git clone <your-repo-url>
|
|
34
|
-
cd telegram-notifier-mcp
|
|
35
|
-
npm install
|
|
36
|
-
npm run build
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
## Configuration
|
|
40
|
-
|
|
41
|
-
The server uses two environment variables:
|
|
42
|
-
|
|
43
|
-
| Variable | Required | Description |
|
|
44
|
-
|---|---|---|
|
|
45
|
-
| `TELEGRAM_BOT_TOKEN` | Yes | Bot token from @BotFather |
|
|
46
|
-
| `TELEGRAM_CHAT_ID` | No | Default chat ID. Can be overridden per-tool call via the `chatId` parameter. |
|
|
47
|
-
|
|
48
|
-
The server will exit with an error if `TELEGRAM_BOT_TOKEN` is not set. If `TELEGRAM_CHAT_ID` is not set, you must pass `chatId` to every tool call.
|
|
26
|
+
### 3. Add to Your MCP Client
|
|
49
27
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
### Claude Desktop
|
|
28
|
+
#### Claude Desktop
|
|
53
29
|
|
|
54
30
|
Add this to your Claude Desktop config file:
|
|
55
31
|
|
|
@@ -60,8 +36,8 @@ Add this to your Claude Desktop config file:
|
|
|
60
36
|
{
|
|
61
37
|
"mcpServers": {
|
|
62
38
|
"telegram-notifier": {
|
|
63
|
-
"command": "
|
|
64
|
-
"args": ["
|
|
39
|
+
"command": "npx",
|
|
40
|
+
"args": ["telegram-notifier-mcp"],
|
|
65
41
|
"env": {
|
|
66
42
|
"TELEGRAM_BOT_TOKEN": "your-bot-token-here",
|
|
67
43
|
"TELEGRAM_CHAT_ID": "your-chat-id-here"
|
|
@@ -71,16 +47,16 @@ Add this to your Claude Desktop config file:
|
|
|
71
47
|
}
|
|
72
48
|
```
|
|
73
49
|
|
|
74
|
-
|
|
50
|
+
#### Claude Code
|
|
75
51
|
|
|
76
|
-
Add to your project's `.mcp.json` or
|
|
52
|
+
Add to your project's `.mcp.json` or `~/.claude.json`:
|
|
77
53
|
|
|
78
54
|
```json
|
|
79
55
|
{
|
|
80
56
|
"mcpServers": {
|
|
81
57
|
"telegram-notifier": {
|
|
82
|
-
"command": "
|
|
83
|
-
"args": ["
|
|
58
|
+
"command": "npx",
|
|
59
|
+
"args": ["telegram-notifier-mcp"],
|
|
84
60
|
"env": {
|
|
85
61
|
"TELEGRAM_BOT_TOKEN": "your-bot-token-here",
|
|
86
62
|
"TELEGRAM_CHAT_ID": "your-chat-id-here"
|
|
@@ -90,6 +66,19 @@ Add to your project's `.mcp.json` or global `~/.claude/claude_code_config.json`:
|
|
|
90
66
|
}
|
|
91
67
|
```
|
|
92
68
|
|
|
69
|
+
That's it — your LLM can now send you Telegram notifications.
|
|
70
|
+
|
|
71
|
+
## Configuration
|
|
72
|
+
|
|
73
|
+
The server uses two environment variables:
|
|
74
|
+
|
|
75
|
+
| Variable | Required | Description |
|
|
76
|
+
|---|---|---|
|
|
77
|
+
| `TELEGRAM_BOT_TOKEN` | Yes | Bot token from @BotFather |
|
|
78
|
+
| `TELEGRAM_CHAT_ID` | No | Default chat ID. Can be overridden per-tool call via the `chatId` parameter. |
|
|
79
|
+
|
|
80
|
+
The server will exit with an error if `TELEGRAM_BOT_TOKEN` is not set. If `TELEGRAM_CHAT_ID` is not set, you must pass `chatId` to every tool call.
|
|
81
|
+
|
|
93
82
|
## Tools
|
|
94
83
|
|
|
95
84
|
### `send_message`
|
|
@@ -151,13 +140,22 @@ Send an audio file to a Telegram chat.
|
|
|
151
140
|
| `parseMode` | string | No | `Markdown`, `MarkdownV2`, or `HTML` |
|
|
152
141
|
| `disableNotification` | boolean | No | Send silently without notification sound |
|
|
153
142
|
|
|
143
|
+
### `get_updates`
|
|
144
|
+
|
|
145
|
+
Check for new messages sent to the bot. Only returns messages received since the last check.
|
|
146
|
+
|
|
147
|
+
| Parameter | Type | Required | Description |
|
|
148
|
+
|---|---|---|---|
|
|
149
|
+
| `limit` | number | No | Max messages to retrieve (1-100, default 10) |
|
|
150
|
+
| `timeout` | number | No | Long-polling timeout in seconds (0-30, default 0). Set >0 to wait for new messages. |
|
|
151
|
+
|
|
154
152
|
## Testing with the MCP Inspector
|
|
155
153
|
|
|
156
154
|
You can test the server interactively using the [MCP Inspector](https://github.com/modelcontextprotocol/inspector):
|
|
157
155
|
|
|
158
156
|
```bash
|
|
159
157
|
TELEGRAM_BOT_TOKEN="your-token" TELEGRAM_CHAT_ID="your-chat-id" \
|
|
160
|
-
npx @modelcontextprotocol/inspector
|
|
158
|
+
npx @modelcontextprotocol/inspector npx telegram-notifier-mcp
|
|
161
159
|
```
|
|
162
160
|
|
|
163
161
|
This opens a browser UI where you can invoke each tool and see the results.
|
|
@@ -183,6 +181,11 @@ Telegram enforces a **50 MB** limit for file uploads via the Bot API. The server
|
|
|
183
181
|
## Development
|
|
184
182
|
|
|
185
183
|
```bash
|
|
184
|
+
git clone https://github.com/AdeshAtole/telegram-notifier-mcp
|
|
185
|
+
cd telegram-notifier-mcp
|
|
186
|
+
npm install
|
|
187
|
+
npm run build
|
|
188
|
+
|
|
186
189
|
# Watch mode — rebuilds on file changes
|
|
187
190
|
npm run dev
|
|
188
191
|
```
|
package/build/index.js
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { z } from "zod";
|
|
5
|
-
import { readFile, stat } from "node:fs/promises";
|
|
6
|
-
import { basename } from "node:path";
|
|
5
|
+
import { readFile, writeFile, stat, mkdir } from "node:fs/promises";
|
|
6
|
+
import { basename, join } from "node:path";
|
|
7
|
+
import { homedir } from "node:os";
|
|
7
8
|
// ---------------------------------------------------------------------------
|
|
8
9
|
// Config
|
|
9
10
|
// ---------------------------------------------------------------------------
|
|
@@ -16,6 +17,57 @@ if (!TELEGRAM_BOT_TOKEN) {
|
|
|
16
17
|
}
|
|
17
18
|
const TELEGRAM_API = `https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}`;
|
|
18
19
|
const MAX_FILE_SIZE = 50 * 1024 * 1024; // 50 MB
|
|
20
|
+
// Persist the getUpdates offset to disk so we don't re-fetch old messages after restart
|
|
21
|
+
const OFFSET_DIR = join(homedir(), ".telegram-notifier-mcp");
|
|
22
|
+
const OFFSET_FILE = join(OFFSET_DIR, "update-offset");
|
|
23
|
+
let updateOffset = 0;
|
|
24
|
+
async function loadOffset() {
|
|
25
|
+
try {
|
|
26
|
+
const data = await readFile(OFFSET_FILE, "utf-8");
|
|
27
|
+
const parsed = parseInt(data.trim(), 10);
|
|
28
|
+
if (!isNaN(parsed))
|
|
29
|
+
updateOffset = parsed;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
// File doesn't exist yet — start from 0
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
async function saveOffset() {
|
|
36
|
+
await mkdir(OFFSET_DIR, { recursive: true });
|
|
37
|
+
await writeFile(OFFSET_FILE, String(updateOffset), "utf-8");
|
|
38
|
+
}
|
|
39
|
+
async function getUpdates(limit, timeout) {
|
|
40
|
+
const params = new URLSearchParams({
|
|
41
|
+
offset: String(updateOffset),
|
|
42
|
+
limit: String(limit),
|
|
43
|
+
timeout: String(timeout),
|
|
44
|
+
allowed_updates: JSON.stringify(["message"]),
|
|
45
|
+
});
|
|
46
|
+
const res = await fetch(`${TELEGRAM_API}/getUpdates?${params}`, {
|
|
47
|
+
method: "GET",
|
|
48
|
+
signal: AbortSignal.timeout((timeout + 5) * 1000),
|
|
49
|
+
});
|
|
50
|
+
return (await res.json());
|
|
51
|
+
}
|
|
52
|
+
function formatUpdate(update) {
|
|
53
|
+
const msg = update.message;
|
|
54
|
+
if (!msg)
|
|
55
|
+
return `[Update ${update.update_id}] (no message)`;
|
|
56
|
+
const from = msg.from
|
|
57
|
+
? `${msg.from.first_name}${msg.from.username ? ` (@${msg.from.username})` : ""}`
|
|
58
|
+
: "Unknown";
|
|
59
|
+
const date = new Date(msg.date * 1000).toISOString();
|
|
60
|
+
let content = msg.text ?? "";
|
|
61
|
+
if (msg.photo)
|
|
62
|
+
content = `[Photo]${msg.caption ? ` ${msg.caption}` : ""}`;
|
|
63
|
+
if (msg.document)
|
|
64
|
+
content = `[Document: ${msg.document.file_name ?? "unknown"}]${msg.caption ? ` ${msg.caption}` : ""}`;
|
|
65
|
+
if (msg.video)
|
|
66
|
+
content = `[Video: ${msg.video.file_name ?? "unknown"}]${msg.caption ? ` ${msg.caption}` : ""}`;
|
|
67
|
+
if (msg.audio)
|
|
68
|
+
content = `[Audio: ${msg.audio.file_name ?? "unknown"}]${msg.caption ? ` ${msg.caption}` : ""}`;
|
|
69
|
+
return `[${date}] ${from} (chat ${msg.chat.id}): ${content}`;
|
|
70
|
+
}
|
|
19
71
|
async function sendMessage(chatId, text, parseMode, disableNotification) {
|
|
20
72
|
const body = { chat_id: chatId, text };
|
|
21
73
|
if (parseMode)
|
|
@@ -211,10 +263,52 @@ server.tool("send_audio", "Send an audio file to a Telegram chat", {
|
|
|
211
263
|
const res = await sendFile("sendAudio", resolvedChatId, filePath, caption, parseMode, disableNotification);
|
|
212
264
|
return telegramResult(res, `Audio sent to chat ${resolvedChatId}.`);
|
|
213
265
|
});
|
|
266
|
+
// -- get_updates ------------------------------------------------------------
|
|
267
|
+
server.tool("get_updates", "Check for new messages sent to the bot. Returns only new messages since the last check.", {
|
|
268
|
+
limit: z
|
|
269
|
+
.number()
|
|
270
|
+
.int()
|
|
271
|
+
.min(1)
|
|
272
|
+
.max(100)
|
|
273
|
+
.optional()
|
|
274
|
+
.describe("Max number of messages to retrieve (1-100, default 10)"),
|
|
275
|
+
timeout: z
|
|
276
|
+
.number()
|
|
277
|
+
.int()
|
|
278
|
+
.min(0)
|
|
279
|
+
.max(30)
|
|
280
|
+
.optional()
|
|
281
|
+
.describe("Long-polling timeout in seconds (0-30, default 0). Set >0 to wait for new messages."),
|
|
282
|
+
}, async ({ limit, timeout }) => {
|
|
283
|
+
const effectiveLimit = limit ?? 10;
|
|
284
|
+
const effectiveTimeout = timeout ?? 0;
|
|
285
|
+
let res;
|
|
286
|
+
try {
|
|
287
|
+
res = await getUpdates(effectiveLimit, effectiveTimeout);
|
|
288
|
+
}
|
|
289
|
+
catch (err) {
|
|
290
|
+
return errorResult(`Failed to fetch updates: ${err instanceof Error ? err.message : String(err)}`);
|
|
291
|
+
}
|
|
292
|
+
if (!res.ok) {
|
|
293
|
+
return errorResult(`Telegram API error: ${res.description ?? "Unknown error"}`);
|
|
294
|
+
}
|
|
295
|
+
const updates = res.result ?? [];
|
|
296
|
+
// Advance the offset and persist so we survive restarts
|
|
297
|
+
if (updates.length > 0) {
|
|
298
|
+
updateOffset = updates[updates.length - 1].update_id + 1;
|
|
299
|
+
await saveOffset();
|
|
300
|
+
}
|
|
301
|
+
if (updates.length === 0) {
|
|
302
|
+
return successResult("No new messages.");
|
|
303
|
+
}
|
|
304
|
+
const formatted = updates.map(formatUpdate).join("\n");
|
|
305
|
+
return successResult(`${updates.length} new message(s):\n\n${formatted}`);
|
|
306
|
+
});
|
|
214
307
|
// ---------------------------------------------------------------------------
|
|
215
308
|
// Start
|
|
216
309
|
// ---------------------------------------------------------------------------
|
|
217
310
|
async function main() {
|
|
311
|
+
await loadOffset();
|
|
218
312
|
const transport = new StdioServerTransport();
|
|
219
313
|
await server.connect(transport);
|
|
220
314
|
console.error("Telegram Notifier MCP server running on stdio");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "telegram-notifier-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "MCP server for sending notifications via Telegram Bot API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -18,6 +18,19 @@
|
|
|
18
18
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
19
19
|
"zod": "^3.24.2"
|
|
20
20
|
},
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/AdeshAtole/telegram-notifier-mcp.git"
|
|
24
|
+
},
|
|
25
|
+
"homepage": "https://github.com/AdeshAtole/telegram-notifier-mcp#readme",
|
|
26
|
+
"keywords": [
|
|
27
|
+
"mcp",
|
|
28
|
+
"telegram",
|
|
29
|
+
"notifications",
|
|
30
|
+
"bot",
|
|
31
|
+
"model-context-protocol"
|
|
32
|
+
],
|
|
33
|
+
"license": "MIT",
|
|
21
34
|
"devDependencies": {
|
|
22
35
|
"typescript": "^5.7.3",
|
|
23
36
|
"@types/node": "^22.13.4"
|