mila-telegram

mila-telegram

Enables running Claude Code sessions from Telegram with group auto-join, voice transcription, reply-quote context, and a restart-proof polling daemon.

Category
Visit Server

README

Mila Telegram

Run your Claude Code session from Telegram — and keep it running.

This is an extended fork of the official Telegram channel plugin (Apache-2.0). The original connects a Telegram bot to a Claude Code session. This fork adds the things you start needing once you actually work that way every day: teams in group chats, voice notes, and a poller that doesn't lose your messages when the session restarts.

What this fork adds

A daemon that outlives the session. In the original, the Telegram poller lives inside the session process: restart Claude Code and every message sent in the meantime is gone. Here the poller runs as a long-lived daemon that appends events to a journal file, and the session replays them from a cursor on startup. Restart, crash, upgrade — incoming messages wait in the file and arrive when the session comes back.

Group auto-join with one-tap approval. Add the bot to a group and send /channel_join. The owner gets a four-button card in a private chat: reply to everyone, mention-only, listen-only (the bot hears the chat, but replies into it are blocked mechanically), or reject. Every decision — rejections included — is appended to an auth-log.jsonl journal, and each connected chat gets a context card in chats/<id>.md plus a generated CHATS.md registry. No numeric chat IDs, no editing JSON by hand; group messages can never change the allowlist themselves — approval only happens in the owner's private chat.

Voice transcription. Voice notes are handed to a transcription command of your choice (set TELEGRAM_VOICE_TRANSCRIBE_CMD), and the text reaches the assistant along with the audio. Unset means voice notes simply pass through untranscribed — no default, no vendor.

Reply-quote context. When someone replies to an earlier message, the quoted text travels with the new one, so the assistant answers about the message you actually pointed at instead of the last thing in the chat.

Disclosure by default. /whoami answers, in any chat and to anyone, that they are talking to an AI, what it is connected to, and who is responsible for it. No access check on that one: a person who finds a bot in their group is entitled to know what it is, whether or not they are on the allowlist.

Everything from the original plugin still works: pairing, allowlists, mention detection, reply / react / edit_message, photos, attachments, typing indicators.

Security

Read this before you put it on a machine that matters.

Messages are untrusted input. Anything anyone sends the bot reaches your assistant as content. Someone in an allowlisted chat can try to instruct it. Treat the channel as a public door into a session that can read files and run commands.

Groups require a mention by default. An approved group is added with requireMention: true, so the bot only responds when addressed. Turning that off means every message from every member goes straight into the session — that is a real prompt-injection surface, not a theoretical one. Turn it off only for a chat where you trust every member.

Attachments can go out. The reply tool sends any absolute path the assistant passes it. That is deliberate — it's how you get files out of a session — but combined with the point above it is also an exfiltration path. Your permission settings, not this plugin, are what stand between a message and a file leaving your machine.

The event journal is a trust boundary. The daemon writes events to a file and the session replays them; whatever can write that file speaks with the voice of Telegram. The journal and its directory are created 0600/0700, and the bridge replays only an allowlist of two methods so a forged line cannot invoke arbitrary ones. Keep the state directory on a filesystem only you can write.

Your token lives in ~/.claude/channels/telegram/.env at 0600, and is stripped from error output before anything is logged.

Prerequisites

  • Bun — the MCP server runs on Bun: curl -fsSL https://bun.sh/install | bash

Setup

1. Create a bot. Message @BotFather, send /newbot, and copy the token it gives you (the whole thing, including the leading digits and colon).

2. Install the plugin. From a Claude Code session:

/plugin marketplace add shakhruz/mila-telegram
/plugin install mila-telegram@mila

3. Give the server the token.

/telegram:configure 123456789:AAHfiqksKZ8...

This writes TELEGRAM_BOT_TOKEN=... to ~/.claude/channels/telegram/.env. You can write that file yourself instead, or set the variable in your shell — the shell wins.

To run several bots on one machine, point TELEGRAM_STATE_DIR at a different directory per instance.

4. Relaunch with the channel flag. The server won't connect without it:

claude --channels plugin:mila-telegram@mila

5. Pair. DM your bot; it replies with a 6-character code. In the session:

/telegram:access pair <code>

6. Lock it down. Pairing exists to capture your ID. Once you're in, switch to an allowlist so strangers stop getting pairing codes:

/telegram:access policy allowlist

Running the daemon

The daemon is what makes restarts free. Run receiver.ts as a service under your process supervisor of choice — launchd on macOS, systemd on Linux — with RECEIVER_DAEMON=1 in its environment:

RECEIVER_DAEMON=1 bun /path/to/plugin/receiver.ts

It writes events to $TELEGRAM_STATE_DIR/inbound/events.jsonl. When a session starts and finds the daemon alive, it reads from that journal instead of polling, so the two never fight over the Telegram API. With no daemon running, the session polls directly exactly as the original plugin does — the daemon is optional.

Configuration

Variable Purpose
TELEGRAM_BOT_TOKEN Bot token from BotFather. Required.
TELEGRAM_STATE_DIR State directory. Default ~/.claude/channels/telegram.
TELEGRAM_VOICE_TRANSCRIBE_CMD Command that transcribes a voice file. Unset means no transcription.
RECEIVER_DAEMON Set to 1 to run receiver.ts as the polling daemon.

Access control

See ACCESS.md for DM policies, groups, mention detection, delivery config, skill commands, and the access.json schema.

IDs are numeric user IDs — get yours from @userinfobot. ackReaction only accepts Telegram's fixed emoji whitelist.

Tools exposed to the assistant

Tool Purpose
reply Send to a chat. Takes chat_id + text, optionally reply_to for native threading and files (absolute paths) for attachments. Images send as photos, everything else as documents, 50MB each. Long text is chunked. Returns the sent message ID(s).
react Add an emoji reaction by message ID. Telegram's fixed whitelist only (👍 👎 ❤ 🔥 👀 …).
edit_message Edit a message the bot sent. Good for "working…" → result. Own messages only.
download_attachment Fetch a file by file_id from an inbound message and return its local path.

Inbound messages trigger a typing indicator automatically.

No history, no search

Telegram's Bot API exposes neither. The bot only sees messages as they arrive — there is no way to fetch a chat's past. If the assistant needs earlier context it has to ask you for it. This is a limit of Telegram, not of this plugin, and it is why photos are downloaded eagerly on arrival.

License and attribution

Apache-2.0. This is a modified fork of the telegram channel plugin from anthropics/claude-plugins-official, copyright Anthropic, PBC. See NOTICE for the list of changes.

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured