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: io.github.daedalus/mcp-telegram-bot
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.
Unofficial Telegram MCP server — read, search, reply and react in your own Telegram account.
An MCP server that let you interact with Cycloid.io Internal Development Portal and Platform
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceAn MCP server that enables interaction with Telegram to send, read, and search messages across chats and dialogs. It supports waiting for incoming messages and retrieving conversation history through natural language commands.15 npm3MIT
- 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.35 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.-