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 "Install 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 installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/matskevich/tg-mcp-guarded'
If you have feedback or need assistance with the MCP directory API, please join our Discord server