@overpod/mcp-telegram 1.38.1 → 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,21 @@ 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
+
15
+ ## [1.38.2](https://github.com/mcp-telegram/mcp-telegram/compare/v1.38.1...v1.38.2) (2026-06-19)
16
+
17
+
18
+ ### Documentation
19
+
20
+ * backfill v1.27–v1.28 in CHANGELOG; bump Pages actions off Node 20 ([#56](https://github.com/mcp-telegram/mcp-telegram/issues/56)) ([7786819](https://github.com/mcp-telegram/mcp-telegram/commit/778681917a2bc133ea8821e2ff8739a3f859b257))
21
+ * generate changelog pages from CHANGELOG.md + refresh README ([#54](https://github.com/mcp-telegram/mcp-telegram/issues/54)) ([4422901](https://github.com/mcp-telegram/mcp-telegram/commit/442290178c5bee43cc6828f794ce468930cbe945))
22
+
8
23
  ## [1.38.1](https://github.com/mcp-telegram/mcp-telegram/compare/v1.38.0...v1.38.1) (2026-06-10)
9
24
 
10
25
 
@@ -350,6 +365,63 @@ Tests: 490/490 pass.
350
365
  - **Album mixed photo+video** — GramJS accepts mixed arrays via extension-based detection, but this is not covered by tests; recommend uniform media type per album until a live-test checkpoint confirms.
351
366
  - **Album flood control** — a 10-item album fans out 10 `messages.UploadMedia` calls inside one rate-limit slot; under heavy contention the later items may still hit `FLOOD_WAIT`. Rate limiter retries apply.
352
367
 
368
+ ## [1.28.1] - 2026-04-22
369
+
370
+ ### Added
371
+ - **`telegram-logout`** — new tool (Auth category, annotated `DESTRUCTIVE`). Fully logs out: calls `auth.LogOut` on Telegram servers (session disappears from Settings → Devices), destroys the GramJS client, deletes the local session file, and clears in-memory state. Takes no parameters. Handles every state cleanly — connected (server revoke + local wipe), disconnected with a session file (local wipe only, with a notice), no session (`fail` "Not logged in"), and `auth.LogOut` throwing (local wipe still happens, with a "check Settings → Devices manually" hint).
372
+
373
+ ### Changed
374
+ - **`TelegramService.logOut()` hardened** — server-revoke and client-destroy are now split: a successful `auth.LogOut` returns `true` even if `client.destroy()` throws (previously misreported "not confirmed"). The local wipe is verified post-unlink and throws if the session file survives (e.g. read-only Docker mount), instead of falsely reporting success. File removal now always runs even when server-revoke fails (network error, `AUTH_KEY_UNREGISTERED`).
375
+ - **Master cancels active QR login on logout** — a `telegram-logout` request now aborts an in-progress QR login flow before acquiring `globalLock`, instead of queuing behind it for up to 5 minutes.
376
+
377
+ ### Testing
378
+ - 322 unit tests (+10 vs v1.28.0) — `hasLocalSession()`, `logOut()` edge cases (connected / disconnected±file / network error / idempotency / FS-throws / destroy-throws-but-revoke-succeeds), and a Master integration test that logout aborts an active login over a real unix socket.
379
+
380
+ ### Notes & known limitations
381
+ - In a 3-client FIFO scenario (A holds the lock via login, B queues a tool call, C requests logout), logout correctly aborts A, but B still runs before logout because FIFO order is preserved. A priority-aware queue is deferred.
382
+
383
+ ## [1.28.0] - 2026-04-22
384
+
385
+ ### Added
386
+ - **QR login through the IPC daemon** — `mcp-telegram login` now flows through the Master daemon over IPC instead of running as a separate process. The new session reaches the Master's memory immediately, with no editor restart. This fixes the "relogin via `mcp-telegram login`" flavor of `AUTH_KEY_DUPLICATED`/`AUTH_KEY_UNREGISTERED`, where the standalone login wrote a fresh session to disk but the running Master kept the old, now-invalidated auth key in memory and then wiped the just-created session on the next tool call.
387
+ - `IpcClient.loginFlow(onQr)` — streams QR frames as they refresh (~every 10s).
388
+ - `handleLoginStart` on Master — runs `startQrLogin` on the shared `TelegramService`.
389
+ - `GlobalLock` (FIFO mutex) — serializes tool calls with the login flow so other clients queue instead of hitting a stale client mid-relogin.
390
+ - `AbortController` — `socket.on("close")` aborts the QR loop if the CLI is interrupted (Ctrl+C / terminal closed); `globalLock` releases immediately instead of blocking tool calls for up to 5 minutes.
391
+ - Session swap now saves to disk first, then adopts in memory and destroys the previous client — prevents orphan Telegram connections accumulating per relogin.
392
+ - Standalone fallback kept — if no Master is running, `mcp-telegram login` works exactly as before.
393
+
394
+ ### Changed
395
+ - **BREAKING (internal IPC only): IPC protocol is now a discriminated union** with a required `type` tag (`tool` / `tool_response` / `login_start` / `login_qr` / `login_done`). A new 1.28.0 client and an older 1.27.x Master cannot talk — restart your editor / Claude Code after upgrading so the Master daemon is replaced. No public API or invocation change. (Parent-crash detection already kills stale Masters in most cases, so this is transparent for VS Code / Claude Desktop users.)
396
+
397
+ ### Fixed
398
+ - Socket errors on Master now log to stderr instead of being silently dropped.
399
+ - `client.destroy()` in abort branches wrapped in try/catch — guarantees the "QR login aborted" message even if Telegram destroy throws.
400
+ - QR render errors (`QRCode.toString` failure) logged to stderr instead of silently dropped.
401
+
402
+ ### Testing
403
+ - 312 unit tests (was 288) — new suites for `GlobalLock` (FIFO ordering, double-release safety), Master login (QR URL forwarding, abort on socket close), child-process integration via tsx, `IpcClient.loginFlow`, and IPC discriminated-union / legacy-protocol rejection.
404
+
405
+ ## [1.27.1] - 2026-04-22
406
+
407
+ ### Changed
408
+ - Patch release — no new tools, no API changes; pure code-quality and test-coverage work. Refactored `telegram-client.ts` re-exports (eliminates 2 Biome `noUnusedImports` false-positives), replaced unsafe `(e as Error).message` casts with `e instanceof Error` guards in the stats methods, and added `McpRegisteredTool` / `McpServerInternal` types to `ipc-protocol.ts` as a single source of truth (previously duplicated across master.ts and client.ts).
409
+
410
+ ### Testing
411
+ - 286 unit tests (+25 vs v1.27.0) — new branch coverage for `wireIpcProxies` (incl. a safety test that the original `TelegramService` handler is never called after wiring), the rate limiter (`throwOnFloodWait`, retry exhaustion, 5xx, GramJS `errorMessage`), and the IPC protocol parser (malformed JSON, blank lines, partial-line buffering, multiple messages per chunk). `rate-limiter.ts` reached 100% statements.
412
+
413
+ ## [1.27.0] - 2026-04-21
414
+
415
+ ### Added
416
+ - **Master/Client IPC daemon — fixes `AUTH_KEY_DUPLICATED` across concurrent Claude sessions.** Opening multiple Claude sessions used to spawn separate `mcp-telegram` processes that each connected to Telegram with the same session, which Telegram rejected as a duplicate. Now the first process to start becomes the **Master** (holds the single GramJS connection, listens on a Unix socket at `~/.mcp-telegram/daemon.sock`) and every subsequent process becomes a thin **Client** that proxies tool calls to the Master over the socket — one connection, one auth key, no duplicates. Zero config change; same invocation.
417
+ - New files: `src/lock.ts` (atomic `O_EXCL` PID lockfile + stale-lock detection via `kill -0`), `src/ipc-protocol.ts` (IPC message types + newline-delimited JSON parser), `src/master.ts` (Unix socket server, sequential queue dispatcher), `src/client.ts` (`IpcClient` with timeout + retry, handler proxy).
418
+ - Security: socket file is `chmod 0o600` (owner-only); the session string never leaves the Master process.
419
+
420
+ ### Fixed
421
+ - Atomic lock via `O_EXCL` prevents a race when multiple sessions start simultaneously; stale socket from a previous crash is removed before `listen` (no `EADDRINUSE`).
422
+ - One-shot connect timeout (not idle) so live connections survive inactivity; 30s IPC call timeout so a stuck Master surfaces a clean error instead of hanging.
423
+ - Sequential `drainQueue()` loop for correct concurrent-request handling; `.catch()` on auto-connect to avoid `unhandledRejection`; idempotent cleanup (`cleanedUp` flag) so SIGINT/SIGTERM don't double-release.
424
+
353
425
  ## [1.26.0] - 2026-04-20
354
426
 
355
427
  ### Added
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  [![npm downloads](https://img.shields.io/npm/dm/@overpod/mcp-telegram)](https://www.npmjs.com/package/@overpod/mcp-telegram)
5
5
  [![Node.js](https://img.shields.io/badge/Node.js-18%2B-339933.svg?logo=node.js&logoColor=white)](https://nodejs.org/)
6
6
  [![TypeScript](https://img.shields.io/badge/TypeScript-6.0-blue.svg?logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
7
- [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-1.27-green.svg)](https://modelcontextprotocol.io/)
7
+ [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-1.29-green.svg)](https://modelcontextprotocol.io/)
8
8
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
9
9
  [![mcp-telegram MCP server](https://glama.ai/mcp/servers/overpod/mcp-telegram/badges/score.svg)](https://glama.ai/mcp/servers/overpod/mcp-telegram)
10
10
 
@@ -18,13 +18,15 @@ An MCP (Model Context Protocol) server that connects AI assistants like Claude t
18
18
 
19
19
  ## Features
20
20
 
21
- - **Comprehensive tool coverage** -- the most full-featured Telegram MCP server available (80+ tools)
21
+ - **Comprehensive tool coverage** -- the most full-featured Telegram MCP server available
22
22
  - **MTProto protocol** -- direct Telegram API access, not the limited Bot API
23
23
  - **Userbot** -- operates as your personal account, not a bot
24
24
  - **Full-featured** -- messaging, reactions, polls, scheduled messages, stickers, media, contacts, and more
25
25
  - **Forum Topics** -- list topics, read per-topic messages, send to specific topics, per-topic unread counts
26
26
  - **Stickers** -- search sticker sets, browse installed/recent stickers, send stickers to any chat
27
27
  - **Account & profile management** -- update profile, set emoji status, birthday, personal channel, profile photo, manage privacy settings, sessions, auto-delete timers
28
+ - **Chat folders** -- create, edit, delete and reorder folders, toggle folder tags, read suggested folders (v1.33.0)
29
+ - **Global privacy** -- read and set account-wide privacy settings (v1.33.0)
28
30
  - **Global search** -- search messages across all chats at once
29
31
  - **Real-time polling** -- fetch updates via stateless cursors; agent owns `{pts, qts, date}` state
30
32
  - **Inline bots & buttons** -- query inline bots, send results, press callback buttons
@@ -34,6 +36,8 @@ An MCP (Model Context Protocol) server that connects AI assistants like Claude t
34
36
  - **Admin controls** -- toggle channel signatures, anti-spam, forum mode, prehistory; approve join requests
35
37
  - **Stats** -- channel and supergroup analytics (GetBroadcastStats / GetMegagroupStats)
36
38
  - **Boosts & Business** -- boost status, boosters list, Telegram Business chat links CRUD, work hours, location, greeting/away/intro messages
39
+ - **Star gifts** -- browse available and saved gifts, save/convert gifts, manage Stars balance and subscriptions (opt-in via `MCP_TELEGRAM_ENABLE_STARS=1`, v1.34.0)
40
+ - **Shared daemon** -- one background process serves multiple MCP clients over a single Telegram session; see the [shared-daemon guide](https://mcp-telegram.github.io/mcp-telegram/guides/shared-daemon) (v1.38.0)
37
41
  - **QR code login** -- authenticate by scanning a QR code in the Telegram app
38
42
  - **Session persistence** -- login once, stay connected across restarts
39
43
  - **Human-readable output** -- sender names are resolved, not just numeric IDs
@@ -63,6 +67,14 @@ A QR code will appear in the terminal. Open Telegram on your phone, go to **Sett
63
67
 
64
68
  > **Custom session path:** set `TELEGRAM_SESSION_PATH=/path/to/session` to store the session file elsewhere.
65
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
+
66
78
  ### 3. Add to Claude
67
79
 
68
80
  ```bash
@@ -363,6 +375,8 @@ All tools are auto-discoverable via MCP — your AI client will see the full lis
363
375
  | **Rich Media Sending** | `telegram-send-voice`, `telegram-send-video-note` (round video), `telegram-send-location` (static or live), `telegram-send-venue`, `telegram-send-contact`, `telegram-send-dice` (🎲🎯🎰🏀⚽🎳), `telegram-send-album` (2–10 grouped photos/videos) |
364
376
  | **Groups** | `telegram-create-group`, `telegram-edit-group`, `telegram-invite-to-group`, `telegram-join-chat`, `telegram-leave-group`, `telegram-kick-user`, `telegram-ban-user`, `telegram-unban-user`, `telegram-set-admin`, `telegram-remove-admin`, `telegram-get-my-role`, `telegram-set-chat-permissions`, `telegram-set-slow-mode`, `telegram-get-admin-log` |
365
377
  | **Chat Info** | `telegram-get-chat-info`, `telegram-get-chat-members`, `telegram-get-chat-folders` |
378
+ | **Folders (v1.33.0)** | `telegram-create-folder`, `telegram-edit-folder`, `telegram-delete-folder`, `telegram-reorder-folders`, `telegram-get-suggested-folders`, `telegram-toggle-folder-tags` |
379
+ | **Global Privacy (v1.33.0)** | `telegram-get-global-privacy-settings`, `telegram-set-global-privacy-settings` |
366
380
  | **Invite Links** | `telegram-create-invite-link`, `telegram-get-invite-links`, `telegram-revoke-invite-link` |
367
381
  | **Contacts** | `telegram-get-contacts`, `telegram-add-contact`, `telegram-get-contact-requests` |
368
382
  | **Moderation** | `telegram-block-user`, `telegram-unblock-user`, `telegram-report-spam` |
@@ -381,7 +395,7 @@ All tools are auto-discoverable via MCP — your AI client will see the full lis
381
395
  | **Read Receipts (v1.30.0)** | `telegram-get-message-read-participants`, `telegram-get-outbox-read-date` |
382
396
  | **Boosts** | `telegram-get-my-boosts`, `telegram-get-boosts-status`, `telegram-get-boosts-list` |
383
397
  | **Business (v1.32.0)** | `telegram-get-business-chat-links`, `telegram-create-business-chat-link`, `telegram-edit-business-chat-link`, `telegram-delete-business-chat-link`, `telegram-resolve-business-chat-link`, `telegram-set-business-hours`, `telegram-set-business-location`, `telegram-set-business-greeting`, `telegram-set-business-away`, `telegram-set-business-intro` |
384
- | **Opt-in (env-gated)** | `telegram-get-group-call`, `telegram-get-group-call-participants` (requires `MCP_TELEGRAM_ENABLE_GROUP_CALLS=1`), `telegram-get-stars-status`, `telegram-get-stars-transactions` (requires `MCP_TELEGRAM_ENABLE_STARS=1`), `telegram-get-quick-replies`, `telegram-get-quick-reply-messages` (requires `MCP_TELEGRAM_ENABLE_QUICK_REPLIES=1`) |
398
+ | **Opt-in (env-gated)** | `telegram-get-group-call`, `telegram-get-group-call-participants` (requires `MCP_TELEGRAM_ENABLE_GROUP_CALLS=1`); Stars & gifts `telegram-get-stars-status`, `telegram-get-stars-transactions`, `telegram-get-stars-topup-options`, `telegram-get-stars-subscriptions`, `telegram-change-stars-subscription`, `telegram-get-available-star-gifts`, `telegram-get-saved-star-gifts`, `telegram-save-star-gift`, `telegram-convert-star-gift` (requires `MCP_TELEGRAM_ENABLE_STARS=1`); `telegram-get-quick-replies`, `telegram-get-quick-reply-messages` (requires `MCP_TELEGRAM_ENABLE_QUICK_REPLIES=1`) |
385
399
 
386
400
  > **Tip**: Ask your AI assistant *"What Telegram tools are available?"* to get the full list with parameters and descriptions.
387
401
 
@@ -392,7 +406,7 @@ Some tools are disabled by default and must be opted in via environment variable
392
406
  | Variable | Value | Tools enabled |
393
407
  |----------|-------|---------------|
394
408
  | `MCP_TELEGRAM_ENABLE_GROUP_CALLS` | `1` | `telegram-get-group-call`, `telegram-get-group-call-participants` |
395
- | `MCP_TELEGRAM_ENABLE_STARS` | `1` | `telegram-get-stars-status`, `telegram-get-stars-transactions` |
409
+ | `MCP_TELEGRAM_ENABLE_STARS` | `1` | Stars balance & transactions, top-up options, subscriptions, and Star Gifts (browse / save / convert) |
396
410
  | `MCP_TELEGRAM_ENABLE_QUICK_REPLIES` | `1` | `telegram-get-quick-replies`, `telegram-get-quick-reply-messages` |
397
411
 
398
412
  Add these to your `.env` file or MCP client config to enable them.
@@ -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.1",
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",
@@ -34,7 +34,10 @@
34
34
  "test": "tsx --test 'src/**/*.test.ts'",
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
+ "gen:changelog": "tsx scripts/gen-changelog-docs.ts",
38
+ "predocs:dev": "npm run gen:changelog",
37
39
  "docs:dev": "vitepress dev docs",
40
+ "predocs:build": "npm run gen:changelog",
38
41
  "docs:build": "vitepress build docs",
39
42
  "docs:preview": "vitepress preview docs"
40
43
  },
@@ -68,12 +71,12 @@
68
71
  "zod": "^4.4.3"
69
72
  },
70
73
  "devDependencies": {
71
- "@biomejs/biome": "^2.4.16",
72
- "@types/node": "^25.9.2",
74
+ "@biomejs/biome": "^2.5.0",
75
+ "@types/node": "^25.9.3",
73
76
  "@types/qrcode": "^1.5.6",
74
77
  "c8": "^11.0.0",
75
78
  "tsx": "^4.22.4",
76
79
  "typescript": "^6.0.3",
77
- "vitepress": "^1.6.4"
80
+ "vitepress": "^2.0.0-alpha.17"
78
81
  }
79
82
  }