chigwell/telegram-mcp: a Telethon bridge from MCP clients to a real Telegram account
Telegram MCP server powered by Telethon to let MCP clients read chats, manage groups, and send/modify messages, media, contacts, and settings.
At a glance
- What is it?
- The project exposes 80+ Telegram operations as MCP tools, so Claude or Cursor can drive a logged-in Telegram account directly. The design is opinionated about contact naming and voice transcription, and it inherits every constraint of the account it logs into.
- Who is it for?
- Adopt it if you already run an MCP client and want an agent to operate a real Telegram account, and you accept that the account's Premium status and your own alias discipline are load-bearing. Do not adopt it if you need a bot-only, multi-tenant integration, or if you cannot keep aliases.json and the Telethon session file off shared storage.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
What problem a Telethon-backed MCP server actually solves
Telegram's Bot API is the usual route for automation, and it is the wrong route for a lot of what people want. A bot cannot read arbitrary chat history, cannot act as you in existing conversations, and sees only what its privacy settings and admin rights allow. The README positions this project on the other side of that line: it is "a Telegram integration for Claude, Cursor, and other MCP-compatible clients", built on Telethon, which speaks the MTProto client protocol as a user account rather than the bot API.
The audience is therefore narrow and specific. You need an MCP client already configured, a Telegram account you are willing to log in with, and a task where reading and writing as that account matters. An agent asked to summarize a group's recent discussion, reply in a thread, or tidy up contact records is the target case. Someone who wants a support bot answering strangers does not need this and should not use it.
The tool surface: accounts, chats, messages, contacts, media
The README states the server currently includes 80+ MCP tools, grouped into accounts, chats and groups, messages, contacts, and media. The grouping is not cosmetic. Account tools list configured accounts and route tool calls by account label, which is how the multi-account setup is meant to work: one server process, several Telegram identities, selected per call.
The chat and group tools cover the administrative surface you would expect from a client rather than a bot: create groups and channels, join or leave, invite or remove users, manage admins, bans, default permissions, slow mode, topics, invite links, common chats, read receipts, and message links. The message tools include sending, scheduling, editing, deleting, forwarding, pinning, marking read, replying, searching, context inspection, polls, reactions, inline button inspection, and pressing inline callbacks.
This is a wide surface, and width has a cost. Every tool takes a chat_id, and the README notes that every tool taking a chat_id understands aliases. That single decision is what makes the breadth usable from a language model, because a model that has to invent numeric identifiers will get them wrong.
Contact aliases and the deliberate refusal to guess
The alias system is the most interesting design decision in the repository. set_contact_alias teaches the server what you call someone, and afterwards send_message("андрей бекендер", ...) resolves. A contact can carry any number of aliases, which the README frames as tags: save both андрей бекендер and бекендер for one person and either resolves.
The refusal is what stands out. Only an exact saved wording ever sends. Similar wording is matched, but only to suggest: the tool sends nothing and asks you to confirm the contact by name. The README gives the reasoning directly, noting that Лена and Леня, or Иван and Иванов, differ exactly as much as a case ending does, so a matcher confident enough to handle declensions is also confident enough to message the wrong person whenever the one you meant is not saved yet. Confirming saves that wording as its own alias.
That is a trade-off stated honestly rather than hidden. The cost is friction on first use of each new phrasing. Set TELEGRAM_CONTACT_FUZZY=0 to drop the suggestions entirely if even the prompt is unwanted.
The storage side is concrete. Aliases live in ${XDG_STATE_HOME:-~/.local/state}/telegram-mcp/aliases.json, owner-only and written atomically. TELEGRAM_ALIASES_FILE overrides the path, and a pre-existing aliases.json next to the code is still read as a fallback. list_contact_aliases shows one row per person with all their aliases, which the README suggests using to spot a wrong memory. delete_contact_alias forgets one, and repointing an alias at someone else requires replace=True. The save path refuses a target it would have to guess at: contacts are saved by @username, phone, numeric ID, or an alias already confirmed for them.
Rich formatting, Premium re-checks, and structured refusals
send_message, reply_to_message, and edit_message support classic formatting through parse_mode='md' or 'html', and server-side rich formatting through parse_mode='rich', 'rich_markdown', or 'rich_html'. The README describes the rich modes as full Markdown or HTML with tables, headings, formulas, and collapsible sections.
Rich modes require Telegram Premium on the account, and the check is not cached: Premium is re-checked on every call. Without it nothing is sent, and the tool returns a structured telegram_premium_required result so the agent can reformat with classic modes and retry. That is a better failure mode than a silent downgrade, because the caller learns why the call failed and has a defined recovery path. It also means a subscription lapse changes behaviour mid-session rather than at startup, which is worth knowing if you are debugging why a previously working prompt stopped producing tables.
Two transcription engines with opposite failure modes
transcribe_voice(chat_id, message_id, engine=None) turns a voice message or video note into text, and the two engines fail in opposite directions.
The groq engine is the default. It uploads the recording to Groq's hosted whisper-large-v3-turbo. The README is explicit that this leaves the server and costs a download plus upload per call, but does not drop the recording's last few words the way native transcription does. It requires GROQ_API_KEY. Groq caps single upload size, so a recording above TELEGRAM_TRANSCRIBE_GROQ_MAX_MB (default 25, described as the free-tier limit) is refused locally with a too_large error naming its size, rather than being downloaded and rejected by the API. Raise the limit if your Groq tier allows bigger files.
The telegram engine uses native Premium transcription via messages.TranscribeAudioRequest. It is free and never leaves Telegram, but the README states it empirically drops the last speech segment in roughly 2 of 3 recordings, and it requires Telegram Premium. Long recordings come back pending and are polled automatically.
Engine selection is per call via the engine argument, falling back to TELEGRAM_TRANSCRIBE_ENGINE. Results are cached by (chat_id, message_id, engine) in a local SQLite file, so repeat reads and repeat listings never re-transcribe. Concurrent requests for the same uncached recording are collapsed: the second waits for the first and returns its transcript, so a burst of callers costs one paid call rather than one per caller. Every transcript carries a note marking it as a machine transcript, not a verbatim quote. That note is the right call, and anyone building on these transcripts should keep it attached rather than stripping it.
Getting it running, and the config keys that matter
The README's Quick Start section is the entry point, followed by MCP Client Configuration, Multi-Account Setup, Device Identity, Proxy Support, and File Path Security. The material available here does not include the literal Quick Start commands or the client configuration block, so treat the repository's own Quick Start as the source for the exact invocation rather than anything reconstructed from this review.
What is confirmed is the configuration surface. TELEGRAM_CONTACT_FUZZY=0 disables alias suggestions. TELEGRAM_ALIASES_FILE overrides the alias store path. TELEGRAM_TRANSCRIBE_ENGINE selects groq or telegram as the default engine. TELEGRAM_TRANSCRIBE_GROQ_MAX_MB sets the local size ceiling for Groq uploads, default 25. GROQ_API_KEY is required for the groq engine. Separate README sections cover device identity and proxy support, which matter because MTProto sessions are tied to a device identity and because Telegram access from some networks needs a proxy. There is also a dedicated File Path Security section, which is a signal that media tools take filesystem paths and that the project has thought about constraining them.
Docker is covered as its own section, and the repository ships a Docker Build and Compose Validation workflow. The project is Python, licence Apache-2.0, and releases are frequent: v3.2.34 on 2026-09-10, v3.2.33 two days earlier, v3.2.32 the day before that. Three releases in four days tells you the API surface is still moving.
Where this is the wrong tool
The account model is the main limitation. Everything here runs as a logged-in user account, which means the blast radius of a confused agent is your account: messages sent as you, contacts modified, groups administered. The alias confirmation flow limits one class of mistake, sending to the wrong person, but it does nothing about a correctly resolved contact receiving a badly worded message. There is no described dry-run mode, no approval queue, and no undo.
Premium dependency is a second constraint that is easy to underestimate. Rich formatting and native transcription both require it, and the Premium check happens per call, so the failure arrives at send time. If you are planning an agent workflow that relies on tables in messages, verify the subscription before designing around it.
The transcription default also deserves scrutiny. The default engine sends voice content to Groq, which the README states plainly. For a personal assistant summarizing your own voice notes that may be fine. For an organization with rules about where message content may be processed, the default is the wrong choice and engine='telegram' or TELEGRAM_TRANSCRIBE_ENGINE=telegram is the setting to change, accepting the dropped-tail problem in exchange.
Finally, the state files are a real operational concern. aliases.json and the transcription SQLite cache are local, and the alias file is explicitly owner-only. Any deployment that puts the state directory on shared storage, or runs several server instances against one alias file, is outside what the README describes.
The alternative, and how the approach differs
The obvious alternative is a bot built directly on the Telegram Bot API, using an existing bot framework. The difference is not a matter of polish, it is structural. A bot is a separate identity with its own username, its own permissions, and no access to your private chat history unless it is added to a conversation and granted rights. It cannot act as you, and it cannot read what you can read. A bot also fits multi-tenant deployments naturally, because each user talks to the same bot and the bot's credentials are not any user's credentials.
This project inverts all of that. It is single-identity by nature, it sees what the account sees, and the credential is a session for a real person's account. The alias system, the Premium re-checks, and the transcription engines exist precisely because the server is operating a human account rather than a service endpoint. If your requirement is a shared assistant for many users, the bot route is the correct one and this project is the wrong shape. If your requirement is an agent that operates your own Telegram presence, the bot route cannot do it at all.
Maintenance cost and the licence
Two maintenance signals are visible. The release cadence is fast, with three releases in the four days before the last push, which means pinning a version is sensible if you build on specific tool behaviour. The project also carries two GitHub Actions workflows, a Python Lint and Format Check and a Docker Build and Compose Validation, so there is automated checking on both the code and the container.
The licence is Apache-2.0, which permits commercial use and modification and includes an explicit patent grant, with the usual conditions around retaining notices and stating changes. That is a permissive licence, but it is not legal advice and the obligations apply to redistributed copies rather than to internal use; read the LICENSE file in the repository if you plan to ship a modified version.
Upgrade cost is concentrated in two places. Tool names and argument shapes can change between minor releases given the cadence, so any prompt or agent configuration that hardcodes tool names should be checked against the release notes. State format changes are the other risk: aliases.json and the transcription cache are local files, and a format change would require either migration or starting fresh. The README documents TELEGRAM_ALIASES_FILE and the fallback read of a pre-existing aliases.json next to the code, which suggests the project has moved this file before.
Editorial conclusion
Adopt it if you already run an MCP client and want an agent to operate a real Telegram account, and you accept that the account's Premium status and your own alias discipline are load-bearing. Do not adopt it if you need a bot-only, multi-tenant integration, or if you cannot keep aliases.json and the Telethon session file off shared storage. Before wiring it into anything, verify three things: whether your account has Telegram Premium (it decides rich formatting and native transcription), whether GROQ_API_KEY is set if you want the default transcription engine, and where ${XDG_STATE_HOME:-~/.local/state}/telegram-mcp/aliases.json actually lands on your machine.
Community notes