grokbot-telegram
Allows sending and reading Telegram messages both as a bot via the Bot API and as a personal user account via MTProto, including tools for managing dialogs, searching chats, fetching message history, and authenticating a user session.
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., "@grokbot-telegramSend a Telegram message to @alex saying 'Running late, be there in 10'."
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 for Cursor
Installable Cursor plugin with two first-party MCP servers:
Mode | MCP server | Identity | Protocol |
Bot |
| Your @BotFather bot | Official HTTP Bot API |
User account |
| Your personal Telegram account |
Use one or both. v0.2.0 kept the v0.1.0 Bot API path and added user-account login; v0.3.0 ships both servers as the single npm package grokbot-telegram and appends a disclaimer footer to every outgoing message.
This is not Bot API–only, and it is not user-account–only. Marketplace copy: a Cursor plugin that can send/read Telegram as a bot and/or as the logged-in user.
Which mode should I use?
Need | Mode |
Notifications, CI alerts, channel posts as | Bot API — |
“Send this in my name”, read my real chats / dialog list | User account — |
Both (bot for alerts, account for DMs) | Configure both variable sets |
Bot API cannot list your personal inbox. User-account mode can (list_dialogs), because it is logged in as you.
Related MCP server: Telegram MCP Server
What you must provide
Bot API (telegram-bot)
Create a bot with @BotFather →
/newbot.Set
TELEGRAM_BOT_TOKENin Cursor Plugins → Configure.Open a DM with the bot and tap Start (
/start). Bots cannot message you first.
No api_id is required for this mode.
User account (telegram-user) — first run
Open https://my.telegram.org/apps, log in with the same phone as the Telegram account you want to use, and create an app.
Set
TELEGRAM_API_IDandTELEGRAM_API_HASHin Plugins → Configure.Complete one-time login (phone + code, QR / Link Desktop Device, or an existing session string).
Persist the session as
TELEGRAM_SESSIONand/or the file~/.grokbot-telegram/user.session(mode0600).
You will be asked for api_id / api_hash / login on first run. A bot token is not used for this server.
The session is full account access. Treat it like a password. Never commit it.
Message disclaimer
Every message sent through either server carries a footer, so the person on the other end knows an agent — not a human typing — produced it:
your message text
— sent by grokbot-telegram on my behalfThe Bot API server uses — sent by grokbot-telegram (it is already visibly a bot). TELEGRAM_DISCLAIMER controls it:
Value | Result |
unset | default wording above |
any text | that text is used verbatim |
| no footer |
The wording names the package rather than linking to it, on purpose. A URL in the footer would make Telegram attach a link-preview card to every message you send, and a link in someone's personal chat reads as advertising — the name is still searchable on npm and GitHub. Set TELEGRAM_DISCLAIMER to a URL yourself if you disagree.
Two details worth knowing:
The footer counts against Telegram's 4096-character limit. If text + footer would exceed it, the send is refused with an error naming the overflow rather than quietly truncating your words or dropping the footer.
With
parse_mode, the footer is escaped for that mode. This matters forMarkdownV2, where an unescaped.or-makes Telegram reject the whole message.
Why you need your own api_id
Telegram issues api_id/api_hash per developer and rejects credentials that have been published: an app that ships a shared pair earns its users an API_ID_PUBLISHED_FLOOD error at login. So this package deliberately does not ship one. It is a one-time, two-minute detour at my.telegram.org/apps, and it keeps the blast radius at one user instead of everyone at once.
You do not have to put the values in a config file. Hand them to the agent and it calls save_api_credentials, which writes them next to the session at mode 0600. TELEGRAM_API_ID / TELEGRAM_API_HASH still work as environment variables and take precedence, which is the path for developers bringing their own app.
Only user-account mode needs this. Bot API mode needs nothing but a @BotFather token, which takes about thirty seconds inside Telegram. If you only want notifications or to post to a channel, use that.
Forum topics
Groups with topics turned on keep their messages in separate threads. list_dialogs marks those with isForum, list_forum_topics lists the threads, and send_message / get_messages take an optional topic_id to work inside one. Without topic_id nothing changes — the message goes to the chat as before.
A wrong topic_id is worth understanding: Telegram does not reject it, it quietly files the message under General, in front of everyone. So send_message checks the id against the topic list before sending, and refuses a closed topic outright. That costs one extra call, only when a topic is named.
Naming a chat
send_message and get_messages take an id, me, an @username, or a chat title. Ids and @usernames go straight out. A title is resolved against your dialog list first, and an ambiguous one is refused with the candidates named rather than guessed at.
This exists because handing a title to the underlying client makes it look for a username that was never there, which in a short-lived process means a minute of hanging instead of an error. Prefer an id from list_dialogs when you have one: it is the only form with no lookup at all.
Security
Bot token
Anyone with TELEGRAM_BOT_TOKEN can send as that bot, read updates, and change bot settings. Set it only in Plugins → Configure. The bot MCP redacts token-shaped strings from errors.
User session (stronger warning)
A GramJS session string or session file is equivalent to being logged in on a new device.
Anyone who has it can read your chats and send as you.
Never commit
TELEGRAM_SESSION,api_hash,.env, or*.sessionfiles.After first login, save the session in Plugins → Configure or keep the
0600session file. Do not leave the string in chat history if you can avoid it.Revoke: Telegram → Settings → Devices → terminate the unknown session, then delete the session file / clear the variable.
Automating a user account is a Telegram ToS gray area. Keep sends human-paced. No spam or bulk broadcast.
The repo never contains real tokens, api hashes, or sessions. mcp.json only has ${VAR} placeholders that match the manifest.
MCP tools
telegram-bot (unchanged Bot API v1)
Tool | Telegram method | Purpose |
|
| Send text as the bot ( |
|
| Confirm the token; bot username |
|
| Debug recent updates |
| derived from | Best-effort recent chats (not a full inbox) |
telegram-user (MTProto)
Tool | Purpose |
| Store api_id / api_hash at 0600 so no config file is needed |
| Credentials + whether the session is authorized |
| Phone + login code (+ optional 2FA password) |
| QR / Link Desktop Device (+ optional 2FA) |
| Logged-in user (not a bot) |
| Real dialog / inbox list, |
| Topics of a forum group, with the ids to send into |
| Filter dialogs by title, username, or id |
| Recent history for a chat |
| Send text as the user ( |
Both servers expose send_message and get_me. Prefer the MCP server name (telegram-bot vs telegram-user). Tool descriptions are prefixed [Bot API] or [User account].
Skills
telegram-mode-guide — when to use bot vs user
telegram-bot-setup — BotFather,
TELEGRAM_BOT_TOKEN, smoke-testsend-telegram-message — send as the bot; confirm first
telegram-user-setup — my.telegram.org, first login, session persistence
send-telegram-user-message — resolve from the dialog list; confirm first
Install
Any MCP client (npx)
Both servers ship as one npm package, grokbot-telegram, behind a single command:
npx grokbot-telegram bot # Bot API server over stdio
npx grokbot-telegram user # user-account (MTProto) server over stdio
npx grokbot-telegram login # interactive login, prints a TELEGRAM_SESSIONWith no command the mode is read from the environment: TELEGRAM_BOT_TOKEN selects bot, TELEGRAM_API_ID + TELEGRAM_API_HASH select user. If both are set it refuses to guess and asks for an explicit mode.
Drop this into any MCP client config (Claude Code, Claude Desktop, Cursor, …):
{
"mcpServers": {
"telegram-bot": {
"command": "npx",
"args": ["-y", "grokbot-telegram@0.4.0", "bot"],
"env": { "TELEGRAM_BOT_TOKEN": "…" }
},
"telegram-user": {
"command": "npx",
"args": ["-y", "grokbot-telegram@0.4.0", "user"],
"env": {
"TELEGRAM_API_ID": "…",
"TELEGRAM_API_HASH": "…",
"TELEGRAM_SESSION": "…"
}
}
}
}Cursor Marketplace (when listed)
Open Customize → search grokbot-telegram, or visit cursor.com/marketplace.
Install the plugin.
Plugins → Configure → set the variables for the mode(s) you want. For user-account mode you can skip this and let the agent store
api_id/api_hashfor you.
From this repository
mkdir -p ~/.cursor/plugins/local
ln -s /path/to/grokbot-telegram ~/.cursor/plugins/local/telegram-botReload the window (Developer: Reload Window). Team / Enterprise admins may need to allow local plugin imports.
Note that mcp.json launches the servers through npx grokbot-telegram, so a symlinked clone still runs the published build, not your working tree. To exercise local changes, either npm run build && npm link in the repo root, or point mcp.json at ${PLUGIN_ROOT}/dist/bot.js and ${PLUGIN_ROOT}/dist/user.js with "command": "node" while you work (npm run validate will flag that, which is the reminder to change it back).
Submit the public repo at cursor.com/marketplace/publish and/or list it on cursor.directory.
Configure
The plugin declares variables in .cursor-plugin/plugin.json and substitutes ${VAR} placeholders in mcp.json. None are required in the schema so you can enable only one mode.
Variable | Mode | Where to get it |
| Bot | |
| User | my.telegram.org/apps — or let the agent store it, see below |
| User | my.telegram.org/apps — or let the agent store it, see below |
| User | Returned after first login (optional if the session file exists) |
| Both | Optional. Footer wording, or |
Optional env (not a marketplace variable): TELEGRAM_SESSION_PATH overrides the default session file ~/.grokbot-telegram/user.session.
Login is two calls — start_login sends the code, complete_login redeems it — and some MCP hosts start a fresh process per call. So the in-flight state is written next to the session file as user.session.pending.json (mode 0600) and picked back up by the next process. It holds a pre-authorization session: it cannot read or send anything, but it is the auth key the code is bound to, so it is deleted the moment login succeeds and discarded after 15 minutes.
QR login does not survive a restart, and cannot. Telegram announces the scan on the connection that exported the code, and the authorization only exists once that same connection redeems it. The process that called start_qr_login has to stay alive until the scan lands. On a host that starts a process per tool call, use phone login. complete_qr_login still adopts an already-authorized session if there is one, and otherwise hands back a fresh login_url, because exporting again yields a different code and re-showing the old one can never work.
The project was called cursor-telegram-plugin before it became grokbot-telegram. A session left in the old ~/.cursor-telegram-plugin/user.session is still read, so an existing login keeps working; new sessions are written to the new path.
Example prompts
Bot
“Verify my Telegram bot with
telegram-botget_me. Do not print the token.”“I just /start’ed the bot. List recent chats, then send ‘hello from Cursor’ to my DM after I confirm.”
User
“I want messages sent as me, not as a bot. Walk me through my.telegram.org and first login.”
“Search my Telegram dialogs for ‘Ada’, then send this text after I confirm.”
“Show recent messages in Saved Messages (
me) using the user account.”
How to test
Bot API send_message
/startthe bot (or add it to a group/channel).Set
TELEGRAM_BOT_TOKEN.In Agent chat:
get_me→list_recent_chats→ confirm →send_message.Optional:
export TELEGRAM_BOT_TOKEN="…" # local shell only
curl -sS "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/getMe"User-account send (as you)
Set
TELEGRAM_API_IDandTELEGRAM_API_HASH.Login once:
export TELEGRAM_API_ID="…"
export TELEGRAM_API_HASH="…"
npx grokbot-telegram loginFrom a clone, the equivalent is node dist/login.js after npm run build.
Or use start_login / start_qr_login from the agent.
For login through a chat, prefer start_login. A QR token lives about 30 seconds and that clock starts when the tool returns, not when the code reaches the screen — which is usually not enough time in a conversation. The token cannot be extended; it is Telegram's limit. What the tools do instead is remove the round trips. start_qr_login and every waiting reply from complete_qr_login return the code as a scannable PNG in the tool result, ahead of the JSON, so the agent forwards an image rather than rendering one itself — and login_url stays in the payload as a fallback for clients that show no images. Each waiting reply also carries a fresh code and expires_in_seconds, so a retry gives the user a full window instead of a dead code.
The QR is drawn locally. A login token is a credential, so it never goes to an external image service.
Not every MCP host paints tool-result images on its own; where it does not, the agent forwards the base64 it was handed. Either way the code is generated once, here, rather than round-tripped through the agent.
The session shows up in Telegram → Settings → Devices as grokbot-telegram, so it is easy to find and revoke.
Confirm
get_meis your user (isBot: false).list_dialogs/search_dialogs, confirm destination + text, thensend_message.
Automated tests mock Telegram (no live token or account). Node.js 20+ is required.
npm install --prefix packages/mcp-server
npm install --prefix packages/mcp-user-server
npm test # both packages
npm run typecheck
npm run build # per-package bundles, then collected into the root dist/
npm run validate # manifest + wiring invariantsWorking on this with a coding agent? Start from AGENTS.md.
Chat list limitation (Bot API only)
The Bot API cannot list every chat a bot belongs to. list_recent_chats only inspects recent getUpdates events. If nothing shows up: the user has not /started the bot, the group is quiet, or a webhook is set.
User-account mode uses the real dialog list and does not have this limitation.
Why first-party servers (not a random npm MCP)
Public packages exist (Bot API wrappers; user MCPs such as @overpod/mcp-telegram / mcp-telegram/mcp-telegram). They are real, but they are either very large or a different product.
This plugin ships two small stdio servers:
Bot: official HTTP Bot API only (
packages/mcp-server)User: teleproto MTProto user client (
packages/mcp-user-server), tools limited to login, dialogs, history, and send
Both are bundled with esbuild — every dependency (including teleproto) is inlined, so the published package installs with zero runtime dependencies. npm run build produces the per-package bundles and collects them into the publishable root dist/:
dist/bot.js,dist/user.js,dist/login.js— the bundlesdist/cli.js— thegrokbot-telegramdispatcher that picks between them
npm run buildmcp.json launches both:
{
"telegram-bot": {
"command": "npx",
"args": ["-y", "grokbot-telegram@0.4.0", "bot"],
"env": { "TELEGRAM_BOT_TOKEN": "${TELEGRAM_BOT_TOKEN}" }
},
"telegram-user": {
"command": "npx",
"args": ["-y", "grokbot-telegram@0.4.0", "user"],
"env": {
"TELEGRAM_API_ID": "${TELEGRAM_API_ID}",
"TELEGRAM_API_HASH": "${TELEGRAM_API_HASH}",
"TELEGRAM_SESSION": "${TELEGRAM_SESSION}"
}
}
}Marketplace notes
Single Cursor Plugin (
.cursor-plugin/plugin.json), not a multi-pluginmarketplace.jsonrepo.MTProto user client + Bot API, not Bot API alone.
Plugin
nameisgrokbot-telegram, matching the npm package and the repository.MIT. Version lives in
package.json;.cursor-plugin/plugin.jsonand the pin inmcp.jsonare checked against it bynpm run validate.Logo at
assets/logo.svg— a plain outline mark, deliberately not Telegram's own logo or brand colour, since this is a third-party plugin.mcp.jsonpins the version it launches, so what a reviewer reads is what runs.Variables:
TELEGRAM_BOT_TOKEN,TELEGRAM_API_ID,TELEGRAM_API_HASH,TELEGRAM_SESSION,TELEGRAM_DISCLAIMER(all optional in the schema).Submit: cursor.com/marketplace/publish.
Layout
.
├── .cursor-plugin/plugin.json
├── mcp.json
├── skills/
│ ├── telegram-mode-guide/SKILL.md
│ ├── telegram-bot-setup/SKILL.md
│ ├── send-telegram-message/SKILL.md
│ ├── telegram-user-setup/SKILL.md
│ └── send-telegram-user-message/SKILL.md
├── assets/logo.svg
├── AGENTS.md # guidance for coding agents (CLAUDE.md points here)
├── src/cli.js # grokbot-telegram dispatcher (bot | user | login)
├── dist/ # published bundles, built (gitignored)
├── packages/mcp-server/ # Bot API MCP + dist bundle
├── packages/mcp-user-server/ # GramJS user MCP + dist bundle
├── LICENSE
└── README.mdLicense
Related MCP Connectors
Telegram bridge for your MCP-compatible agent. Bidirectional, no LLM in our stack.
Unofficial Telegram MCP server — read, search, reply and react in your own Telegram account.
Run a Telegram channel from your AI agent. Posts go out through your own bot, not your account.
Drive WhatsApp from any MCP client: pair devices, send text and media, manage contacts and groups.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to interact with Telegram accounts through MCP, supporting messaging, contacts, groups, media, and admin functions.4Apache 2.0
- AlicenseNot gradedqualityCmaintenanceEnables AI assistants to interact with Telegram accounts, chats, messages, media, and more via MCP, using Telethon.Apache 2.0
- FlicenseBqualityCmaintenanceEnables MCP clients to interact with Telegram user accounts, providing tools for messaging, contacts, groups, channels, and media management through the Telegram API.853-
- AlicenseAqualityBmaintenanceEnables use of a personal Telegram account within MCP clients for reading and sending messages, searching chats, and managing media, all running locally.163 npmMIT