Skip to main content
Glama
sdaveas

Telegram Userbot MCP Server

by sdaveas

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), and

  • an 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

getMessages reaches back arbitrarily far - no scrolling

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

api_id + api_hash

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. .env values

Your phone number

asked once by npm run login, at the prompt, together with the login code Telegram sends. The QR flow never asks for it - you scan with your phone instead. Not a .env value

Node 20+

or Docker, if you would rather not install anything on the host. Software to install - not a .env value

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 .env value. Only needed to let an agent drive this; the CLI works on its own

Optional, and worth doing - all of these are .env values:

TG_2FA

your two-step password, if the account has one. The QR login reads it from .env; npm run login prompts for it instead

MCP_TOKEN

any random string. Without it the HTTP endpoint is unauthenticated

NOTIFY_CHAT

a chat title for marker notifications; defaults to your own Saved Messages

SEND_ALLOWLIST

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 scan

The 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 id

Chats 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/mcp

stdio 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

tg_list_chats

list dialogs: title, unread count, last-message preview

tg_unread

only dialogs with unread messages

tg_search

filter dialogs by title substring (accent-insensitive)

tg_read_chat

most recent messages of a chat

tg_read_history

a larger slice of history (up to 500)

tg_send_message

send a text message as you

tg_send_media

send an image / GIF / video / file, from a URL or a local path

tg_send_sticker

send a sticker from your installed packs

tg_react

add or clear a reaction on a message

tg_reactions

reactions on messages in a chat; reactors: true adds who reacted

tg_forward

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 reactions

In 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 --build

deploy/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

TG_API_ID

-

required - from my.telegram.org

TG_API_HASH

-

required - from my.telegram.org

TG_SESSION_FILE

./session.txt

where the session is stored

TG_SESSION

-

inline session string instead of a file

ENV_FILE

.env

which env file to load

EXCLUDE

-

chat titles to ignore in watch output

SELF_NAME

ME

how your own messages are labelled

TZ_NAME

system tz

timezone for message timestamps

MCP_HTTP_PORT

-

HTTP transport port; empty = stdio

MCP_HOST

127.0.0.1

HTTP bind interface

MCP_TOKEN

-

bearer token for /mcp - set this

SEND_ALLOWLIST

-

restrict tg_send_message to these titles

REACTION_EMOJI

✍️

the marker reaction

NOTIFY_CHAT

me

where marker notifications go

MARKER_FILTER

-

regex; only matching text is forwarded

MARKER_MESSAGE

built-in

notification body, with {chat} {sender} {text} {msgId}

START_LISTENER

-

1 = attach the marker inside the MCP process

SEEN_FILE

./tg-marker-seen.json

marker dedup state

QR_PORT / QR_BIND

8791 / 127.0.0.1

QR login page

TG_2FA

-

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.md

Risks

  • session.txt is your account. Anyone who has it has your Telegram. It is gitignored and written 0600; 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_DUPLICATED and 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_TOKEN the HTTP endpoint is unauthenticated. Anyone who can reach the port can read your chats and send messages as you. Bind to 127.0.0.1 or 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.

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    Enables 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
    -
  • F
    license
    B
    quality
    B
    maintenance
    Provides full access to a personal Telegram account via MTProto, enabling MCP clients to read chats, send messages, manage media, and more.
    70
    -
  • A
    license
    B
    quality
    C
    maintenance
    Enables 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.
    28
    MIT