tg-mcp-guarded
Provides tools for interacting with the Telegram API, enabling AI agents to perform read-only analytics (e.g., fetching participants, messages, groups, dialogs) and guarded write actions (e.g., sending messages, joining groups) with built-in rate limiting, anti-spam protection, and session management.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@tg-mcp-guardedlist my recent direct messages"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
tg-mcp (tg-mcp-guarded)
MCP server + Python library for Telegram API with built-in rate limiting, anti-spam protection, and session management.
Dual-plane Telegram MCP: read-only analytics + guarded write actions with anti-spam and block-risk controls.
What is this?
tg-mcp provides:
Two MCP servers —
ReadandActionsprofiles with different risk levelsRate limiting — Token bucket (4 RPS), daily quotas (20 DM/day, 20 joins/day)
Multi-process protection — optional shared RPS limiter across projects/processes
Anti-spam — FLOOD_WAIT retry with exponential backoff
Circuit breaker — auto cooldown after long FLOOD_WAIT events
Session security — chmod 700/600 hardening for session files
Data exporters — participants, messages, groups, dialogs
Related MCP server: MCP Telegram
Positioning
tg-mcp-guarded emphasizes what makes this project different from generic Telegram MCP integrations:
Risk split by design — separate
tgmcp-readandtgmcp-actionsserversGuarded write path — write operations only via Action MCP policy gates
Block-risk awareness — anti-flood, anti-spam, retry/backoff, and circuit-breaker defaults
Quick Start
# Install dependencies
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
# Configure
cp .env.sample .env
# Edit .env with your TG_API_ID, TG_API_HASH
# Run tests
PYTHONPATH=tganalytics:. python3 -m pytest tests/ -qDeveloper Setup Shortcut
For local development, you can bootstrap everything (env sync, dependencies, and required data directories) with one command:
make dev-setupBefore opening a PR, run the standard repository checks:
make dev-checkSession Bootstrap (Auth)
read profile remains write-safe, but initial Telegram login requires auth requests.
Use dedicated auth bootstrap helpers:
# Interactive code login (enables auth-only requests internally)
PYTHONPATH=tganalytics:. python3 tganalytics/examples/create_telegram_session.py \
--session-name my_read
# QR login fallback (more reliable when code is delayed)
PYTHONPATH=tganalytics:. python3 scripts/create_session_qr.py \
--session-name my_readWhere to find login code:
Telegram app in-app message (most common,
SentCodeTypeApp)Archived chats folder
Another active device/session notifications
Secrets Without .env (Keychain / Secret Provider)
TG_API_ID and TG_API_HASH can be loaded from secrets providers:
TG_SECRET_PROVIDER=env(default): read plain env valuesTG_SECRET_PROVIDER=keychain: load from macOS KeychainTG_SECRET_PROVIDER=command: load from command outputs
macOS Keychain example
security add-generic-password -a TG_API_ID -s tg-mcp -w "12345678" -U
security add-generic-password -a TG_API_HASH -s tg-mcp -w "your_api_hash" -USet env:
TG_SECRET_PROVIDER=keychain
TG_KEYCHAIN_SERVICE=tg-mcp
TG_KEYCHAIN_ACCOUNT_API_ID=TG_API_ID
TG_KEYCHAIN_ACCOUNT_API_HASH=TG_API_HASHGeneric command provider example
TG_SECRET_PROVIDER=command
TG_SECRET_CMD_API_ID="pass tg/api_id"
TG_SECRET_CMD_API_HASH="pass tg/api_hash"Agent Onboarding
Start here if this repository will be used by other AI agents/operators:
Agent Playbook - execution flows, error handling, batch workflow
Action Policy Toggles - what can be toggled and safe change procedure
Anti-spam and Security Model - deeper technical details
Installation Profiles
Use one repository, choose profile by risk level:
readprofile: installs onlytgmcp-read(recommended default for new users)fullprofile: installstgmcp-read+tgmcp-actions
Generate config:
# prereq check
bash scripts/check_tg_mcp.sh /absolute/path/to/tg-mcp
# read-only config (safe default)
python3 scripts/render_mcp_config.py \
--repo /absolute/path/to/tg-mcp \
--profile read \
--read-session-name my_account_ro
# full config (read + actions)
python3 scripts/render_mcp_config.py \
--repo /absolute/path/to/tg-mcp \
--profile full \
--read-session-name my_account_ro \
--actions-session-name my_account
# preflight: fail fast on same-session misconfig
python3 scripts/check_session_paths.py \
--read-session-path /absolute/path/to/tg-mcp/data/sessions/my_account_ro.session \
--actions-session-path /absolute/path/to/tg-mcp/data/sessions/my_account.sessionGlobal MCP Setup (All Projects in Codex)
For one global setup across all working directories, add servers via codex mcp add.
Recommended default: separate session files for read and actions.
Routine concurrent read+actions traffic on one Telethon sqlite session can trigger database is locked.
REPO="/absolute/path/to/tg-mcp"
codex mcp add tgmcp-read \
--env PYTHONPATH="$REPO/tganalytics:$REPO" \
--env TG_SESSIONS_DIR="$REPO/data/sessions" \
--env TG_SESSION_PATH="$REPO/data/sessions/my_account_ro.session" \
--env TG_READ_SESSION_PATH="$REPO/data/sessions/my_account_ro.session" \
--env TG_ACTIONS_SESSION_PATH="$REPO/data/sessions/my_account.session" \
--env TG_SESSION_PATH_CONFLICT_MODE=warn \
--env TG_SESSION_CONFLICT_REGISTRY_FILE="$REPO/data/anti_spam/session_registry.json" \
--env TG_EXPECTED_USERNAME="my_main_account" \
--env TG_ALLOW_SESSION_SWITCH=0 \
--env TG_BLOCK_DIRECT_TELETHON_WRITE=1 \
--env TG_ALLOW_DIRECT_TELETHON_WRITE=0 \
--env TG_ENFORCE_ACTION_PROCESS=1 \
--env TG_DIRECT_TELETHON_WRITE_ALLOWED_CONTEXTS=actions_mcp \
--env TG_WRITE_CONTEXT=read_mcp \
--env TG_ACTION_PROCESS=0 \
--env TG_SESSION_RUNTIME_MODE=copy \
--env TG_RECEIVE_UPDATES=0 \
--env TG_SESSION_LOCK_MODE=shared \
--env TG_GLOBAL_RPS_MODE=shared \
-- "$REPO/venv/bin/python3" "$REPO/tganalytics/mcp_server_read.py"Optional actions server:
REPO="/absolute/path/to/tg-mcp"
codex mcp add tgmcp-actions \
--env PYTHONPATH="$REPO/tganalytics:$REPO" \
--env TG_SESSIONS_DIR="$REPO/data/sessions" \
--env TG_SESSION_PATH="$REPO/data/sessions/my_account.session" \
--env TG_READ_SESSION_PATH="$REPO/data/sessions/my_account_ro.session" \
--env TG_ACTIONS_SESSION_PATH="$REPO/data/sessions/my_account.session" \
--env TG_SESSION_PATH_CONFLICT_MODE=warn \
--env TG_SESSION_CONFLICT_REGISTRY_FILE="$REPO/data/anti_spam/session_registry.json" \
--env TG_EXPECTED_USERNAME="my_main_account" \
--env TG_ALLOW_SESSION_SWITCH=0 \
--env TG_ACTIONS_ENABLED=1 \
--env TG_ACTIONS_REQUIRE_ALLOWLIST=1 \
--env TG_ACTIONS_ALLOWED_GROUPS="-1001234567890" \
--env TG_ACTIONS_REQUIRE_CONFIRMATION_TEXT=1 \
--env TG_ACTIONS_REQUIRE_APPROVAL_CODE=1 \
--env TG_ACTIONS_IDEMPOTENCY_ENABLED=1 \
--env TG_BLOCK_DIRECT_TELETHON_WRITE=1 \
--env TG_ALLOW_DIRECT_TELETHON_WRITE=0 \
--env TG_ENFORCE_ACTION_PROCESS=1 \
--env TG_DIRECT_TELETHON_WRITE_ALLOWED_CONTEXTS=actions_mcp \
--env TG_WRITE_CONTEXT=actions_mcp \
--env TG_ACTION_PROCESS=1 \
--env TG_RECEIVE_UPDATES=0 \
--env TG_SESSION_LOCK_MODE=shared \
--env TG_GLOBAL_RPS_MODE=shared \
-- "$REPO/venv/bin/python3" "$REPO/tganalytics/mcp_server_actions.py"MCP Servers
Add to your project's .mcp.json:
{
"mcpServers": {
"tgmcp-read": {
"command": "path/to/tg-mcp/venv/bin/python3",
"args": ["path/to/tg-mcp/tganalytics/mcp_server_read.py"],
"env": {
"PYTHONPATH": "path/to/tg-mcp/tganalytics:path/to/tg-mcp",
"TG_SESSIONS_DIR": "path/to/tg-mcp/data/sessions",
"TG_SESSION_PATH": "path/to/tg-mcp/data/sessions/my_account_ro.session",
"TG_READ_SESSION_PATH": "path/to/tg-mcp/data/sessions/my_account_ro.session",
"TG_ACTIONS_SESSION_PATH": "path/to/tg-mcp/data/sessions/my_account.session",
"TG_SESSION_PATH_CONFLICT_MODE": "warn",
"TG_SESSION_CONFLICT_REGISTRY_FILE": "path/to/tg-mcp/data/anti_spam/session_registry.json",
"TG_EXPECTED_USERNAME": "my_main_account",
"TG_ALLOW_SESSION_SWITCH": "0",
"TG_BLOCK_DIRECT_TELETHON_WRITE": "1",
"TG_ALLOW_DIRECT_TELETHON_WRITE": "0",
"TG_ENFORCE_ACTION_PROCESS": "1",
"TG_DIRECT_TELETHON_WRITE_ALLOWED_CONTEXTS": "actions_mcp",
"TG_WRITE_CONTEXT": "read_mcp",
"TG_ACTION_PROCESS": "0",
"TG_SESSION_RUNTIME_MODE": "copy",
"TG_RECEIVE_UPDATES": "0",
"TG_SESSION_LOCK_MODE": "shared",
"TG_GLOBAL_RPS_MODE": "shared"
}
},
"tgmcp-actions": {
"command": "path/to/tg-mcp/venv/bin/python3",
"args": ["path/to/tg-mcp/tganalytics/mcp_server_actions.py"],
"env": {
"PYTHONPATH": "path/to/tg-mcp/tganalytics:path/to/tg-mcp",
"TG_SESSIONS_DIR": "path/to/tg-mcp/data/sessions",
"TG_SESSION_PATH": "path/to/tg-mcp/data/sessions/my_account.session",
"TG_READ_SESSION_PATH": "path/to/tg-mcp/data/sessions/my_account_ro.session",
"TG_ACTIONS_SESSION_PATH": "path/to/tg-mcp/data/sessions/my_account.session",
"TG_SESSION_PATH_CONFLICT_MODE": "warn",
"TG_SESSION_CONFLICT_REGISTRY_FILE": "path/to/tg-mcp/data/anti_spam/session_registry.json",
"TG_EXPECTED_USERNAME": "my_main_account",
"TG_ALLOW_SESSION_SWITCH": "0",
"TG_ACTIONS_ENABLED": "1",
"TG_ACTIONS_REQUIRE_ALLOWLIST": "1",
"TG_ACTIONS_ALLOWED_GROUPS": "my_safe_group,-1001234567890",
"TG_ACTIONS_MAX_MESSAGE_LEN": "2000",
"TG_ACTIONS_MAX_FILE_MB": "20",
"TG_ACTIONS_REQUIRE_CONFIRMATION_TEXT": "1",
"TG_ACTIONS_CONFIRMATION_PHRASE": "",
"TG_ACTIONS_MIN_CONFIRM_TEXT_LEN": "6",
"TG_ACTIONS_REQUIRE_APPROVAL_CODE": "1",
"TG_ACTIONS_APPROVAL_TTL_SEC": "1800",
"TG_ACTIONS_APPROVAL_MIN_AGE_SEC": "30",
"TG_ACTIONS_APPROVAL_FILE": "data/anti_spam/action_approvals.json",
"TG_ACTIONS_IDEMPOTENCY_ENABLED": "1",
"TG_ACTIONS_IDEMPOTENCY_WINDOW_SEC": "86400",
"TG_ACTIONS_BATCH_FILE": "data/anti_spam/action_batches.json",
"TG_ACTIONS_BATCH_TTL_HOURS": "168",
"TG_ACTIONS_BATCH_APPROVAL_LEASE_SEC": "86400",
"TG_ACTIONS_BATCH_RUN_LEASE_SEC": "1800",
"TG_ACTIONS_UNSAFE_OVERRIDE": "0",
"TG_BLOCK_DIRECT_TELETHON_WRITE": "1",
"TG_ALLOW_DIRECT_TELETHON_WRITE": "0",
"TG_ENFORCE_ACTION_PROCESS": "1",
"TG_DIRECT_TELETHON_WRITE_ALLOWED_CONTEXTS": "actions_mcp",
"TG_WRITE_CONTEXT": "actions_mcp",
"TG_ACTION_PROCESS": "1",
"TG_RECEIVE_UPDATES": "0",
"TG_SESSION_LOCK_MODE": "shared",
"TG_GLOBAL_RPS_MODE": "shared",
"TG_FLOOD_CIRCUIT_THRESHOLD_SEC": "300",
"TG_FLOOD_CIRCUIT_COOLDOWN_SEC": "900",
"MAX_GROUP_MSGS_PER_DAY": "30"
}
}
}
}mcp_server.py is kept as backward-compatible alias to mcp_server_read.py.
Read Tools
Tool | Description |
| List available Telegram sessions |
| Switch active session |
| Get group/channel/direct dialog info |
| Export group members |
| Search members by query |
| Export messages from a dialog target |
| Export messages newest-to-oldest until a cutoff date |
| Get dialog message count |
| Get dialog first-message date |
| List account dialogs |
| Resolve username to entity |
| Get user by numeric ID |
| Download media from message |
| Anti-spam system stats |
| Check current session authorization status |
Actions Tools
Tool | Description |
| List available Telegram sessions |
| Switch active session (if enabled) |
| Validate target group |
| Browse possible targets |
| Resolve target username |
| Send message with anti-spam + policy gates ( |
| Send local file with anti-spam + policy gates ( |
| Delete specific messages in a dialog/group ( |
| Clear dialog history with |
| Leave an allowlisted channel/group ( |
| Edit a message in a dialog/group ( |
| Forward messages between allowlisted dialogs ( |
| Add user to group/channel ( |
| Remove user from group/channel ( |
| Add new user + remove old user in one safe flow (same confirm + approval gates on execution) |
| Build one add-member batch for many groups (single approval for whole task) |
| Build batch from failed groups in JSON report |
| Build delete batch from a reviewed privacy scrubber manifest |
| Build leave batch from reviewed channel candidates |
| One-time approve a batch |
| Check batch progress and pending groups |
| Run approved batch in chunks (no per-group approvals) |
| Run approved delete batch in chunks |
| Run approved leave-dialog batch in chunks |
| Show active action restrictions |
| Anti-spam system stats |
| Check current session authorization status |
Session Concurrency
Default mode is
TG_SESSION_LOCK_MODE=shared: multiple MCP servers/projects can use one.session.tgmcp-readnow defaults toTG_SESSION_RUNTIME_MODE=copy: each MCP process reads from its own runtime shadow of the configured session file, which avoids sqlite lock collisions across multiple Codex clients.Optional strict mode:
TG_SESSION_LOCK_MODE=exclusiveblocks concurrent use of the same session.Shared session is still allowed, but NOT recommended for routine concurrent
tgmcp-read+tgmcp-actions.Recommended default:
tgmcp-read-> dedicated*_ro.sessiontgmcp-actions-> dedicated write session (for exampleaccount.session)If both profiles point to one sqlite session file, Telethon can fail with
database is locked.Set
TG_READ_SESSION_PATH+TG_ACTIONS_SESSION_PATHin both server envs so runtime can warn/fail on same-session misconfig.TG_SESSION_PATH_CONFLICT_MODE=warnprints startup/runtime warning; setfailto block server start on same-session conflict.Run
python3 scripts/check_session_paths.py --config /path/to/.mcp.jsonbefore enabling both servers.If a Codex thread did not expose native
mcp__tgmcp_actions__*tools, usepython3 scripts/tg_action_bridge.py ...as a shell fallback. The bridge still talks tomcp_server_actions.pyover MCP/JSON-RPC, defaults toTG_SESSION_RUNTIME_MODE=copy, and preserves the same allowlist/approval/confirm policy.TG_EXPECTED_USERNAMEenables fail-fast on session/account mismatch (@expectedvs actual account in session).TG_RECEIVE_UPDATES=0(default) disables Telethon updates loop to reduce sqlite session lock contention.TG_GLOBAL_RPS_MODE=sharedapplies one shared RPS budget across all processes using the samedata/anti_spam.TG_FLOOD_CIRCUIT_THRESHOLD_SEC+TG_FLOOD_CIRCUIT_COOLDOWN_SECpause all calls after critical FLOOD_WAIT.Non-dry-run write actions require
confirm=trueand exactconfirmation_text(TG_ACTIONS_CONFIRMATION_PHRASE).Non-dry-run write actions also require one-time
approval_codefrom the matchingdry_runpreview (TG_ACTIONS_REQUIRE_APPROVAL_CODE=1).approval_codehas minimum age (TG_ACTIONS_APPROVAL_MIN_AGE_SEC, default 30s): immediate execute right after dry_run is blocked.Action MCP blocks duplicate sends/actions by payload hash for 24h (
TG_ACTIONS_IDEMPOTENCY_*), unlessforce_resend=true.Action state files (
approval/idempotency/batch) now use file locks + atomic write, so parallel ActionMCP processes do not corrupt JSON state.Batch execution uses a per-batch run lease lock (
TG_ACTIONS_BATCH_RUN_LEASE_SEC) to avoid duplicate processing of the same batch by two workers.For long tasks, batch mode supports scoped approval:
tg_create_add_member_batch->tg_approve_batch-> repeattg_run_add_member_batchuntil complete.Batch run permission is time-limited (
TG_ACTIONS_BATCH_APPROVAL_LEASE_SEC, default 24h). After lease expiry, re-approve the same batch.ActionMCP is fail-closed by default: weakening core safe flags auto-disables actions unless
TG_ACTIONS_UNSAFE_OVERRIDE=1.If allowlist is required, ActionMCP also fails closed when
TG_ACTIONS_ALLOWED_GROUPSis empty.Direct
TelegramClient.send_*writes and raw MTProto write requests (client(Request)) are blocked by default.Use
tgmcp-actionstools for any write operation.In strict mode (
TG_ENFORCE_ACTION_PROCESS=1), write is allowed only when process entrypoint ismcp_server_actions.py.For hard session isolation, run MCP under a dedicated OS user and keep
data/sessionsowned by that user only.
Fallback bridge examples:
# list tools from ~/.codex/config.toml server tgmcp_actions
python3 scripts/tg_action_bridge.py tools
# raw call
python3 scripts/tg_action_bridge.py call tg_get_actions_policy --args-json '{}'
# canonical write flow: dry_run -> approval_code -> confirm
python3 scripts/tg_action_bridge.py write-call tg_send_message \
--args-json '{"group":"@mybot","message_text":"hello"}'Structure
tganalytics/
├── tganalytics/ # Core package
│ ├── infra/ # Clients, rate limiting, metrics
│ ├── domain/ # GroupManager, participants
│ └── config/ # Configuration
├── mcp_server.py # Backward-compatible read alias
├── mcp_server_read.py # Read-focused MCP server
├── mcp_server_actions.py # Actions-focused MCP server
├── mcp_server_common.py # Shared state/session helpers
├── mcp_actions_policy.py # Policy helpers (allowlist/hash/confirmation/safe defaults)
├── mcp_actions_state.py # Locked JSON state helpers (approval/idempotency/batch)
├── mcp_actions_batch.py # Batch record + summary helpers
└── examples/ # Usage examplesArchitecture
All Telegram API calls go through a 5-layer protection chain:
_safe_api_call → safe_call → TokenBucket → Telegram API
↓ ↓ ↓
DM/join quotas 4 RPS FLOOD_WAIT retry + backoffSee docs/ANTISPAM_SECURITY.md for details.
License
MIT. See LICENSE.
Governance
This is an open-source project with maintainer-led merge policy:
external contributors are welcome via issues and pull requests
commits to
mainare performed by maintainer only
See CONTRIBUTING.md.
This server cannot be deployed
Maintenance
Related MCP Connectors
Multi-tenant Telegram gateway for AI agents — HTTP+stdio, 8 tools, MTProto User API
Telegram bridge for your MCP-compatible agent. Bidirectional, no LLM in our stack.
Unified messaging MCP server: WhatsApp, Instagram, Telegram, SMS, Messenger & email support inbox
Read-only MCP server for The Quiet Protocol's engines, benchmarks, proof, and business data.
Related MCP Servers
- AlicenseNot gradedqualityFmaintenanceProduction-grade MCP server for Telegram with dual-mode Bot API and MTProto. 6 composite tools covering messages, chats, media, contacts management with 3-tier token optimization.532 PyPI11Apache 2.0
- AlicenseNot gradedqualityBmaintenanceTelegram MTProto-based MCP server with 19 tools for reading, searching, sending, forwarding, and summarizing messagesMIT
- AlicenseNot gradedqualityDmaintenancePrivacy-first Telegram MCP server enabling maintainers to triage chats, inspect context, search messages, draft replies, and send authorized messages locally without a cloud relay.32 npm1MIT
- AlicenseNot gradedqualityAmaintenanceA safe-by-default MCP server for real Telegram accounts powered by TDLib, enabling AI agents to read and act on your account with read-only mode and human approval for destructive actions.8Apache 2.0