Telegram Userbot MCP Server
Runs as your own Telegram account over MTProto to list chats, find unread messages, search dialogs, read chat history, send text/media/stickers, add or clear reactions, forward messages, and optionally forward marker-reacted messages to a chosen chat.
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., "@Telegram Userbot MCP Serverlist my unread Telegram chats and read the latest from Mary"
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.
telegram-userbot
A standalone Telegram userbot that runs as your own account over MTProto (GramJS) - it reads, searches, sends and reacts without a browser.
It ships as two things over the same code:
a CLI (
watch,send), andan MCP server, so an agent can drive your Telegram.
This is transport, not intelligence. The server makes no decisions: no summarising, no classifying, no "should I reply". Anything that needs judgement belongs to the agent on the other end. Keeping the bot dumb is what makes it small, predictable and safe to hand to a model.
Why MTProto instead of a browser
Real-time | event-driven updates, not polling or DOM scraping |
Full history |
|
No Chromium | runs happily in a 60 MB container |
Stable | does not break when Telegram ships a CSS class rename |
Related MCP server: telegram-mcp
What you need to provide
Read "provide" as: install it if it is software, otherwise put it into your
.env file. npm run login writes TG_API_ID / TG_API_HASH into .env for
you; the QR flow (npm run login:qr) reads all of its values from there and
never prompts. See Install for the copy step.
A Telegram account | this runs as you - it is a userbot, not a bot account |
| from https://my.telegram.org -> API development tools. You create the app once; Telegram may take a while to approve it, and the form asks for an app name and platform - anything sensible works. |
Your phone number | asked once by |
Node 20+ | or Docker, if you would rather not install anything on the host. Software to install - not a |
An MCP client | an agent that speaks MCP (Claude Code, Cursor, Codex, OpenClaw, ...). Install it and register this server with it. Software to install - not a |
Optional, and worth doing - all of these are .env values:
| your two-step password, if the account has one. The QR login reads it from |
| any random string. Without it the HTTP endpoint is unauthenticated |
| a chat title for marker notifications; defaults to your own Saved Messages |
| the chat titles an agent is allowed to send to |
What you must never share: your .env file (gitignored - it holds the
credentials above), session.txt, your api_hash, and your MCP_TOKEN. The
session file is a complete login to your account.
Install
npm install
cp .env.example .env # then fill in TG_API_ID / TG_API_HASH
npm run login # phone + code (+ 2FA password if you have one)npm run login writes a StringSession to session.txt and saves the API
credentials back into .env. On a headless box use the QR flow instead - it
serves an auto-refreshing QR code over HTTP:
QR_PORT=8791 npm run login:qr # open http://127.0.0.1:8791/ and scanThe QR flow never asks for a phone number - you scan with the phone. If the
account has two-step verification, put the password in .env as TG_2FA
before running it.
CLI
npm start # stream new messages as they arrive
EXCLUDE="Work,News" npm start # ...ignoring these chat titles
npm run send -- "@username" "hello there" # send as yourself
npm run send -- "Mary" "good morning" # by (fuzzy) chat title
npm run send -- -1001234567890 "hi group" # by numeric idChats can be addressed by @username, phone number, numeric id, me (your own
Saved Messages), or a title from your dialogs (accent- and
case-insensitive).
MCP server
npm run mcp # stdio
MCP_HTTP_PORT=8790 npm run mcp # streamable HTTP at http://127.0.0.1:8790/mcpstdio client configuration:
{
"mcpServers": {
"telegram": {
"command": "node",
"args": ["/absolute/path/to/telegram-userbot/src/mcp.mjs"]
}
}
}HTTP client configuration - always pair it with MCP_TOKEN:
{
"mcpServers": {
"telegram": {
"url": "http://telegram-userbot:8790/mcp",
"transport": "streamable-http",
"headers": { "Authorization": "Bearer <MCP_TOKEN>" }
}
}
}A GET /health endpoint returns ok, which is what the Docker healthcheck uses.
Tools
Tool | What it does |
| list dialogs: title, unread count, last-message preview |
| only dialogs with unread messages |
| filter dialogs by title substring (accent-insensitive) |
| most recent messages of a chat |
| a larger slice of history (up to 500) |
| send a text message as you |
| send an image / GIF / video / file, from a URL or a local path |
| send a sticker from your installed packs |
| add or clear a reaction on a message |
| reactions on messages in a chat; |
| forward a message between chats (any media) |
Reaction marker
React to any message with ✍️ (configurable) and the bot forwards that message to a chat of your choice - by default your own Saved Messages. Useful as a "flag this for the agent" gesture.
REACTION_EMOJI=✍️ NOTIFY_CHAT=me npm run reactionsIn Docker the marker listener is attached inside the MCP process
(START_LISTENER=1) so a single MTProto session is shared. One session must
only ever be open in one process - see Risks.
Docker
mkdir -p deploy/config
cp .env.example deploy/config/.env # fill in; add session.txt too
docker compose -f deploy/docker-compose.yml up -d --builddeploy/config/ is mounted read-only at /config and is gitignored - that
is where session.txt and your secrets live.
Configuration
Everything is optional except the first two. See .env.example for the
annotated version.
Variable | Default | Purpose |
| - | required - from my.telegram.org |
| - | required - from my.telegram.org |
|
| where the session is stored |
| - | inline session string instead of a file |
|
| which env file to load |
| - | chat titles to ignore in |
|
| how your own messages are labelled |
| system tz | timezone for message timestamps |
| - | HTTP transport port; empty = stdio |
|
| HTTP bind interface |
| - | bearer token for |
| - | restrict |
|
| the marker reaction |
|
| where marker notifications go |
| - | regex; only matching text is forwarded |
| built-in | notification body, with |
| - |
|
|
| marker dedup state |
|
| QR login page |
| - | two-step password for QR login |
Values are read from the environment first, then from the env file, so the
environment always wins. Double-quoted values are unescaped, so \n, \t,
\" and \\ work.
For agents
skills/telegram/SKILL.md is an agent-facing usage
guide in Agent Skill format: which tool to reach for, the safety rules that
matter when the account is a real person's, and worked recipes. It is plain
Markdown with name/description frontmatter, so the same folder works in
Claude Code, Cursor, Codex, OpenClaw - any agent that reads a SKILL.md.
skills/README.md has the per-client install steps and the
MCP client configuration for both transports.
It exists because this repo deliberately contains no intelligence of its own - so the guidance for the model driving it has to live somewhere, and that somewhere is here rather than in the server.
How it is put together
src/
mcp.mjs MCP server (stdio + streamable HTTP), all the tools
client.mjs GramJS client construction, session loading, chat lookup
env.mjs dependency-free .env loader
format.mjs display/text helpers
login.mjs interactive login (phone + code + 2FA)
login-qr.mjs headless QR login
watch.mjs real-time NewMessage watcher
send.mjs CLI send
reaction-marker.mjs reaction marker listener
reactions-watch.mjs standalone runner for the marker listener
deploy/
Dockerfile docker-compose.yml entrypoint.sh
skills/
telegram/SKILL.md how an agent should drive these tools
README.md installing the skill + the server, per client
test/ unit tests (node:test)
CONTRIBUTING.md SECURITY.md CHANGELOG.mdRisks
session.txtis your account. Anyone who has it has your Telegram. It is gitignored and written0600; treat it like a password. Revoke it from Settings -> Devices if it leaks.One session = one process. Running the same session in two clients gets you
AUTH_KEY_DUPLICATEDand a broken login.Automating a personal account runs against Telegram's API ToS. Accounts have been limited or banned for it. This is your risk to take, not a supported use of Telegram.
Without
MCP_TOKENthe HTTP endpoint is unauthenticated. Anyone who can reach the port can read your chats and send messages as you. Bind to127.0.0.1or set a token.Message content is untrusted input. A chat message can contain text that tries to steer the agent driving this server (prompt injection). Give the agent the narrowest set of tools you can, and use
SEND_ALLOWLIST.
License
MIT - see LICENSE.
This server cannot be deployed
Maintenance
Related MCP Connectors
Telegram bridge for your MCP-compatible agent. Bidirectional, no LLM in our stack.
Hosted Telegram MCP server for Claude, ChatGPT and Cursor, with each action switched per account.
Hosted MCP server and REST API that lets AI agents use apps with no API, signed in as you.
Multi-tenant Telegram gateway for AI agents — HTTP+stdio, 8 tools, MTProto User API
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceEnables AI agents to control a real Telegram user account via MTProto, allowing message sending, chat reading/searching, and message management through MCP tools.15 npm-
- FlicenseBqualityBmaintenanceProvides full access to a personal Telegram account via MTProto, enabling MCP clients to read chats, send messages, manage media, and more.70-
- AlicenseBqualityCmaintenanceEnables MCP clients to operate a Telegram account as a userbot, providing tools for reading and sending messages, searching chats and contacts, managing media, and performing other account actions via MTProto with a locally encrypted session.28MIT
- AlicenseNot gradedqualityCmaintenanceLets any MCP-compatible AI agent read, search, and send Telegram messages through your real account via MTProto.13 npm1MIT