Skip to main content
Glama
telegram-tools

telegram-tools

by craft-agent

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 @ref

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 .sha

OpenCode

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>.tgz

Claude Code

claude plugin marketplace add craft-agent/telegram-tools#v<version>
claude plugin install telegram-tools@craft-agent

Codex

codex plugin marketplace add craft-agent/telegram-tools --ref v<version>
codex plugin add telegram-tools@craft-agent

Codex 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> --trust

Grok 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-tools

If 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

telegram_account_read

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

telegram_chat_read

finds chats and reads messages by method: list, view, topics, search, around, or older history fetch

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

telegram_sync_read

lists the supergroups and channels on the sync list

read

telegram_sync_add

puts supergroups or channels on the sync list to mirror from now on

idempotent

telegram_sync_remove

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

telegram_message_send

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

telegram_file_save

downloads one message's file from Telegram and saves it into download_dir

add

telegram_file_send

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

telegram_message_edit

replaces the text or caption of one of the user's own messages

overwrite

telegram_message_delete

deletes one of the user's own messages for everyone

administrative

telegram_message_react

sets or removes the user's emoji reaction on a message

overwrite

telegram_message_pin

pins or unpins a message

overwrite

telegram_chat_mark_read

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

  1. Write the spec in src/core/<domain>/<verb>.ts with defineTool: an ID <prefix>_<domain>_<verb>, a title and description, the Zod argument and output schemas, its toolset and write class, and execute(args, context). A write tool also gives targets(args, context): what the call writes to, as a permission rule should name it.

  2. Test it beside the spec (<verb>.test.ts), calling it through invoke as the entry points do. A tool that runs a program is tested against a fake of that program on PATH; no test reaches a real service.

  3. List it in src/tools.ts. A new toolset goes into toolsets in src/project.ts (and defaultToolsets when it is on by default), and a variable the tool reads besides the settings into toolVariables.

  4. Add or update the toolset's skill, skills/<prefix>-<toolset>/SKILL.md, whose frontmatter names the toolset under metadata.

  5. Run pnpm run build && pnpm run manifests to list the tool in plugin.yaml and 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 mcp__plugin_telegram-tools_telegram-tools__*_read in permissions.allow of ~/.claude/settings.json

Codex

always; Codex runs tools that declare readOnlyHint unasked, as the read tools do

Grok Build

grok --allow 'MCPTool(telegram-tools__*_read)' --allow 'MCPTool(telegram_*_read)' (both rules; see below)

Antigravity

the rule mcp(telegram-tools_telegram-tools/<tool id>) in permissions.allow, one for each read tool

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 under grok -p, refuse a write they cannot ask about, unless it is allowed beforehand.

  • Grok Build calls the plugin's tools through its use_tool tool, 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 -p refuses a tool it cannot ask about.

  • Hermes hermes chat -q refuses a write it cannot ask about; hermes -z and --yolo skip 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

toolsets

--toolsets

TELEGRAM_TOOLS_TOOLSETS

the toolsets that are on, or all; default those src/project.ts names

read_only

--read-only

TELEGRAM_TOOLS_READ_ONLY

registers no tool that writes; wins over every other setting

tools

--tools

TELEGRAM_TOOLS_TOOLS

single tools to add by ID, whatever their toolset

exclude_tools

--exclude-tools

TELEGRAM_TOOLS_EXCLUDE_TOOLS

single tools to remove by ID; wins over tools and toolsets

confirm

--confirm

TELEGRAM_TOOLS_CONFIRM

MCP server only: harness (default) or elicitation, as the conventions say

language

--language

TELEGRAM_TOOLS_LANGUAGE

native plugins only: the approval prompt's language, such as ja

state_dir

--state-dir

TELEGRAM_TOOLS_STATE_DIR

where every label's state lives; default $XDG_STATE_HOME/telegram-tools

download_dir

--download-dir

TELEGRAM_TOOLS_DOWNLOAD_DIR

where telegram_file_save saves received files, a folder per message; default ~/Downloads/telegram-tools; read by the tools, no service restart needed

download_max_mb

--download-max-mb <n>

TELEGRAM_TOOLS_DOWNLOAD_MAX_MB

the largest file a download takes, in MB; integer 1..2000, default 100; read by the tools, no service restart needed

send_dirs

--send-dirs

TELEGRAM_TOOLS_SEND_DIRS

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

--keep-disappearing

TELEGRAM_TOOLS_KEEP_DISAPPEARING

keep disappearing and view-once messages for the user; off by default; read by sync and service install, run service install again after changing it

channels

--channels

TELEGRAM_TOOLS_CHANNELS

list, read, mirror and send to channels; off by default, see below; read by sync and service install, run service install again after changing it

Lists are comma-separated in a flag or a variable. Where each harness reads them:

  • OpenCode takes the plugin options shown above, with lists as arrays, read_only as a boolean, state_dir and download_dir as paths, and download_max_mb as a number, send_dirs as 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 where opencode run starts 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.settings in config.yaml:

    plugins:
      entries:
        telegram-tools:
          settings:
            read_only: false
            exclude_tools: []

    Without a language setting, the approval card follows Hermes's own display.language (or HERMES_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 PATH and HOME) 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 in toolVariables in src/project.ts; pnpm run manifests adds 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's config.toml instead of using the plugin: flags go in args, env_vars names the variables to forward from Codex's environment, and env sets 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 _ (default when logging in names none). A tool's account argument 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, and incoming/, the short-lived copies of files in transit. state_dir defaults 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; fetch adds 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

login [--account <label>] [--phone | --telethon]

logs in by QR code (default), phone and code (--phone), or Telethon v1 string session (--telethon)

sync [--account <label>] [--keep-disappearing] [--channels]

runs the sync service in the foreground, logging to standard error, until it is stopped

service install | uninstall | status [--account <label>]

registers the sync service as a launchd agent (macOS) or a systemd user unit (Linux), removes it, or reports it

logout [--account <label>]

ends the session at Telegram and keeps the mirror, while removing local session storage

remove --account <label> [--yes]

uninstalls the service and permanently deletes the label's directory, after --yes or typing the label back

  • Logging in: login shows a QR code by default. Scan it in Telegram on your phone under Settings → Devices → Link Desktop Device. --phone logs in with your phone number and login code; --telethon imports a Telethon v1 string session, read without echo on a terminal or from standard input. If your account uses a two-step verification password, login asks for it without echo. TELEGRAM_TOOLS_API_ID and TELEGRAM_TOOLS_API_HASH are used when set in the environment; otherwise login asks 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 channels setting 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>.log that service install prints. On Linux, it logs to the systemd user journal (journalctl --user -u <unit> as service status shows); run loginctl enable-linger to 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, which service install writes to ~/Library/Application Support/io.github.craft-agent.telegram-tools/ and service uninstall deletes. Switching the item off there stops syncing.

  • Service updates and restarts: service install records this package's executable path and Node's, so run service install again after updating either, or changing keep_disappearing or channels. Where Node's path holds its version, as under a version manager, service install records a node on PATH at a path without it that starts the same Node, and warns when there is none. That node then runs whichever Node the manager makes current. service status and telegram_account_read say 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, logout and remove refuse while the service runs; stop it first (service uninstall, or stop foreground sync).

  • Logging out and removing: logout ends the session at Telegram and keeps the mirror, removing only the session storage. remove uninstalls the service and permanently deletes the label's directory, and on macOS the service's log too (service uninstall keeps 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_read tells 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_disappearing and channels are 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), so telegram_file_save can 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 turns channels on accepts that §3.3 risk. With channels off, 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 to dist/mcp.js and run by telegram-tools.

  • src/opencode/ — the OpenCode plugin, built to dist/opencode.js.

  • src/cli/ — the command line the Hermes plugin calls, built to dist/cli.js and run by telegram-tools-cli.

  • src/hermes/, plugin.yaml and __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/ and mcp_config.json — the plugin manifests of the other harnesses. Codex reads .codex-plugin/plugin.json; a root plugin.json would make it load the plugin as an Agent Plugin, whose tool budget hides tools past a fixed size. The root .mcp.json is 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

AGPL-3.0 with Commons Clause

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      D
      maintenance
      Enables 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.
      1
      MIT
    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables AI agents to interact with a user's Telegram account: list chats, read history, search, and send messages through Telegram's MTProto API.
      2
      MIT
    • A
      license
      Not graded
      quality
      B
      maintenance
      Enables 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.
      1
      MIT