@ebowwa/mcp-telegram 0.1.4
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/auth-signin.js +182 -0
- package/auth-signin.ts +122 -0
- package/auth-verify.js +234 -0
- package/auth-verify.ts +180 -0
- package/auth.js +154 -0
- package/auth.ts +98 -0
- package/bun.lock +302 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +807 -0
- package/dist/index.js.map +1 -0
- package/package.json +52 -0
- package/src/index.js +918 -0
- package/src/index.ts +907 -0
- package/tg.sh +44 -0
- package/tsconfig.json +20 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,807 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @ebowwa/telegram-mcp - Telegram MTProto Client MCP Server
|
|
4
|
+
*
|
|
5
|
+
* Full-featured Telegram user account integration using GramJS:
|
|
6
|
+
* - Connect/authenticate with API ID and hash
|
|
7
|
+
* - Send messages to users, groups, channels
|
|
8
|
+
* - Get dialogs (chat list)
|
|
9
|
+
* - Get messages from chats
|
|
10
|
+
* - Get entity info (users, chats, channels)
|
|
11
|
+
*
|
|
12
|
+
* Prerequisites:
|
|
13
|
+
* 1. Get API ID and API Hash from https://my.telegram.org/apps
|
|
14
|
+
* 2. First connection requires phone number + verification code
|
|
15
|
+
* 3. Session string is saved for subsequent connections
|
|
16
|
+
*/
|
|
17
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
18
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
19
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
20
|
+
import { TelegramClient } from "telegram";
|
|
21
|
+
import { StringSession } from "telegram/sessions";
|
|
22
|
+
import { Api } from "telegram";
|
|
23
|
+
import bigInt from "big-integer";
|
|
24
|
+
import { homedir } from "os";
|
|
25
|
+
import { join } from "path";
|
|
26
|
+
import { mkdirSync, existsSync, writeFileSync, readFileSync } from "fs";
|
|
27
|
+
import { execSync } from "child_process";
|
|
28
|
+
// ==============
|
|
29
|
+
// Configuration
|
|
30
|
+
// ==============
|
|
31
|
+
const SESSION_DIR = join(homedir(), ".telegram-mcp");
|
|
32
|
+
const SESSION_FILE = join(SESSION_DIR, "session.txt");
|
|
33
|
+
// Ensure session directory exists
|
|
34
|
+
if (!existsSync(SESSION_DIR)) {
|
|
35
|
+
mkdirSync(SESSION_DIR, { recursive: true });
|
|
36
|
+
}
|
|
37
|
+
// Global client instance
|
|
38
|
+
let client = null;
|
|
39
|
+
let sessionString = "";
|
|
40
|
+
// ==============
|
|
41
|
+
// Helper Functions
|
|
42
|
+
// ==============
|
|
43
|
+
async function loadSession() {
|
|
44
|
+
if (existsSync(SESSION_FILE)) {
|
|
45
|
+
return readFileSync(SESSION_FILE, "utf-8").trim();
|
|
46
|
+
}
|
|
47
|
+
return "";
|
|
48
|
+
}
|
|
49
|
+
async function saveSession(session) {
|
|
50
|
+
writeFileSync(SESSION_FILE, session, "utf-8");
|
|
51
|
+
}
|
|
52
|
+
async function getClient() {
|
|
53
|
+
if (client && client.connected) {
|
|
54
|
+
return client;
|
|
55
|
+
}
|
|
56
|
+
// Auto-connect using saved session
|
|
57
|
+
const apiId = parseInt(process.env.TELEGRAM_API_ID || "0");
|
|
58
|
+
const apiHash = process.env.TELEGRAM_API_HASH;
|
|
59
|
+
if (!apiId || !apiHash) {
|
|
60
|
+
throw new Error("TELEGRAM_API_ID and TELEGRAM_API_HASH environment variables required.");
|
|
61
|
+
}
|
|
62
|
+
const savedSession = await loadSession();
|
|
63
|
+
if (!savedSession) {
|
|
64
|
+
throw new Error("No saved session. Run telegram_connect with phone number first.");
|
|
65
|
+
}
|
|
66
|
+
const session = new StringSession(savedSession);
|
|
67
|
+
client = new TelegramClient(session, apiId, apiHash, {
|
|
68
|
+
connectionRetries: 5,
|
|
69
|
+
});
|
|
70
|
+
await client.connect();
|
|
71
|
+
if (!(await client.checkAuthorization())) {
|
|
72
|
+
client = null;
|
|
73
|
+
throw new Error("Session expired. Run telegram_connect again.");
|
|
74
|
+
}
|
|
75
|
+
return client;
|
|
76
|
+
}
|
|
77
|
+
function formatEntity(entity) {
|
|
78
|
+
if (!entity)
|
|
79
|
+
return null;
|
|
80
|
+
const base = {
|
|
81
|
+
id: entity.id?.toString(),
|
|
82
|
+
className: entity.className,
|
|
83
|
+
};
|
|
84
|
+
if (entity.className === "User") {
|
|
85
|
+
return {
|
|
86
|
+
...base,
|
|
87
|
+
firstName: entity.firstName,
|
|
88
|
+
lastName: entity.lastName,
|
|
89
|
+
username: entity.username,
|
|
90
|
+
phone: entity.phone,
|
|
91
|
+
bot: entity.bot,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
if (entity.className === "Chat" || entity.className === "Channel") {
|
|
95
|
+
return {
|
|
96
|
+
...base,
|
|
97
|
+
title: entity.title,
|
|
98
|
+
username: entity.username,
|
|
99
|
+
megagroup: entity.megagroup,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
return base;
|
|
103
|
+
}
|
|
104
|
+
function formatMessage(message) {
|
|
105
|
+
if (!message)
|
|
106
|
+
return null;
|
|
107
|
+
return {
|
|
108
|
+
id: message.id,
|
|
109
|
+
date: message.date ? new Date(message.date * 1000).toISOString() : null,
|
|
110
|
+
message: message.message,
|
|
111
|
+
fromId: message.fromId?.userId?.toString() || message.fromId?.toString(),
|
|
112
|
+
peerId: message.peerId?.userId?.toString() || message.peerId?.chatId?.toString() || message.peerId?.channelId?.toString(),
|
|
113
|
+
out: message.out,
|
|
114
|
+
media: message.media?.className,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
// ==============
|
|
118
|
+
// MCP Server
|
|
119
|
+
// ==============
|
|
120
|
+
const server = new Server({
|
|
121
|
+
name: "@ebowwa/telegram-mcp",
|
|
122
|
+
version: "0.1.0",
|
|
123
|
+
}, {
|
|
124
|
+
capabilities: {
|
|
125
|
+
tools: {},
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
// List available tools
|
|
129
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
130
|
+
return {
|
|
131
|
+
tools: [
|
|
132
|
+
// Connection Management
|
|
133
|
+
{
|
|
134
|
+
name: "telegram_connect",
|
|
135
|
+
description: "Connect to Telegram with API credentials. Requires TELEGRAM_API_ID and TELEGRAM_API_HASH env vars. First connection needs phone/code verification.",
|
|
136
|
+
inputSchema: {
|
|
137
|
+
type: "object",
|
|
138
|
+
properties: {
|
|
139
|
+
phoneNumber: {
|
|
140
|
+
type: "string",
|
|
141
|
+
description: "Phone number with country code (e.g., +1234567890). Required for first connection.",
|
|
142
|
+
},
|
|
143
|
+
code: {
|
|
144
|
+
type: "string",
|
|
145
|
+
description: "Verification code received via Telegram. Required after initial phone submission.",
|
|
146
|
+
},
|
|
147
|
+
password: {
|
|
148
|
+
type: "string",
|
|
149
|
+
description: "2FA password if enabled on account.",
|
|
150
|
+
},
|
|
151
|
+
sessionString: {
|
|
152
|
+
type: "string",
|
|
153
|
+
description: "Existing session string to restore (optional, overrides saved session).",
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
name: "telegram_disconnect",
|
|
160
|
+
description: "Disconnect from Telegram and clear the session.",
|
|
161
|
+
inputSchema: {
|
|
162
|
+
type: "object",
|
|
163
|
+
properties: {},
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
name: "telegram_get_session",
|
|
168
|
+
description: "Get the current session string for backup/restore purposes.",
|
|
169
|
+
inputSchema: {
|
|
170
|
+
type: "object",
|
|
171
|
+
properties: {},
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
name: "telegram_get_me",
|
|
176
|
+
description: "Get information about the currently authenticated user.",
|
|
177
|
+
inputSchema: {
|
|
178
|
+
type: "object",
|
|
179
|
+
properties: {},
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
name: "telegram_restart",
|
|
184
|
+
description: "Restart the Telegram MCP systemd service. Useful for applying config changes or recovering from errors.",
|
|
185
|
+
inputSchema: {
|
|
186
|
+
type: "object",
|
|
187
|
+
properties: {
|
|
188
|
+
serviceName: {
|
|
189
|
+
type: "string",
|
|
190
|
+
description: "Systemd service name (default: telegram-mcp)",
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
// Messaging
|
|
196
|
+
{
|
|
197
|
+
name: "telegram_send_message",
|
|
198
|
+
description: "Send a message to a user, group, or channel.",
|
|
199
|
+
inputSchema: {
|
|
200
|
+
type: "object",
|
|
201
|
+
properties: {
|
|
202
|
+
entity: {
|
|
203
|
+
type: "string",
|
|
204
|
+
description: "Username, phone, or entity ID to send message to (e.g., 'username', '+1234567890', '123456789')",
|
|
205
|
+
},
|
|
206
|
+
message: {
|
|
207
|
+
type: "string",
|
|
208
|
+
description: "Message text to send",
|
|
209
|
+
},
|
|
210
|
+
parseMode: {
|
|
211
|
+
type: "string",
|
|
212
|
+
enum: ["md", "html", ""],
|
|
213
|
+
description: "Parse mode for formatting (md=Markdown, html=HTML)",
|
|
214
|
+
},
|
|
215
|
+
replyTo: {
|
|
216
|
+
type: "number",
|
|
217
|
+
description: "Message ID to reply to (optional)",
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
required: ["entity", "message"],
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
name: "telegram_get_dialogs",
|
|
225
|
+
description: "Get list of all chats/conversations (dialogs).",
|
|
226
|
+
inputSchema: {
|
|
227
|
+
type: "object",
|
|
228
|
+
properties: {
|
|
229
|
+
limit: {
|
|
230
|
+
type: "number",
|
|
231
|
+
description: "Maximum number of dialogs to return (default: 100)",
|
|
232
|
+
},
|
|
233
|
+
offsetDate: {
|
|
234
|
+
type: "number",
|
|
235
|
+
description: "Offset date for pagination",
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
name: "telegram_get_messages",
|
|
242
|
+
description: "Get messages from a chat/conversation.",
|
|
243
|
+
inputSchema: {
|
|
244
|
+
type: "object",
|
|
245
|
+
properties: {
|
|
246
|
+
entity: {
|
|
247
|
+
type: "string",
|
|
248
|
+
description: "Username, phone, or entity ID of the chat",
|
|
249
|
+
},
|
|
250
|
+
limit: {
|
|
251
|
+
type: "number",
|
|
252
|
+
description: "Maximum number of messages to return (default: 100)",
|
|
253
|
+
},
|
|
254
|
+
offsetId: {
|
|
255
|
+
type: "number",
|
|
256
|
+
description: "Message ID to start from (for pagination)",
|
|
257
|
+
},
|
|
258
|
+
minId: {
|
|
259
|
+
type: "number",
|
|
260
|
+
description: "Minimum message ID to fetch",
|
|
261
|
+
},
|
|
262
|
+
maxId: {
|
|
263
|
+
type: "number",
|
|
264
|
+
description: "Maximum message ID to fetch",
|
|
265
|
+
},
|
|
266
|
+
reverse: {
|
|
267
|
+
type: "boolean",
|
|
268
|
+
description: "Fetch messages in reverse order (oldest first)",
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
required: ["entity"],
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
name: "telegram_get_entity",
|
|
276
|
+
description: "Get information about a user, chat, or channel by username or ID.",
|
|
277
|
+
inputSchema: {
|
|
278
|
+
type: "object",
|
|
279
|
+
properties: {
|
|
280
|
+
entity: {
|
|
281
|
+
type: "string",
|
|
282
|
+
description: "Username, phone, or entity ID to look up",
|
|
283
|
+
},
|
|
284
|
+
},
|
|
285
|
+
required: ["entity"],
|
|
286
|
+
},
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
name: "telegram_mark_read",
|
|
290
|
+
description: "Mark messages as read in a chat.",
|
|
291
|
+
inputSchema: {
|
|
292
|
+
type: "object",
|
|
293
|
+
properties: {
|
|
294
|
+
entity: {
|
|
295
|
+
type: "string",
|
|
296
|
+
description: "Username, phone, or entity ID of the chat",
|
|
297
|
+
},
|
|
298
|
+
maxId: {
|
|
299
|
+
type: "number",
|
|
300
|
+
description: "Maximum message ID to mark as read (default: latest)",
|
|
301
|
+
},
|
|
302
|
+
},
|
|
303
|
+
required: ["entity"],
|
|
304
|
+
},
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
name: "telegram_delete_messages",
|
|
308
|
+
description: "Delete messages from a chat.",
|
|
309
|
+
inputSchema: {
|
|
310
|
+
type: "object",
|
|
311
|
+
properties: {
|
|
312
|
+
entity: {
|
|
313
|
+
type: "string",
|
|
314
|
+
description: "Username, phone, or entity ID of the chat",
|
|
315
|
+
},
|
|
316
|
+
messageIds: {
|
|
317
|
+
type: "array",
|
|
318
|
+
items: { type: "number" },
|
|
319
|
+
description: "Array of message IDs to delete",
|
|
320
|
+
},
|
|
321
|
+
revoke: {
|
|
322
|
+
type: "boolean",
|
|
323
|
+
description: "Delete for both parties (default: true)",
|
|
324
|
+
},
|
|
325
|
+
},
|
|
326
|
+
required: ["entity", "messageIds"],
|
|
327
|
+
},
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
name: "telegram_edit_message",
|
|
331
|
+
description: "Edit a sent message.",
|
|
332
|
+
inputSchema: {
|
|
333
|
+
type: "object",
|
|
334
|
+
properties: {
|
|
335
|
+
entity: {
|
|
336
|
+
type: "string",
|
|
337
|
+
description: "Username, phone, or entity ID of the chat",
|
|
338
|
+
},
|
|
339
|
+
messageId: {
|
|
340
|
+
type: "number",
|
|
341
|
+
description: "ID of the message to edit",
|
|
342
|
+
},
|
|
343
|
+
message: {
|
|
344
|
+
type: "string",
|
|
345
|
+
description: "New message text",
|
|
346
|
+
},
|
|
347
|
+
parseMode: {
|
|
348
|
+
type: "string",
|
|
349
|
+
enum: ["md", "html", ""],
|
|
350
|
+
description: "Parse mode for formatting",
|
|
351
|
+
},
|
|
352
|
+
},
|
|
353
|
+
required: ["entity", "messageId", "message"],
|
|
354
|
+
},
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
name: "telegram_forward_messages",
|
|
358
|
+
description: "Forward messages to another chat.",
|
|
359
|
+
inputSchema: {
|
|
360
|
+
type: "object",
|
|
361
|
+
properties: {
|
|
362
|
+
fromEntity: {
|
|
363
|
+
type: "string",
|
|
364
|
+
description: "Source chat username or ID",
|
|
365
|
+
},
|
|
366
|
+
toEntity: {
|
|
367
|
+
type: "string",
|
|
368
|
+
description: "Destination chat username or ID",
|
|
369
|
+
},
|
|
370
|
+
messageIds: {
|
|
371
|
+
type: "array",
|
|
372
|
+
items: { type: "number" },
|
|
373
|
+
description: "Array of message IDs to forward",
|
|
374
|
+
},
|
|
375
|
+
},
|
|
376
|
+
required: ["fromEntity", "toEntity", "messageIds"],
|
|
377
|
+
},
|
|
378
|
+
},
|
|
379
|
+
// Contacts & Users
|
|
380
|
+
{
|
|
381
|
+
name: "telegram_get_contacts",
|
|
382
|
+
description: "Get list of contacts.",
|
|
383
|
+
inputSchema: {
|
|
384
|
+
type: "object",
|
|
385
|
+
properties: {
|
|
386
|
+
limit: {
|
|
387
|
+
type: "number",
|
|
388
|
+
description: "Maximum number of contacts to return",
|
|
389
|
+
},
|
|
390
|
+
},
|
|
391
|
+
},
|
|
392
|
+
},
|
|
393
|
+
// Chats & Channels
|
|
394
|
+
{
|
|
395
|
+
name: "telegram_create_group",
|
|
396
|
+
description: "Create a new group chat.",
|
|
397
|
+
inputSchema: {
|
|
398
|
+
type: "object",
|
|
399
|
+
properties: {
|
|
400
|
+
title: {
|
|
401
|
+
type: "string",
|
|
402
|
+
description: "Group title",
|
|
403
|
+
},
|
|
404
|
+
users: {
|
|
405
|
+
type: "array",
|
|
406
|
+
items: { type: "string" },
|
|
407
|
+
description: "Array of user usernames or IDs to add",
|
|
408
|
+
},
|
|
409
|
+
},
|
|
410
|
+
required: ["title", "users"],
|
|
411
|
+
},
|
|
412
|
+
},
|
|
413
|
+
{
|
|
414
|
+
name: "telegram_get_participants",
|
|
415
|
+
description: "Get participants of a group or channel.",
|
|
416
|
+
inputSchema: {
|
|
417
|
+
type: "object",
|
|
418
|
+
properties: {
|
|
419
|
+
entity: {
|
|
420
|
+
type: "string",
|
|
421
|
+
description: "Group/channel username or ID",
|
|
422
|
+
},
|
|
423
|
+
limit: {
|
|
424
|
+
type: "number",
|
|
425
|
+
description: "Maximum number of participants to return",
|
|
426
|
+
},
|
|
427
|
+
},
|
|
428
|
+
required: ["entity"],
|
|
429
|
+
},
|
|
430
|
+
},
|
|
431
|
+
],
|
|
432
|
+
};
|
|
433
|
+
});
|
|
434
|
+
// Handle tool calls
|
|
435
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
436
|
+
const { name, arguments: args } = request.params;
|
|
437
|
+
try {
|
|
438
|
+
switch (name) {
|
|
439
|
+
// ==================
|
|
440
|
+
// Connection
|
|
441
|
+
// ==================
|
|
442
|
+
case "telegram_connect": {
|
|
443
|
+
const apiId = parseInt(process.env.TELEGRAM_API_ID || "0");
|
|
444
|
+
const apiHash = process.env.TELEGRAM_API_HASH;
|
|
445
|
+
if (!apiId || !apiHash) {
|
|
446
|
+
throw new Error("TELEGRAM_API_ID and TELEGRAM_API_HASH environment variables required. Get them from https://my.telegram.org/apps");
|
|
447
|
+
}
|
|
448
|
+
// Use provided session or load saved one
|
|
449
|
+
const savedSession = args?.sessionString || await loadSession();
|
|
450
|
+
const session = new StringSession(savedSession);
|
|
451
|
+
client = new TelegramClient(session, apiId, apiHash, {
|
|
452
|
+
connectionRetries: 5,
|
|
453
|
+
});
|
|
454
|
+
const phoneNumber = args?.phoneNumber;
|
|
455
|
+
const code = args?.code;
|
|
456
|
+
const password = args?.password;
|
|
457
|
+
// Start the client with auth options
|
|
458
|
+
const startOptions = {
|
|
459
|
+
onError: (err) => console.error("Telegram auth error:", err),
|
|
460
|
+
};
|
|
461
|
+
if (phoneNumber) {
|
|
462
|
+
startOptions.phoneNumber = async () => phoneNumber;
|
|
463
|
+
}
|
|
464
|
+
if (code) {
|
|
465
|
+
startOptions.phoneCode = async () => code;
|
|
466
|
+
}
|
|
467
|
+
if (password) {
|
|
468
|
+
startOptions.password = async () => password;
|
|
469
|
+
}
|
|
470
|
+
await client.start(startOptions);
|
|
471
|
+
// Save session for future use
|
|
472
|
+
sessionString = client.session.save();
|
|
473
|
+
await saveSession(sessionString);
|
|
474
|
+
const me = await client.getMe();
|
|
475
|
+
return {
|
|
476
|
+
content: [{
|
|
477
|
+
type: "text",
|
|
478
|
+
text: JSON.stringify({
|
|
479
|
+
success: true,
|
|
480
|
+
message: "Connected to Telegram",
|
|
481
|
+
user: formatEntity(me),
|
|
482
|
+
sessionString: sessionString,
|
|
483
|
+
}, null, 2),
|
|
484
|
+
}],
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
case "telegram_disconnect": {
|
|
488
|
+
if (client) {
|
|
489
|
+
await client.disconnect();
|
|
490
|
+
client = null;
|
|
491
|
+
sessionString = "";
|
|
492
|
+
}
|
|
493
|
+
return {
|
|
494
|
+
content: [{
|
|
495
|
+
type: "text",
|
|
496
|
+
text: JSON.stringify({ success: true, message: "Disconnected from Telegram" }),
|
|
497
|
+
}],
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
case "telegram_get_session": {
|
|
501
|
+
if (!sessionString && existsSync(SESSION_FILE)) {
|
|
502
|
+
sessionString = await loadSession();
|
|
503
|
+
}
|
|
504
|
+
return {
|
|
505
|
+
content: [{
|
|
506
|
+
type: "text",
|
|
507
|
+
text: JSON.stringify({ sessionString: sessionString || null }),
|
|
508
|
+
}],
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
case "telegram_get_me": {
|
|
512
|
+
const c = await getClient();
|
|
513
|
+
const me = await c.getMe();
|
|
514
|
+
return {
|
|
515
|
+
content: [{
|
|
516
|
+
type: "text",
|
|
517
|
+
text: JSON.stringify(formatEntity(me), null, 2),
|
|
518
|
+
}],
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
case "telegram_restart": {
|
|
522
|
+
const serviceName = args?.serviceName || "telegram-mcp";
|
|
523
|
+
try {
|
|
524
|
+
// Restart the systemd service
|
|
525
|
+
execSync(`sudo systemctl restart ${serviceName}`, {
|
|
526
|
+
encoding: "utf-8",
|
|
527
|
+
timeout: 30000,
|
|
528
|
+
});
|
|
529
|
+
return {
|
|
530
|
+
content: [{
|
|
531
|
+
type: "text",
|
|
532
|
+
text: JSON.stringify({
|
|
533
|
+
success: true,
|
|
534
|
+
message: `Systemd service '${serviceName}' restarted successfully`,
|
|
535
|
+
serviceName,
|
|
536
|
+
}, null, 2),
|
|
537
|
+
}],
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
catch (error) {
|
|
541
|
+
// Check if systemctl is available (not on macOS)
|
|
542
|
+
if (error.message.includes("systemctl: command not found")) {
|
|
543
|
+
return {
|
|
544
|
+
content: [{
|
|
545
|
+
type: "text",
|
|
546
|
+
text: JSON.stringify({
|
|
547
|
+
error: true,
|
|
548
|
+
message: "systemctl not found. This command is only available on Linux systems with systemd.",
|
|
549
|
+
hint: "On macOS, use 'brew services restart' or manual process management.",
|
|
550
|
+
}, null, 2),
|
|
551
|
+
}],
|
|
552
|
+
isError: true,
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
throw error;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
// ==================
|
|
559
|
+
// Messaging
|
|
560
|
+
// ==================
|
|
561
|
+
case "telegram_send_message": {
|
|
562
|
+
if (!args?.entity || !args?.message) {
|
|
563
|
+
throw new Error("entity and message are required");
|
|
564
|
+
}
|
|
565
|
+
const c = await getClient();
|
|
566
|
+
const entity = await c.getInputEntity(args.entity);
|
|
567
|
+
const result = await c.sendMessage(entity, {
|
|
568
|
+
message: args.message,
|
|
569
|
+
parseMode: args.parseMode,
|
|
570
|
+
replyTo: args.replyTo,
|
|
571
|
+
});
|
|
572
|
+
return {
|
|
573
|
+
content: [{
|
|
574
|
+
type: "text",
|
|
575
|
+
text: JSON.stringify({
|
|
576
|
+
success: true,
|
|
577
|
+
message: formatMessage(result),
|
|
578
|
+
}, null, 2),
|
|
579
|
+
}],
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
case "telegram_get_dialogs": {
|
|
583
|
+
const c = await getClient();
|
|
584
|
+
const limit = args?.limit || 100;
|
|
585
|
+
const dialogs = await c.getDialogs({ limit });
|
|
586
|
+
const formatted = dialogs.map((d) => ({
|
|
587
|
+
id: d.id?.toString(),
|
|
588
|
+
name: d.name,
|
|
589
|
+
unreadCount: d.unreadCount,
|
|
590
|
+
archived: d.archived,
|
|
591
|
+
pinned: d.pinned,
|
|
592
|
+
entity: formatEntity(d.entity),
|
|
593
|
+
}));
|
|
594
|
+
return {
|
|
595
|
+
content: [{
|
|
596
|
+
type: "text",
|
|
597
|
+
text: JSON.stringify(formatted, null, 2),
|
|
598
|
+
}],
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
case "telegram_get_messages": {
|
|
602
|
+
if (!args?.entity) {
|
|
603
|
+
throw new Error("entity is required");
|
|
604
|
+
}
|
|
605
|
+
const c = await getClient();
|
|
606
|
+
const entity = await c.getInputEntity(args.entity);
|
|
607
|
+
const messages = await c.getMessages(entity, {
|
|
608
|
+
limit: args.limit || 100,
|
|
609
|
+
offsetId: args.offsetId,
|
|
610
|
+
minId: args.minId,
|
|
611
|
+
maxId: args.maxId,
|
|
612
|
+
reverse: args.reverse,
|
|
613
|
+
});
|
|
614
|
+
const formatted = messages.map(formatMessage);
|
|
615
|
+
return {
|
|
616
|
+
content: [{
|
|
617
|
+
type: "text",
|
|
618
|
+
text: JSON.stringify({
|
|
619
|
+
total: messages.total,
|
|
620
|
+
messages: formatted,
|
|
621
|
+
}, null, 2),
|
|
622
|
+
}],
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
case "telegram_get_entity": {
|
|
626
|
+
if (!args?.entity) {
|
|
627
|
+
throw new Error("entity is required");
|
|
628
|
+
}
|
|
629
|
+
const c = await getClient();
|
|
630
|
+
const entity = await c.getEntity(args.entity);
|
|
631
|
+
return {
|
|
632
|
+
content: [{
|
|
633
|
+
type: "text",
|
|
634
|
+
text: JSON.stringify(formatEntity(entity), null, 2),
|
|
635
|
+
}],
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
case "telegram_mark_read": {
|
|
639
|
+
if (!args?.entity) {
|
|
640
|
+
throw new Error("entity is required");
|
|
641
|
+
}
|
|
642
|
+
const c = await getClient();
|
|
643
|
+
const entity = await c.getInputEntity(args.entity);
|
|
644
|
+
await c.invoke(new Api.messages.ReadHistory({
|
|
645
|
+
peer: entity,
|
|
646
|
+
maxId: args.maxId || 0,
|
|
647
|
+
}));
|
|
648
|
+
return {
|
|
649
|
+
content: [{
|
|
650
|
+
type: "text",
|
|
651
|
+
text: JSON.stringify({ success: true, message: "Messages marked as read" }),
|
|
652
|
+
}],
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
case "telegram_delete_messages": {
|
|
656
|
+
if (!args?.entity || !args?.messageIds) {
|
|
657
|
+
throw new Error("entity and messageIds are required");
|
|
658
|
+
}
|
|
659
|
+
const c = await getClient();
|
|
660
|
+
const entity = await c.getInputEntity(args.entity);
|
|
661
|
+
await c.deleteMessages(entity, args.messageIds, { revoke: args.revoke !== false });
|
|
662
|
+
return {
|
|
663
|
+
content: [{
|
|
664
|
+
type: "text",
|
|
665
|
+
text: JSON.stringify({ success: true, message: "Messages deleted" }),
|
|
666
|
+
}],
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
case "telegram_edit_message": {
|
|
670
|
+
if (!args?.entity || !args?.messageId || !args?.message) {
|
|
671
|
+
throw new Error("entity, messageId, and message are required");
|
|
672
|
+
}
|
|
673
|
+
const c = await getClient();
|
|
674
|
+
const entity = await c.getInputEntity(args.entity);
|
|
675
|
+
const result = await c.editMessage(entity, {
|
|
676
|
+
message: args.messageId,
|
|
677
|
+
text: args.message,
|
|
678
|
+
parseMode: args.parseMode,
|
|
679
|
+
});
|
|
680
|
+
return {
|
|
681
|
+
content: [{
|
|
682
|
+
type: "text",
|
|
683
|
+
text: JSON.stringify({
|
|
684
|
+
success: true,
|
|
685
|
+
message: formatMessage(result),
|
|
686
|
+
}, null, 2),
|
|
687
|
+
}],
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
case "telegram_forward_messages": {
|
|
691
|
+
if (!args?.fromEntity || !args?.toEntity || !args?.messageIds) {
|
|
692
|
+
throw new Error("fromEntity, toEntity, and messageIds are required");
|
|
693
|
+
}
|
|
694
|
+
const c = await getClient();
|
|
695
|
+
const fromEntity = await c.getInputEntity(args.fromEntity);
|
|
696
|
+
const toEntity = await c.getInputEntity(args.toEntity);
|
|
697
|
+
const result = await c.forwardMessages(toEntity, {
|
|
698
|
+
messages: args.messageIds,
|
|
699
|
+
fromPeer: fromEntity,
|
|
700
|
+
});
|
|
701
|
+
return {
|
|
702
|
+
content: [{
|
|
703
|
+
type: "text",
|
|
704
|
+
text: JSON.stringify({
|
|
705
|
+
success: true,
|
|
706
|
+
forwarded: result.length,
|
|
707
|
+
}, null, 2),
|
|
708
|
+
}],
|
|
709
|
+
};
|
|
710
|
+
}
|
|
711
|
+
// ==================
|
|
712
|
+
// Contacts
|
|
713
|
+
// ==================
|
|
714
|
+
case "telegram_get_contacts": {
|
|
715
|
+
const c = await getClient();
|
|
716
|
+
const result = await c.invoke(new Api.contacts.GetContacts({
|
|
717
|
+
hash: bigInt(0),
|
|
718
|
+
}));
|
|
719
|
+
const contacts = result.users?.map(formatEntity) || [];
|
|
720
|
+
return {
|
|
721
|
+
content: [{
|
|
722
|
+
type: "text",
|
|
723
|
+
text: JSON.stringify(contacts, null, 2),
|
|
724
|
+
}],
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
// ==================
|
|
728
|
+
// Groups & Channels
|
|
729
|
+
// ==================
|
|
730
|
+
case "telegram_create_group": {
|
|
731
|
+
if (!args?.title || !args?.users) {
|
|
732
|
+
throw new Error("title and users are required");
|
|
733
|
+
}
|
|
734
|
+
const c = await getClient();
|
|
735
|
+
const users = await Promise.all(args.users.map((u) => c.getInputEntity(u)));
|
|
736
|
+
const result = await c.invoke(new Api.messages.CreateChat({
|
|
737
|
+
users: users,
|
|
738
|
+
title: args.title,
|
|
739
|
+
}));
|
|
740
|
+
return {
|
|
741
|
+
content: [{
|
|
742
|
+
type: "text",
|
|
743
|
+
text: JSON.stringify({
|
|
744
|
+
success: true,
|
|
745
|
+
chat: formatEntity(result.chats?.[0]),
|
|
746
|
+
}, null, 2),
|
|
747
|
+
}],
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
case "telegram_get_participants": {
|
|
751
|
+
if (!args?.entity) {
|
|
752
|
+
throw new Error("entity is required");
|
|
753
|
+
}
|
|
754
|
+
const c = await getClient();
|
|
755
|
+
const entity = await c.getInputEntity(args.entity);
|
|
756
|
+
const result = await c.invoke(new Api.channels.GetParticipants({
|
|
757
|
+
channel: entity,
|
|
758
|
+
filter: new Api.ChannelParticipantsRecent(),
|
|
759
|
+
limit: args.limit || 100,
|
|
760
|
+
offset: 0,
|
|
761
|
+
}));
|
|
762
|
+
const participants = result.participants?.map((p) => ({
|
|
763
|
+
userId: p.userId?.toString(),
|
|
764
|
+
date: p.date ? new Date(p.date * 1000).toISOString() : null,
|
|
765
|
+
className: p.className,
|
|
766
|
+
})) || [];
|
|
767
|
+
return {
|
|
768
|
+
content: [{
|
|
769
|
+
type: "text",
|
|
770
|
+
text: JSON.stringify({
|
|
771
|
+
total: result.count,
|
|
772
|
+
participants,
|
|
773
|
+
}, null, 2),
|
|
774
|
+
}],
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
default:
|
|
778
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
catch (error) {
|
|
782
|
+
return {
|
|
783
|
+
content: [{
|
|
784
|
+
type: "text",
|
|
785
|
+
text: JSON.stringify({
|
|
786
|
+
error: true,
|
|
787
|
+
message: error.message,
|
|
788
|
+
stack: error.stack,
|
|
789
|
+
}, null, 2),
|
|
790
|
+
}],
|
|
791
|
+
isError: true,
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
});
|
|
795
|
+
// ==============
|
|
796
|
+
// Start Server
|
|
797
|
+
// ==============
|
|
798
|
+
async function main() {
|
|
799
|
+
const transport = new StdioServerTransport();
|
|
800
|
+
await server.connect(transport);
|
|
801
|
+
console.error("Telegram MCP server running on stdio");
|
|
802
|
+
}
|
|
803
|
+
main().catch((error) => {
|
|
804
|
+
console.error("Fatal error:", error);
|
|
805
|
+
process.exit(1);
|
|
806
|
+
});
|
|
807
|
+
//# sourceMappingURL=index.js.map
|