telegram-ai-cli-mcp

Open source · MIT · Python 3.12+

CLI and MCP server for AI agents on a personal Telegram account

Read chats, search messages and resolve usernames over MTProto from Claude Code, Claude Desktop, Codex or Cursor; every write is a plan a person applies.

Get started Read the docsView on GitHub

What it does

What it does

CommandAnswers
tg-ai fleetWhich accounts are configured, authorized, expired or locked right now
tg-ai chatsWhich chats exist, and what their chat id is (search by title)
tg-ai chat readWhat was said in a chat — paged with --before-id, filtered to one forum topic with --topic-id, with media metadata
tg-ai chat topicsWhich topics a forum supergroup has, and where the unread messages are
tg-ai inboxWhat's waiting for a reply right now, across every configured account
tg-ai watchWait for the next incoming message instead of polling for it — a burst of fast replies comes back as one answer, and the wait is capped
tg-ai searchWhich messages match a phrase, and where — with --context N, the messages either side of each match
tg-ai whoisWho a @username, numeric id or invite resolves to; for channels, the "message the admins" Direct Messages chat — its name, and an id you can read or send to without joining anything
tg-ai chat membersWho's in a chat, and who administers it
tg-ai media fetchSave a message's photo, video or document to a server-controlled path
tg-ai media transcribeTurn a voice message into text — Whisper in an optional container, on your own machine, with the network switched off
tg-ai archive syncCopy one named chat into a local SQLite archive, resuming where the last run stopped
tg-ai archive searchSearch that archive offline — substring or regular expression, by sender, by date — with no Telegram request at all
tg-ai archive statusWhich chats are archived, how many messages, and when each was last synced
tg-ai archive forgetErase one chat, and every message of it, from the local archive
tg-ai draftsWhat was started and never sent, and in which chat
tg-ai scheduledWhat is queued to be sent to a chat later, and when
tg-ai account sessionsWhich devices and apps this account is signed in on — and whether one of them isn't yours
tg-ai message send / message send-file / chat join / chat promote / …Validate and save an intent — send a message or a file, edit, delete, forward, join, leave, create a group, invite, promote, change the profile — as a plan. Nothing goes out yet
tg-ai chat ban / unban / kick / restrict / demoteModeration, planned the same way: one member per plan, and the preview says who, where, which rights and for how long
tg-ai message scheduleQueue a message for an exact time — or for when the other person is next online — as a plan. Once applied it sits in Telegram's own scheduled queue, visible and cancellable in the app
tg-ai chat archive / chat mutePut a chat in Archived, or silence it for a while or indefinitely — settings only this account can see, planned like every other write
tg-ai folder addPut a chat into one of the folders the account already has. Telegram replaces a folder wholesale when it is edited, so everything already in it is preserved verbatim — including the chats this configuration may not list
tg-ai plan list / plan show <id>See what's waiting for a decision, and exactly what applying it would do
tg-ai plan apply <id>Carry out exactly what a saved plan describes
tg-ai plan reject <id>Decline a pending plan
tg-ai daemon serve --account <label>Hold one account open so several local callers share it instead of the second one being refused
tg-ai mcp / tg-ai mcp --httpServe MCP over stdio, or over loopback HTTP with a mandatory bearer token

Safety

Read operations run immediately. Nothing else does — every operation that changes something on Telegram is a two-step plan, then apply.

CallerReadPlanApply
CLI, a person at a terminalimmediatetg-ai message send … (each write operation is its own command; it saves a plan and prints the id)tg-ai plan apply <id>
MCP client with no shell access (Claude Desktop, most IDE/editor integrations)immediatetelegram_plan_<operation> tool — only under profile: plan; the default readonly profile does not publish plan tools at allnot reachable — there is no MCP tool that applies a plan
MCP client that also has its own shell (Claude Code, Codex, and similar coding agents)immediatetelegram_plan_<operation> tool — only under profile: plancan run tg-ai plan apply <id> itself, through its own shell — see the note below

Read-only by default, writing by explicit opt-in. A fresh install runs the readonly profile, and its MCP tool list contains no telegram_plan_* tool: a client sees the read tools and four local tools that write only into configured directories on this machine (see MCP tools), and a plan tool called by name anyway is refused with FORBIDDEN_BY_PROFILE before anything runs. Publishing the plan tools takes profile: plan in the config or TGAI_PROFILE=plan in the client's environment — and even then nothing is sent until a person runs tg-ai plan apply.

A write command never sends anything by itself — it records a plan and tells you how to carry it out:

$ tg-ai message send --chat 987654321 --text "Meeting moved to 3pm"
{
  "plan_id": "4f0f8a2b9c7e1d3a5b6c7d8e9f0a1b2c",
  "operation": "message.send",
  "summary": "send to Marketing Team (group, 987654321): \"Meeting moved to 3pm\"",
  "state": "pending",
  "next": "tg-ai plan apply 4f0f8a2b9c7e1d3a5b6c7d8e9f0a1b2c"
}

tg-ai plan show <id> prints exactly what applying it would do — every field already passed through the same sanitizer that protects the terminal from a hostile chat title or message body (see render.py) — and only tg-ai plan apply <id> (with a confirmation prompt, or --yes to skip it) actually carries it out.

The honest limit of this design, stated plainly: there is no MCP tool that applies a plan, on purpose — a confirmation an agent can send over MCP is a confirmation prompt injection can send. But that boundary is a property of the transport, not of the agent. An MCP client that has its own shell — Claude Code, Codex, and comparable coding agents — can simply run tg-ai plan apply <id> as a shell command, the same as a person would. Nothing in this project detects or blocks that, because nothing at the process level can tell "the person typed this command" apart from "the agent typed this command" once both share a terminal.

For that class of client, this design does not pretend to hold a hard line at plan/apply. The line it actually offers is: every attempt and every outcome is written to the audit log before and after the RPC (audit.py), rate limits persist across restarts so an agent (or an injection driving one) cannot out-run them by being restarted (limits.py), the hard denylist on 777000 (Telegram's login-code chat) and Saved Messages cannot be reconfigured away, and everything an agent is about to approve is sanitized before it reaches a terminal so the text being approved is the text that will actually send (render.py). If your MCP client can run shell commands, you are trusting the agent, and the safety net is visibility and limits, not an unbreakable gate. A profile named full — direct send with no plan step, from any caller — does not exist in this project; it was considered and rejected. A stronger boundary (a separate OS principal, or a TOTP-gated apply) is out of scope for v0.1 and is written up as a path forward in docs/threat-model.md.

Reading, chats and profiles

Groups and channels are readable as soon as an account is configured; direct messages are not readable until a chat id is explicitly allowlisted — an empty dms allowlist means none, not all. 777000 (Telegram Service Notifications, where login codes and 2FA resets arrive) and Saved Messages are closed in code and cannot be reopened by any configuration. Every write capability (send, admin, join, profile changes) is fail-closed the same way: an empty list means nothing is permitted until you add to it. Two profiles exist — readonly (default: only the read and local tools are published) and plan (read, plus creating plans; the plan tools appear only under it). See telegram_ai_cli_mcp/safety.py for the exact rule order.

Importing an existing session

Importing a tdata folder from Telegram Desktop is one way to authorize an account without a fresh login flow. Two things to know before you do it:

Install

uvx --from telegram-ai-cli-mcp tg-ai mcp

That one command is the server. It speaks MCP on stdin and waits; every client block below runs the same uvx command. Sign the account in first, from a terminal, because neither the server nor the Desktop extension can prompt for a login code:

uvx --from telegram-ai-cli-mcp tg-ai account login --label work --phone "<your phone, E.164>"

--label names the account; put your own number in E.164 form; --phone is needed only on the first sign-in of that label. account login-qr --label work signs in by QR code instead, with no phone number.

The session is stored under ~/.local/state/telegram-ai-cli-mcp/. The server sends nothing by itself: a plan tool records an intention, and a person applies it with tg-ai plan apply.

The package is telegram-ai-cli-mcp on PyPI; uvx caches what it installed and may keep running that version; --from telegram-ai-cli-mcp@latest checks PyPI again, and --from telegram-ai-cli-mcp==0.1.3 pins one. This README follows main, so what the changelog lists under Unreleased is not in the PyPI release yet. Since 0.1.2 the readonly profile lists no telegram_plan_* tool; 0.1.1 and earlier still list them under readonly and refuse every call, so a uvx cache holding one of those shows plan tools that do not work. To run main itself, use uvx --from git+https://github.com/stufently/telegram-ai-cli-mcp tg-ai mcp. For a lasting tg-ai on PATH, pipx install telegram-ai-cli-mcp or uv tool install telegram-ai-cli-mcp. A checkout still works, for development:

git clone https://github.com/stufently/telegram-ai-cli-mcp.git
cd telegram-ai-cli-mcp
pip install .
tg-ai --version

Give it an environment of its own — pipx, uv tool, or a plain python3 -m venv — rather than a shared user site, and put the resulting bin/tg-ai on PATH. The dependency floor here is deliberately current (mcp>=2.0, telethon>=1.44), and a shared site-packages holds exactly one version of a package for every program on the machine: anything pinned below that floor loses, silently, the moment this project is installed or upgraded alongside it.

That collision is live on the maintainer's host, where the tg-claude-userbot project pins mcp==1.27.2 and telethon==1.43.2. Since 2026-09-03 tg-ai lives in its own virtualenv there and the shared entry point is a symlink into it:

/home/deploy/.venvs/tg-ai            # editable install of this repo
/home/deploy/.local/bin/tg-ai   ->   /home/deploy/.venvs/tg-ai/bin/tg-ai

Details of that layout, and what was left behind in the shared site, are in Operations.

docker build -t telegram-ai-cli-mcp .
docker run --rm telegram-ai-cli-mcp --version   # arguments go to tg-ai
docker run --rm -i telegram-ai-cli-mcp          # default: tg-ai mcp over stdio

Example prompts

Ask in plain language. The server picks the tool.

FAQ

How do I let Claude read my Telegram?

Run tg-ai mcp and register it as an MCP server with Claude Code or Claude Desktop. The readonly profile is active by default and groups and channels are readable immediately; direct messages stay closed until you add specific chat ids to safety.read.dms.allow.

Is there an MCP server for Telegram user accounts?

Yes, this one. It is built on MTProto through Telethon, not the Bot API, so it can read existing chat history, resolve usernames and see who is in a group the way a human member's client can.

Can an AI agent send messages on my Telegram account through this?

Only after a two-step plan and apply. An MCP client without shell access cannot apply a plan. A client that also has a shell (Claude Code, Codex and similar) can run tg-ai plan apply itself, so for those the safety net is the audit log and rate limits, not a hard gate.

Is this a Telegram bot?

No. It automates a personal user account over MTProto. A Bot API bot is a different kind of Telegram entity with different limits and different visibility into chat history.

Will Telegram ban my account for using this?

Telegram can limit or ban any account it judges to be running abusive automation, whatever tool sent the requests. The tool is built around ordinary reading and occasional allowlisted sending; mass joins, high-volume sends and member scraping are not something it tries to make safe.

Which Python version do I need?

3.12 or newer; 3.12 to 3.14 are tested in CI. Linux is tested, macOS is expected to work but not tested, and Windows is not supported because session locking uses fcntl.

Documentation pages