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.
Files changed (3) hide show
  1. package/README.md +39 -36
  2. package/build/index.js +96 -2
  3. package/package.json +14 -1
package/README.md CHANGED
@@ -1,14 +1,10 @@
1
1
  # Telegram Notifier MCP Server
2
2
 
3
- A one-way notification [MCP](https://modelcontextprotocol.io/) server that lets an LLM send messages and files to a user via a Telegram bot. No external HTTP or Telegram libraries — just the native `fetch` API and the official MCP SDK.
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
- ## Prerequisites
5
+ ## Quick Start
6
6
 
7
- - Node.js 18+
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. Install and Build
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
- ## Adding to Your MCP Client
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": "node",
64
- "args": ["/absolute/path/to/telegram-notifier-mcp/build/index.js"],
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
- ### Claude Code
50
+ #### Claude Code
75
51
 
76
- Add to your project's `.mcp.json` or global `~/.claude/claude_code_config.json`:
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": "node",
83
- "args": ["/absolute/path/to/telegram-notifier-mcp/build/index.js"],
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 node build/index.js
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.0.0",
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"