grokbot-telegram-bridge
Bridges Telegram and Grok Bot: receives Telegram Bot API webhooks via a local listener, spools inbound updates on disk, and exposes stdio MCP tools for sending/editing/deleting messages, showing progress drafts, listing and acking spooled updates, and managing the webhook (set webhook, get webhook info, pre-webhook long-polling).
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-telegram-bridgecheck my Telegram messages and reply to the newest one"
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.
grokbot-telegram-bridge
Open-source Telegram ↔ Grok Bot bridge: a local webhook listener, on-disk spool, and stdio MCP server so Grok Bot can drain Telegram messages and reply immediately when woken.
What this is / what it is not
Is
A small Node.js service that receives Telegram Bot API webhooks on
127.0.0.1:8787An atomic on-disk spool of inbound updates (
spool/<update_id>.json)A stdio MCP server exposing Telegram helpers (
tg_send_message,tg_edit_message,tg_list_spool, …) that Grok Bot can call as a custom MCPDesigned to wake Grok Bot via a webhook routine on each new Telegram message (NOT cron)
OpenClaw-style progress drafts: one editable status message while the agent works, then delete + final answer
Is not
Native Telegram support inside Grok Bot (there is none)
A hosted SaaS or always-on cloud bot framework
A cron-first design — polling/cron drain was the wrong architecture; use a webhook routine
A replacement for BotFather, Telegram clients, or Grok Bot itself
Full OpenClaw tool-stream UI (this bridge approximates
streaming.mode=progresswith edit/delete, not native Telegram streaming)
Architecture in one line: Telegram → HTTPS relay → local listener → spool + progress draft + keepalive ticker → wake POST → Grok Bot webhook routine → MCP (tg_progress hold / tg_delete_message / tg_send_message).
Related MCP server: tgfmcp
Progress drafts (OpenClaw streaming.mode=progress)
OpenClaw’s Telegram channel can show a live progress message that is edited as the agent works, then cleared when the final answer lands. This bridge mirrors that UX.
Telegram twin of Grok in-app commentary
The progress draft is the Telegram twin of Grok Bot’s in-app working commentary (the short status lines you see in the app while tools run — e.g. Running a few commands).
At each beat, the agent must call
tg_progress(ortg_edit_message) with the same wording as that in-app commentary — not a paraphrase, not a custom “status protocol”.Example: when the app shows
Running a few commands, Telegram should showRunning a few commands(optionally with a leading●if you keep the draft’s bullet style consistent).Before the final answer: delete the draft (
tg_delete_message), thentg_send_messagewith the real reply.The listener’s first line is
● waking Grok Bot, then the listener keepalive cycles Grok-style lines until the agent’s firsttg_progress/tg_edit_messagesetshold: true(or the draft is deleted / spool acked / ~90s). After hold, agent edits should be exact commentary mirrors.
Phase | What happens |
Inbound | Listener sends one status draft ( |
Listener keepalive | After a successful draft, the listener starts a non-blocking ticker (~every 2.5s, up to ~90s) that |
Agent hold | When |
Working | Agent edits that same message via |
Done | Agent deletes the progress draft ( |
Typing | Optional/secondary keepalive only — progress text is the signal of life, not “typing…”. |
Keepalive stop conditions: spool file for that update_id is gone (acked); Telegram returns message-not-found (agent deleted draft); meta.json has hold: true (agent took over); ~90s timeout. The ticker never blocks the webhook 200 or the wake POST. Never logs the bot token.
A static “Queued…” receipt is not the design. Progress text that updates — mirroring in-app commentary — is.
Agent routine behaviour (recommended)
When woken, the webhook routine should:
Call
tg_list_spool(preview includesprogress_message_idwhen present; wake body may also carry it)If empty → stay silent
While working: at each commentary beat,
tg_progress(chat_id, progress_message_id, "<exact in-app wording>")(e.g.Running a few commands)When done:
tg_delete_message(chat_id, progress_message_id)→ thentg_send_messagewith the final answertg_ack_spoolfor each handledupdate_id
Suggested prompt sketch:
On wake: call
tg_list_spool. Ifcountis 0, do nothing. Otherwise, for each pending item, useprogress_message_idfrom the preview (or wake payload). The progress draft is the Telegram twin of Grok in-app commentary: whenever the app would show a working line (e.g.Running a few commands), calltg_progress/tg_edit_messagewith that same wording. When finished: delete the progress draft withtg_delete_message, then send the final answer as a newtg_send_message. Ack withtg_ack_spool. Never print tokens or secrets. Typing alone is not progress — mirror commentary into the draft.
Prerequisites
Node.js 18+ (20+ recommended)
A Telegram bot from @BotFather (API token)
Your numeric Telegram chat id (allowlisted)
Grok Bot desktop (to register the stdio MCP and create a webhook routine)
A public HTTPS front-door for Telegram webhooks (see Public HTTPS)
Install
git clone <this-repo-url> grokbot-telegram-bridge
cd grokbot-telegram-bridge
npm installSecrets (never commit)
Create secrets outside the repo or only in gitignored paths. Modes should be 0600.
Helper (creates empty/minted files in the package directory — they are gitignored):
npm run setup-secrets
# or: bash scripts/setup-secrets.shThen edit:
File | Purpose |
| BotFather token (or set |
| Random |
| Your numeric chat id (progress draft, typing, and wake respect this) |
| Full HTTPS URL of the Grok Bot webhook routine |
| Sender key / secret for that routine (Authorization Bearer + |
example.env.example shows placeholder env keys only:
TELEGRAM_BOT_TOKEN=
ALLOWED_CHAT_ID=
# Optional override: GROKBOT_WAKE_HEADER="Header-Name: value"Do not put real tokens in the repo, in README snippets, or in chat. If a token is ever pasted into chat, revoke/rotate it in BotFather.
Run the listener
Bind address is hard-coded to loopback:
GET http://127.0.0.1:8787/healthz→200 okPOST http://127.0.0.1:8787/telegram-webhook→ Telegram updates (requires secret header)
Foreground:
npm run listener
# or: node listener.mjsOptional supervisor (restart-friendly, writes listener.pid / listener.log):
npm run supervise
# or: bash supervisor.shConfirm health:
curl -sS -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8787/healthz
# expect 200Public HTTPS — prefer smee.io
Telegram requires a public HTTPS webhook URL. Your listener stays on 127.0.0.1:8787.
Cloudflare Tunnel often fails Telegram’s DNS/resolve checks for some setups. Prefer a relay such as smee.io:
Create a smee channel; note the public HTTPS URL.
Run the smee client locally, forwarding to
http://127.0.0.1:8787/telegram-webhook.Point Telegram’s webhook at the smee (or other relay) public URL that ultimately POSTs to
/telegram-webhook.
Any stable HTTPS reverse proxy/tunnel that Telegram can resolve is fine; smee is the documented default recommendation here because Cloudflare tunnels frequently fail Telegram resolve.
Do not paste your public smee/relay URL into shared issue trackers or chat logs.
setWebhook (and never mix with getUpdates)
With the listener running and the relay forwarding:
Ensure
webhook-secretexists (npm run setup-secrets).Call Telegram
setWebhookwith:url= your public HTTPS webhook URLsecret_token= contents ofwebhook-secret(same value the listener checks onX-Telegram-Bot-Api-Secret-Token)
Via MCP (once registered):
Tool:
tg_set_webhookArg:
public_url= your public HTTPS URL
Or via curl (do not echo the token/secret into shell history logs you share):
# Illustrative only — load token/secret from files; do not paste into chat
curl -sS -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/setWebhook" \
-H 'Content-Type: application/json' \
-d "{\"url\":\"https://YOUR_PUBLIC_HOST/telegram-webhook\",\"secret_token\":\"${WEBHOOK_SECRET}\"}"Important: While a webhook is active, do not call getUpdates. Telegram rejects long-polling when a webhook is set. Use tg_get_updates only for pre-webhook debugging. Inspect webhook state with tg_webhook_info.
Register as a Grok Bot custom MCP (stdio)
In Grok Bot → custom MCP / Add MCP server (stdio):
Field | Suggested value |
Name |
|
Command |
|
Args |
|
CWD (if offered) |
|
Dogfood / box path example:
Command:
nodeArgs:
/home/box/.local/telegram-mcp/mcp-server.mjs
The MCP process reads token / webhook-secret / spool/ relative to the script directory (or TELEGRAM_BOT_TOKEN from the environment). Never put the token in the MCP args.
MCP tools
Tool | Purpose |
| Bot identity |
|
|
|
|
|
|
| Alias of |
|
|
| Pending inbound updates (preview includes |
| Archive |
| Telegram |
| Pre-webhook long-poll only |
| Set webhook; reads |
Wake Grok Bot with a webhook routine (primary path)
Cron/polling drain was the wrong design. Wire immediacy like this:
In Grok Bot, create a webhook routine (not a scheduled/cron routine).
Copy the routine’s HTTPS URL into
grokbot-wake-url(mode0600).Copy the routine’s sender key / secret into
grokbot-wake-secret(mode0600).Restart the listener (or supervisor) so it can read the new files.
On each new spool write (created === true), after Telegram already got 200 ok, the listener sends the progress draft (when allowlisted), then asynchronously POSTs:
{
"source": "telegram-bridge",
"update_id": "...",
"chat_id": 123,
"progress_message_id": 456
}progress_message_id is included when the draft send succeeded. Auth headers (default): both Authorization: Bearer <secret> and X-Webhook-Secret: <secret>.
Optional override: set env GROKBOT_WAKE_HEADER to Name: value to send that single header instead.
If either wake file is missing/empty: spool + progress draft + typing still run; wake is skipped with log line wake skipped: missing grokbot-wake-url/secret. Wake failures never fail the Telegram webhook response. Only allowlisted chats (ALLOWED_CHAT_ID) trigger a wake (same gate as the progress draft).
Optional smoke test (does not print secrets):
node scripts/send-wake-test.mjsSecurity
Never commit
token,webhook-secret,grokbot-wake-url,grokbot-wake-secret,ALLOWED_CHAT_ID,allowed-chat-id,.env,spool/, or logsFile mode 0600 for secrets and
.meta.json; spool dirs preferably0700Allowlist your chat id; do not run an open relay for the world
Rotate the BotFather token if it was pasted into chat, committed, or leaked
Listener binds 127.0.0.1 only; expose it only through a deliberate HTTPS relay
Validate Telegram’s
X-Telegram-Bot-Api-Secret-Token(built-in) — keepwebhook-secretlong and randomDo not log tokens, secrets, wake URLs, or full public relay URLs in shared issue trackers
Dogfood checklist
npm installsucceeds on Node 18+npm run setup-secretsthen filltoken+ALLOWED_CHAT_ID(0600)Create Grok Bot webhook routine; paste URL + sender key into
grokbot-wake-url/grokbot-wake-secret(0600)npm run supervise(ornpm run listener) —GET /healthzreturns 200HTTPS relay (prefer smee.io) forwards to
http://127.0.0.1:8787/telegram-webhooktg_set_webhook/setWebhookwithsecret_token;tg_webhook_infoshows the URLSend yourself a Telegram message → file under
spool/+ progress draft (● waking Grok Bot) cycling keepalive lines +.meta.json+ wake POST; agenttg_progresssetshold: trueMCP registered in Grok Bot (
node+ absolutemcp-server.mjs)Webhook routine: edit progress → delete draft →
tg_send_messagefinal →tg_ack_spoolEmpty spool runs stay silent
Confirm
.gitignoreexcludes secrets; no secrets under version control
Layout
grokbot-telegram-bridge/
listener.mjs # HTTP webhook + spool + progress draft + keepalive + wake POST
mcp-server.mjs # stdio MCP tools (incl. edit/delete/progress)
supervisor.sh # simple process keeper
package.json
scripts/setup-secrets.sh
scripts/send-wake-test.mjs
example.env.example
LICENSE # MIT
README.mdRuntime data (gitignored): token, webhook-secret, grokbot-wake-url, grokbot-wake-secret, ALLOWED_CHAT_ID, spool/ (incl. *.meta.json), listener.pid, *.log.
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.
Remote MCP server for supportsheep: run AI interviews and manage support content for your blog.
Hosted Telegram MCP server for Claude, ChatGPT and Cursor, with each action switched per account.
Unofficial Telegram MCP server — read, search, reply and react in your own Telegram account.
Related MCP Servers
- AlicenseAqualityCmaintenanceMCP server that exposes a Telegram bot, enabling sending messages and retrieving updates through natural language.31MIT
- AlicenseNot gradedqualityDmaintenanceMCP server that enables Telegram bot interaction via Telegraf, providing tools for sending, replying, reacting, editing, deleting, forwarding messages, and receiving Telegram events over an optional notification channel.28 npm4MIT
- FlicenseNot gradedqualityBmaintenanceAn MCP server that connects to a Telegram group chat, persists messages to a local SQLite database, and exposes tools to search, retrieve, and send messages via SSE.-
- FlicenseNot gradedqualityDmaintenanceAn MCP server that sends and receives Telegram messages to a fixed contact, and triggers claude.ai routines for automatic replies.-