better-telegram-mcp
CLAUDE.md11 starsChanged 21 days agoApache-2.0View on GitHub
MCP Server cho Telegram. Python 3.13, uv, hatchling, src layout. Dual-mode: Bot API (httpx) + MTProto (Telethon). 7 tools: message, chat, media, contact, config, help, config_openrelay. Stdio mode (default): local single-user mode. Bot mode uses TELEGRAMBOTTOKEN; user mode can be configured with the local auth CLI (login is a deprecated alias). HTTP mode (--http / MCPTRANSPORT=http): mcp-core runhttpserver serves the local OAuth AS at /authorize. The custom credential form collects bottoken OR phone, posts to /save, and chains OTP /…
We split this CLAUDE.md into the rules it gives an agent
1 rule. About 0 are common, 1 is distinctive, and 0 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
0
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 88
Commit message: enforce Conventional Commits
≈ “Common” is an estimate: at least 1 in 5 of the 25,680 CLAUDE.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
CLAUDE.md - telegram
MCP Server cho Telegram. Python 3.13, uv, hatchling, src layout. Dual-mode: Bot API (httpx) + MTProto (Telethon). 7 tools: message, chat, media, contact, config, help, config__open_relay.
Commands
uv sync --group dev # Setup
uv run ruff check . # Lint
uv run ruff format --check . # Format check
uv run ty check # Type check
uv run pytest # Test (integration excluded)
uv run ruff check --fix . && uv run ruff format . # FixCau truc
src/telegram/
config.py # Pydantic Settings, env prefix TELEGRAM_
server.py # FastMCP + lifespan + auth mode switching
credential_state.py # save_credentials/on_step_submitted callbacks +
# _current_sub contextvar (multi-user routing)
auth/
telegram_auth_provider.py # Per-sub Telethon backend lifecycle
in_memory_session_store.py # bearer/sub -> SessionInfo (RAM only)
backends/ # TelegramBackend ABC -> BotBackend (httpx), UserBackend (Telethon)
backends/security.py # validate_url, validate_file_path, validate_output_dir
relay_setup.py # Shared error sanitization, field constants
relay_schema.py # Relay schema + TELEGRAM_TABS layout + render_telegram_form
# (Bot/User tabs drawn by mcp-core render_credential_form)
transports/
http.py # _start_single_user_http + _start_multi_user_http
# (both via mcp-core run_http_server)
credential_store.py# Master-secret resolution helper
tools/ # messages, chats, media, contacts, config_tool, helpAuth flow
Stdio mode (default): local single-user mode. Bot mode uses
TELEGRAM_BOT_TOKEN; user mode can be configured with the local auth CLI
(login is a deprecated alias).
HTTP mode (--http / MCP_TRANSPORT=http): mcp-core run_http_server
serves the local OAuth AS at /authorize. The custom credential form
collects bot_token OR phone, posts to /save, and chains OTP / 2FA via
/otp.
- Single-user: writes to
config.enc, hot-reloads the global Telethon backend inserver.py. - Multi-user remote (
PUBLIC_URL+MCP_DCR_SERVER_SECRETset; legacyDCR_SERVER_SECRETstill accepted): per-sub Telethon backends managed byTelegramAuthProvider; the auth_scope middleware (_per_request_sub_scope) pins the JWT sub + per-user backend into contextvars before each MCP tool call.
Session persist: ~/.telegram/<name>.session, permission 600 (moved once from the pre-rename ~/.better-telegram-mcp/ by config.resolve_default_data_dir(); ~/.telegram-mcp written by mcp-core PerPluginStore is a separate dir, unchanged).
Env vars
TELEGRAM_BOT_TOKEN-- bot modeTELEGRAM_API_ID+TELEGRAM_API_HASH-- user mode (built-in defaults, override optional)TELEGRAM_PHONE-- phone for local auth or the HTTP relay formTELEGRAM_SESSION_NAME,TELEGRAM_DATA_DIR-- optionalMCP_TRANSPORT/TRANSPORT_MODE-- set tohttpto opt into HTTP mode (default wire transport is stdio);--httpCLI flag does the samePUBLIC_URL-- deployed hostname; presence flips on the multi-user OAuth branch (with the DCR secret + api_id/api_hash)MCP_DCR_SERVER_SECRET-- multi-user remote OAuth shared secret (withPUBLIC_URL); legacyDCR_SERVER_SECRETstill accepted
No TELEGRAM_AUTH_URL -- auth is not configured through a URL environment
variable. HTTP user setup is served by mcp-core's OAuth AS and relay form,
locally or under PUBLIC_URL; local single-user setup uses the auth CLI.
NO TELEGRAM_PASSWORD -- HTTP relay 2FA nhap qua web UI; local CLI auth prompts interactively, KHONG luu env.
Release & Deploy
- Conventional Commits. Tag format:
v{version} - CD: PSR v10 -> PyPI
n24q02m-telegram(uv publish) -> Docker multi-arch (amd64 + arm64) -> MCP Registry - Docker images:
n24q02m/telegram(Docker Hub) +ghcr.io/n24q02m/telegram
Pre-commit hooks
- Ruff lint (
--fix) + format - ty type check
- pytest (
--tb=short -q) - Commit message: enforce Conventional Commits
Luu y
- Config tool:
status|set|cache_clear|setup_*(HTTP user auth lives in mcp-core's OAuth AS; local single-user auth uses theauthCLI) - Security: SSRF, path traversal, error sanitization, rate limiting on relay
- Secrets: skret SSM namespace
/better-telegram-mcp/prod(regionap-southeast-1)
Known bugs
(All 2026-04-18 E2E findings have been resolved. Section retained as a short audit trail with the fix commit so future investigators do not re-open closed issues.)
- Bot token leak via httpx INFO logs -- RESOLVED.
server.py:24setslogging.getLogger("httpx").setLevel(logging.WARNING)so request URLs (which include the bot token) never reach stderr at INFO level. - User mode OTP/2FA multi-step flow -- RESOLVED. mcp-core's shared
credential form (schema-level
tabs, mcp-core >=1.20.0b1) renders the Bot/User tabs; itsshowStepInputJS handlesnext_step.type === "otp_required"+password_required. Wired into mcp-core's local OAuth AS viarun_http_server(..., custom_credential_form_html=render_telegram_form, on_step_submitted=on_step_submitted)—render_telegram_form(relay_schema.py) declaresTELEGRAM_TABS+ theinitial_tabhint and delegates rendering torender_credential_form. After phone submit, the same page re-renders the OTP input, posts to/otp, and chains to the 2FA password step if Telethon reports it. The forkedcredential_form.pywas dropped once the tabs capability landed in mcp-core. - Setup-complete UI redirect -- RESOLVED via mcp-core
feedback_relay_form_must_follow_redirect.mdfix; the form followsredirect_urlinstead of showing a static "close tab" page.
E2E
Driven by mcp-core/scripts/e2e/ (matrix-locked, 15 configs). Run a single config from this repo via make e2e (proxy) or directly:
cd ../mcp-core && uv run --project scripts/e2e python -m e2e.driver <config-id>Configs for this repo: telegram-bot, telegram-user.
telegram-user is t2-interaction (browser-form OTP + 2FA, 600s timeout).
Tier policy:
- T0 (precommit + CI on PR / main push) - runs without upstream identity. Skret keys not required.
- T2 non-interaction (
make e2e-config CONFIG=<id>locally) - driver pre-fills relay form from skret AWS SSM/better-telegram-mcp/prod(ap-southeast-1). No user gate. - T2 interaction - driver fills relay form, then prints upstream user-gate URL; user signs in / types OTP at provider. Driver enforces per-flow timeouts (device-code 900s, oauth-redirect 300s, browser-form 600s) and emits
[poll] elapsed=Xs remaining=Ys status=<body>every 30s. On timeout, container logs + lastsetup-statusare saved to<tmp>/e2e-diag/BEFORE teardown for post-mortem.
Multi-user remote mode (deployment property; not a separate config) requires MCP_DCR_SERVER_SECRET (legacy DCR_SERVER_SECRET still accepted) in the same skret namespace - driver refuses to start the container without it when PUBLIC_URL is set.
References: mcp-core/scripts/e2e/matrix.yaml, ~/.claude/skills/mcp-dev/references/e2e-full-matrix.md (harness-readiness gate), ~/.claude/skills/mcp-dev/references/secrets-skret.md (per-server credential layout), ~/.claude/skills/mcp-dev/references/multi-user-pattern.md (per-JWT-sub isolation).
In this repository3 other files it gives its agents.
- AGENTS.mdAGENTS.md
- channel-postSkill
- setup-botSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

