Telegram MCP Server

MCP serverCommunication

This app connects your AI to Telegram and gives it 110 ready-made actions to work with. It runs locally on your own machine, keeps separate Telegram accounts isolated from one another, and includes guardrails built for production use.

Unavailable. This server has no hosted endpoint yet, so ahel can't serve it.

Add the app, then connect your Telegram account and tell your AI what you want done in Telegram.

What your AI can do with it

  • Work with Telegram through 110 built-in actions
  • Run locally on your own machine
  • Keep multiple Telegram accounts isolated from each other
  • Operate within production guardrails while tasks run

From the project's README

As published by tolboy/telegram-mcp-tdlib in README.md.

Telegram MCP for real accounts — safe by default, TDLib-powered, production-ready.

A local-first Model Context Protocol server that gives an AI agent real Telegram-account access without handing it the keys to your account. It starts read-only, hides write tools until you opt in, and runs over STDIO for desktop clients or Streamable HTTP at /mcp for managed deployments.

Why this one?

  • Safe by default — boots in read-only mode with a small inbox/reader profile; write and quota-consuming tools are hidden from the model, not merely blocked, until you turn them on.
  • Human approval that does not depend on your client — destructive tools ask a person first: through the MCP host where elicitation exists, otherwise on a single-use loopback page this server hosts. Either way the question leaves and the answer returns outside the model's turn, so an injected instruction can make the model request a ban, not approve one.
  • Real user accounts, not just bots — built on TDLib (via tdlight-java), so an agent can read and act on your actual account, not only a Bot API subset.
  • Isolated multi-account — each account gets its own session, mandatory selection, and scoped API keys; reads never fan out across accounts.
  • Two transports — STDIO for Claude Desktop / Cursor / VS Code / Codex, Streamable HTTP for a managed service.
  • No JDK to install — runtime-inclusive release bundles for Windows, Linux x64/ARM64, and Apple-silicon macOS, with checksums, an SBOM, and signed container digests.

The image is an illustrative mockup; a real screencast is on the way.

Safe first run

Runtime-inclusive releases need no JDK, Gradle, Git, Python, or Node.js. Use a test account for your first run if you can.

1. Install (macOS Apple silicon / Linux with Homebrew):

brew install --formula https://github.com/tolboy/telegram-mcp-tdlib/releases/latest/download/telegram-mcp.rb

Windows with Scoop:

scoop install https://github.com/tolboy/telegram-mcp-tdlib/releases/latest/download/telegram-mcp.json

2. Log in with a QR scan — the one-time code never touches your shell history:

telegram-mcp auth --method qr

3. Check the session is ready — this reads local state and starts no server:

telegram-mcp session doctor

Do not run serve --transport stdio yourself to try it out. With STDIO your AI client starts the server and owns its lifetime; a copy you launched in a terminal holds the TDLib session, and the client's own copy then exits with code 2 because it cannot lock td.binlog. Give the command to the client instead (below) and let it do the starting.

Then try a first prompt that cannot change anything:

“Summarize my last 20 conversations. Do not send or modify anything.”

In this mode write and quota-consuming tools are absent from the tool list entirely, so the model has nothing destructive to call. Switch to MCP_READ_ONLY=false only after you have reviewed the surface. When you do, add MCP_DESTRUCTIVE_APPROVAL=auto so deletes, bans and leaves wait for your answer through host elicitation where available, or through the server's loopback page otherwise, rather than trusting the model's own "confirmed": true.

Connect your client

Generate the entry instead of transcribing one — it fills in the key your client actually expects and pins the version it was generated from:

telegram-mcp config --client claude

--client claude-code|cursor|vscode|codex switches target — Codex gets TOML, VS Code gets the servers key it actually reads, Claude Code gets the explicit transport type. --writes enables write tools and the approval prompt that guards the destructive ones — pair it with a write-capable --profile inbox|community-admin|all, since the default reader surface hides write tools before read-only mode is consulted. --docker default emits a container entry with a pinned image, and --http default emits the shared-daemon entry for Claude Code, Cursor, VS Code, or Codex. Claude Desktop does not read remote servers from claude_desktop_config.json; add a network-reachable remote endpoint under Settings → Connectors instead. Claude remote connectors do not accept the generator's custom API-key header, so this server must be exposed over HTTPS with MCP_AUTH_MODE=oauth. The generator therefore rejects --client claude --http.

STDIO is the low-friction path for desktop clients. The generated entry looks like:

{
  "mcpServers": {
    "telegram": {
      "command": "telegram-mcp",
      "args": ["serve", "--transport", "stdio"],
      "env": {
        "TDLIB_API_ID": "123456",
        "TDLIB_API_HASH_FILE": "/absolute/path/to/telegram-api-hash",
        "MCP_TOOL_PROFILE": "inbox",
        "MCP_READ_ONLY": "true"
      }
    }
  }
}
  • Claude Desktop — add the block above to claude_desktop_config.json, then quit the app from the tray and start it again. Use Settings → Developer → Edit Config to open the file: it points at the right copy for your build. Editing %APPDATA%\Claude by hand works only for the installer build — the Microsoft Store build is packaged as MSIX and keeps its config in %LOCALAPPDATA%\Packages\Claude_pzs8sxrjxfjjc\LocalCache\Roaming\Claude\ instead.
  • Cursor — add it to ~/.cursor/mcp.json (or Settings → MCP → Add).
  • VS Code — use .vscode/mcp.json; VS Code names the top-level key servers instead of mcpServers and requires an explicit "type": "stdio" or "type": "http".

For a managed HTTP deployment instead of STDIO:

docker run --rm -p 127.0.0.1:8080:8080 \
  -e TDLIB_API_ID -e TDLIB_API_HASH -e MCP_API_KEY \
  ghcr.io/tolboy/telegram-mcp-tdlib:latest

See CLI and STDIO, interactive authentication, and client compatibility for exact platform/client variants.

Recipes

Copy-paste configurations and prompts for real tasks, each with the smallest tool surface that can do the job: summarize your inbox, find a lost message, research public groups, read-only community health check, and draft replies without sending — index in docs/recipes.

How it compares

Most Telegram MCP servers wrap the Bot API or a Telethon user session and expose every capability to the model at once. This project optimizes for connecting an agent to a real account you care about, safely:

DimensionThis serverTypical Telethon / Bot-API MCP servers
Account accessReal user account via TDLib (tdlight-java)Often bot-only, or a single Telethon user session
Default postureRead-only; write/quota tools hidden until enabledUsually all tools exposed from the start
Multi-accountIsolated sessions, mandatory selection, scoped keys, no cross-account read fan-outSingle account, or implicit fan-out
TransportsSTDIO and Streamable HTTP /mcpUsually STDIO only
GuardrailsConfirmation gating, audit log, anti-spam limits, chat allow-list, untrusted-content markingMinimal
DistributionSigned runtime bundles (no JDK), SBOM + provenance, GHCR imageSource install via pip/npx

A fuller architectural comparison (TDLib vs Telethon vs Bot API, and why "hidden, not blocked" matters) is in docs/COMPARISON.md. The detailed, dated benchmark against the leading public servers — including features deliberately declined (raw MTProto escape hatch, ownership transfer, bulk contact export) — is in PUBLIC_BENCHMARK_AND_ROADMAP.md.

Tool profiles

You don't expose 110 tools on day one. MCP_TOOL_PROFILE narrows the advertised surface before a client ever sees it, without weakening account scoping, read-only mode, confirmation, audit, or anti-spam:

ProfileSurface
readerAlways non-mutating — safe for a first look
inboxPersonal messages, drafts, media, contacts, privacy
community-adminModeration, group/channel, permissions, bot commands
researchBounded account/public discovery and reading
allThe full inventory (opt in deliberately)

See TOOL_PROFILES.md for the exact intent of each surface.

Features

  • 110 MCP tools — messages, polls, read receipts, scheduled sends, chats, folders, invite-link administration, contacts, media, drafts, privacy, bot commands, detailed group permissions, profile, search, and account routing.
  • TDLib via tdlight-java — real user accounts, not just the Bot API.
  • Isolated multi-account mode — independent sessions, mandatory account selection, and optional per-key account scopes with no cross-account read fan-out.
  • Safe by default — read-only tool surface, human approval for destructive actions (MCP_DESTRUCTIVE_APPROVAL, over the host or a loopback page), and task-focused profiles (reader/inbox/community-admin/research/all).
  • Two transports — STDIO for desktop clients and Streamable HTTP /mcp (Spring AI 2.0 / MCP SDK 2.0), with API-key auth.
  • Guardrails — audit logging, anti-spam via Resilience4j rate limiter (30 req/s) and circuit breaker, chat allow-list, prompt-injection patterns, and untrusted-content marking.
  • Observability — Micrometer metrics, Prometheus endpoint, and structured JSON logging with MDC (traceId, sessionId, toolName).
  • Verified, runtime-inclusive releases — Windows x64, Linux x64/ARM64, and Apple-silicon macOS bundles with checksums, an SBOM, and signed container digests — no JDK required.
  • Entity resolution — resolve @username, +phone, or numeric IDs transparently.
  • MCP behavior annotations — every advertised tool declares read-only, destructive, retry, and open-world hints for safer client UX.
  • Structured, marked output — backward-compatible JSON text plus structuredContent; Telegram-controlled fields are explicitly untrusted and presentation-control Unicode is escaped.
  • Host-friendly discovery via /.well-known/mcp-server.json for installers, desktop hosts, and service managers.
  • File security service for safe media uploads; Actuator health/info/metrics; graceful shutdown with configurable timeout.
  • Multi-stage Docker build + docker-compose with dev hot-reload.
  • Runtime-inclusive app images — the supported release archives include their own Java runtime and pass an actual STDIO handshake before publication.
  • Offline session doctor — inspect configured TDLib state paths and lock availability without starting TDLib or printing secrets.
  • Clean Architecture — config / model / client / service / tool / security / util / exception.
  • Language-neutral public search — callers pass synonyms, translations, and spelling variants in any language; product-specific policy interpretation stays in the MCP host.

Tech Stack

ComponentVersion
Java25
Kotlin2.4.0
Spring Boot4.1.0
Spring AI MCP2.0.0
MCP Java SDK2.0.0
TDLib (tdlight)3.5.3+td.1.8.65
Gradle9.6.1
Resilience4j2.4.0

Quick Start

Prerequisites

  • Java 25+ (or use Docker)
  • Telegram API credentials from https://my.telegram.org (TDLIB_API_ID, TDLIB_API_HASH)
  • Exactly one authentication mode: TDLIB_PHONE_NUMBER for user-account mode or TDLIB_BOT_TOKEN for bot mode

1. Clone and configure

git clone https://github.com/tolboy/telegram-mcp-tdlib.git
cd telegram-mcp-tdlib
cp .env.example .env
# Edit .env — set TDLIB_API_ID, TDLIB_API_HASH, one of TDLIB_PHONE_NUMBER/TDLIB_BOT_TOKEN, and MCP_API_KEY

On Windows PowerShell, use Copy-Item .env.example .env instead of cp.

2. Run locally

./gradlew bootRun

The server starts on http://localhost:8080. Its MCP Streamable HTTP endpoint is http://localhost:8080/mcp.

Your first 60 seconds

  1. Add a remote Streamable HTTP MCP server in your client with URL http://127.0.0.1:8080/mcp and Authorization: Bearer <MCP_API_KEY>. Cursor, VS Code, Codex, Claude Code, and Inspector can use that local HTTP endpoint. Claude Desktop's config file supports the local STDIO entry above; its remote connectors are added through Settings → Connectors and require a network-reachable HTTPS endpoint with MCP_AUTH_MODE=oauth, rather than this loopback URL plus a custom header. Exact connection data is kept in MCP_CLIENT_COMPATIBILITY.md.

  2. Start safely with MCP_READ_ONLY=true and one focused surface, for example MCP_TOOL_PROFILE=inbox or MCP_TOOL_PROFILE=research.

  3. Ask the client one of these concrete first questions:

    • Inbox: “Summarize my last 20 relevant conversations. Do not send or modify anything.”
    • Community: “Show recent admin actions and default permissions in this group; propose changes, but do not apply them.”
    • Research: “Find public chats matching these English and Russian query variants, and report evidence from descriptions and recent messages.”

Switch to MCP_READ_ONLY=false only after reviewing the discovered surface; confirmation remains enabled by default for destructive actions.

On Windows, Gradle outputs default to the system temp directory to avoid file-lock issues in synchronized folders. Set KTM_BUILD_DIR or pass -Pktm.buildDir=... to override.

3. Run with Docker

docker compose up --build

The compose stack persists TDLib session data in the tdlib-data volume, downloads in telegram-downloads, and mounts ${MCP_UPLOADS_DIR:-./docker-data/uploads} into the container as /data/uploads for upload/download tools.

By default, compose binds the service to 127.0.0.1 via MCP_BIND_HOST so the local boxed/developer scenario is not exposed on the LAN by accident.

Dev mode with hot-reload:

docker compose --profile dev up telegram-mcp-dev

With Prometheus monitoring:

docker compose --profile monitoring up

Production note: terminate TLS in front of the container (reverse proxy, ingress, or load balancer). API keys should not traverse plaintext HTTP outside trusted local development networks.

Deployment guidance for local boxed installs vs remote/VPS exposure is documented in docs/DEPLOYMENT_MODES.md.

Connector discovery for a multi-server MCP host or installer is documented in docs/CONNECTOR_DISCOVERY.md.

Client compatibility, a portable JSON-Schema profile, and a Streamable HTTP smoke test are documented in MCP_CLIENT_COMPATIBILITY.md.

Offline-safe TDLib session inspection and clearing are documented in SESSION_MAINTENANCE.md.

The comparison with the leading public Telegram MCP servers and the prioritized follow-up work are documented in PUBLIC_BENCHMARK_AND_ROADMAP.md.

Releases

CI runs on every pull request and push to master. A signed-off release is a Git tag in the vX.Y.Z form; the release workflow builds the images, runs the cross-platform tool-surface smoke and the container lifecycle contract against the pushed digest, attests and signs it, and only then moves the public tags (latest, X.Y.Z, X.Y, immutable sha-<commit>, and the -stdio variants) onto that exact digest — verifying afterwards that each tag resolves to it. Release bundles for Windows x64, Linux x64/ARM64, and macOS ARM64 ship alongside. Public releases include an SPDX SBOM for the runnable JAR, GitHub provenance attestations for release assets, and a keyless Sigstore signature plus provenance for the container digest. See release-bundle verification.

Use a concrete semver tag for reproducible deployments. v1.0.0 is the first Streamable HTTP / MCP SDK 2.0 public baseline; v1.1.0 adds account isolation, scoped keys, and cross-platform native packaging; v1.2.0 adds premium voice-note transcription and the neutral package namespace; v1.3.0 adds privacy, bot-command, detailed group-permission controls, and verified release bundles; v1.4.0 adds focused MCP tool profiles; v1.7.x adds the STDIO transport, CLI with an interactive auth wizard, structured tool output, optional OAuth resource-server mode, and runtime-inclusive release images; v1.13.0 adds human approval for destructive tools over the host or a loopback page, and telegram-mcp config to generate client entries; v1.14.0 ties every published image tag to the digest that passed verification and signing; v1.15.0 makes the running build checkable from the connector's own answer; v1.16.0 fixes interactive login: authorization requests actually reach TDLib, a second login attempt no longer aborts the process, and the auth state says where Telegram delivered the code. The complete history is in CHANGELOG.md.

To find out which release is actually answering, call the _manifest tool: the response carries serverVersion next to schemaVersion. It is read from the jar's own build metadata — the Gradle version derived from the release tag — so MCP_SERVER_VERSION cannot rewrite it, and telegram-mcp version on the same artifact prints the same string.

The current TDLight native release publishes an Apple-silicon macOS binary but not an Intel macOS classifier. The server supports Intel macOS at the JVM/path level, but TDLib-backed Telegram access on that platform requires an upstream native package before it can run.

To create the runtime-inclusive app image for the current matching platform:

./scripts/package-app-image.ps1 -Version <version> -Target windows-x64 -OutputDirectory release-assets

The build verifies the actual BOOT-INF/lib contents, launcher-reported version, and a live STDIO handshake. See RELEASE_BUNDLES.md for the target matrix.

git checkout master
git pull --rebase origin master
git tag -a vX.Y.Z -m "Telegram MCP Server vX.Y.Z"
git push origin master vX.Y.Z

Environment Variables

TDLib (primary Telegram client)

VariableRequiredDefaultDescription
TDLIB_API_IDYes*API ID from https://my.telegram.org
TDLIB_API_HASHYes*API hash from https://my.telegram.org
TDLIB_API_HASH_FILENoSecret-file alternative to TDLIB_API_HASH
TDLIB_PHONE_NUMBERNo†Phone for user-account mode (full API access)
TDLIB_BOT_TOKENNo†Bot token for bot mode
TDLIB_BOT_TOKEN_FILENoSecret-file alternative to bot token
TDLIB_2FA_PASSWORDNo2FA password (supply for headless startup)
TDLIB_2FA_PASSWORD_FILENoSecret-file alternative to 2FA password
TDLIB_AUTH_CODENoOne-time login code (supply for headless first run)
TDLIB_AUTH_CODE_FILENoSecret-file alternative to auth code
TDLIB_DATA_DIRNoOS app-data dirSession database directory
TDLIB_DOWNLOADS_DIRNo(under data dir)Directory for downloaded media
TDLIB_LOG_VERBOSITYNo1TDLib native log verbosity (0–10)
TDLIB_SYSTEM_LANGUAGE_CODENoenLanguage code reported for the Telegram session
TDLIB_DEVICE_MODELNoTelegram MCP ServerRecognizable device label shown in Telegram session settings
TDLIB_PROXY_TYPENosocks5, http (HTTP CONNECT), or mtproto
TDLIB_PROXY_SERVER / TDLIB_PROXY_PORTWith proxyProxy hostname/IP and port
TDLIB_PROXY_USERNAMENoSOCKS5/HTTP proxy username (requires password too)
TDLIB_PROXY_PASSWORD / _FILENoSOCKS5/HTTP proxy password; _FILE is the mounted-secret alternative
TDLIB_PROXY_SECRET / _FILEMTProto onlyMTProto secret; _FILE is the mounted-secret alternative
TDLIB_PROXY_HTTP_ONLYNofalseRestrict an HTTP proxy to HTTP requests; normally leave false for CONNECT

* Required when TDLib integration is enabled.
† Set exactly one: TDLIB_PHONE_NUMBER or TDLIB_BOT_TOKEN.

Non-interactive auth: on a fresh container, set TDLIB_AUTH_CODE once (plus TDLIB_2FA_PASSWORD if 2FA is enabled). After the session persists to TDLIB_DATA_DIR, neither is needed on restart. Every direct secret setting has a mutually exclusive *_FILE alternative for Docker/Podman/Kubernetes secret mounts.

For a local first-time login without putting the one-time code in the environment, use the phone/code or QR flow documented in INTERACTIVE_AUTH.md. TDLIB_DEVICE_MODEL and TDLIB_SYSTEM_LANGUAGE_CODE control the recognizable session identity shown under Telegram Settings → Devices.

At-rest encryption note: SimpleTelegramClient (tdlight-java) does not expose the TDLib database-encryption key. For production, encrypt the TDLIB_DATA_DIR volume at the filesystem level (LUKS, FileVault, EFS) rather than relying on a library-level flag. Additional use-file-database, use-chat-info-database, use-message-database toggles are available in TdLibProperties.kt.

Telegram proxy

The server configures TDLib's own proxy before login, so it covers both initial authentication and normal Telegram traffic. Use one of these complete configurations; incomplete or invalid proxy settings make startup fail before the account can connect.

# SOCKS5 with optional username/password authentication
TDLIB_PROXY_TYPE=socks5
TDLIB_PROXY_SERVER=proxy.example.net
TDLIB_PROXY_PORT=1080
TDLIB_PROXY_USERNAME=connector
TDLIB_PROXY_PASSWORD_FILE=/run/secrets/telegram_proxy_password

# HTTP CONNECT (set HTTP_ONLY=false to route all TDLib traffic through it)
# TDLIB_PROXY_TYPE=http
# TDLIB_PROXY_SERVER=proxy.example.net
# TDLIB_PROXY_PORT=3128
# TDLIB_PROXY_HTTP_ONLY=false

# MTProto (secret must be supplied directly or through *_FILE)
# TDLIB_PROXY_TYPE=mtproto
# TDLIB_PROXY_SERVER=proxy.example.net
# TDLIB_PROXY_PORT=443
# TDLIB_PROXY_SECRET_FILE=/run/secrets/telegram_mtproto_secret

Shortened here. Read the whole README on GitHub.

Signals

GitHub stars
6
Forks
1
Last commit
Sep 2026

ahel review

  • S4info
    community integration — published by tolboy, not telegram

Automated review, not a security audit. Ruleset v1.

Advanced
Delivery
telegram-mcp-tdlib MCP server → your ahel gateway (mcp.ahel.ai) → every connected AI client.
Catalog kind
mcp-server
Gateway key
io-github-tolboy-telegram-mcp-tdlib
Source
github.com/tolboy/telegram-mcp-tdlib