@overpod/mcp-telegram 1.11.1 → 1.12.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/dist/index.js +377 -275
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -40,30 +40,36 @@ async function requireConnection() {
|
|
|
40
40
|
const reason = telegram.lastError ? ` ${telegram.lastError}` : "";
|
|
41
41
|
return `Not connected to Telegram.${reason} Run telegram-login first.`;
|
|
42
42
|
}
|
|
43
|
+
/** MCP tool annotation presets */
|
|
44
|
+
const READ_ONLY = { readOnlyHint: true, openWorldHint: true };
|
|
45
|
+
const WRITE = { readOnlyHint: false, openWorldHint: true };
|
|
46
|
+
const DESTRUCTIVE = { readOnlyHint: false, destructiveHint: true, openWorldHint: true };
|
|
47
|
+
/** Helper: success response */
|
|
48
|
+
function ok(text) {
|
|
49
|
+
return { content: [{ type: "text", text }] };
|
|
50
|
+
}
|
|
51
|
+
/** Helper: error response with isError flag */
|
|
52
|
+
function fail(e) {
|
|
53
|
+
return { content: [{ type: "text", text: `Error: ${e.message}` }], isError: true };
|
|
54
|
+
}
|
|
43
55
|
// --- Tools ---
|
|
44
|
-
server.
|
|
56
|
+
server.registerTool("telegram-status", { description: "Check Telegram connection status", annotations: READ_ONLY }, async () => {
|
|
45
57
|
if (await telegram.ensureConnected()) {
|
|
46
58
|
try {
|
|
47
59
|
const me = await telegram.getMe();
|
|
48
|
-
return {
|
|
49
|
-
content: [
|
|
50
|
-
{
|
|
51
|
-
type: "text",
|
|
52
|
-
text: `Connected as ${me.firstName ?? ""} (@${me.username ?? "unknown"}, id: ${me.id})`,
|
|
53
|
-
},
|
|
54
|
-
],
|
|
55
|
-
};
|
|
60
|
+
return ok(`Connected as ${me.firstName ?? ""} (@${me.username ?? "unknown"}, id: ${me.id})`);
|
|
56
61
|
}
|
|
57
62
|
catch {
|
|
58
|
-
return
|
|
63
|
+
return ok("Connected, but failed to get user info");
|
|
59
64
|
}
|
|
60
65
|
}
|
|
61
66
|
const reason = telegram.lastError ? ` Reason: ${telegram.lastError}` : "";
|
|
62
|
-
return {
|
|
63
|
-
content: [{ type: "text", text: `Not connected.${reason} Use telegram-login to authenticate via QR code.` }],
|
|
64
|
-
};
|
|
67
|
+
return ok(`Not connected.${reason} Use telegram-login to authenticate via QR code.`);
|
|
65
68
|
});
|
|
66
|
-
server.
|
|
69
|
+
server.registerTool("telegram-login", {
|
|
70
|
+
description: "Login to Telegram via QR code. Returns QR image. IMPORTANT: pass the entire result to user without modifications.",
|
|
71
|
+
annotations: WRITE,
|
|
72
|
+
}, async () => {
|
|
67
73
|
let qrDataUrl = "";
|
|
68
74
|
let qrRawUrl = "";
|
|
69
75
|
const loginPromise = telegram.startQrLogin((dataUrl) => {
|
|
@@ -77,7 +83,7 @@ server.tool("telegram-login", "Login to Telegram via QR code. Returns QR image.
|
|
|
77
83
|
await new Promise((r) => setTimeout(r, 500));
|
|
78
84
|
}
|
|
79
85
|
if (!qrDataUrl) {
|
|
80
|
-
return
|
|
86
|
+
return fail(new Error("Failed to generate QR code"));
|
|
81
87
|
}
|
|
82
88
|
// Login continues in background
|
|
83
89
|
loginPromise.then((result) => {
|
|
@@ -116,36 +122,44 @@ server.tool("telegram-login", "Login to Telegram via QR code. Returns QR image.
|
|
|
116
122
|
],
|
|
117
123
|
};
|
|
118
124
|
});
|
|
119
|
-
server.
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
+
server.registerTool("telegram-send-message", {
|
|
126
|
+
description: "Send a message to a Telegram chat",
|
|
127
|
+
inputSchema: {
|
|
128
|
+
chatId: z.string().describe("Chat ID or username (e.g. @username or numeric ID)"),
|
|
129
|
+
text: z.string().describe("Message text"),
|
|
130
|
+
replyTo: z.number().optional().describe("Message ID to reply to"),
|
|
131
|
+
parseMode: z.enum(["md", "html"]).optional().describe("Message format: md (Markdown) or html"),
|
|
132
|
+
topicId: z.number().optional().describe("Forum topic ID to send message into (for groups with Topics enabled)"),
|
|
133
|
+
},
|
|
134
|
+
annotations: WRITE,
|
|
125
135
|
}, async ({ chatId, text, replyTo, parseMode, topicId }) => {
|
|
126
136
|
const err = await requireConnection();
|
|
127
137
|
if (err)
|
|
128
|
-
return
|
|
138
|
+
return fail(new Error(err));
|
|
129
139
|
try {
|
|
130
140
|
await telegram.sendMessage(chatId, text, replyTo, parseMode, topicId);
|
|
131
141
|
const dest = topicId ? `topic ${topicId} in ${chatId}` : chatId;
|
|
132
|
-
return
|
|
142
|
+
return ok(`Message sent to ${dest}`);
|
|
133
143
|
}
|
|
134
144
|
catch (e) {
|
|
135
|
-
return
|
|
136
|
-
}
|
|
137
|
-
});
|
|
138
|
-
server.
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
.
|
|
143
|
-
|
|
144
|
-
|
|
145
|
+
return fail(e);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
server.registerTool("telegram-list-chats", {
|
|
149
|
+
description: "List Telegram chats with unread counts, type indicators, and contact status",
|
|
150
|
+
inputSchema: {
|
|
151
|
+
limit: z.number().default(20).describe("Number of chats to return"),
|
|
152
|
+
offsetDate: z.number().optional().describe("Unix timestamp offset for pagination"),
|
|
153
|
+
filterType: z
|
|
154
|
+
.enum(["private", "group", "channel", "contact_requests"])
|
|
155
|
+
.optional()
|
|
156
|
+
.describe("Filter by chat type. 'contact_requests' shows only private chats from non-contacts"),
|
|
157
|
+
},
|
|
158
|
+
annotations: READ_ONLY,
|
|
145
159
|
}, async ({ limit, offsetDate, filterType }) => {
|
|
146
160
|
const err = await requireConnection();
|
|
147
161
|
if (err)
|
|
148
|
-
return
|
|
162
|
+
return fail(new Error(err));
|
|
149
163
|
try {
|
|
150
164
|
const dialogs = await telegram.getDialogs(limit, offsetDate, filterType);
|
|
151
165
|
const text = dialogs
|
|
@@ -157,98 +171,118 @@ server.tool("telegram-list-chats", "List Telegram chats", {
|
|
|
157
171
|
return `${prefix} ${d.name} (${d.id})${botTag}${contactTag}${unread}`;
|
|
158
172
|
})
|
|
159
173
|
.join("\n");
|
|
160
|
-
return
|
|
174
|
+
return ok(sanitize(text) || "No chats");
|
|
161
175
|
}
|
|
162
176
|
catch (e) {
|
|
163
|
-
return
|
|
164
|
-
}
|
|
165
|
-
});
|
|
166
|
-
server.
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
177
|
+
return fail(e);
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
server.registerTool("telegram-read-messages", {
|
|
181
|
+
description: "Read recent messages from a Telegram chat with sender names, dates, media info, and reactions",
|
|
182
|
+
inputSchema: {
|
|
183
|
+
chatId: z.string().describe("Chat ID or username"),
|
|
184
|
+
limit: z.number().default(10).describe("Number of messages to return"),
|
|
185
|
+
offsetId: z.number().optional().describe("Message ID to start from (for pagination)"),
|
|
186
|
+
minDate: z.number().optional().describe("Unix timestamp: only messages after this date"),
|
|
187
|
+
maxDate: z.number().optional().describe("Unix timestamp: only messages before this date"),
|
|
188
|
+
},
|
|
189
|
+
annotations: READ_ONLY,
|
|
172
190
|
}, async ({ chatId, limit, offsetId, minDate, maxDate }) => {
|
|
173
191
|
const err = await requireConnection();
|
|
174
192
|
if (err)
|
|
175
|
-
return
|
|
193
|
+
return fail(new Error(err));
|
|
176
194
|
try {
|
|
177
195
|
const messages = await telegram.getMessages(chatId, limit, offsetId, minDate, maxDate);
|
|
178
196
|
const text = messages
|
|
179
197
|
.map((m) => `[#${m.id}] [${m.date}] ${m.sender}: ${m.text}${m.media ? ` [${m.media.type}${m.media.fileName ? `: ${m.media.fileName}` : ""}]` : ""}${formatReactions(m.reactions)}`)
|
|
180
198
|
.join("\n\n");
|
|
181
|
-
return
|
|
199
|
+
return ok(sanitize(text) || "No messages");
|
|
182
200
|
}
|
|
183
201
|
catch (e) {
|
|
184
|
-
return
|
|
202
|
+
return fail(e);
|
|
185
203
|
}
|
|
186
204
|
});
|
|
187
|
-
server.
|
|
188
|
-
|
|
189
|
-
|
|
205
|
+
server.registerTool("telegram-search-chats", {
|
|
206
|
+
description: "Search for Telegram chats, users, or channels by name or username. Returns description and member count",
|
|
207
|
+
inputSchema: {
|
|
208
|
+
query: z.string().describe("Search query (name or username)"),
|
|
209
|
+
limit: z.number().default(10).describe("Max results"),
|
|
210
|
+
},
|
|
211
|
+
annotations: READ_ONLY,
|
|
190
212
|
}, async ({ query, limit }) => {
|
|
191
213
|
const err = await requireConnection();
|
|
192
214
|
if (err)
|
|
193
|
-
return
|
|
215
|
+
return fail(new Error(err));
|
|
194
216
|
try {
|
|
195
217
|
const results = await telegram.searchChats(query, limit);
|
|
196
218
|
const text = results
|
|
197
219
|
.map((c) => `${c.type === "group" ? "G" : c.type === "channel" ? "C" : "P"} ${c.name}${c.username ? ` (@${c.username})` : ""} (${c.id})${c.membersCount ? ` [${c.membersCount} members]` : ""}${c.description ? ` — ${c.description.split("\n")[0].slice(0, 100)}` : ""}`)
|
|
198
220
|
.join("\n");
|
|
199
|
-
return
|
|
221
|
+
return ok(sanitize(text) || "No results");
|
|
200
222
|
}
|
|
201
223
|
catch (e) {
|
|
202
|
-
return
|
|
203
|
-
}
|
|
204
|
-
});
|
|
205
|
-
server.
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
224
|
+
return fail(e);
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
server.registerTool("telegram-search-global", {
|
|
228
|
+
description: "Search messages globally across all public Telegram chats and channels",
|
|
229
|
+
inputSchema: {
|
|
230
|
+
query: z.string().describe("Search text"),
|
|
231
|
+
limit: z.number().default(20).describe("Max results"),
|
|
232
|
+
minDate: z.number().optional().describe("Unix timestamp: only messages after this date"),
|
|
233
|
+
maxDate: z.number().optional().describe("Unix timestamp: only messages before this date"),
|
|
234
|
+
},
|
|
235
|
+
annotations: READ_ONLY,
|
|
210
236
|
}, async ({ query, limit, minDate, maxDate }) => {
|
|
211
237
|
const err = await requireConnection();
|
|
212
238
|
if (err)
|
|
213
|
-
return
|
|
239
|
+
return fail(new Error(err));
|
|
214
240
|
try {
|
|
215
241
|
const messages = await telegram.searchGlobal(query, limit, minDate, maxDate);
|
|
216
242
|
const text = messages
|
|
217
243
|
.map((m) => `[#${m.id}] [${m.date}] [${m.chat.type === "channel" ? "C" : m.chat.type === "group" ? "G" : "P"} ${m.chat.name}${m.chat.username ? ` @${m.chat.username}` : ""}] ${m.sender}: ${m.text}${m.media ? ` [${m.media.type}${m.media.fileName ? `: ${m.media.fileName}` : ""}]` : ""}${formatReactions(m.reactions)}`)
|
|
218
244
|
.join("\n\n");
|
|
219
|
-
return
|
|
245
|
+
return ok(sanitize(text) || "No messages found");
|
|
220
246
|
}
|
|
221
247
|
catch (e) {
|
|
222
|
-
return
|
|
223
|
-
}
|
|
224
|
-
});
|
|
225
|
-
server.
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
248
|
+
return fail(e);
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
server.registerTool("telegram-search-messages", {
|
|
252
|
+
description: "Search messages in a specific Telegram chat by text",
|
|
253
|
+
inputSchema: {
|
|
254
|
+
chatId: z.string().describe("Chat ID or username"),
|
|
255
|
+
query: z.string().describe("Search text"),
|
|
256
|
+
limit: z.number().default(20).describe("Max results"),
|
|
257
|
+
minDate: z.number().optional().describe("Unix timestamp: only messages after this date"),
|
|
258
|
+
maxDate: z.number().optional().describe("Unix timestamp: only messages before this date"),
|
|
259
|
+
},
|
|
260
|
+
annotations: READ_ONLY,
|
|
231
261
|
}, async ({ chatId, query, limit, minDate, maxDate }) => {
|
|
232
262
|
const err = await requireConnection();
|
|
233
263
|
if (err)
|
|
234
|
-
return
|
|
264
|
+
return fail(new Error(err));
|
|
235
265
|
try {
|
|
236
266
|
const messages = await telegram.searchMessages(chatId, query, limit, minDate, maxDate);
|
|
237
267
|
const text = messages
|
|
238
268
|
.map((m) => `[#${m.id}] [${m.date}] ${m.sender}: ${m.text}${m.media ? ` [${m.media.type}${m.media.fileName ? `: ${m.media.fileName}` : ""}]` : ""}${formatReactions(m.reactions)}`)
|
|
239
269
|
.join("\n\n");
|
|
240
|
-
return
|
|
270
|
+
return ok(sanitize(text) || "No messages found");
|
|
241
271
|
}
|
|
242
272
|
catch (e) {
|
|
243
|
-
return
|
|
273
|
+
return fail(e);
|
|
244
274
|
}
|
|
245
275
|
});
|
|
246
|
-
server.
|
|
247
|
-
|
|
276
|
+
server.registerTool("telegram-get-unread", {
|
|
277
|
+
description: "Get chats with unread messages. Forums show per-topic unread breakdown",
|
|
278
|
+
inputSchema: {
|
|
279
|
+
limit: z.number().default(20).describe("Number of unread chats to return"),
|
|
280
|
+
},
|
|
281
|
+
annotations: READ_ONLY,
|
|
248
282
|
}, async ({ limit }) => {
|
|
249
283
|
const err = await requireConnection();
|
|
250
284
|
if (err)
|
|
251
|
-
return
|
|
285
|
+
return fail(new Error(err));
|
|
252
286
|
try {
|
|
253
287
|
const dialogs = await telegram.getUnreadDialogs(limit);
|
|
254
288
|
const text = dialogs
|
|
@@ -265,83 +299,95 @@ server.tool("telegram-get-unread", "Get unread Telegram chats", {
|
|
|
265
299
|
return line;
|
|
266
300
|
})
|
|
267
301
|
.join("\n");
|
|
268
|
-
return
|
|
302
|
+
return ok(sanitize(text) || "No unread chats");
|
|
269
303
|
}
|
|
270
304
|
catch (e) {
|
|
271
|
-
return
|
|
305
|
+
return fail(e);
|
|
272
306
|
}
|
|
273
307
|
});
|
|
274
|
-
server.
|
|
275
|
-
|
|
308
|
+
server.registerTool("telegram-mark-as-read", {
|
|
309
|
+
description: "Mark a Telegram chat as read",
|
|
310
|
+
inputSchema: { chatId: z.string().describe("Chat ID or username") },
|
|
311
|
+
annotations: WRITE,
|
|
276
312
|
}, async ({ chatId }) => {
|
|
277
313
|
const err = await requireConnection();
|
|
278
314
|
if (err)
|
|
279
|
-
return
|
|
315
|
+
return fail(new Error(err));
|
|
280
316
|
try {
|
|
281
317
|
await telegram.markAsRead(chatId);
|
|
282
|
-
return
|
|
318
|
+
return ok(`Marked ${chatId} as read`);
|
|
283
319
|
}
|
|
284
320
|
catch (e) {
|
|
285
|
-
return
|
|
321
|
+
return fail(e);
|
|
286
322
|
}
|
|
287
323
|
});
|
|
288
|
-
server.
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
324
|
+
server.registerTool("telegram-forward-message", {
|
|
325
|
+
description: "Forward messages between Telegram chats",
|
|
326
|
+
inputSchema: {
|
|
327
|
+
fromChatId: z.string().describe("Source chat ID or username"),
|
|
328
|
+
toChatId: z.string().describe("Destination chat ID or username"),
|
|
329
|
+
messageIds: z.array(z.number()).describe("Array of message IDs to forward"),
|
|
330
|
+
},
|
|
331
|
+
annotations: WRITE,
|
|
292
332
|
}, async ({ fromChatId, toChatId, messageIds }) => {
|
|
293
333
|
const err = await requireConnection();
|
|
294
334
|
if (err)
|
|
295
|
-
return
|
|
335
|
+
return fail(new Error(err));
|
|
296
336
|
try {
|
|
297
337
|
await telegram.forwardMessage(fromChatId, toChatId, messageIds);
|
|
298
|
-
return {
|
|
299
|
-
content: [
|
|
300
|
-
{ type: "text", text: `Forwarded ${messageIds.length} message(s) from ${fromChatId} to ${toChatId}` },
|
|
301
|
-
],
|
|
302
|
-
};
|
|
338
|
+
return ok(`Forwarded ${messageIds.length} message(s) from ${fromChatId} to ${toChatId}`);
|
|
303
339
|
}
|
|
304
340
|
catch (e) {
|
|
305
|
-
return
|
|
341
|
+
return fail(e);
|
|
306
342
|
}
|
|
307
343
|
});
|
|
308
|
-
server.
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
344
|
+
server.registerTool("telegram-edit-message", {
|
|
345
|
+
description: "Edit a previously sent message in Telegram",
|
|
346
|
+
inputSchema: {
|
|
347
|
+
chatId: z.string().describe("Chat ID or username"),
|
|
348
|
+
messageId: z.number().describe("ID of the message to edit"),
|
|
349
|
+
text: z.string().describe("New message text"),
|
|
350
|
+
},
|
|
351
|
+
annotations: WRITE,
|
|
312
352
|
}, async ({ chatId, messageId, text }) => {
|
|
313
353
|
const err = await requireConnection();
|
|
314
354
|
if (err)
|
|
315
|
-
return
|
|
355
|
+
return fail(new Error(err));
|
|
316
356
|
try {
|
|
317
357
|
await telegram.editMessage(chatId, messageId, text);
|
|
318
|
-
return
|
|
358
|
+
return ok(`Message ${messageId} edited in ${chatId}`);
|
|
319
359
|
}
|
|
320
360
|
catch (e) {
|
|
321
|
-
return
|
|
361
|
+
return fail(e);
|
|
322
362
|
}
|
|
323
363
|
});
|
|
324
|
-
server.
|
|
325
|
-
|
|
326
|
-
|
|
364
|
+
server.registerTool("telegram-delete-message", {
|
|
365
|
+
description: "Delete messages in a Telegram chat",
|
|
366
|
+
inputSchema: {
|
|
367
|
+
chatId: z.string().describe("Chat ID or username"),
|
|
368
|
+
messageIds: z.array(z.number()).describe("Array of message IDs to delete"),
|
|
369
|
+
},
|
|
370
|
+
annotations: DESTRUCTIVE,
|
|
327
371
|
}, async ({ chatId, messageIds }) => {
|
|
328
372
|
const err = await requireConnection();
|
|
329
373
|
if (err)
|
|
330
|
-
return
|
|
374
|
+
return fail(new Error(err));
|
|
331
375
|
try {
|
|
332
376
|
await telegram.deleteMessages(chatId, messageIds);
|
|
333
|
-
return
|
|
377
|
+
return ok(`Deleted ${messageIds.length} message(s) in ${chatId}`);
|
|
334
378
|
}
|
|
335
379
|
catch (e) {
|
|
336
|
-
return
|
|
380
|
+
return fail(e);
|
|
337
381
|
}
|
|
338
382
|
});
|
|
339
|
-
server.
|
|
340
|
-
|
|
383
|
+
server.registerTool("telegram-get-chat-info", {
|
|
384
|
+
description: "Get detailed info about a Telegram chat including name, type, members, description, and forum status",
|
|
385
|
+
inputSchema: { chatId: z.string().describe("Chat ID or username") },
|
|
386
|
+
annotations: READ_ONLY,
|
|
341
387
|
}, async ({ chatId }) => {
|
|
342
388
|
const err = await requireConnection();
|
|
343
389
|
if (err)
|
|
344
|
-
return
|
|
390
|
+
return fail(new Error(err));
|
|
345
391
|
try {
|
|
346
392
|
const info = await telegram.getChatInfo(chatId);
|
|
347
393
|
const lines = [
|
|
@@ -353,114 +399,138 @@ server.tool("telegram-get-chat-info", "Get detailed info about a Telegram chat",
|
|
|
353
399
|
...(info.description ? [`Description: ${info.description}`] : []),
|
|
354
400
|
...(info.membersCount != null ? [`Members: ${info.membersCount}`] : []),
|
|
355
401
|
];
|
|
356
|
-
return
|
|
402
|
+
return ok(lines.join("\n"));
|
|
357
403
|
}
|
|
358
404
|
catch (e) {
|
|
359
|
-
return
|
|
405
|
+
return fail(e);
|
|
360
406
|
}
|
|
361
407
|
});
|
|
362
|
-
server.
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
408
|
+
server.registerTool("telegram-send-file", {
|
|
409
|
+
description: "Send a file (photo, document, video, etc.) to a Telegram chat",
|
|
410
|
+
inputSchema: {
|
|
411
|
+
chatId: z.string().describe("Chat ID or username"),
|
|
412
|
+
filePath: z.string().describe("Absolute path to file"),
|
|
413
|
+
caption: z.string().optional().describe("File caption"),
|
|
414
|
+
},
|
|
415
|
+
annotations: WRITE,
|
|
366
416
|
}, async ({ chatId, filePath, caption }) => {
|
|
367
417
|
const err = await requireConnection();
|
|
368
418
|
if (err)
|
|
369
|
-
return
|
|
419
|
+
return fail(new Error(err));
|
|
370
420
|
try {
|
|
371
421
|
await telegram.sendFile(chatId, filePath, caption);
|
|
372
|
-
return
|
|
422
|
+
return ok(`File sent to ${chatId}`);
|
|
373
423
|
}
|
|
374
424
|
catch (e) {
|
|
375
|
-
return
|
|
425
|
+
return fail(e);
|
|
376
426
|
}
|
|
377
427
|
});
|
|
378
|
-
server.
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
428
|
+
server.registerTool("telegram-download-media", {
|
|
429
|
+
description: "Download media from a Telegram message to a local file",
|
|
430
|
+
inputSchema: {
|
|
431
|
+
chatId: z.string().describe("Chat ID or username"),
|
|
432
|
+
messageId: z.number().describe("Message ID containing media"),
|
|
433
|
+
downloadPath: z.string().describe("Absolute path to save file"),
|
|
434
|
+
},
|
|
435
|
+
annotations: READ_ONLY,
|
|
382
436
|
}, async ({ chatId, messageId, downloadPath }) => {
|
|
383
437
|
const err = await requireConnection();
|
|
384
438
|
if (err)
|
|
385
|
-
return
|
|
439
|
+
return fail(new Error(err));
|
|
386
440
|
try {
|
|
387
441
|
const path = await telegram.downloadMedia(chatId, messageId, downloadPath);
|
|
388
|
-
return
|
|
442
|
+
return ok(`Media downloaded to ${path}`);
|
|
389
443
|
}
|
|
390
444
|
catch (e) {
|
|
391
|
-
return
|
|
445
|
+
return fail(e);
|
|
392
446
|
}
|
|
393
447
|
});
|
|
394
|
-
server.
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
448
|
+
server.registerTool("telegram-pin-message", {
|
|
449
|
+
description: "Pin a message in a Telegram chat",
|
|
450
|
+
inputSchema: {
|
|
451
|
+
chatId: z.string().describe("Chat ID or username"),
|
|
452
|
+
messageId: z.number().describe("Message ID to pin"),
|
|
453
|
+
silent: z.boolean().default(false).describe("Pin without notification"),
|
|
454
|
+
},
|
|
455
|
+
annotations: WRITE,
|
|
398
456
|
}, async ({ chatId, messageId, silent }) => {
|
|
399
457
|
const err = await requireConnection();
|
|
400
458
|
if (err)
|
|
401
|
-
return
|
|
459
|
+
return fail(new Error(err));
|
|
402
460
|
try {
|
|
403
461
|
await telegram.pinMessage(chatId, messageId, silent);
|
|
404
|
-
return
|
|
462
|
+
return ok(`Message ${messageId} pinned in ${chatId}`);
|
|
405
463
|
}
|
|
406
464
|
catch (e) {
|
|
407
|
-
return
|
|
465
|
+
return fail(e);
|
|
408
466
|
}
|
|
409
467
|
});
|
|
410
|
-
server.
|
|
411
|
-
|
|
412
|
-
|
|
468
|
+
server.registerTool("telegram-unpin-message", {
|
|
469
|
+
description: "Unpin a message in a Telegram chat",
|
|
470
|
+
inputSchema: {
|
|
471
|
+
chatId: z.string().describe("Chat ID or username"),
|
|
472
|
+
messageId: z.number().describe("Message ID to unpin"),
|
|
473
|
+
},
|
|
474
|
+
annotations: WRITE,
|
|
413
475
|
}, async ({ chatId, messageId }) => {
|
|
414
476
|
const err = await requireConnection();
|
|
415
477
|
if (err)
|
|
416
|
-
return
|
|
478
|
+
return fail(new Error(err));
|
|
417
479
|
try {
|
|
418
480
|
await telegram.unpinMessage(chatId, messageId);
|
|
419
|
-
return
|
|
481
|
+
return ok(`Message ${messageId} unpinned in ${chatId}`);
|
|
420
482
|
}
|
|
421
483
|
catch (e) {
|
|
422
|
-
return
|
|
484
|
+
return fail(e);
|
|
423
485
|
}
|
|
424
486
|
});
|
|
425
|
-
server.
|
|
426
|
-
|
|
487
|
+
server.registerTool("telegram-get-contacts", {
|
|
488
|
+
description: "Get your Telegram contacts list with phone numbers",
|
|
489
|
+
inputSchema: { limit: z.number().default(50).describe("Number of contacts to return") },
|
|
490
|
+
annotations: READ_ONLY,
|
|
427
491
|
}, async ({ limit }) => {
|
|
428
492
|
const err = await requireConnection();
|
|
429
493
|
if (err)
|
|
430
|
-
return
|
|
494
|
+
return fail(new Error(err));
|
|
431
495
|
try {
|
|
432
496
|
const contacts = await telegram.getContacts(limit);
|
|
433
497
|
const text = contacts
|
|
434
498
|
.map((c) => `P ${c.name}${c.username ? ` (@${c.username})` : ""} (${c.id})${c.phone ? ` +${c.phone}` : ""}`)
|
|
435
499
|
.join("\n");
|
|
436
|
-
return
|
|
500
|
+
return ok(sanitize(text) || "No contacts");
|
|
437
501
|
}
|
|
438
502
|
catch (e) {
|
|
439
|
-
return
|
|
503
|
+
return fail(e);
|
|
440
504
|
}
|
|
441
505
|
});
|
|
442
|
-
server.
|
|
443
|
-
|
|
444
|
-
|
|
506
|
+
server.registerTool("telegram-get-chat-members", {
|
|
507
|
+
description: "Get members of a Telegram group or channel",
|
|
508
|
+
inputSchema: {
|
|
509
|
+
chatId: z.string().describe("Chat ID or username"),
|
|
510
|
+
limit: z.number().default(50).describe("Number of members to return"),
|
|
511
|
+
},
|
|
512
|
+
annotations: READ_ONLY,
|
|
445
513
|
}, async ({ chatId, limit }) => {
|
|
446
514
|
const err = await requireConnection();
|
|
447
515
|
if (err)
|
|
448
|
-
return
|
|
516
|
+
return fail(new Error(err));
|
|
449
517
|
try {
|
|
450
518
|
const members = await telegram.getChatMembers(chatId, limit);
|
|
451
519
|
const text = members.map((m) => `${m.name}${m.username ? ` (@${m.username})` : ""} (${m.id})`).join("\n");
|
|
452
|
-
return
|
|
520
|
+
return ok(sanitize(text) || "No members found");
|
|
453
521
|
}
|
|
454
522
|
catch (e) {
|
|
455
|
-
return
|
|
523
|
+
return fail(e);
|
|
456
524
|
}
|
|
457
525
|
});
|
|
458
|
-
server.
|
|
459
|
-
|
|
526
|
+
server.registerTool("telegram-get-profile", {
|
|
527
|
+
description: "Get detailed profile info of a Telegram user including bio, birthday, premium status, business info and more",
|
|
528
|
+
inputSchema: { userId: z.string().describe("User ID or username") },
|
|
529
|
+
annotations: READ_ONLY,
|
|
460
530
|
}, async ({ userId }) => {
|
|
461
531
|
const err = await requireConnection();
|
|
462
532
|
if (err)
|
|
463
|
-
return
|
|
533
|
+
return fail(new Error(err));
|
|
464
534
|
try {
|
|
465
535
|
const profile = await telegram.getProfile(userId);
|
|
466
536
|
const lines = [
|
|
@@ -478,83 +548,87 @@ server.tool("telegram-get-profile", "Get detailed profile info of a Telegram use
|
|
|
478
548
|
...(profile.businessLocation ? [`Business location: ${profile.businessLocation}`] : []),
|
|
479
549
|
...(profile.businessWorkHours ? [`Business hours timezone: ${profile.businessWorkHours}`] : []),
|
|
480
550
|
];
|
|
481
|
-
return
|
|
551
|
+
return ok(lines.join("\n"));
|
|
482
552
|
}
|
|
483
553
|
catch (e) {
|
|
484
|
-
return
|
|
485
|
-
}
|
|
486
|
-
});
|
|
487
|
-
server.
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
.
|
|
492
|
-
|
|
493
|
-
|
|
554
|
+
return fail(e);
|
|
555
|
+
}
|
|
556
|
+
});
|
|
557
|
+
server.registerTool("telegram-get-profile-photo", {
|
|
558
|
+
description: "Download profile photo of a Telegram user, group, or channel. Returns inline image or saves to file",
|
|
559
|
+
inputSchema: {
|
|
560
|
+
entityId: z.string().describe("User/Chat/Channel ID or username"),
|
|
561
|
+
savePath: z.string().optional().describe("Absolute path to save file. If omitted, returns inline base64 image"),
|
|
562
|
+
size: z
|
|
563
|
+
.enum(["small", "big"])
|
|
564
|
+
.optional()
|
|
565
|
+
.describe("Photo size: 'small' (160x160) or 'big' (640x640). Default: big"),
|
|
566
|
+
},
|
|
567
|
+
annotations: READ_ONLY,
|
|
494
568
|
}, async ({ entityId, savePath, size }) => {
|
|
495
569
|
const err = await requireConnection();
|
|
496
570
|
if (err)
|
|
497
|
-
return
|
|
571
|
+
return fail(new Error(err));
|
|
498
572
|
try {
|
|
499
573
|
const result = await telegram.downloadProfilePhoto(entityId, {
|
|
500
574
|
isBig: size !== "small",
|
|
501
575
|
savePath,
|
|
502
576
|
});
|
|
503
577
|
if (!result) {
|
|
504
|
-
return
|
|
578
|
+
return ok("No profile photo found");
|
|
505
579
|
}
|
|
506
580
|
if ("filePath" in result) {
|
|
507
|
-
return
|
|
581
|
+
return ok(`Downloaded to: ${result.filePath}`);
|
|
508
582
|
}
|
|
509
583
|
return {
|
|
510
584
|
content: [
|
|
511
585
|
{ type: "image", data: result.buffer.toString("base64"), mimeType: result.mimeType },
|
|
512
|
-
{
|
|
586
|
+
{
|
|
587
|
+
type: "text",
|
|
588
|
+
text: `Profile photo (${(result.buffer.length / 1024).toFixed(0)} KB, ${result.mimeType})`,
|
|
589
|
+
},
|
|
513
590
|
],
|
|
514
591
|
};
|
|
515
592
|
}
|
|
516
593
|
catch (e) {
|
|
517
|
-
return
|
|
594
|
+
return fail(e);
|
|
518
595
|
}
|
|
519
596
|
});
|
|
520
|
-
server.
|
|
521
|
-
|
|
597
|
+
server.registerTool("telegram-join-chat", {
|
|
598
|
+
description: "Join a Telegram group or channel by username or invite link",
|
|
599
|
+
inputSchema: { target: z.string().describe("Username (@group), link (t.me/group), or invite link (t.me/+xxx)") },
|
|
600
|
+
annotations: WRITE,
|
|
522
601
|
}, async ({ target }) => {
|
|
523
602
|
const err = await requireConnection();
|
|
524
603
|
if (err)
|
|
525
|
-
return
|
|
604
|
+
return fail(new Error(err));
|
|
526
605
|
try {
|
|
527
606
|
const result = await telegram.joinChat(target);
|
|
528
|
-
return {
|
|
529
|
-
content: [
|
|
530
|
-
{
|
|
531
|
-
type: "text",
|
|
532
|
-
text: `Joined ${result.type}: ${result.title} (ID: ${result.id})`,
|
|
533
|
-
},
|
|
534
|
-
],
|
|
535
|
-
};
|
|
607
|
+
return ok(`Joined ${result.type}: ${result.title} (ID: ${result.id})`);
|
|
536
608
|
}
|
|
537
609
|
catch (e) {
|
|
538
|
-
return
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
610
|
+
return fail(e);
|
|
611
|
+
}
|
|
612
|
+
});
|
|
613
|
+
server.registerTool("telegram-send-reaction", {
|
|
614
|
+
description: "Send emoji reaction(s) to a message. Supports multiple reactions and adding to existing ones. Omit emoji to remove all reactions",
|
|
615
|
+
inputSchema: {
|
|
616
|
+
chatId: z.string().describe("Chat ID or username"),
|
|
617
|
+
messageId: z.number().describe("Message ID to react to"),
|
|
618
|
+
emoji: z
|
|
619
|
+
.union([z.string(), z.array(z.string())])
|
|
620
|
+
.optional()
|
|
621
|
+
.describe("Reaction emoji(s): single '👍' or array ['👍','🔥']. Omit to remove all reactions"),
|
|
622
|
+
addToExisting: z
|
|
623
|
+
.boolean()
|
|
624
|
+
.default(false)
|
|
625
|
+
.describe("If true, add reaction(s) to existing ones instead of replacing"),
|
|
626
|
+
},
|
|
627
|
+
annotations: WRITE,
|
|
554
628
|
}, async ({ chatId, messageId, emoji, addToExisting }) => {
|
|
555
629
|
const err = await requireConnection();
|
|
556
630
|
if (err)
|
|
557
|
-
return
|
|
631
|
+
return fail(new Error(err));
|
|
558
632
|
try {
|
|
559
633
|
const updated = await telegram.sendReaction(chatId, messageId, emoji, addToExisting);
|
|
560
634
|
const emojiStr = Array.isArray(emoji) ? emoji.join("") : emoji;
|
|
@@ -562,45 +636,53 @@ server.tool("telegram-send-reaction", "Send emoji reaction(s) to a message. Supp
|
|
|
562
636
|
const reactionsInfo = updated
|
|
563
637
|
? ` | Reactions: ${updated.map((r) => `${r.emoji}×${r.count}${r.me ? "(me)" : ""}`).join(" ")}`
|
|
564
638
|
: "";
|
|
565
|
-
return
|
|
639
|
+
return ok(`${action} message ${messageId} in ${chatId}${reactionsInfo}`);
|
|
566
640
|
}
|
|
567
641
|
catch (e) {
|
|
568
|
-
return
|
|
642
|
+
return fail(e);
|
|
569
643
|
}
|
|
570
644
|
});
|
|
571
|
-
server.
|
|
572
|
-
|
|
573
|
-
|
|
645
|
+
server.registerTool("telegram-get-reactions", {
|
|
646
|
+
description: "Get detailed reaction info for a message: which reactions, counts, and who reacted (when visible)",
|
|
647
|
+
inputSchema: {
|
|
648
|
+
chatId: z.string().describe("Chat ID or username"),
|
|
649
|
+
messageId: z.number().describe("Message ID to get reactions for"),
|
|
650
|
+
},
|
|
651
|
+
annotations: READ_ONLY,
|
|
574
652
|
}, async ({ chatId, messageId }) => {
|
|
575
653
|
const err = await requireConnection();
|
|
576
654
|
if (err)
|
|
577
|
-
return
|
|
655
|
+
return fail(new Error(err));
|
|
578
656
|
try {
|
|
579
657
|
const result = await telegram.getMessageReactions(chatId, messageId);
|
|
580
658
|
if (result.reactions.length === 0) {
|
|
581
|
-
return
|
|
659
|
+
return ok(`No reactions on message ${messageId}`);
|
|
582
660
|
}
|
|
583
661
|
const lines = result.reactions.map((r) => {
|
|
584
662
|
const usersStr = r.users.length > 0 ? `: ${r.users.map((u) => u.name).join(", ")}` : "";
|
|
585
663
|
return `${r.emoji} × ${r.count}${usersStr}`;
|
|
586
664
|
});
|
|
587
665
|
lines.push(`\nTotal: ${result.total} reactions`);
|
|
588
|
-
return
|
|
666
|
+
return ok(lines.join("\n"));
|
|
589
667
|
}
|
|
590
668
|
catch (e) {
|
|
591
|
-
return
|
|
592
|
-
}
|
|
593
|
-
});
|
|
594
|
-
server.
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
669
|
+
return fail(e);
|
|
670
|
+
}
|
|
671
|
+
});
|
|
672
|
+
server.registerTool("telegram-send-scheduled", {
|
|
673
|
+
description: "Send a scheduled message to a Telegram chat. The message will be delivered at the specified time by Telegram servers",
|
|
674
|
+
inputSchema: {
|
|
675
|
+
chatId: z.string().describe("Chat ID or username (use 'me' or 'self' for Saved Messages)"),
|
|
676
|
+
text: z.string().describe("Message text"),
|
|
677
|
+
scheduleDate: z.number().describe("Unix timestamp when to send the message (must be in the future)"),
|
|
678
|
+
replyTo: z.number().optional().describe("Message ID to reply to"),
|
|
679
|
+
parseMode: z.enum(["md", "html"]).optional().describe("Message format: md (Markdown) or html"),
|
|
680
|
+
},
|
|
681
|
+
annotations: WRITE,
|
|
600
682
|
}, async ({ chatId, text, scheduleDate, replyTo, parseMode }) => {
|
|
601
683
|
const err = await requireConnection();
|
|
602
684
|
if (err)
|
|
603
|
-
return
|
|
685
|
+
return fail(new Error(err));
|
|
604
686
|
// Resolve 'me'/'self' to Saved Messages
|
|
605
687
|
let target = chatId;
|
|
606
688
|
if (target === "me" || target === "self") {
|
|
@@ -609,47 +691,53 @@ server.tool("telegram-send-scheduled", "Send a scheduled message to a Telegram c
|
|
|
609
691
|
target = me.id;
|
|
610
692
|
}
|
|
611
693
|
catch {
|
|
612
|
-
return
|
|
694
|
+
return fail(new Error("Failed to resolve Saved Messages"));
|
|
613
695
|
}
|
|
614
696
|
}
|
|
615
697
|
try {
|
|
616
698
|
await telegram.sendScheduledMessage(target, text, scheduleDate, replyTo, parseMode);
|
|
617
699
|
const date = new Date(scheduleDate * 1000).toISOString();
|
|
618
|
-
return
|
|
700
|
+
return ok(`Message scheduled for ${date} in ${chatId}`);
|
|
619
701
|
}
|
|
620
702
|
catch (e) {
|
|
621
|
-
return
|
|
622
|
-
}
|
|
623
|
-
});
|
|
624
|
-
server.
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
703
|
+
return fail(e);
|
|
704
|
+
}
|
|
705
|
+
});
|
|
706
|
+
server.registerTool("telegram-create-poll", {
|
|
707
|
+
description: "Create a poll in a Telegram chat (multiple choice or quiz mode)",
|
|
708
|
+
inputSchema: {
|
|
709
|
+
chatId: z.string().describe("Chat ID or username"),
|
|
710
|
+
question: z.string().describe("Poll question"),
|
|
711
|
+
answers: z.array(z.string()).min(2).max(10).describe("Answer options (2-10)"),
|
|
712
|
+
multipleChoice: z.boolean().default(false).describe("Allow multiple answers"),
|
|
713
|
+
quiz: z.boolean().default(false).describe("Quiz mode (one correct answer)"),
|
|
714
|
+
correctAnswer: z.number().optional().describe("Index of correct answer (0-based, required for quiz mode)"),
|
|
715
|
+
},
|
|
716
|
+
annotations: WRITE,
|
|
631
717
|
}, async ({ chatId, question, answers, multipleChoice, quiz, correctAnswer }) => {
|
|
632
718
|
const err = await requireConnection();
|
|
633
719
|
if (err)
|
|
634
|
-
return
|
|
720
|
+
return fail(new Error(err));
|
|
635
721
|
try {
|
|
636
722
|
const msgId = await telegram.createPoll(chatId, question, answers, { multipleChoice, quiz, correctAnswer });
|
|
637
|
-
return
|
|
723
|
+
return ok(`Poll created in ${chatId}${msgId ? ` (message #${msgId})` : ""}`);
|
|
638
724
|
}
|
|
639
725
|
catch (e) {
|
|
640
|
-
return
|
|
726
|
+
return fail(e);
|
|
641
727
|
}
|
|
642
728
|
});
|
|
643
|
-
server.
|
|
644
|
-
|
|
729
|
+
server.registerTool("telegram-get-contact-requests", {
|
|
730
|
+
description: "Get incoming messages from non-contacts (contact requests). Shows who messaged you without being in your contacts, with message preview",
|
|
731
|
+
inputSchema: { limit: z.number().default(20).describe("Number of contact requests to return") },
|
|
732
|
+
annotations: READ_ONLY,
|
|
645
733
|
}, async ({ limit }) => {
|
|
646
734
|
const err = await requireConnection();
|
|
647
735
|
if (err)
|
|
648
|
-
return
|
|
736
|
+
return fail(new Error(err));
|
|
649
737
|
try {
|
|
650
738
|
const requests = await telegram.getContactRequests(limit);
|
|
651
739
|
if (requests.length === 0) {
|
|
652
|
-
return
|
|
740
|
+
return ok("No contact requests");
|
|
653
741
|
}
|
|
654
742
|
const text = requests
|
|
655
743
|
.map((r) => {
|
|
@@ -660,66 +748,76 @@ server.tool("telegram-get-contact-requests", "Get incoming messages from non-con
|
|
|
660
748
|
return `${tag} ${r.name}${username} (${r.id})${unread}${preview}`;
|
|
661
749
|
})
|
|
662
750
|
.join("\n");
|
|
663
|
-
return
|
|
751
|
+
return ok(sanitize(text));
|
|
664
752
|
}
|
|
665
753
|
catch (e) {
|
|
666
|
-
return
|
|
667
|
-
}
|
|
668
|
-
});
|
|
669
|
-
server.
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
754
|
+
return fail(e);
|
|
755
|
+
}
|
|
756
|
+
});
|
|
757
|
+
server.registerTool("telegram-add-contact", {
|
|
758
|
+
description: "Add a user to your Telegram contacts. Use this to accept contact requests from non-contacts",
|
|
759
|
+
inputSchema: {
|
|
760
|
+
userId: z.string().describe("User ID or username to add"),
|
|
761
|
+
firstName: z.string().describe("First name for the contact"),
|
|
762
|
+
lastName: z.string().optional().describe("Last name for the contact"),
|
|
763
|
+
phone: z.string().optional().describe("Phone number for the contact"),
|
|
764
|
+
},
|
|
765
|
+
annotations: WRITE,
|
|
674
766
|
}, async ({ userId, firstName, lastName, phone }) => {
|
|
675
767
|
const err = await requireConnection();
|
|
676
768
|
if (err)
|
|
677
|
-
return
|
|
769
|
+
return fail(new Error(err));
|
|
678
770
|
try {
|
|
679
771
|
await telegram.addContact(userId, firstName, lastName, phone);
|
|
680
|
-
return {
|
|
681
|
-
content: [{ type: "text", text: `Contact added: ${firstName}${lastName ? ` ${lastName}` : ""} (${userId})` }],
|
|
682
|
-
};
|
|
772
|
+
return ok(`Contact added: ${firstName}${lastName ? ` ${lastName}` : ""} (${userId})`);
|
|
683
773
|
}
|
|
684
774
|
catch (e) {
|
|
685
|
-
return
|
|
775
|
+
return fail(e);
|
|
686
776
|
}
|
|
687
777
|
});
|
|
688
|
-
server.
|
|
689
|
-
|
|
778
|
+
server.registerTool("telegram-block-user", {
|
|
779
|
+
description: "Block a Telegram user. Blocked users cannot send you messages",
|
|
780
|
+
inputSchema: { userId: z.string().describe("User ID or username to block") },
|
|
781
|
+
annotations: WRITE,
|
|
690
782
|
}, async ({ userId }) => {
|
|
691
783
|
const err = await requireConnection();
|
|
692
784
|
if (err)
|
|
693
|
-
return
|
|
785
|
+
return fail(new Error(err));
|
|
694
786
|
try {
|
|
695
787
|
await telegram.blockUser(userId);
|
|
696
|
-
return
|
|
788
|
+
return ok(`User blocked: ${userId}`);
|
|
697
789
|
}
|
|
698
790
|
catch (e) {
|
|
699
|
-
return
|
|
791
|
+
return fail(e);
|
|
700
792
|
}
|
|
701
793
|
});
|
|
702
|
-
server.
|
|
703
|
-
|
|
794
|
+
server.registerTool("telegram-report-spam", {
|
|
795
|
+
description: "Report a chat as spam to Telegram",
|
|
796
|
+
inputSchema: { chatId: z.string().describe("Chat ID or username to report") },
|
|
797
|
+
annotations: WRITE,
|
|
704
798
|
}, async ({ chatId }) => {
|
|
705
799
|
const err = await requireConnection();
|
|
706
800
|
if (err)
|
|
707
|
-
return
|
|
801
|
+
return fail(new Error(err));
|
|
708
802
|
try {
|
|
709
803
|
await telegram.reportSpam(chatId);
|
|
710
|
-
return
|
|
804
|
+
return ok(`Reported as spam: ${chatId}`);
|
|
711
805
|
}
|
|
712
806
|
catch (e) {
|
|
713
|
-
return
|
|
807
|
+
return fail(e);
|
|
714
808
|
}
|
|
715
809
|
});
|
|
716
|
-
server.
|
|
717
|
-
|
|
718
|
-
|
|
810
|
+
server.registerTool("telegram-list-topics", {
|
|
811
|
+
description: "List forum topics in a Telegram group with Topics enabled. Shows topic names, unread counts, and status",
|
|
812
|
+
inputSchema: {
|
|
813
|
+
chatId: z.string().describe("Chat ID or username of a group with Topics enabled"),
|
|
814
|
+
limit: z.number().default(100).describe("Max topics to return"),
|
|
815
|
+
},
|
|
816
|
+
annotations: READ_ONLY,
|
|
719
817
|
}, async ({ chatId, limit }) => {
|
|
720
818
|
const err = await requireConnection();
|
|
721
819
|
if (err)
|
|
722
|
-
return
|
|
820
|
+
return fail(new Error(err));
|
|
723
821
|
try {
|
|
724
822
|
const topics = await telegram.getForumTopics(chatId, limit);
|
|
725
823
|
const text = topics
|
|
@@ -730,30 +828,34 @@ server.tool("telegram-list-topics", "List forum topics in a Telegram group with
|
|
|
730
828
|
return `# ${t.title} (id: ${t.id})${flagStr}${unread}`;
|
|
731
829
|
})
|
|
732
830
|
.join("\n");
|
|
733
|
-
return
|
|
831
|
+
return ok(sanitize(text) || "No topics found");
|
|
734
832
|
}
|
|
735
833
|
catch (e) {
|
|
736
|
-
return
|
|
737
|
-
}
|
|
738
|
-
});
|
|
739
|
-
server.
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
834
|
+
return fail(e);
|
|
835
|
+
}
|
|
836
|
+
});
|
|
837
|
+
server.registerTool("telegram-read-topic-messages", {
|
|
838
|
+
description: "Read messages from a specific forum topic in a Telegram group",
|
|
839
|
+
inputSchema: {
|
|
840
|
+
chatId: z.string().describe("Chat ID or username"),
|
|
841
|
+
topicId: z.number().describe("Topic ID (get from telegram-list-topics)"),
|
|
842
|
+
limit: z.number().default(20).describe("Number of messages to return"),
|
|
843
|
+
offsetId: z.number().optional().describe("Message ID to start from (for pagination)"),
|
|
844
|
+
},
|
|
845
|
+
annotations: READ_ONLY,
|
|
744
846
|
}, async ({ chatId, topicId, limit, offsetId }) => {
|
|
745
847
|
const err = await requireConnection();
|
|
746
848
|
if (err)
|
|
747
|
-
return
|
|
849
|
+
return fail(new Error(err));
|
|
748
850
|
try {
|
|
749
851
|
const messages = await telegram.getTopicMessages(chatId, topicId, limit, offsetId);
|
|
750
852
|
const text = messages
|
|
751
853
|
.map((m) => `[#${m.id}] [${m.date}] ${m.sender}: ${m.text}${m.media ? ` [${m.media.type}${m.media.fileName ? `: ${m.media.fileName}` : ""}]` : ""}${formatReactions(m.reactions)}`)
|
|
752
854
|
.join("\n\n");
|
|
753
|
-
return
|
|
855
|
+
return ok(sanitize(text) || "No messages in this topic");
|
|
754
856
|
}
|
|
755
857
|
catch (e) {
|
|
756
|
-
return
|
|
858
|
+
return fail(e);
|
|
757
859
|
}
|
|
758
860
|
});
|
|
759
861
|
// --- Start ---
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@overpod/mcp-telegram",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.12.0",
|
|
4
4
|
"description": "MCP server for Telegram userbot — messages, media, reactions, polls & more. Built on GramJS/MTProto.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"url": "https://github.com/overpod/mcp-telegram/issues"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
52
|
+
"@modelcontextprotocol/sdk": "^1.28.0",
|
|
53
53
|
"dotenv": "^17.3.1",
|
|
54
54
|
"qrcode": "^1.5.4",
|
|
55
55
|
"telegram": "^2.26.22",
|