{"id":26937453,"url":"https://github.com/chigwell/telegram-mcp","last_synced_at":"2026-08-27T23:26:50.614Z","repository":{"id":283511788,"uuid":"951958279","full_name":"chigwell/telegram-mcp","owner":"chigwell","description":"Telegram MCP server powered by Telethon to let MCP clients read chats, manage groups, and send/modify messages, media, contacts, and settings.","archived":false,"fork":false,"pushed_at":"2026-08-21T18:29:53.000Z","size":2083,"stargazers_count":1488,"open_issues_count":37,"forks_count":392,"subscribers_count":7,"default_branch":"main","last_synced_at":"2026-08-21T20:24:13.075Z","etag":null,"topics":["admin","api","chat-management","contacts","groups","mcp","media","messaging","search","telegram","telegram-api","telegram-client","telethon"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chigwell.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"claude":null,"gemini":null,"cursor":null,"copilot":null,"dco":null,"cla":null,"disclosure":null}},"created_at":"2025-03-20T14:06:35.000Z","updated_at":"2026-08-21T18:29:36.000Z","dependencies_parsed_at":"2026-08-21T20:08:06.324Z","dependency_job_id":"4d911ecb-ef2e-47a7-b74c-0451158e7d81","html_url":"https://github.com/chigwell/telegram-mcp","commit_stats":null,"previous_names":["chigwell/telegram-mcp"],"tags_count":101,"template":false,"template_full_name":null,"purl":"pkg:github/chigwell/telegram-mcp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chigwell%2Ftelegram-mcp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chigwell%2Ftelegram-mcp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chigwell%2Ftelegram-mcp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chigwell%2Ftelegram-mcp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chigwell","download_url":"https://codeload.github.com/chigwell/telegram-mcp/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chigwell%2Ftelegram-mcp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":36947767,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-08-22T15:14:58.755Z","status":"online","status_checked_at":"2026-08-27T02:00:07.166Z","response_time":96,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["admin","api","chat-management","contacts","groups","mcp","media","messaging","search","telegram","telegram-api","telegram-client","telethon"],"created_at":"2025-04-02T13:15:15.022Z","updated_at":"2026-08-27T23:26:50.604Z","avatar_url":"https://github.com/chigwell.png","language":"Python","funding_links":[],"categories":["Communication \u0026 Messaging","Messaging Mcp Servers","📚 Projects (1974 total)","Community Servers","MCP 服务器精选列表","پیاده‌سازی‌های سرور","🤖 AI/ML","Python","Table of Contents","Real-Time Collaboration","MCP Servers","Server Implementations","Servers","Communication"],"sub_categories":["Messengers","MCP Servers","💬 通讯与协作 (Slack, Email, Calendar, Social, etc.)","💬 \u003ca name=\"communication\"\u003e\u003c/a\u003eارتباطات","Communication","Social \u0026 Messaging","💬 \u003ca name=\"communication\"\u003e\u003c/a\u003eCommunication"],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"https://capsule-render.vercel.app/api?type=waving\u0026color=gradient\u0026height=200\u0026section=header\u0026text=Telegram%20MCP%20Server\u0026fontSize=50\u0026fontAlignY=35\u0026animation=fadeIn\u0026fontColor=FFFFFF\u0026descAlignY=55\u0026descAlign=62\" alt=\"Telegram MCP Server\" width=\"100%\" /\u003e\n\u003c/div\u003e\n\n![MCP Badge](https://badge.mcpx.dev)\n[![License: Apache 2.0](https://img.shields.io/badge/license-Apache%202.0-green?style=flat-square)](https://opensource.org/licenses/Apache-2.0)\n[![Python Lint \u0026 Format Check](https://github.com/chigwell/telegram-mcp/actions/workflows/python-lint-format.yml/badge.svg)](https://github.com/chigwell/telegram-mcp/actions/workflows/python-lint-format.yml)\n[![Docker Build \u0026 Compose Validation](https://github.com/chigwell/telegram-mcp/actions/workflows/docker-build.yml/badge.svg)](https://github.com/chigwell/telegram-mcp/actions/workflows/docker-build.yml)\n\nA Telegram integration for Claude, Cursor, and other MCP-compatible clients. It exposes Telegram account, chat, message, contact, media, folder, and admin operations through the [Model Context Protocol](https://modelcontextprotocol.io/) using [Telethon](https://docs.telethon.dev/).\n\n## 🤖 MCP in Action\n\nBasic Telegram MCP usage in Claude:\n\n![Telegram MCP in action](screenshots/1.png)\n\nAsking Claude to analyze chat history and send a response:\n\n![Telegram MCP Request](screenshots/2.png)\n\nMessage sent successfully:\n\n![Telegram MCP Result](screenshots/3.png)\n\n## Contents\n\n- [What It Can Do](#what-it-can-do)\n- [Requirements](#requirements)\n- [Quick Start](#quick-start)\n- [MCP Client Configuration](#mcp-client-configuration)\n- [Multi-Account Setup](#multi-account-setup)\n- [Device Identity](#device-identity)\n- [Proxy Support](#proxy-support)\n- [File Path Security](#file-path-security)\n- [Docker](#docker)\n- [Development](#development)\n- [Security Notes](#security-notes)\n- [Troubleshooting](#troubleshooting)\n- [License](#license)\n\n## What It Can Do\n\nThe server currently includes 80+ MCP tools grouped into these areas:\n\n- **Accounts:** list configured accounts and route tool calls by account label.\n- **Chats and groups:** list chats, inspect metadata, create groups/channels, join or leave chats, invite users, manage admins, bans, default permissions, slow mode, topics, invite links, common chats, read receipts, and message links.\n- **Messages:** send, schedule, edit, delete, forward, pin, unpin, mark read, reply, search, inspect context, create polls, manage reactions, inspect inline buttons, and press inline callbacks. `send_message`, `reply_to_message`, and `edit_message` support classic formatting (`parse_mode='md'`/`'html'`) and server-side rich formatting (`parse_mode='rich'`/`'rich_markdown'`/`'rich_html'` — full Markdown/HTML with tables, headings, formulas, and collapsible sections). Rich modes require Telegram Premium on the account; Premium is re-checked on every call, and without it nothing is sent — the tool returns a structured `telegram_premium_required` result so the agent can reformat with classic modes and retry.\n- **Contacts:** list, search, add, delete, block, unblock, import, export, inspect direct chats, find recent contact interactions, and remember contacts by the names you actually use (see below).\n\n### Remembered contacts\n\n`set_contact_alias` teaches the server what you call someone, and every tool that takes a `chat_id` understands it from then on — `send_message(\"андрей бекендер\", ...)` just works. A contact can carry any number of aliases, which is how tags work: save both `андрей бекендер` and `бекендер` for the same person and either resolves.\n\n**Only an exact saved wording ever sends.** Similar wording (`Андрею бекендеру` for a saved `андрей бекендер`) is matched too, but only to *suggest*: the tool sends nothing and asks you to confirm the contact by name. This is deliberate — `Лена`/`Леня` 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, so each new phrasing costs one yes/no the first time and nothing ever again. Set `TELEGRAM_CONTACT_FUZZY=0` to drop the suggestions too.\n\nWhen a reference is unknown, resembles one contact, matches several, or points at a contact that no longer resolves, tools send nothing and return a structured instruction telling the agent exactly what to ask you, to save the answer with `set_contact_alias`, and to retry once. `list_contact_aliases` shows one row per person with all their aliases (use it to spot a wrong memory), `delete_contact_alias` forgets one, and repointing an alias at someone else requires `replace=True`. The save path itself refuses a target it would have to guess at: contacts are saved by @username, phone, numeric ID, or an alias already confirmed for them.\n\nAliases live in `${XDG_STATE_HOME:-~/.local/state}/telegram-mcp/aliases.json` (owner-only, written atomically); `TELEGRAM_ALIASES_FILE` overrides the path, and a pre-existing `aliases.json` next to the code is still read as a fallback.\n- **Media:** send files, download media, upload files, send voice notes, stickers, GIFs, and inspect message media.\n- **Profile and privacy:** get your own account info, update profile fields, set or delete profile photos, inspect privacy settings, get user info/photos/status, and manage bot commands.\n- **Folders and drafts:** list, create, update, reorder, and delete Telegram folders; save, list, and clear drafts.\n- **Events:** wait for incoming messages with debounce (`wait_for_new_message`, `wait_for_settled_message`), optionally for one chat only via `chat_id` — without it any unrelated conversation wakes the wait — or enable the opt-in incoming event feed for callback-style delivery (see below).\n\nAll tool results that include Telegram user-controlled content are sanitized and, where practical, returned as structured JSON.\n\n### Incoming Event Feed (callback mode, Claude Code only)\n\nBy default, an agent waits for replies by calling `wait_for_settled_message`, which blocks up to the MCP tool timeout and must be re-called — that works everywhere (Codex, Cursor, etc.) and is unchanged.\n\nClients that can wake an agent on external output (Claude Code's persistent `Monitor` on `tail -f`) can switch to callback mode instead:\n\n1. The agent calls `enable_incoming_feed` (or set `TELEGRAM_EVENT_FEED=1` in the environment to auto-enable). Each settled incoming burst is appended as one JSON line to `${XDG_STATE_HOME:-~/.local/state}/telegram-mcp/incoming_feed.jsonl`, created owner-only (0600). Override the path with `TELEGRAM_EVENT_FEED_FILE` — an explicit path's directory must already exist. `incoming_feed_status` reports the effective path and a ready-to-use watch command.\n2. The agent arms a persistent Monitor with the `watch_command` returned by the tool. Every new line re-invokes the agent with the burst summary; no blocking tool call is held open, and the chat stays free.\n\n`disable_incoming_feed` switches back; `incoming_feed_status` reports the current mode. While the feed is enabled it consumes settled bursts, so don't combine it with `wait_for_settled_message`. Feed lines contain user-generated `name` fields — treat them as untrusted data.\n\n## Requirements\n\n- Python 3.10+\n- Telegram API credentials from [my.telegram.org/apps](https://my.telegram.org/apps)\n- A Telegram session string or file-based session\n- An MCP client such as Claude Desktop, Cursor, or another MCP-compatible host\n- Optional: [uv](https://docs.astral.sh/uv/) for local development\n\n## Quick Start\n\n\u003e Do not install this server with `uvx telegram-mcp`, `uvx --from telegram-mcp`,\n\u003e or `pip install telegram-mcp`. The `telegram-mcp` name on PyPI is currently\n\u003e owned by a different project and does not install this repository. Passing\n\u003e `TELEGRAM_API_ID`, `TELEGRAM_API_HASH`, or `TELEGRAM_SESSION_STRING` to that\n\u003e package can expose Telegram account credentials to unrelated third-party code.\n\n### 1. Clone and Install\n\n```bash\ngit clone https://github.com/chigwell/telegram-mcp.git\ncd telegram-mcp\nuv sync\n```\n\n### 2. Generate a Session String\n\n```bash\nuv run session_string_generator.py\n```\n\nFollow the prompts. Save the generated session string securely.\n\nFor scripted setup or operational runbooks, choose the login method explicitly:\n\n```bash\n# QR login, recommended when you already have Telegram open on another device\nuv run session_string_generator.py --qr\n\n# Phone number + verification code login\nuv run session_string_generator.py --phone\n```\n\nWithout a flag, the generator keeps the interactive method prompt.\n\n### 3. Configure Environment\n\nCopy the example file and fill in your real values:\n\n```bash\ncp .env.example .env\n```\n\nSingle-account setup:\n\n```env\nTELEGRAM_API_ID=your_api_id_here\nTELEGRAM_API_HASH=your_api_hash_here\nTELEGRAM_SESSION_STRING=your_session_string_here\n```\n\nBy default, all Telegram MCP tools are exposed. If you want to prevent MCP\nclients from sending messages or performing chat/account mutations, set\n`TELEGRAM_EXPOSED_TOOLS=read-only` to expose only tools annotated with\n`readOnlyHint=True`:\n\n```env\nTELEGRAM_EXPOSED_TOOLS=read-only\n```\n\nIf read-only is too strict but `all` is too broad, append `+` and a\ncomma-separated list of tool names to also expose those specific write tools.\nEvery other write tool stays unregistered:\n\n```env\nTELEGRAM_EXPOSED_TOOLS=read-only+send_message,reply_to_message,send_file\n```\n\nAn unknown name in the allowlist aborts startup, so a typo cannot silently\ndegrade into a narrower surface that looks like it worked.\n\nThis is an MCP tool-surface restriction, not a Telegram session sandbox or\nreduced Telegram account permission. The Telegram session string still has its\nnormal authority inside the server process; read-only mode only prevents\nnon-read-only tools from being registered and exposed through MCP. Accepted\nvalues are `all` (the default), `read-only`, and `read-only+\u003ctool\u003e,\u003ctool\u003e`.\n\nRun the server locally:\n\n```bash\nuv run main.py\n```\n\n## MCP Client Configuration\n\nFor Claude Desktop or Cursor, point the MCP server at a cloned checkout of\nthis project:\n\n```json\n{\n  \"mcpServers\": {\n    \"telegram-mcp\": {\n      \"command\": \"uv\",\n      \"args\": [\n        \"--directory\",\n        \"/full/path/to/telegram-mcp\",\n        \"run\",\n        \"main.py\"\n      ],\n      \"env\": {\n        \"TELEGRAM_API_ID\": \"your_api_id_here\",\n        \"TELEGRAM_API_HASH\": \"your_api_hash_here\",\n        \"TELEGRAM_SESSION_STRING\": \"your_session_string_here\"\n      }\n    }\n  }\n}\n```\n\nTo expose only read-only tools in Claude Desktop or Cursor, add this to the\nserver `env` block:\n\n```json\n\"TELEGRAM_EXPOSED_TOOLS\": \"read-only\"\n```\n\nOr keep read-only as the baseline and allow a few write tools on top:\n\n```json\n\"TELEGRAM_EXPOSED_TOOLS\": \"read-only+send_message,reply_to_message\"\n```\n\nAlternatively, install this repository directly from GitHub into a virtual\nenvironment using a specific release tag or commit:\n\n```bash\npython -m venv .venv\n. .venv/bin/activate\npip install \"git+https://github.com/chigwell/telegram-mcp.git@\u003ctag-or-commit\u003e\"\n```\n\nThen configure your MCP client to run the installed console script:\n\n```json\n{\n  \"mcpServers\": {\n    \"telegram-mcp\": {\n      \"command\": \"/full/path/to/.venv/bin/telegram-mcp\",\n      \"env\": {\n        \"TELEGRAM_API_ID\": \"your_api_id_here\",\n        \"TELEGRAM_API_HASH\": \"your_api_hash_here\",\n        \"TELEGRAM_SESSION_STRING\": \"your_session_string_here\"\n      }\n    }\n  }\n}\n```\n\nGenerate a session string without cloning the repo by sourcing this repository\nfrom GitHub explicitly:\n\n```bash\nuvx --from \"git+https://github.com/chigwell/telegram-mcp.git@\u003cpinned-release-tag-or-commit\u003e\" telegram-mcp-generate-session\n```\n\n### Transports\n\nThe server speaks three MCP transports, selected with `MCP_TRANSPORT`:\n\n| Value   | Transport                  | Use case                                                        |\n| ------- | -------------------------- | --------------------------------------------------------------- |\n| `stdio` | stdio (default)            | One dedicated server process per MCP client                     |\n| `http`  | streamable HTTP            | One shared server for many clients (Claude Code, Codex, Cursor) |\n| `sse`   | SSE (legacy HTTP)          | Clients that only support the deprecated SSE transport          |\n\nFor `http` and `sse`, the server binds `MCP_HOST`:`MCP_PORT` (default\n`127.0.0.1:8765`); the streamable HTTP endpoint is `/mcp`, the SSE endpoint is\n`/sse`.\n\nIf the server is reachable via a domain (e.g. behind a reverse proxy) rather\nthan only `127.0.0.1`/`localhost`, set `MCP_ALLOWED_HOSTS` (and optionally\n`MCP_ALLOWED_ORIGINS`) to enable DNS-rebinding protection and allow that Host\nheader, e.g. `MCP_ALLOWED_HOSTS=mcp.example.com`. Comma-separated; supports a\n`:*` suffix to allow any port. Left unset, DNS-rebinding protection stays off\n(the historical default).\n\nPrefer `http` when more than one MCP client (or many coding-agent sessions)\nwill use the server: a single long-lived process holds one Telegram\nconnection, instead of every client spawning its own Telethon session —\nTelegram throttles and may flag accounts that open many parallel sessions.\n\nRegister the shared server with clients:\n\n```bash\n# Claude Code\nclaude mcp add --transport http telegram http://127.0.0.1:8765/mcp\n\n# Codex\ncodex mcp add telegram --url http://127.0.0.1:8765/mcp\n```\n\nFor stdio-only clients, bridge with [mcp-remote](https://www.npmjs.com/package/mcp-remote):\n\n```json\n{\n  \"mcpServers\": {\n    \"telegram-mcp\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"mcp-remote\", \"http://127.0.0.1:8765/mcp\"]\n    }\n  }\n}\n```\n\n## Multi-Account Setup\n\nUse suffixed session variables to configure multiple Telegram accounts:\n\n```env\nTELEGRAM_API_ID=your_api_id_here\nTELEGRAM_API_HASH=your_api_hash_here\nTELEGRAM_SESSION_STRING_WORK=session_string_for_work\nTELEGRAM_SESSION_STRING_PERSONAL=session_string_for_personal\n```\n\nLabels are lowercased and become the `account` parameter value in tools.\n\n- In single-account mode, `account` is optional.\n- In multi-account mode, write tools require `account`.\n- Read-only tools fan out to all accounts when `account` is omitted.\n\nExample prompts:\n\n- \"List my accounts\"\n- \"Show unread messages from all accounts\"\n\n### Session pool (one account, several concurrent clients)\n\nTo run several MCP clients against the **same** Telegram account at once (for\nexample the desktop app *and* a terminal CLI), give each client its own\nauthorized session. Telegram forbids one session (auth key) being used from two\nIPs simultaneously, so on a VPN or dual-stack host two local clients can collide\nwith `AuthKeyDuplicatedError`. List several interchangeable session strings in\n`TELEGRAM_SESSION_STRINGS` (separated by whitespace, comma or semicolon); each\nprocess claims a free one via an advisory file lock, so clients deterministically\npick distinct sessions:\n\n```env\nTELEGRAM_SESSION_STRINGS=\u003csession A\u003e \u003csession B\u003e \u003csession C\u003e\n```\n\nGenerate extra sessions with `uv run session_string_generator.py`. The pool\ntakes precedence over `TELEGRAM_SESSION_STRING` for the default account. As an\nextra safety net, a transient `AuthKeyDuplicatedError` at connect time (e.g.\nduring a VPN reconnect) is retried with backoff before the server gives up.\n\nSize the pool to the number of clients you actually run concurrently. If every\nslot is already claimed, the server refuses to start with an explicit error\nrather than reusing a session another client holds — reuse would make Telegram\npermanently invalidate that session for both clients.\n- \"Send this from my work account to @example\"\n\n## Device Identity\n\nThese optional variables control how the client appears in Telegram under\n**Settings \u003e Devices** (the active-sessions list):\n\n```env\nTELEGRAM_DEVICE_MODEL=Telegram MCP\nTELEGRAM_SYSTEM_VERSION=1.0\nTELEGRAM_APP_VERSION=1.0\n```\n\nIf left unset, Telethon falls back to the host platform (for example `arm64`).\nBecause these values are re-sent on every connection, a long-running server\nwould otherwise overwrite the name chosen during login on each reconnect, so\nset them to keep a stable, recognisable device name. The same variables are\nread both by the session string generator (at login) and by the server (on\nevery connect), so set them in the same place as your other credentials.\n\n## Proxy Support\n\nRoute Telegram traffic through a proxy by setting the `TELEGRAM_PROXY_*`\nenvironment variables. Supported types are `socks5`, `socks4`, `http`, and\n`mtproxy`.\n\nSOCKS and HTTP proxies require the optional `python-socks` package:\n\n```bash\nuv sync --extra proxy\n# or\npip install python-socks\n```\n\nSingle-account configuration:\n\n```env\nTELEGRAM_PROXY_TYPE=socks5\nTELEGRAM_PROXY_HOST=127.0.0.1\nTELEGRAM_PROXY_PORT=1080\nTELEGRAM_PROXY_USERNAME=optional_user\nTELEGRAM_PROXY_PASSWORD=optional_pass\nTELEGRAM_PROXY_RDNS=true\n```\n\nMTProxy:\n\n```env\nTELEGRAM_PROXY_TYPE=mtproxy\nTELEGRAM_PROXY_HOST=mtproxy.example\nTELEGRAM_PROXY_PORT=443\nTELEGRAM_PROXY_SECRET=ee0123456789abcdef...\n```\n\nPer-account overrides use the same `_\u003cLABEL\u003e` suffix as session variables and\ntake precedence over the unsuffixed defaults:\n\n```env\nTELEGRAM_PROXY_TYPE=socks5\nTELEGRAM_PROXY_HOST=127.0.0.1\nTELEGRAM_PROXY_PORT=1080\n\nTELEGRAM_PROXY_TYPE_WORK=http\nTELEGRAM_PROXY_HOST_WORK=proxy.work.example\nTELEGRAM_PROXY_PORT_WORK=3128\n```\n\nMisconfigured proxy settings (unknown type, missing host/port, invalid port,\nmissing MTProxy secret, or a missing `python-socks` package) cause the server\nto fail fast at startup with a clear error message instead of silently\nbypassing the proxy.\n\n## File Path Security\n\nFile-path tools are disabled until allowed roots are configured. This affects tools such as `send_file`, `download_media`, `upload_file`, `send_voice`, `send_sticker`, `set_profile_photo`, and `edit_chat_photo`.\n\nAllowed roots can come from:\n\n- Server CLI arguments, used as a fallback.\n- MCP client Roots, when supported by the client.\n\nSecurity behavior:\n\n- Client MCP Roots replace server CLI roots when available.\n- Some clients (notably Cursor) return workspace roots as bare absolute paths\n  instead of `file://` URIs. That breaks MCP SDK validation of `list_roots`;\n  the server recovers those absolute paths from the validation error so\n  file-path tools keep working.\n- Empty client Roots are treated as deny-all by default. Some clients implement\n  the Roots capability but advertise an empty list, which disables file tools\n  even when server CLI roots are configured. Set\n  `TELEGRAM_ALLOW_SERVER_ROOTS_FALLBACK=1` to fall back to the server CLI roots\n  in that case (opt-in; the default stays deny-all). The same opt-in also applies\n  when `list_roots` fails unexpectedly and no client paths could be recovered.\n- Paths are resolved through real paths and must stay inside an allowed root.\n- Traversal, wildcard-like, shell-like, and null-byte path patterns are rejected.\n- Relative paths resolve under the first allowed root.\n- Downloads default to `\u003cfirst_root\u003e/downloads/`.\n- Size and extension limits are enforced for sensitive media tools.\n\nRun with allowed roots:\n\n```bash\nuv run main.py /data/telegram /tmp/telegram-mcp\n```\n\nFrom an MCP client configuration, pass the same roots after `main.py`:\n\n```json\n{\n  \"mcpServers\": {\n    \"telegram-mcp\": {\n      \"command\": \"uv\",\n      \"args\": [\n        \"--directory\",\n        \"/full/path/to/telegram-mcp\",\n        \"run\",\n        \"main.py\",\n        \"/data/telegram\",\n        \"/tmp/telegram-mcp\"\n      ],\n      \"env\": {\n        \"TELEGRAM_API_ID\": \"your_api_id_here\",\n        \"TELEGRAM_API_HASH\": \"your_api_hash_here\",\n        \"TELEGRAM_SESSION_STRING\": \"your_session_string_here\"\n      }\n    }\n  }\n}\n```\n\n## Docker\n\nBuild the image:\n\n```bash\ndocker build -t telegram-mcp:latest .\n```\n\n### Shared server (recommended)\n\nRun one long-lived container serving streamable HTTP, and point every MCP\nclient at it (see [Transports](#transports) for client registration):\n\n```bash\ndocker run -d --name telegram-mcp --restart unless-stopped \\\n  --env-file .env \\\n  -e MCP_TRANSPORT=http \\\n  -e MCP_HOST=0.0.0.0 \\\n  -p 127.0.0.1:8765:8765 \\\n  telegram-mcp:latest\n```\n\n`MCP_HOST=0.0.0.0` binds inside the container so the published port works;\n`-p 127.0.0.1:8765:8765` keeps the server reachable only from the local\nmachine — the endpoint is unauthenticated, so never publish it on a public\ninterface.\n\nThe bundled Compose file runs the same setup:\n\n```bash\ndocker compose up --build -d\n```\n\n### One container per client (stdio)\n\nAlternatively, an MCP client can spawn a dedicated container itself:\n\n```json\n{\n  \"mcpServers\": {\n    \"telegram-mcp\": {\n      \"command\": \"docker\",\n      \"args\": [\"run\", \"-i\", \"--rm\", \"--env-file\", \"/full/path/to/.env\", \"telegram-mcp:latest\"]\n    }\n  }\n}\n```\n\nThis is fine for a single client, but with several clients (or coding agents\nthat spawn subagent sessions) each one starts its own container and its own\nTelegram session, which Telegram throttles; a client that exits uncleanly can\nalso leave its container running. Prefer the shared server above in those\nsetups.\n\nFor multiple accounts, pass variables such as `TELEGRAM_SESSION_STRING_WORK` and `TELEGRAM_SESSION_STRING_PERSONAL`.\n\n## Development\n\nThe implementation is split into a small compatibility entrypoint and modular package code:\n\n```text\nmain.py                    # historical entrypoint and compatibility exports\ntelegram_mcp/runtime.py    # shared MCP setup, account routing, validation, file safety\ntelegram_mcp/runner.py     # application startup\ntelegram_mcp/tools/        # tool modules grouped by domain\nsanitize.py                # output sanitization helpers\ntests/                     # pytest suite\n```\n\nRun tests:\n\n```bash\nuv run pytest\n```\n\nRun tests with coverage:\n\n```bash\nuv run pytest --cov --cov-report=term-missing --cov-report=xml\n```\n\nCoverage is configured in `pyproject.toml` with an 80% minimum gate for deterministic unit-testable core modules. GitHub Actions runs the same coverage command and uploads `coverage.xml`.\n\nRun formatting checks:\n\n```bash\nuv run black --check .\nuv run flake8 .\n```\n\n## Security Notes\n\n- Never commit `.env`, session strings, or `.session` files.\n- A Telegram session string grants access to the account it belongs to.\n- The `telegram-mcp` package name on PyPI is not controlled by this project.\n  Avoid PyPI-based `telegram-mcp` install commands unless ownership changes and\n  the package is verified.\n- This repository includes a best-effort startup guard that refuses installed\n  `telegram-mcp` distributions without a source checkout or direct git/file\n  install record. That guard cannot run when the unrelated PyPI package itself\n  is launched, so use clone-based or explicit git installs.\n- Prefer session strings over file sessions when running multiple server instances.\n- By default, Telegram API calls go directly from your machine/container to Telegram.\n  If `TELEGRAM_PROXY_*` is configured, Telegram traffic is routed through the\n  configured SOCKS/HTTP/MTProxy proxy instead.\n- User-generated Telegram content is sanitized before being returned to MCP clients.\n\n### Prompt Injection Protection\n\nTelegram messages, display names, chat titles, and button labels are untrusted content. The server mitigates prompt-injection risk with:\n\n- Structured JSON output for user-controlled data where practical.\n- `sanitize_user_content()`, `sanitize_name()`, and `sanitize_dict()` for control-character stripping, invisible-character stripping, and length limits.\n- MCP content annotations marking returned content as user audience data.\n- Tool descriptions that warn clients not to treat returned Telegram fields as model instructions.\n- No brittle keyword-based filtering.\n\n## Troubleshooting\n\n- **No Telegram session configured:** set `TELEGRAM_SESSION_STRING`, `TELEGRAM_SESSION_NAME`, or suffixed multi-account variants.\n- **Session is not authorized:** run `uv run session_string_generator.py --qr` outside\n  the MCP server when you can scan from an existing Telegram app, or\n  `uv run session_string_generator.py --phone` when you need phone-code login.\n  Then set `TELEGRAM_SESSION_STRING` in `.env`. The MCP server does not perform\n  interactive phone-code login over stdio.\n- **Invalid API credentials:** verify `TELEGRAM_API_ID` and `TELEGRAM_API_HASH` at [my.telegram.org/apps](https://my.telegram.org/apps).\n- **Database is locked:** prefer string sessions, or make sure no other process is using the same file session.\n- **`AuthKeyDuplicatedError` / \"Another telegram-mcp process is already connected with this session\":** two processes tried to connect the same Telegram session at once (e.g. an MCP client restarted the connector before the old process exited), which Telegram rejects and can invalidate the session for both. The server now takes an exclusive lock per session before connecting; a second concurrent launch waits briefly (default 20s, override with `TELEGRAM_LOCK_GRACE_SECONDS`) for the first to release it and otherwise exits without ever calling `connect()`, instead of racing into a duplicate connection. Retry once only one instance is running.\n- **File tools are disabled:** pass allowed roots or configure MCP Roots in your client.\n- **Path rejected:** ensure the path is inside an allowed root and does not use traversal or wildcard patterns.\n- **Auth errors after password changes:** regenerate your session string.\n- **Bot-only tool rejected:** regular user accounts cannot manage bot command settings.\n- **Need details:** check your MCP client logs, terminal output, and `mcp_errors.log`.\n\n## Contributing\n\n1. Fork and clone the repository.\n2. Install dependencies and git hooks:\n   - `uv sync`\n   - `uv run pre-commit install --hook-type pre-commit --hook-type pre-push`\n3. Create a focused branch.\n4. Add or update tests when behavior changes.\n5. Run checks locally:\n   - `uv run pre-commit run --all-files`\n   - `uv run pre-commit run --hook-stage pre-push --all-files`\n6. Open a pull request with a concise description.\n\n## License\n\nThis project is licensed under the [Apache 2.0 License](LICENSE).\n\n## Acknowledgements\n\n- [Telethon](https://github.com/LonamiWebs/Telethon)\n- [Model Context Protocol](https://modelcontextprotocol.io/)\n- [Claude](https://www.anthropic.com/) and [Cursor](https://cursor.so/)\n- [chigwell/telegram-mcp](https://github.com/chigwell/telegram-mcp) upstream project\n\nMaintained by [@chigwell](https://github.com/chigwell) and [@l1v0n1](https://github.com/l1v0n1). PRs welcome.\n\n## Star History\n\n[![Star History Chart](https://api.star-history.com/svg?repos=chigwell/telegram-mcp\u0026type=Date)](https://www.star-history.com/#chigwell/telegram-mcp\u0026Date)\n\n## Contributors\n\n\u003ca href=\"https://github.com/chigwell/telegram-mcp/graphs/contributors\"\u003e\n  \u003cimg src=\"https://contrib.rocks/image?repo=chigwell/telegram-mcp\" /\u003e\n\u003c/a\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchigwell%2Ftelegram-mcp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchigwell%2Ftelegram-mcp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchigwell%2Ftelegram-mcp/lists"}