@overpod/mcp-telegram 1.38.2 → 1.39.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/CHANGELOG.md CHANGED
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.39.0](https://github.com/mcp-telegram/mcp-telegram/compare/v1.38.2...v1.39.0) (2026-06-21)
9
+
10
+
11
+ ### Added
12
+
13
+ * complete QR login for accounts with 2FA ([#59](https://github.com/mcp-telegram/mcp-telegram/issues/59)) ([8b57965](https://github.com/mcp-telegram/mcp-telegram/commit/8b5796584edcc974d31b410db15fe1ee31ca84ca))
14
+
8
15
  ## [1.38.2](https://github.com/mcp-telegram/mcp-telegram/compare/v1.38.1...v1.38.2) (2026-06-19)
9
16
 
10
17
 
package/README.md CHANGED
@@ -67,6 +67,14 @@ A QR code will appear in the terminal. Open Telegram on your phone, go to **Sett
67
67
 
68
68
  > **Custom session path:** set `TELEGRAM_SESSION_PATH=/path/to/session` to store the session file elsewhere.
69
69
 
70
+ > **Two-step verification (2FA):** if your account has a cloud password enabled, scanning the QR code is not enough — Telegram also requires the password. Provide it via `TELEGRAM_2FA_PASSWORD` so the login can complete:
71
+ >
72
+ > ```bash
73
+ > TELEGRAM_API_ID=YOUR_ID TELEGRAM_API_HASH=YOUR_HASH TELEGRAM_2FA_PASSWORD=YOUR_PASSWORD npx @overpod/mcp-telegram login
74
+ > ```
75
+ >
76
+ > The password is only used locally to answer Telegram's SRP challenge and is never persisted.
77
+
70
78
  ### 3. Add to Claude
71
79
 
72
80
  ```bash
@@ -3,6 +3,30 @@ import { Api } from "telegram/tl/index.js";
3
3
  import type { AllStoriesSummary, BoostsListSummary, BoostsStatusSummary, BroadcastStatsSummary, BusinessChatLinksSummary, ChannelDifferenceSummary, ChatPermissions, DiscussionMessageSummary, EmojiStatusSummary, GroupCallParticipantsSummary, GroupCallSummary, GroupsForDiscussionSummary, MegagroupStatsSummary, MessageButtonDescriptor, MyBoostsSummary, PeerStoriesSummary, PollSummary, QuickRepliesSummary, QuickReplyMessagesSummary, ReadParticipantsSummary, ReportResultSummary, ResolvedBusinessChatLinkSummary, StarsStatusSummary, StoriesByIdSummary, StoryPrivacy, StoryViewsListSummary, UpdatesDifferenceSummary } from "./telegram-helpers.js";
4
4
  export type { AllStoriesSummary, BoostSummary, BoostsListSummary, BoostsStatusSummary, BroadcastStatsSummary, BusinessChatLinkSummary, BusinessChatLinksSummary, ChannelDifferenceSummary, ChatPermissions, CompactPeer, CompactStatsGraph, DiscussionMessageSummary, EmojiStatusSummary, GroupCallInfoSummary, GroupCallParticipantSummary, GroupCallParticipantsSummary, GroupCallSummary, GroupsForDiscussionSummary, MegagroupStatsSummary, MessageButtonDescriptor, MyBoostSummary, MyBoostsSummary, PeerStoriesSummary, PeerSummary, PollSummary, PrepaidGiveawaySummary, QuickRepliesSummary, QuickReplyMessageSummary, QuickReplyMessagesSummary, QuickReplySummary, ReadParticipantsSummary, ReportResultSummary, ResolvedBusinessChatLinkSummary, StarsAmountSummary, StarsStatusSummary, StarsSubscriptionPricingSummary, StarsSubscriptionSummary, StarsTransactionPeerSummary, StarsTransactionSummary, StatsValue, StoriesByIdSummary, StoryItemSummary, StoryPrivacy, StoryViewSummary, StoryViewsListSummary, UpdatesDifferenceSummary, UpdatesMessageSummary, } from "./telegram-helpers.js";
5
5
  export { buildStoryPrivacyRules, describeAdminLogAction, describeAdminLogDetails, describeKeyboardButton, detectMediaType, extractPeerId, extractPollMediaFromUpdates, extractStoryIdFromUpdates, mergeBannedRights, peerToCompact, reactionToEmoji, summarizeAllStories, summarizeBoost, summarizeBoostsList, summarizeBoostsStatus, summarizeBroadcastStats, summarizeBusinessChatLink, summarizeBusinessChatLinks, summarizeChannelDifference, summarizeDiscussionMessage, summarizeEmojiStatus, summarizeGroupCall, summarizeGroupCallInfo, summarizeGroupCallParticipant, summarizeGroupCallParticipants, summarizeGroupsForDiscussion, summarizeMegagroupStats, summarizeMyBoost, summarizeMyBoosts, summarizePeer, summarizePeerStories, summarizePoll, summarizePrepaidGiveaway, summarizeQuickReplies, summarizeQuickReply, summarizeQuickReplyMessage, summarizeQuickReplyMessages, summarizeReadParticipants, summarizeReportResult, summarizeStarsAmount, summarizeStarsStatus, summarizeStarsSubscription, summarizeStarsTransaction, summarizeStarsTransactionPeer, summarizeStoriesById, summarizeStoryItem, summarizeStoryView, summarizeStoryViewsList, summarizeUpdatesDifference, } from "./telegram-helpers.js";
6
+ /** Minimal client surface the 2FA SRP step needs — lets us unit-test the
7
+ * branch logic with a stub instead of a live TelegramClient. */
8
+ interface SrpClient {
9
+ invoke(request: unknown): Promise<unknown>;
10
+ }
11
+ /** The SRP digest function (GetPassword response + plaintext → InputCheckPasswordSRP).
12
+ * Injectable so tests can exercise the orchestration without GramJS's real crypto. */
13
+ type ComputeCheckFn = (request: Api.account.Password, password: string) => Promise<Api.TypeInputCheckPasswordSRP>;
14
+ /**
15
+ * Complete a QR login that Telegram answered with SESSION_PASSWORD_NEEDED by
16
+ * running the SRP cloud-password check: GetPassword → computeCheck → CheckPassword.
17
+ *
18
+ * Returns a discriminated outcome rather than throwing so the caller owns
19
+ * connection teardown. The password is only used to answer the SRP challenge
20
+ * and is never logged or persisted.
21
+ *
22
+ * `compute` is injectable for tests; production always uses GramJS `computeCheck`.
23
+ */
24
+ export declare function completeTwoFactorLogin(client: SrpClient, password: string | undefined, compute?: ComputeCheckFn): Promise<{
25
+ ok: true;
26
+ } | {
27
+ ok: false;
28
+ message: string;
29
+ }>;
6
30
  export type ChatEntity = Api.User | Api.Chat | Api.Channel | Api.TypeUser | Api.TypeChat;
7
31
  export declare class TelegramService {
8
32
  private client;
@@ -7,6 +7,7 @@ import bigInt from "big-integer";
7
7
  import QRCode from "qrcode";
8
8
  import { TelegramClient } from "telegram";
9
9
  import { CustomFile } from "telegram/client/uploads.js";
10
+ import { computeCheck } from "telegram/Password.js";
10
11
  import { StringSession } from "telegram/sessions/index.js";
11
12
  import { Api } from "telegram/tl/index.js";
12
13
  import { RateLimiter } from "./rate-limiter.js";
@@ -22,6 +23,46 @@ const NOT_CONNECTED_ERROR = "Not connected. Run telegram-status to check or tele
22
23
  function resolveSessionPath(sessionPath) {
23
24
  return sessionPath ?? process.env.TELEGRAM_SESSION_PATH ?? DEFAULT_SESSION_FILE;
24
25
  }
26
+ // Cloud password (2FA) for accounts that have two-step verification enabled.
27
+ // QR login alone cannot complete such logins — Telegram answers the imported
28
+ // login token with SESSION_PASSWORD_NEEDED, after which an SRP password check
29
+ // is required. Supplied via env so it works across all login entry points
30
+ // (standalone CLI, daemon IPC, and the telegram-login MCP tool), none of which
31
+ // can reliably prompt interactively mid-flow.
32
+ function resolveTwoFactorPassword() {
33
+ return process.env.TELEGRAM_2FA_PASSWORD || undefined;
34
+ }
35
+ /**
36
+ * Complete a QR login that Telegram answered with SESSION_PASSWORD_NEEDED by
37
+ * running the SRP cloud-password check: GetPassword → computeCheck → CheckPassword.
38
+ *
39
+ * Returns a discriminated outcome rather than throwing so the caller owns
40
+ * connection teardown. The password is only used to answer the SRP challenge
41
+ * and is never logged or persisted.
42
+ *
43
+ * `compute` is injectable for tests; production always uses GramJS `computeCheck`.
44
+ */
45
+ export async function completeTwoFactorLogin(client, password, compute = computeCheck) {
46
+ if (!password) {
47
+ return {
48
+ ok: false,
49
+ message: "2FA is enabled on this account. Set TELEGRAM_2FA_PASSWORD to your cloud password and run login again.",
50
+ };
51
+ }
52
+ try {
53
+ const passwordInfo = (await client.invoke(new Api.account.GetPassword()));
54
+ const check = await compute(passwordInfo, password);
55
+ await client.invoke(new Api.auth.CheckPassword({ password: check }));
56
+ return { ok: true };
57
+ }
58
+ catch (pwErr) {
59
+ const reason = pwErr instanceof Error ? pwErr.message : String(pwErr);
60
+ return {
61
+ ok: false,
62
+ message: `2FA password check failed: ${reason}. Verify TELEGRAM_2FA_PASSWORD is correct.`,
63
+ };
64
+ }
65
+ }
25
66
  function resolveProxy() {
26
67
  const ip = process.env.TELEGRAM_PROXY_IP;
27
68
  const port = process.env.TELEGRAM_PROXY_PORT;
@@ -318,8 +359,22 @@ export class TelegramService {
318
359
  catch (err) {
319
360
  const error = err;
320
361
  if (error.errorMessage === "SESSION_PASSWORD_NEEDED") {
321
- await client.disconnect();
322
- return { success: false, message: "2FA enabled QR login not supported with 2FA" };
362
+ // The QR was scanned, but the account has two-step verification.
363
+ // Complete the login with an SRP password check if we have the
364
+ // cloud password; otherwise tell the user how to provide it.
365
+ const outcome = await completeTwoFactorLogin(client, resolveTwoFactorPassword());
366
+ if (outcome.ok) {
367
+ resolved = true;
368
+ break;
369
+ }
370
+ // destroy() (not disconnect()) to free the auth_key/socket, matching
371
+ // every other failure exit in this method — important in daemon mode
372
+ // where the process lives on across logins.
373
+ try {
374
+ await client.destroy();
375
+ }
376
+ catch { }
377
+ return { success: false, message: outcome.message };
323
378
  }
324
379
  }
325
380
  if (!resolved) {
@@ -50,6 +50,8 @@ export function registerAuthTools(server, telegram) {
50
50
  `If the QR image is not visible, it's also saved to: ${qrFilePath}`,
51
51
  "",
52
52
  "After scanning, run **telegram-status** to verify the connection.",
53
+ "",
54
+ "If the account has two-step verification (2FA), set the `TELEGRAM_2FA_PASSWORD` environment variable to the cloud password and log in again — scanning alone cannot complete a 2FA login.",
53
55
  ].join("\n");
54
56
  return {
55
57
  content: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@overpod/mcp-telegram",
3
- "version": "1.38.2",
3
+ "version": "1.39.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",
@@ -35,7 +35,6 @@
35
35
  "test:watch": "tsx --test --watch 'src/**/*.test.ts'",
36
36
  "test:coverage": "c8 --all --src src --exclude 'src/**/*.test.ts' --reporter=text tsx --test 'src/**/*.test.ts'",
37
37
  "gen:changelog": "tsx scripts/gen-changelog-docs.ts",
38
- "gen:changelog:check": "tsx scripts/gen-changelog-docs.ts --check",
39
38
  "predocs:dev": "npm run gen:changelog",
40
39
  "docs:dev": "vitepress dev docs",
41
40
  "predocs:build": "npm run gen:changelog",