@overpod/mcp-telegram 1.25.0 → 1.26.1

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 (40) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +35 -5
  3. package/dist/index.js +4 -1
  4. package/dist/rate-limiter.d.ts +9 -3
  5. package/dist/rate-limiter.js +23 -16
  6. package/dist/telegram-client.d.ts +134 -18
  7. package/dist/telegram-client.js +601 -136
  8. package/dist/telegram-helpers.d.ts +470 -0
  9. package/dist/telegram-helpers.js +870 -0
  10. package/dist/tools/account.js +22 -6
  11. package/dist/tools/boosts.d.ts +3 -0
  12. package/dist/tools/boosts.js +65 -0
  13. package/dist/tools/chats.js +155 -5
  14. package/dist/tools/contacts.js +3 -3
  15. package/dist/tools/extras.js +3 -3
  16. package/dist/tools/group-calls.d.ts +4 -0
  17. package/dist/tools/group-calls.js +77 -0
  18. package/dist/tools/index.js +10 -0
  19. package/dist/tools/messages.js +203 -11
  20. package/dist/tools/quick-replies.d.ts +4 -0
  21. package/dist/tools/quick-replies.js +58 -0
  22. package/dist/tools/reactions.js +45 -2
  23. package/dist/tools/shared.d.ts +3 -3
  24. package/dist/tools/shared.js +8 -7
  25. package/dist/tools/stars.d.ts +4 -0
  26. package/dist/tools/stars.js +71 -0
  27. package/dist/tools/stickers.js +5 -5
  28. package/dist/tools/stories.d.ts +3 -0
  29. package/dist/tools/stories.js +107 -0
  30. package/package.json +1 -1
  31. package/dist/__tests__/admin-log.test.d.ts +0 -1
  32. package/dist/__tests__/admin-log.test.js +0 -41
  33. package/dist/__tests__/rate-limiter.test.d.ts +0 -1
  34. package/dist/__tests__/rate-limiter.test.js +0 -81
  35. package/dist/__tests__/reactions.test.d.ts +0 -1
  36. package/dist/__tests__/reactions.test.js +0 -23
  37. package/dist/__tests__/set-chat-permissions-merge.test.d.ts +0 -1
  38. package/dist/__tests__/set-chat-permissions-merge.test.js +0 -107
  39. package/dist/__tests__/tools/shared.test.d.ts +0 -1
  40. package/dist/__tests__/tools/shared.test.js +0 -110
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import { DESTRUCTIVE, fail, ok, READ_ONLY, requireConnection, sanitize, WRITE } from "./shared.js";
2
+ import { DESTRUCTIVE, fail, ok, READ_ONLY, requireConnection, WRITE } from "./shared.js";
3
3
  const MUTE_FOREVER_UNTIL = 2147483647; // max 32-bit signed int
4
4
  export function registerAccountTools(server, telegram) {
5
5
  server.registerTool("telegram-mute-chat", {
@@ -58,7 +58,7 @@ export function registerAccountTools(server, telegram) {
58
58
  const text = folders
59
59
  .map((f) => `[${f.id}] ${f.emoticon ? `${f.emoticon} ` : ""}${f.title} (${f.includeCount} chats, ${f.pinnedCount} pinned)`)
60
60
  .join("\n");
61
- return ok(sanitize(text));
61
+ return ok(text);
62
62
  }
63
63
  catch (e) {
64
64
  return fail(e);
@@ -103,7 +103,7 @@ export function registerAccountTools(server, telegram) {
103
103
  const text = sessions
104
104
  .map((s) => `${s.current ? "→ " : " "}${s.device} (${s.platform}) — ${s.appName} ${s.appVersion}\n IP: ${s.ip} (${s.country}) | Last active: ${s.dateActive}${s.current ? " [CURRENT]" : ""}\n Hash: ${s.hash}`)
105
105
  .join("\n\n");
106
- return ok(sanitize(text));
106
+ return ok(text);
107
107
  }
108
108
  catch (e) {
109
109
  return fail(e);
@@ -252,7 +252,7 @@ export function registerAccountTools(server, telegram) {
252
252
  const text = links
253
253
  .map((l) => `${l.link}${l.title ? ` (${l.title})` : ""} — ${l.usageCount} uses${l.expired ? " [EXPIRED]" : ""}${l.revoked ? " [REVOKED]" : ""}`)
254
254
  .join("\n");
255
- return ok(sanitize(text));
255
+ return ok(text);
256
256
  }
257
257
  catch (e) {
258
258
  return fail(e);
@@ -291,7 +291,7 @@ export function registerAccountTools(server, telegram) {
291
291
  if (drafts.length === 0)
292
292
  return ok("No drafts");
293
293
  const text = drafts.map((d) => `[${d.chatId}] ${d.chatTitle} (${d.date})\n ${d.text}`).join("\n\n");
294
- return ok(sanitize(text));
294
+ return ok(text);
295
295
  }
296
296
  catch (e) {
297
297
  return fail(e);
@@ -344,7 +344,7 @@ export function registerAccountTools(server, telegram) {
344
344
  if (dialogs.length === 0)
345
345
  return ok("No saved dialogs");
346
346
  const text = dialogs.map((d) => `[${d.peerId}] ${d.peerTitle} — last msg #${d.lastMsgId}`).join("\n");
347
- return ok(sanitize(text));
347
+ return ok(text);
348
348
  }
349
349
  catch (e) {
350
350
  return fail(e);
@@ -369,4 +369,20 @@ export function registerAccountTools(server, telegram) {
369
369
  return fail(e);
370
370
  }
371
371
  });
372
+ server.registerTool("telegram-get-business-chat-links", {
373
+ description: "List Telegram Business chat links configured for the account (account.GetBusinessChatLinks). Each entry includes the t.me/... link, the prefilled message, optional title (admin-facing label), views count, and entityCount (number of formatting entities in the message). Requires a Telegram Business-enabled account — returns an empty list when the account has none configured. Read-only.",
374
+ inputSchema: {},
375
+ annotations: READ_ONLY,
376
+ }, async () => {
377
+ const err = await requireConnection(telegram);
378
+ if (err)
379
+ return fail(new Error(err));
380
+ try {
381
+ const result = await telegram.getBusinessChatLinks();
382
+ return ok(JSON.stringify(result));
383
+ }
384
+ catch (e) {
385
+ return fail(e);
386
+ }
387
+ });
372
388
  }
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { TelegramService } from "../telegram-client.js";
3
+ export declare function registerBoostTools(server: McpServer, telegram: TelegramService): void;
@@ -0,0 +1,65 @@
1
+ import { z } from "zod";
2
+ import { fail, ok, READ_ONLY, requireConnection } from "./shared.js";
3
+ export function registerBoostTools(server, telegram) {
4
+ server.registerTool("telegram-get-my-boosts", {
5
+ description: "List the user's premium boost slots (premium.GetMyBoosts). Each entry includes slot index, the peer it currently boosts (if any), the date the boost was applied, expiration timestamp, and cooldownUntilDate (when a slot can be reassigned). Premium users have multiple slots; non-Premium users typically have a single slot. Read-only.",
6
+ inputSchema: {},
7
+ annotations: READ_ONLY,
8
+ }, async () => {
9
+ const err = await requireConnection(telegram);
10
+ if (err)
11
+ return fail(new Error(err));
12
+ try {
13
+ const result = await telegram.getMyBoosts();
14
+ return ok(JSON.stringify(result));
15
+ }
16
+ catch (e) {
17
+ return fail(e);
18
+ }
19
+ });
20
+ server.registerTool("telegram-get-boosts-status", {
21
+ description: "Fetch the boost status of a channel/supergroup (premium.GetBoostsStatus). Returns current boost level, total boosts, progress to next level (currentLevelBoosts/nextLevelBoosts), giftBoosts, premiumAudience ratio, public boostUrl, and whether the current user is boosting (myBoost + myBoostSlots). Also includes any prepaidGiveaways attached to the chat. Read-only.",
22
+ inputSchema: {
23
+ chat: z.string().describe("Channel or supergroup to query — id, @username, or display name fragment"),
24
+ },
25
+ annotations: READ_ONLY,
26
+ }, async ({ chat }) => {
27
+ const err = await requireConnection(telegram);
28
+ if (err)
29
+ return fail(new Error(err));
30
+ try {
31
+ const result = await telegram.getBoostsStatus(chat);
32
+ return ok(JSON.stringify(result));
33
+ }
34
+ catch (e) {
35
+ return fail(e);
36
+ }
37
+ });
38
+ server.registerTool("telegram-get-boosts-list", {
39
+ description: "List the boosts applied to a channel/supergroup (premium.GetBoostsList). Returns paginated boost entries with id, userId (or undefined for anonymous gift boosts), date, expires, flags (gift, giveaway, unclaimed), optional giveawayMsgId, usedGiftSlug, multiplier, and stars. Requires channel admin permissions. Supports pagination via nextOffset and an optional gifts filter to show only gift boosts. Read-only.",
40
+ inputSchema: {
41
+ chat: z.string().describe("Channel or supergroup to query — id, @username, or display name fragment"),
42
+ gifts: z.boolean().optional().describe("If true, return only gift boosts"),
43
+ offset: z.string().optional().describe("Pagination cursor returned as nextOffset from the previous call"),
44
+ limit: z
45
+ .number()
46
+ .int()
47
+ .min(1)
48
+ .max(100)
49
+ .optional()
50
+ .describe("Max boosts to return per page (default 50, max 100)"),
51
+ },
52
+ annotations: READ_ONLY,
53
+ }, async ({ chat, gifts, offset, limit }) => {
54
+ const err = await requireConnection(telegram);
55
+ if (err)
56
+ return fail(new Error(err));
57
+ try {
58
+ const result = await telegram.getBoostsList(chat, { gifts, offset, limit });
59
+ return ok(JSON.stringify(result));
60
+ }
61
+ catch (e) {
62
+ return fail(e);
63
+ }
64
+ });
65
+ }
@@ -27,7 +27,7 @@ export function registerChatTools(server, telegram) {
27
27
  return `${prefix} ${d.name} (${d.id})${botTag}${contactTag}${unread}`;
28
28
  })
29
29
  .join("\n");
30
- return ok(sanitize(text) || "No chats");
30
+ return ok(text || "No chats");
31
31
  }
32
32
  catch (e) {
33
33
  return fail(e);
@@ -49,7 +49,7 @@ export function registerChatTools(server, telegram) {
49
49
  const text = results
50
50
  .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)}` : ""}`)
51
51
  .join("\n");
52
- return ok(sanitize(text) || "No results");
52
+ return ok(text || "No results");
53
53
  }
54
54
  catch (e) {
55
55
  return fail(e);
@@ -99,7 +99,7 @@ export function registerChatTools(server, telegram) {
99
99
  return `${m.name}${m.username ? ` (@${m.username})` : ""} (${m.id})${role}`;
100
100
  })
101
101
  .join("\n");
102
- return ok(sanitize(text) || "No members found");
102
+ return ok(text || "No members found");
103
103
  }
104
104
  catch (e) {
105
105
  return fail(e);
@@ -386,7 +386,7 @@ export function registerChatTools(server, telegram) {
386
386
  return `[#${e.id}] [${e.date}] ${e.userName}: ${e.action}${details}`;
387
387
  })
388
388
  .join("\n");
389
- return ok(sanitize(text) || "No admin log events");
389
+ return ok(text || "No admin log events");
390
390
  }
391
391
  catch (e) {
392
392
  return fail(e);
@@ -473,6 +473,110 @@ export function registerChatTools(server, telegram) {
473
473
  return fail(e);
474
474
  }
475
475
  });
476
+ server.registerTool("telegram-toggle-channel-signatures", {
477
+ description: "Enable or disable author signatures on broadcast channel posts. Channel admin required; not supported for supergroups",
478
+ inputSchema: {
479
+ chatId: z.string().describe("Channel ID or username"),
480
+ enabled: z.boolean().describe("true to enable author signatures, false to disable"),
481
+ },
482
+ annotations: WRITE,
483
+ }, async ({ chatId, enabled }) => {
484
+ const err = await requireConnection(telegram);
485
+ if (err)
486
+ return fail(new Error(err));
487
+ try {
488
+ await telegram.toggleChannelSignatures(chatId, enabled);
489
+ return ok(JSON.stringify({ ok: true, signaturesEnabled: enabled }));
490
+ }
491
+ catch (e) {
492
+ return fail(e);
493
+ }
494
+ });
495
+ server.registerTool("telegram-toggle-anti-spam", {
496
+ description: "Enable or disable aggressive anti-spam filtering in a supergroup. Supergroup only (not broadcast channels); requires admin with ban_users permission",
497
+ inputSchema: {
498
+ chatId: z.string().describe("Supergroup ID or username"),
499
+ enabled: z.boolean().describe("true to enable aggressive anti-spam, false to disable"),
500
+ },
501
+ annotations: WRITE,
502
+ }, async ({ chatId, enabled }) => {
503
+ const err = await requireConnection(telegram);
504
+ if (err)
505
+ return fail(new Error(err));
506
+ try {
507
+ await telegram.toggleAntiSpam(chatId, enabled);
508
+ return ok(`${enabled ? "Enabled" : "Disabled"} aggressive anti-spam in ${chatId}`);
509
+ }
510
+ catch (e) {
511
+ return fail(e);
512
+ }
513
+ });
514
+ server.registerTool("telegram-toggle-forum-mode", {
515
+ description: "Enable or disable forum/topics mode in a supergroup. Supergroup only; requires creator or admin. " +
516
+ "WARNING: disabling removes ALL existing topics — pass confirm=true to proceed with disable",
517
+ inputSchema: {
518
+ chatId: z.string().describe("Supergroup ID or username"),
519
+ enabled: z.boolean().describe("true to enable forum mode, false to disable"),
520
+ confirm: z
521
+ .boolean()
522
+ .optional()
523
+ .describe("Must be true when disabling (enabled=false) — disabling deletes all existing topics"),
524
+ },
525
+ annotations: DESTRUCTIVE,
526
+ }, async ({ chatId, enabled, confirm }) => {
527
+ const err = await requireConnection(telegram);
528
+ if (err)
529
+ return fail(new Error(err));
530
+ if (!enabled && confirm !== true) {
531
+ return fail(new Error("Disabling forum mode deletes all existing topics. Pass confirm=true to proceed with this destructive action."));
532
+ }
533
+ try {
534
+ await telegram.toggleForumMode(chatId, enabled);
535
+ return ok(`${enabled ? "Enabled" : "Disabled"} forum mode in ${chatId}`);
536
+ }
537
+ catch (e) {
538
+ return fail(e);
539
+ }
540
+ });
541
+ server.registerTool("telegram-toggle-prehistory-hidden", {
542
+ description: "Toggle pre-history visibility for new members in a supergroup. When hidden=true, new joiners cannot see messages posted before they joined. Supergroup only; requires admin",
543
+ inputSchema: {
544
+ chatId: z.string().describe("Supergroup ID or username"),
545
+ hidden: z.boolean().describe("true to hide prior history from new members, false to make it visible"),
546
+ },
547
+ annotations: WRITE,
548
+ }, async ({ chatId, hidden }) => {
549
+ const err = await requireConnection(telegram);
550
+ if (err)
551
+ return fail(new Error(err));
552
+ try {
553
+ await telegram.togglePrehistoryHidden(chatId, hidden);
554
+ return ok(`${hidden ? "Hid" : "Revealed"} prehistory for new members in ${chatId}`);
555
+ }
556
+ catch (e) {
557
+ return fail(e);
558
+ }
559
+ });
560
+ server.registerTool("telegram-approve-join-request", {
561
+ description: "Approve or deny a pending join request for a supergroup or channel (basic groups are not supported). Admin with invite_users permission required",
562
+ inputSchema: {
563
+ chatId: z.string().describe("Chat ID or username where the join request is pending"),
564
+ userId: z.string().describe("User ID or username of the requesting user"),
565
+ approved: z.boolean().describe("true to approve the join request, false to deny"),
566
+ },
567
+ annotations: WRITE,
568
+ }, async ({ chatId, userId, approved }) => {
569
+ const err = await requireConnection(telegram);
570
+ if (err)
571
+ return fail(new Error(err));
572
+ try {
573
+ await telegram.approveChatJoinRequest(chatId, userId, approved);
574
+ return ok(`${approved ? "Approved" : "Denied"} join request from ${userId} in ${chatId}`);
575
+ }
576
+ catch (e) {
577
+ return fail(e);
578
+ }
579
+ });
476
580
  server.registerTool("telegram-create-topic", {
477
581
  description: "Create a new forum topic in a forum-enabled supergroup",
478
582
  inputSchema: {
@@ -502,7 +606,7 @@ export function registerChatTools(server, telegram) {
502
606
  return fail(new Error(err));
503
607
  try {
504
608
  const topic = await telegram.createForumTopic(chatId, title, iconColor, iconEmojiId);
505
- return ok(sanitize(`Created topic "${topic.title}" (id=${topic.id}) in ${chatId}`));
609
+ return ok(`Created topic "${topic.title}" (id=${topic.id}) in ${chatId}`);
506
610
  }
507
611
  catch (e) {
508
612
  return fail(e);
@@ -546,6 +650,52 @@ export function registerChatTools(server, telegram) {
546
650
  return fail(e);
547
651
  }
548
652
  });
653
+ server.registerTool("telegram-get-broadcast-stats", {
654
+ description: "Get broadcast channel statistics: followers, views/shares/reactions per post & story, notification percent, recent post interactions. Broadcast channels only (use telegram-get-megagroup-stats for supergroups). Admin rights required; some channels may require Telegram Premium to expose stats",
655
+ inputSchema: {
656
+ chatId: z.string().describe("Broadcast channel ID or username"),
657
+ includeGraphs: z
658
+ .boolean()
659
+ .default(false)
660
+ .describe("Include raw graph data for each series (growth, followers, interactions, etc.). Default false — returns only aggregate numbers + metadata"),
661
+ dark: z.boolean().default(false).describe("Prefer dark-theme palette when Telegram renders graphs"),
662
+ },
663
+ annotations: READ_ONLY,
664
+ }, async ({ chatId, includeGraphs, dark }) => {
665
+ const err = await requireConnection(telegram);
666
+ if (err)
667
+ return fail(new Error(err));
668
+ try {
669
+ const stats = await telegram.getBroadcastStats(chatId, { dark, includeGraphs });
670
+ return ok(JSON.stringify(stats));
671
+ }
672
+ catch (e) {
673
+ return fail(e);
674
+ }
675
+ });
676
+ server.registerTool("telegram-get-megagroup-stats", {
677
+ description: "Get supergroup statistics: members, messages, viewers, posters (current vs previous period), top posters/admins/inviters. Supergroups only (use telegram-get-broadcast-stats for broadcast channels). Admin rights required. Telegram rate-limits this endpoint to roughly 1 request per 30 minutes per channel — expect FLOOD_WAIT on rapid repeat calls",
678
+ inputSchema: {
679
+ chatId: z.string().describe("Supergroup ID or username"),
680
+ includeGraphs: z
681
+ .boolean()
682
+ .default(false)
683
+ .describe("Include raw graph data for each series (growth, members, messages, actions, top hours, weekdays, etc.). Default false — returns only aggregate numbers + top lists"),
684
+ dark: z.boolean().default(false).describe("Prefer dark-theme palette when Telegram renders graphs"),
685
+ },
686
+ annotations: READ_ONLY,
687
+ }, async ({ chatId, includeGraphs, dark }) => {
688
+ const err = await requireConnection(telegram);
689
+ if (err)
690
+ return fail(new Error(err));
691
+ try {
692
+ const stats = await telegram.getMegagroupStats(chatId, { dark, includeGraphs });
693
+ return ok(JSON.stringify(stats));
694
+ }
695
+ catch (e) {
696
+ return fail(e);
697
+ }
698
+ });
549
699
  server.registerTool("telegram-delete-topic", {
550
700
  description: "Delete a forum topic and all its message history",
551
701
  inputSchema: {
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import { fail, ok, READ_ONLY, requireConnection, sanitize, WRITE } from "./shared.js";
2
+ import { fail, ok, READ_ONLY, requireConnection, WRITE } from "./shared.js";
3
3
  export function registerContactTools(server, telegram) {
4
4
  server.registerTool("telegram-get-contacts", {
5
5
  description: "Get your Telegram contacts list with phone numbers",
@@ -14,7 +14,7 @@ export function registerContactTools(server, telegram) {
14
14
  const text = contacts
15
15
  .map((c) => `P ${c.name}${c.username ? ` (@${c.username})` : ""} (${c.id})${c.phone ? ` +${c.phone}` : ""}`)
16
16
  .join("\n");
17
- return ok(sanitize(text) || "No contacts");
17
+ return ok(text || "No contacts");
18
18
  }
19
19
  catch (e) {
20
20
  return fail(e);
@@ -73,7 +73,7 @@ export function registerContactTools(server, telegram) {
73
73
  return `${tag} ${r.name}${username} (${r.id})${unread}${preview}`;
74
74
  })
75
75
  .join("\n");
76
- return ok(sanitize(text));
76
+ return ok(text);
77
77
  }
78
78
  catch (e) {
79
79
  return fail(e);
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import { fail, formatReactions, ok, READ_ONLY, requireConnection, sanitize, WRITE } from "./shared.js";
2
+ import { fail, formatReactions, ok, READ_ONLY, requireConnection, WRITE } from "./shared.js";
3
3
  export function registerExtraTools(server, telegram) {
4
4
  server.registerTool("telegram-pin-message", {
5
5
  description: "Pin a message in a Telegram chat",
@@ -122,7 +122,7 @@ export function registerExtraTools(server, telegram) {
122
122
  return `# ${t.title} (id: ${t.id})${flagStr}${unread}`;
123
123
  })
124
124
  .join("\n");
125
- return ok(sanitize(text) || "No topics found");
125
+ return ok(text || "No topics found");
126
126
  }
127
127
  catch (e) {
128
128
  return fail(e);
@@ -146,7 +146,7 @@ export function registerExtraTools(server, telegram) {
146
146
  const text = messages
147
147
  .map((m) => `[#${m.id}] [${m.date}] ${m.sender}: ${m.text}${m.media ? ` [${m.media.type}${m.media.fileName ? `: ${m.media.fileName}` : ""}]` : ""}${formatReactions(m.reactions)}`)
148
148
  .join("\n\n");
149
- return ok(sanitize(text) || "No messages in this topic");
149
+ return ok(text || "No messages in this topic");
150
150
  }
151
151
  catch (e) {
152
152
  return fail(e);
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { TelegramService } from "../telegram-client.js";
3
+ export declare function isGroupCallsEnabled(): boolean;
4
+ export declare function registerGroupCallTools(server: McpServer, telegram: TelegramService): void;
@@ -0,0 +1,77 @@
1
+ import { z } from "zod";
2
+ import { fail, ok, READ_ONLY, requireConnection } from "./shared.js";
3
+ export function isGroupCallsEnabled() {
4
+ return process.env.MCP_TELEGRAM_ENABLE_GROUP_CALLS === "1";
5
+ }
6
+ export function registerGroupCallTools(server, telegram) {
7
+ if (!isGroupCallsEnabled())
8
+ return;
9
+ server.registerTool("telegram-get-group-call", {
10
+ description: "Fetch metadata + an optional initial slice of participants for the active group call (voice/video chat) attached to a chat (phone.GetGroupCall). Returns call info (id, accessHash, participantsCount, title, scheduleDate, recordStartDate, streamDcId, flags) plus a participant slice (peer, date, muted/left/self flags, source, volume, video/presentation indicators) and participantsNextOffset. Pass limit:0 (default) for metadata only. Opt-in: register only when MCP_TELEGRAM_ENABLE_GROUP_CALLS=1. Read-only.",
11
+ inputSchema: {
12
+ chat: z
13
+ .string()
14
+ .describe("Group/supergroup/channel that currently has an active group call — id, @username, or display name fragment"),
15
+ limit: z
16
+ .number()
17
+ .int()
18
+ .min(0)
19
+ .max(500)
20
+ .optional()
21
+ .describe("Max participants to include (default 0 — metadata only; use telegram-get-group-call-participants for pagination)"),
22
+ },
23
+ annotations: READ_ONLY,
24
+ }, async ({ chat, limit }) => {
25
+ const err = await requireConnection(telegram);
26
+ if (err)
27
+ return fail(new Error(err));
28
+ try {
29
+ const result = await telegram.getGroupCall(chat, { limit });
30
+ return ok(JSON.stringify(result));
31
+ }
32
+ catch (e) {
33
+ return fail(e);
34
+ }
35
+ });
36
+ server.registerTool("telegram-get-group-call-participants", {
37
+ description: "List participants of the active group call (voice/video chat) attached to a chat with pagination (phone.GetGroupParticipants). Looks up the chat's current InputGroupCall automatically, then returns {count, participants[], nextOffset?, version}. Each participant includes peer, date, source, volume, muted/self/left/videoJoined flags, raise-hand rating, about text, and hasVideo/hasPresentation indicators. Pass offset from a prior call's nextOffset to paginate; pass ids (user/channel peers) or sources to filter to specific participants. Opt-in: register only when MCP_TELEGRAM_ENABLE_GROUP_CALLS=1. Read-only.",
38
+ inputSchema: {
39
+ chat: z
40
+ .string()
41
+ .describe("Group/supergroup/channel that currently has an active group call — id, @username, or display name fragment"),
42
+ ids: z
43
+ .array(z.string())
44
+ .max(100)
45
+ .optional()
46
+ .describe("Optional list of participant peer ids/@usernames to filter (max 100)"),
47
+ sources: z
48
+ .array(z.number().int())
49
+ .max(100)
50
+ .optional()
51
+ .describe("Optional list of numeric source ids (audio SSRC) to filter participants"),
52
+ offset: z
53
+ .string()
54
+ .optional()
55
+ .describe("Pagination cursor from a previous response's nextOffset; omit for the first page"),
56
+ limit: z
57
+ .number()
58
+ .int()
59
+ .min(1)
60
+ .max(500)
61
+ .optional()
62
+ .describe("Max participants to return in this page (default 100)"),
63
+ },
64
+ annotations: READ_ONLY,
65
+ }, async ({ chat, ids, sources, offset, limit }) => {
66
+ const err = await requireConnection(telegram);
67
+ if (err)
68
+ return fail(new Error(err));
69
+ try {
70
+ const result = await telegram.getGroupCallParticipants(chat, { ids, sources, offset, limit });
71
+ return ok(JSON.stringify(result));
72
+ }
73
+ catch (e) {
74
+ return fail(e);
75
+ }
76
+ });
77
+ }
@@ -1,12 +1,17 @@
1
1
  import { registerAccountTools } from "./account.js";
2
2
  import { registerAuthTools } from "./auth.js";
3
+ import { registerBoostTools } from "./boosts.js";
3
4
  import { registerChatTools } from "./chats.js";
4
5
  import { registerContactTools } from "./contacts.js";
5
6
  import { registerExtraTools } from "./extras.js";
7
+ import { registerGroupCallTools } from "./group-calls.js";
6
8
  import { registerMediaTools } from "./media.js";
7
9
  import { registerMessageTools } from "./messages.js";
10
+ import { registerQuickRepliesTools } from "./quick-replies.js";
8
11
  import { registerReactionTools } from "./reactions.js";
12
+ import { registerStarsTools } from "./stars.js";
9
13
  import { registerStickerTools } from "./stickers.js";
14
+ import { registerStoryTools } from "./stories.js";
10
15
  export function registerTools(server, telegram) {
11
16
  registerAuthTools(server, telegram);
12
17
  registerMessageTools(server, telegram);
@@ -17,4 +22,9 @@ export function registerTools(server, telegram) {
17
22
  registerExtraTools(server, telegram);
18
23
  registerAccountTools(server, telegram);
19
24
  registerStickerTools(server, telegram);
25
+ registerStoryTools(server, telegram);
26
+ registerBoostTools(server, telegram);
27
+ registerGroupCallTools(server, telegram);
28
+ registerStarsTools(server, telegram);
29
+ registerQuickRepliesTools(server, telegram);
20
30
  }