Skip to main content
Glama

TeleMCP

Telegram intelligence and action layer for the Model Context Protocol.

TeleMCP turns Telegram conversations, groups, channels, files and links into a searchable, auditable knowledge layer that an AI agent can query — and, with a human in the loop, act on.

  • Telegram Bot API (webhook) and MTProto personal mode, cleanly separated

  • 40 MCP tools, 7 resources, 4 prompts

  • Hybrid search: PostgreSQL full text + trigram + pgvector semantic search

  • Inbox AI: remember a message, get it back by meaning months later

  • Human approval for everything that writes; reinforced approval for deletes

  • Full audit trail, per-chat policy, deny by default, multi-tenant isolation

  • Fly.io for compute, Neon PostgreSQL for durable state

How it behaves

Three rules explain most of the design:

  1. Reading is free, writing is not. Every WRITE tool returns APPROVAL_REQUIRED on its first call, with an approval id. A human approves; the agent calls again with the same arguments plus approval_id. Changing a single character invalidates the approval. Approvals are single-use and expire in minutes.

  2. Chats are deny-by-default. Connecting an account indexes nothing. A chat is read only after it is explicitly allowlisted.

  3. Message content is data, never instruction. Retrieved text is fenced before it reaches a model and flagged when it looks like prompt injection. The policy engine never reads content at all, which is what makes the defence real rather than advisory.

Related MCP server: agent-telegram-mcp

Quick start

git clone <your-fork> && cd telemcp
cp .env.example .env          # then fill it in — see "Configuration"
uv sync                       # or: pip install -e ".[dev,files]"

Bring up PostgreSQL with the required extensions:

docker run -d --name telemcp-db \
  -e POSTGRES_USER=telemcp -e POSTGRES_PASSWORD=telemcp -e POSTGRES_DB=telemcp \
  -p 5432:5432 pgvector/pgvector:pg16

Apply the schema and start the two processes:

python scripts/migrate.py upgrade
uvicorn app.main:app --host 0.0.0.0 --port 8080   # web
python -m app.jobs.worker                          # worker

Check it is alive:

curl localhost:8080/healthz    # {"status":"ok"}
curl localhost:8080/readyz     # database, extensions, migration revision

Connect a Telegram bot

  1. Create the bot with @BotFather and copy its token into TELEGRAM_BOT_TOKEN.

  2. Generate a webhook secret and set TELEGRAM_WEBHOOK_SECRET plus TELEGRAM_WEBHOOK_BASE_URL (your public HTTPS base URL).

  3. Register the webhook:

python scripts/create_bot_webhook.py
  1. Send /start to the bot. It replies with your Telegram user id; put it in TELEGRAM_OWNER_IDS and restart. Ownership is declared, never inferred — otherwise anyone in a group could change what gets indexed.

  2. Add the bot to a chat and send /allowchat. Nothing is indexed before that. /privacy shows exactly what is stored.

Connect the MCP endpoint

The endpoint is https://<host>/mcp (Streamable HTTP) and accepts two kinds of credential, because clients differ in what they can send.

A shared bearer token, for clients that support custom headers — Claude Code among them:

claude mcp add --transport http telemcp https://telemcp.fly.dev/mcp \
  --header "Authorization: Bearer $MCP_AUTH_SECRET"

OAuth 2.1, for clients that only speak OAuth — ChatGPT's custom connectors among them. Add the server URL in the client; it discovers the authorization server, registers itself, and sends you to a consent page. Approve it there with your MCP_AUTH_SECRET and the client receives a token of its own.

Both paths land on the same policy engine: an OAuth client is constrained by the scopes it was granted, and every write still needs human approval.

Personal mode (optional)

Personal mode connects your Telegram account over MTProto so history can be backfilled. It never bypasses Telegram's permissions — it sees only what your account can already see, and only in allowlisted chats.

python scripts/create_personal_session.py   # prompts for phone, code, 2FA

The session string is sealed with AES-256-GCM under SESSION_ENCRYPTION_KEY before it touches the database, is never logged, and can be destroyed at any time with the telegram_revoke_personal_session tool.

The approval flow, concretely

agent: telegram_send_message(chat_id, text)
   ↓
{"ok": false, "error": "APPROVAL_REQUIRED",
 "approval": {"id": "…", "summary": "send message · chat -100… · “…”"}}
   ↓
human: POST /approvals/{id}/approve
   ↓
agent: telegram_send_message(chat_id, text, approval_id="…")
   ↓
{"ok": true, "data": {"message_id": "…", "sent_at": "…"}}

GET /approvals lists what is waiting. Every step is audited.

Tool catalogue

Group

Tools

Messages

send_message, reply_message, get_message, get_recent_messages, search_messages, search_semantic, search, get_context, forward_message, delete_message

Chats

get_chat, list_known_chats, get_group_messages, get_channel_posts, get_thread

Files

download_file, send_file, send_photo, list_files, find_documents

Updates / Polls

get_updates, create_poll

Intelligence

summarize_chat, summarize_channel, summarize_thread, find_links, find_mentions, find_decisions, extract_tasks, extract_people, extract_topics

Inbox AI

remember_message, save_item, list_saved_items, search_saved_items, forget_item

Admin

sync_chat, reindex_chat, get_sync_status, revoke_personal_session

All names are prefixed telegram_. Approval requirements are listed in SECURITY.md.

Resources: telegram://chat/{id}, …/recent, telegram://message/{chat}/{id}, telegram://thread/{chat}/{id}, telegram://channel/{id}, telegram://file/{id}/metadata, telegram://saved/{id}.

Prompts: telegram_chat_digest, telegram_decision_review, telegram_action_items, telegram_research_digest.

Configuration

Everything is environment-driven; .env.example documents every key. The ones that matter most:

Variable

Meaning

DATABASE_URL

Neon pooled connection, used by web and worker

DATABASE_URL_DIRECT

Neon direct connection, used by migrations

MCP_AUTH_SECRET

Bearer token for /mcp and /approvals (≥32 chars)

TELEGRAM_WEBHOOK_SECRET

Verified on every webhook call

SESSION_ENCRYPTION_KEY

base64 32 bytes; seals MTProto sessions

CHAT_DEFAULT_POLICY

deny (keep it)

MCP_ALLOWED_HOSTS

Hostnames the MCP endpoint answers to; required in production

OAUTH_ISSUER

Public HTTPS base URL, used by OAuth discovery and consent

TELEGRAM_OWNER_IDS

Telegram ids allowed to run /allowchat

EMBEDDING_PROVIDER

hash (local, no key) or openai

LLM_PROVIDER

heuristic (local, no key) or openai

Running without any API keys

EMBEDDING_PROVIDER=hash and LLM_PROVIDER=heuristic are the defaults, and they are not stubs: hashed embeddings give a real, deterministic vector space, and the intelligence tools fall back to deterministic extraction. Every tool works end to end with no external AI provider. Each summary reports which path produced it in its engine field. Switch to openai when you want model-quality semantics.

Deploying to Fly.io

fly launch --no-deploy
fly secrets set \
  DATABASE_URL="…" DATABASE_URL_DIRECT="…" \
  MCP_AUTH_SECRET="…" TELEGRAM_BOT_TOKEN="…" \
  TELEGRAM_WEBHOOK_SECRET="…" SESSION_ENCRYPTION_KEY="…"
fly deploy
fly scale count web=1 worker=1
fly status && fly checks list

fly.toml runs migrations as the release command, so a deploy cannot serve traffic against an unmigrated schema. The worker is only required for embeddings, enrichment and personal mode — but without it, semantic search stays empty.

Neon setup

  1. Create the project and a telemcp database. Pick a region near your Fly region — gru pairs with São Paulo. Leave Neon Auth off: TeleMCP authenticates through its own bearer credential and provisions its own users, so Neon Auth would add unused tables.

  2. Copy both connection strings (pooled and direct).

  3. CREATE EXTENSION vector; CREATE EXTENSION pg_trgm; CREATE EXTENSION pgcrypto;

  4. python scripts/migrate.py upgrade

Verified against PostgreSQL 16. Newer majors work provided pgvector is available; if CREATE EXTENSION vector fails, recreate the project on 17.

Connect the application as a non-superuser role. Superusers bypass row level security, and RLS is what enforces tenant isolation at the database level. See SECURITY.md.

Operating it

python scripts/manage_policies.py list                     # the allowlist
python scripts/manage_policies.py allow -100123456         # index a chat
python scripts/manage_policies.py set -100123456 --write   # permit sending
python scripts/reindex.py                                  # rebuild the index

Bot commands: /start, /help, /status, /privacy, /remember, /search, /recent, /summary, /decisions, /tasks, /saved, and for the owner /allowchat, /denychat, /link, /unlink, /reindex.

/metrics exposes Prometheus counters and histograms: tool calls and errors, messages ingested, search latency, queue depth, approval requests.

Architecture

MCP client ──HTTP──▶ web process ──▶ Neon PostgreSQL ◀── worker process
                     ├ MCP server                       ├ job runner
                     ├ Telegram webhook                 ├ embeddings
                     └ approval API                     └ MTProto

Fly.io runs compute; Neon holds all durable state. The web process is stateless, so it scales and restarts freely.

Every tool call takes exactly one path:

authenticate → resolve principal → tool policy → scope → rate limit
             → chat policy → approval → execute → audit

A tool implementation never re-implements a check, and no tool can skip one.

Development

pip install -e ".[dev,files]"
ruff check .
pytest tests/unit tests/contract tests/security -q      # no services needed

docker run -d --name telemcp-test -p 5433:5432 \
  -e POSTGRES_USER=telemcp -e POSTGRES_PASSWORD=telemcp \
  -e POSTGRES_DB=telemcp_test pgvector/pgvector:pg16
export TEST_DATABASE_URL=postgresql+asyncpg://telemcp:telemcp@localhost:5433/telemcp_test
pytest -q                                                # everything

262 tests: unit, contract (every tool's schema, class and annotations), security (injection, SSRF, webhook spoofing, replay, tenant isolation) and integration against a real PostgreSQL with pgvector.

Project status

V0.1. See STATUS.md for the Definition of Done, item by item, and what is deliberately deferred.

License

Apache-2.0.

A
license - permissive license
Not graded
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables AI agents to read, send, and organize Telegram messages and chats. Supports tools for listing chats, fetching messages, sending/reply, archiving, muting, and folder management.
    1
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    Connect any AI agent to your personal Telegram messages through the official Business API, enabling message history search and draft replies with optional manual approval.
    7
    14
    MIT

View all related MCP servers

Related MCP Connectors

  • Persistent memory and knowledge management for AI agents with semantic search and 50+ tools.

  • Your company's brain for AI agents. Cited, permission-aware knowledge across every system.

  • Universal memory for AI agents and tools. Save, organize and search context anywhere.

View all MCP Connectors

Latest Blog Posts

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/charlesmmorais/telemcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server