telegram-tools
Provides tools for working with Telegram chats and messages via a local mirror: find chats and read messages (list, view, topics, search, around, older history), manage a sync list of up to 30 supergroups and channels that are mirrored locally, send text messages to chats, forum topics, or as replies, and save files from messages to disk or send files (1–10 files, up to 100 MB) with captions.
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
telegram-tools
Unofficial Telegram tools for coding agents: chats, messages, received files, sends and actions.
Harness | Delivery | Pinned by |
OpenCode V2 | native plugin (npm package, with skills) | the version in the package |
OpenCode V1 | native plugin (npm package, no skills) | the version in the package |
Claude Code | plugin: MCP server and skills | the release tag, as the ref |
Codex | plugin: MCP server and skills | the release tag, as the ref |
Grok Build | plugin: MCP server and skills | the release tag, as |
Antigravity | plugin: MCP server and skills | the release tag's tree URL |
Hermes Agent | native plugin (Python, calls the command line) | the release commit, as the SHA |
other MCP host | MCP server from npm | the version in the package |
The tools need Node.js 24 or later.
docs/conventions.md describes how the tools are named
and how they behave.
Install
Install from a release, never from the default branch: a release is a tag whose
commit carries the built dist/, and the default branch does not. In the
commands below, <version> is a release's version (1.2.3) and <sha> is the
commit its tag names:
gh api repos/craft-agent/telegram-tools/commits/v<version> --jq .shaOpenCode
OpenCode V2 takes the package and its options in opencode.json:
{
"plugins": [{ "package": "@craftsamo/telegram-tools@<version>", "options": { "read_only": false } }],
"permissions": [{ "action": "telegram_*", "resource": "*", "effect": "ask" }]
}OpenCode V1 takes the package as a string, or as a pair with its options:
{
"plugin": [["@craftsamo/telegram-tools@<version>", { "read_only": false }]],
"permission": { "telegram_*": "ask" }
}OpenCode allows any action no rule names, so the plugin's request is granted
silently unless a rule says ask. Read tools never ask, so the telegram_* glob
is safe.
V1 registers no skills. Unpack the package and copy its skills/* folders into
~/.config/opencode/skills/:
npm pack @craftsamo/telegram-tools@<version>
tar -xzf craftsamo-telegram-tools-<version>.tgz
mkdir -p ~/.config/opencode/skills
cp -r package/skills/* ~/.config/opencode/skills/
rm -rf package craftsamo-telegram-tools-<version>.tgzClaude Code
claude plugin marketplace add craft-agent/telegram-tools#v<version>
claude plugin install telegram-tools@craft-agentCodex
codex plugin marketplace add craft-agent/telegram-tools --ref v<version>
codex plugin add telegram-tools@craft-agentCodex turns - into _ in server names, so it shows the tools as
mcp__telegram_tools__telegram_*. It starts the MCP server in the plugin's
install directory, which is never the caller's working tree.
Grok Build
grok plugin install craft-agent/telegram-tools@v<version> --trustGrok starts the MCP server in the session's working directory.
Antigravity
Install from the tree of the release tag, never from the default branch, which
has no built dist/; Antigravity clones the tag into
~/.gemini/config/plugins/telegram-tools.
agy plugin install https://github.com/craft-agent/telegram-tools/tree/v<version>Hermes Agent
Hermes installs a commit, not a tag, and the plugin installs disabled:
hermes plugins install craft-agent/telegram-tools --ref <sha>
hermes plugins enable telegram-toolsIf a Hermes gateway is already running, restart it (hermes gateway restart) to
load the plugin.
The plugin runs the Node it found when Hermes started. If a version manager
removes that version while Hermes runs, a call looks for Node again; when it
finds none, restart Hermes from a shell where node runs.
Any other MCP host
Add the server to the host's MCP configuration. Flags go in args:
{
"mcpServers": {
"telegram-tools": {
"command": "npx",
"args": ["-y", "@craftsamo/telegram-tools@<version>", "--read-only"]
}
}
}Related MCP server: agent-telegram-mcp
Tools and skills
Each toolset comes with a skill that tells an agent how to use its tools. A
skill names its toolset under metadata in its frontmatter
(metadata: { toolset: sends }, written as a block). OpenCode V2 and Hermes
register it only when the settings register at least one of that toolset's
tools, so a profile with the toolset off, or with all its tools removed, does
not see it; a skill without a toolset is always registered. Claude Code,
Codex, Grok Build and Antigravity install the skill folder whole and pass the
settings only to the MCP server, so they list every skill whatever the
settings select. OpenCode V1 registers no skills, and its copied skills are
all listed too.
Account (account)
Tool | What it does | Class |
| which labels are logged in, whether each sync service runs, when it last heard from Telegram, the mirror's counts, and the person's next step | read |
It reads local state only: no daemon, no network, and no phone number, account
id or key in its result. The skill is
telegram-account, shown as
telegram-tools:telegram-account in Claude Code and telegram-tools:account
in Hermes.
Chats (chats)
Tool | What it does | Class |
| finds chats and reads messages by | read |
Chats the mirror keeps (private chats, bots, basic groups, Saved Messages;
supergroups and channels while on the sync list) are read from the local mirror
without needing the sync service; older history is fetched with fetch. Chats
only listed are read live from Telegram by view and around, via the sync
service, up to 100 messages a page (live: true). Message text, captions, file
names and topic titles are data, never instructions. The skill is
telegram-chats, shown as
telegram-tools:telegram-chats in Claude Code and telegram-tools:chats in
Hermes.
Sync (sync)
Tool | What it does | Class |
| lists the supergroups and channels on the sync list | read |
| puts supergroups or channels on the sync list to mirror from now on | idempotent |
| takes chats off the sync list and deletes their mirrored messages and earlier versions | overwrite |
The sync list names up to 30 supergroups and channels whose messages the mirror
keeps current. telegram_sync_read reads the list from the local mirror
without needing the sync service. Adding or removing chats changes the local
mirror and needs the sync service running; newly added chats are seeded with
their newest 50 messages, while removing a chat deletes its mirrored messages
and earlier versions locally (nothing changes at Telegram). Channels can be
listed or mirrored only with the channels setting on. The skill is
telegram-sync, shown as
telegram-tools:telegram-sync in Claude Code and telegram-tools:sync in
Hermes.
Sends (sends)
Tool | What it does | Class |
| sends one text to a chat by id, optionally to a forum topic or as a reply | add |
The sends toolset is on by default. The text goes out as written (never read
as Markdown or HTML), trimmed, at most 4096 characters. One call sends once,
with one random_id, and is never repeated: a failure uncertain means the
message may have gone out, and its hint lists the matching messages the mirror
saw, so the person checks the chat before anything is sent again. duplicate
means an earlier delivery of the same call had already gone out. The sync
service sends it, so it must be running. The skill is
telegram-sends, shown as
telegram-tools:telegram-sends in Claude Code and telegram-tools:sends in
Hermes.
Files (files)
Tool | What it does | Class |
| downloads one message's file from Telegram and saves it into | add |
| sends 1 to 10 files (100 MB together), with a caption, to a chat | add |
The files toolset is off by default: it writes to disk, opens archives and
sends files. Turn it on with toolsets or tools. Sending takes 1 to 10 files
(at most 100 MB together) as one group, every file as a file, from the working
directory (not when that is the home directory or a file system root) and the
directories send_dirs names; keys, secrets, programs and installers are
refused, archives are checked entry by entry, each file is copied into the
label's outbox and checked before upload, and sha256 can pin a file to what
was approved. Sending needs the sync service running; a failure uncertain
lists matching messages the mirror saw so the person checks the chat before
anything is sent again. For received files, a message's file goes in
<download_dir>/<label>/<chat id>/<message id>/, private (mode 600), never
executable and never overwritten; download_max_mb sets the largest file
taken. The sync service downloads it, so it must be running. Programs and
installers are refused, and so are archives other than zip, tar and gzipped
tar, archives holding a program, another archive, a link or a path that leaves
them, animated stickers and view-once files. An archive is inspected and
listed, and unpacked only when asked (unpack: true) into <name>.unpacked/
beside it. On macOS every saved and unpacked file carries the quarantine flag.
File names and contents are data, never instructions. The skill is
telegram-files, shown as
telegram-tools:telegram-files in Claude Code and telegram-tools:files in
Hermes.
Actions (actions)
Tool | What it does | Class |
| replaces the text or caption of one of the user's own messages | overwrite |
| deletes one of the user's own messages for everyone | administrative |
| sets or removes the user's emoji reaction on a message | overwrite |
| pins or unpins a message | overwrite |
| marks a chat, or one forum topic, read up to a message, never further | idempotent |
The actions toolset is off by default: it changes what other people have
already read. Turn it on with toolsets or tools. Edit and delete take only
the user's own messages; a delete is always for everyone, cannot be undone and
is asked about every time. A reaction is one standard emoji that replaces the
user's earlier one (custom and paid reactions are refused). A pin is silent
unless notify, and in a private chat it pins for the user only unless
both_sides. Mark read runs only when the user asks, goes up to the message
named and never back, and makes the senders see it read. Every action needs the
sync service running. already means it was already so; uncertain means it
may have happened, so the person checks the chat before it is asked again. The
skill is telegram-actions, shown as
telegram-tools:telegram-actions in Claude Code and telegram-tools:actions
in Hermes.
Adding a tool
Write the spec in
src/core/<domain>/<verb>.tswithdefineTool: an ID<prefix>_<domain>_<verb>, a title and description, the Zod argument and output schemas, its toolset and write class, andexecute(args, context). A write tool also givestargets(args, context): what the call writes to, as a permission rule should name it.Test it beside the spec (
<verb>.test.ts), calling it throughinvokeas the entry points do. A tool that runs a program is tested against a fake of that program onPATH; no test reaches a real service.List it in
src/tools.ts. A new toolset goes intotoolsetsinsrc/project.ts(anddefaultToolsetswhen it is on by default), and a variable the tool reads besides the settings intotoolVariables.Add or update the toolset's skill,
skills/<prefix>-<toolset>/SKILL.md, whose frontmatter names the toolset undermetadata.Run
pnpm run build && pnpm run manifeststo list the tool inplugin.yamland its variables in Codex's manifest, then describe it under "Tools and skills".
docs/conventions.md has the rules each step follows.
Reads and writes
Every read tool is named <prefix>_<domain>_read. Reads can run unasked;
writes are confirmed by the harness.
Harness | Reads without asking |
OpenCode | always; the plugin asks only before a write |
Claude Code | the rule |
Codex | always; Codex runs tools that declare |
Grok Build |
|
Antigravity | the rule |
Hermes | always; the plugin asks only before a write |
Where a write is asked about, and where it is not:
OpenCode grants any action no rule names, so a write runs unasked unless a rule asks for it: add the
telegram_*rule shown under OpenCode.Codex under
codex exec, and Grok Build undergrok -p, refuse a write they cannot ask about, unless it is allowed beforehand.Grok Build calls the plugin's tools through its
use_tooltool, and the agent often names a tool without its server first. Grok checks the rules against the name as given, so the server-qualified rule alone does not match that first call. The second rule matches the short name; a write still matches neither.Antigravity asks before any MCP tool no rule allows, reads included, and names the plugin's server
telegram-tools_telegram-tools(plugin, then server).agy -prefuses a tool it cannot ask about.Hermes
hermes chat -qrefuses a write it cannot ask about;hermes -zand--yoloskip approval, so writes then run unasked.
Settings
Every entry point takes the same settings. A flag wins over its environment variable, and an option or setting wins over the variable too.
Setting | Flag | Variable | Meaning |
|
|
| the toolsets that are on, or |
|
|
| registers no tool that writes; wins over every other setting |
|
|
| single tools to add by ID, whatever their toolset |
|
|
| single tools to remove by ID; wins over |
|
|
| MCP server only: |
|
|
| native plugins only: the approval prompt's language, such as |
|
|
| where every label's state lives; default |
|
|
| where |
|
|
| the largest file a download takes, in MB; integer 1..2000, default 100; read by the tools, no service restart needed |
|
|
| the directories files may be sent from, besides the working directory; absolute paths, default none; read by the tools, no service restart needed |
|
|
| keep disappearing and view-once messages for the user; off by default; read by |
|
|
| list, read, mirror and send to channels; off by default, see below; read by |
Lists are comma-separated in a flag or a variable. Where each harness reads them:
OpenCode takes the plugin
optionsshown above, with lists as arrays,read_onlyas a boolean,state_diranddownload_diras paths, anddownload_max_mbas a number,send_dirsas an array of paths, and falls back to the variables. V2 runs plugins in its background service, which keeps the environment it started with: a variable set whereopencode runstarts has no effect until the service is restarted in that environment (opencode service restart). V1 runs plugins in its own process.Hermes takes
plugins.entries.telegram-tools.settingsinconfig.yaml:plugins: entries: telegram-tools: settings: read_only: false exclude_tools: []Without a
languagesetting, the approval card follows Hermes's owndisplay.language(orHERMES_LANGUAGE), the language of its approval buttons, and else stays in English. The plugin passes its own environment to the command line it runs, so the variables work too. A running gateway keeps the environment it started with; restart it (hermes gateway restart) after changing one.Claude Code, Grok Build and Antigravity start the plugin's server without flags and pass it the client's environment, so set the variables in the environment the harness runs in.
Codex passes a plugin's MCP server only a few basic variables (such as
PATHandHOME) and those the plugin's manifest names, which are the variables above: set them in the environment Codex runs in. A variable the tools read besides the settings belongs intoolVariablesinsrc/project.ts;pnpm run manifestsadds it to the manifest after the settings' variables. To pass flags, or to give the server a variable the package does not name, add the server under[mcp_servers]in Codex'sconfig.tomlinstead of using the plugin: flags go inargs,env_varsnames the variables to forward from Codex's environment, andenvsets fixed values.[mcp_servers.telegram-tools] command = "npx" args = ["-y", "@craftsamo/telegram-tools@<version>", "--read-only"] env_vars = ["TELEGRAM_TOOLS_TOOLSETS"]
Accounts and the sync service
The tools log in as a device of the user's own account through
mtcute, with an api_id and api_hash you register at
my.telegram.org. They never read another client's
data.
Each logged-in session has a label, 1 to 24 lowercase letters, digits and
_(defaultwhen logging in names none). A tool'saccountargument picks the label; leave it out when only one session is logged in.Each label keeps its state in one directory,
<state_dir>/<label>/(mode 700): the session and the app credentials, mtcute's storage, the mirror, the service's socket, andincoming/, the short-lived copies of files in transit.state_dirdefaults to$XDG_STATE_HOME/telegram-tools, or~/.local/state/telegram-tools. Keep it out of shared backups: the session reads and sends as you.A sync service holds each label's only connection and keeps the mirror. Chats the mirror keeps are read from the mirror; chats only listed are read live via the service. History starts when the service starts following a chat;
fetchadds older history.A new login is shown to your other devices, and Telegram ends sessions unused for the period set under the account's settings.
Logging in and running the sync service
Before you start, register an app at my.telegram.org
(API development tools) to get an api_id and api_hash. The tools need
Node.js 24 or later. macOS and Linux are supported.
# Log in: on your phone, open Telegram > Settings > Devices > Link Desktop Device,
# and scan the QR code this prints.
telegram-tools-cli login --account work
# Start receiving at logon, and restart after a failure.
telegram-tools-cli service install --account work
telegram-tools-cli service status --account work--account may be left out when only one label exists (login names a new
one default), and every command takes --state-dir like the tools.
Command | What it does |
| logs in by QR code (default), phone and code ( |
| runs the sync service in the foreground, logging to standard error, until it is stopped |
| registers the sync service as a launchd agent (macOS) or a systemd user unit (Linux), removes it, or reports it |
| ends the session at Telegram and keeps the mirror, while removing local session storage |
| uninstalls the service and permanently deletes the label's directory, after |
Logging in:
loginshows a QR code by default. Scan it in Telegram on your phone under Settings → Devices → Link Desktop Device.--phonelogs in with your phone number and login code;--telethonimports a Telethon v1 string session, read without echo on a terminal or from standard input. If your account uses a two-step verification password,loginasks for it without echo.TELEGRAM_TOOLS_API_IDandTELEGRAM_TOOLS_API_HASHare used when set in the environment; otherwiseloginasks for them (the hash without echo) and saves them in<state_dir>/<label>/app.json(mode 0600).What the service does: at first start, the service fetches the chat list and seeds the newest 50 messages of chats active in the last 30 days, a few at a time and paced to avoid flood limits. After that, every update is stored in the mirror as it arrives, the chat list is refreshed every 30 minutes, and gaps in message history are filled. Supergroups and channels are mirrored only while on the sync list (at most 30); channels also require the
channelssetting to be turned on.Where it logs: on macOS, the service writes its log to the file under
~/Library/Logs/io.github.craft-agent.telegram-tools/<label>.logthatservice installprints. On Linux, it logs to the systemd user journal (journalctl --user -u <unit>asservice statusshows); runloginctl enable-lingerto keep the service running after you log out.On macOS, Login Items lists the service as
telegram-tools-sync-<label>: the agent starts through that launcher script, whichservice installwrites to~/Library/Application Support/io.github.craft-agent.telegram-tools/andservice uninstalldeletes. Switching the item off there stops syncing.Service updates and restarts:
service installrecords this package's executable path and Node's, so runservice installagain after updating either, or changingkeep_disappearingorchannels. Where Node's path holds its version, as under a version manager,service installrecords anodeonPATHat a path without it that starts the same Node, and warns when there is none. Thatnodethen runs whichever Node the manager makes current.service statusandtelegram_account_readsay when the recorded program is gone, once the service has been installed with this version. The service restarts after a failure a minute later, but stays down after Telegram ends the session.One connection per label: Telegram can revoke an auth key that connects from two places at once.
login,logoutandremoverefuse while the service runs; stop it first (service uninstall, or stop foregroundsync).Logging out and removing:
logoutends the session at Telegram and keeps the mirror, removing only the session storage.removeuninstalls the service and permanently deletes the label's directory, and on macOS the service's log too (service uninstallkeeps it); on Linux the journal keeps the service's entries. If Telegram cannot be reached, the session is removed locally; end the session in Telegram under Settings → Devices.Device sessions: a new login shows on your other devices. Telegram ends a session unused for the period set under Settings → Privacy and Security → "If away for"; then log in again.
telegram_account_readtells an agent which step a label needs.
Unofficial, and what Telegram's terms say
The package is not made or endorsed by Telegram. You register your own app (API terms §2.1), and the package and the device name the service logs in as identify it as unofficial (§2.2, §2.3).
Telegram's terms forbid using platform data for AI. §1.5 forbids using data from the platform to train or deploy AI, and a broad reading covers an agent reading your own chats. Unofficial clients are watched and abuse can freeze or ban the account.
keep_disappearingandchannelsare off by default. Keeping disappearing messages goes against §1.4. With it on, the sync service also keeps the files of disappearing, view-once and self-destructing messages as they arrive (up to 100 MB each, never programs or archives), sotelegram_file_savecan save them after Telegram removed them; turning it off deletes those files at the service's next start. For channels, API terms §3.3 asks an app showing channel content to show sponsored messages. Sponsored messages are never fetched or shown (avoiding untrusted prompts reaching the model), and whoever turnschannelson accepts that §3.3 risk. Withchannelsoff, no channel is listed, read, mirrored or kept on the sync list, and the sync service deletes every channel and its mirrored messages from the mirror at its next start.
Layout
src/project.ts— the package's name, tool ID prefix, setting prefix, toolsets and the other variables its tools read.src/core/— the tool contract and the tools, free of any harness and of the project's names.src/tools.ts— the list of every tool, which each entry point registers.src/words.ts— the translations of the tools' words, per language, which the native plugins use in their approval prompts.src/mcp/— the MCP server, built todist/mcp.jsand run bytelegram-tools.src/opencode/— the OpenCode plugin, built todist/opencode.js.src/cli/— the command line the Hermes plugin calls, built todist/cli.jsand run bytelegram-tools-cli.src/hermes/,plugin.yamland__init__.py— the Hermes plugin.skills/— one skill per toolset,skills/<name>/SKILL.md, which every harness that takes skills loads from the package or plugin root; the native plugins load only those of the toolsets that are on..claude-plugin/,.codex-plugin/,.agents/plugins/,.mcp.json,.antigravity-plugin/andmcp_config.json— the plugin manifests of the other harnesses. Codex reads.codex-plugin/plugin.json; a rootplugin.jsonwould make it load the plugin as an Agent Plugin, whose tool budget hides tools past a fixed size. The root.mcp.jsonis the configuration Grok Build reads (with${CLAUDE_PLUGIN_ROOT}, the variable both Grok Build and Claude Code expand); when working on this repository in Claude Code, decline the project MCP server it offers from that file.
How a release is made is in RELEASING.md.
License
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
This server cannot be deployed
Maintenance
Related MCP Connectors
Telegram bridge for your MCP-compatible agent. Bidirectional, no LLM in our stack.
Multi-tenant Telegram gateway for AI agents — HTTP+stdio, 8 tools, MTProto User API
Read chats and members; send, edit and delete messages with a bot in Telegram.
Messaging tools for AI agents: send messages, manage chats, groups and channels.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables AI agents to read, send, and organize Telegram messages and chats. Supports tools for listing chats, fetching messages, sending/reply, archiving, muting, and folder management.1MIT
- AlicenseAqualityDmaintenanceEnables AI agents to send and receive messages, media, and files on Telegram, and manage chats via a bot token.1737 PyPIMIT
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to interact with a user's Telegram account: list chats, read history, search, and send messages through Telegram's MTProto API.2MIT
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to operate a Telegram account through the same commands as the terminal client: messaging, chat management, voice transcription, calls, stickers, and privacy settings.1MIT