@overpod/mcp-telegram 1.8.0 → 1.9.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 CHANGED
@@ -1,4 +1,10 @@
1
1
  #!/usr/bin/env node
2
+ // Redirect console.log to stderr BEFORE any imports.
3
+ // GramJS Logger uses console.log (stdout) which corrupts MCP JSON-RPC stream.
4
+ const _origLog = console.log;
5
+ console.log = (...args) => {
6
+ console.error(...args);
7
+ };
2
8
  import "dotenv/config";
3
9
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
10
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -177,7 +183,7 @@ server.tool("telegram-search-chats", "Search for Telegram chats/users/channels b
177
183
  try {
178
184
  const results = await telegram.searchChats(query, limit);
179
185
  const text = results
180
- .map((c) => `${c.type === "group" ? "G" : c.type === "channel" ? "C" : "P"} ${c.name}${c.username ? ` (@${c.username})` : ""} (${c.id})`)
186
+ .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)}` : ""}`)
181
187
  .join("\n");
182
188
  return { content: [{ type: "text", text: text || "No results" }] };
183
189
  }
@@ -185,6 +191,26 @@ server.tool("telegram-search-chats", "Search for Telegram chats/users/channels b
185
191
  return { content: [{ type: "text", text: `Error: ${e.message}` }] };
186
192
  }
187
193
  });
194
+ server.tool("telegram-search-global", "Search messages globally across all public Telegram chats and channels", {
195
+ query: z.string().describe("Search text"),
196
+ limit: z.number().default(20).describe("Max results"),
197
+ minDate: z.number().optional().describe("Unix timestamp: only messages after this date"),
198
+ maxDate: z.number().optional().describe("Unix timestamp: only messages before this date"),
199
+ }, async ({ query, limit, minDate, maxDate }) => {
200
+ const err = await requireConnection();
201
+ if (err)
202
+ return { content: [{ type: "text", text: err }] };
203
+ try {
204
+ const messages = await telegram.searchGlobal(query, limit, minDate, maxDate);
205
+ const text = messages
206
+ .map((m) => `[${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}` : ""}]` : ""}`)
207
+ .join("\n\n");
208
+ return { content: [{ type: "text", text: text || "No messages found" }] };
209
+ }
210
+ catch (e) {
211
+ return { content: [{ type: "text", text: `Error: ${e.message}` }] };
212
+ }
213
+ });
188
214
  server.tool("telegram-search-messages", "Search messages in a Telegram chat by text", {
189
215
  chatId: z.string().describe("Chat ID or username"),
190
216
  query: z.string().describe("Search text"),
@@ -116,6 +116,25 @@ export declare class TelegramService {
116
116
  name: string;
117
117
  type: string;
118
118
  username?: string;
119
+ membersCount?: number;
120
+ description?: string;
121
+ }>>;
122
+ searchGlobal(query: string, limit?: number, minDate?: number, maxDate?: number): Promise<Array<{
123
+ id: number;
124
+ text: string;
125
+ sender: string;
126
+ date: string;
127
+ chat: {
128
+ id: string;
129
+ name: string;
130
+ type: string;
131
+ username?: string;
132
+ };
133
+ media?: {
134
+ type: string;
135
+ fileName?: string;
136
+ size?: number;
137
+ };
119
138
  }>>;
120
139
  searchMessages(chatId: string, query: string, limit?: number, minDate?: number, maxDate?: number): Promise<Array<{
121
140
  id: number;
@@ -642,7 +642,12 @@ export class TelegramService {
642
642
  }
643
643
  for (const chat of result.chats) {
644
644
  if (chat instanceof Api.Chat) {
645
- chats.push({ id: chat.id.toString(), name: chat.title, type: "group" });
645
+ chats.push({
646
+ id: chat.id.toString(),
647
+ name: chat.title,
648
+ type: "group",
649
+ membersCount: chat.participantsCount ?? undefined,
650
+ });
646
651
  }
647
652
  else if (chat instanceof Api.Channel) {
648
653
  chats.push({
@@ -650,11 +655,104 @@ export class TelegramService {
650
655
  name: chat.title,
651
656
  type: chat.megagroup ? "group" : "channel",
652
657
  username: chat.username ?? undefined,
658
+ membersCount: chat.participantsCount ?? undefined,
653
659
  });
654
660
  }
655
661
  }
662
+ // Enrich channels/groups with description and accurate members count
663
+ for (const chat of chats) {
664
+ if (chat.type === "private")
665
+ continue;
666
+ try {
667
+ const entity = await this.client.getEntity(chat.id);
668
+ if (entity instanceof Api.Channel) {
669
+ const full = await this.client.invoke(new Api.channels.GetFullChannel({ channel: entity }));
670
+ if (full.fullChat instanceof Api.ChannelFull) {
671
+ chat.description = full.fullChat.about || undefined;
672
+ chat.membersCount = full.fullChat.participantsCount ?? chat.membersCount;
673
+ }
674
+ }
675
+ else if (entity instanceof Api.Chat) {
676
+ const full = await this.client.invoke(new Api.messages.GetFullChat({ chatId: entity.id }));
677
+ if (full.fullChat instanceof Api.ChatFull) {
678
+ chat.description = full.fullChat.about || undefined;
679
+ }
680
+ }
681
+ }
682
+ catch {
683
+ // Skip enrichment on error (private channels, etc.)
684
+ }
685
+ }
656
686
  return chats;
657
687
  }
688
+ async searchGlobal(query, limit = 20, minDate, maxDate) {
689
+ if (!this.client || !this.connected)
690
+ throw new Error("Not connected");
691
+ const result = await this.client.invoke(new Api.messages.SearchGlobal({
692
+ q: query,
693
+ filter: new Api.InputMessagesFilterEmpty(),
694
+ minDate: minDate || 0,
695
+ maxDate: maxDate || 0,
696
+ offsetRate: 0,
697
+ offsetPeer: new Api.InputPeerEmpty(),
698
+ offsetId: 0,
699
+ limit,
700
+ }));
701
+ const chatsMap = new Map();
702
+ if ("chats" in result) {
703
+ for (const chat of result.chats) {
704
+ if (chat instanceof Api.Channel) {
705
+ chatsMap.set(chat.id.toString(), {
706
+ id: chat.id.toString(),
707
+ name: chat.title,
708
+ type: chat.megagroup ? "group" : "channel",
709
+ username: chat.username ?? undefined,
710
+ });
711
+ }
712
+ else if (chat instanceof Api.Chat) {
713
+ chatsMap.set(chat.id.toString(), {
714
+ id: chat.id.toString(),
715
+ name: chat.title,
716
+ type: "group",
717
+ });
718
+ }
719
+ }
720
+ }
721
+ if ("users" in result) {
722
+ for (const user of result.users) {
723
+ if (user instanceof Api.User) {
724
+ const parts = [user.firstName, user.lastName].filter(Boolean);
725
+ chatsMap.set(user.id.toString(), {
726
+ id: user.id.toString(),
727
+ name: parts.join(" ") || "Unknown",
728
+ type: "private",
729
+ username: user.username ?? undefined,
730
+ });
731
+ }
732
+ }
733
+ }
734
+ const rawMessages = "messages" in result ? result.messages : [];
735
+ const messages = rawMessages.filter((m) => m instanceof Api.Message);
736
+ const results = await Promise.all(messages.map(async (m) => {
737
+ const peerId = m.peerId;
738
+ let chatId = "";
739
+ if (peerId instanceof Api.PeerChannel)
740
+ chatId = peerId.channelId.toString();
741
+ else if (peerId instanceof Api.PeerChat)
742
+ chatId = peerId.chatId.toString();
743
+ else if (peerId instanceof Api.PeerUser)
744
+ chatId = peerId.userId.toString();
745
+ return {
746
+ id: m.id,
747
+ text: m.message ?? "",
748
+ sender: await this.resolveSenderName(m.senderId),
749
+ date: new Date((m.date ?? 0) * 1000).toISOString(),
750
+ chat: chatsMap.get(chatId) || { id: chatId, name: "Unknown", type: "unknown" },
751
+ media: this.extractMediaInfo(m.media),
752
+ };
753
+ }));
754
+ return results;
755
+ }
658
756
  async searchMessages(chatId, query, limit = 20, minDate, maxDate) {
659
757
  if (!this.client || !this.connected)
660
758
  throw new Error("Not connected");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@overpod/mcp-telegram",
3
- "version": "1.8.0",
3
+ "version": "1.9.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",
@@ -56,7 +56,7 @@
56
56
  "zod": "^4.3.6"
57
57
  },
58
58
  "devDependencies": {
59
- "@biomejs/biome": "^2.4.7",
59
+ "@biomejs/biome": "^2.4.8",
60
60
  "@types/node": "^25.5.0",
61
61
  "@types/qrcode": "^1.5.6",
62
62
  "tsx": "^4.21.0",