Skip to main content
Glama
better-tg-cli MCP server

better-tg-cli MCP server

by TheVilfer
README.md
<div align="center">
  <picture>
    <source srcset="https://raw.githubusercontent.com/TheVilfer/better-tg-cli/main/assets/banner-dark.png" media="(prefers-color-scheme: dark)"/>
    <source srcset="https://raw.githubusercontent.com/TheVilfer/better-tg-cli/main/assets/banner-light.png" media="(prefers-color-scheme: light)"/>
    <img src="https://raw.githubusercontent.com/TheVilfer/better-tg-cli/main/assets/banner-light.png" alt="Better-TG-CLI"/>
  </picture>

  [![npm version](https://img.shields.io/npm/v/better-tg-cli?style=flat&colorA=000000&colorB=000000)](https://www.npmjs.com/package/better-tg-cli)
  [![CI](https://img.shields.io/github/actions/workflow/status/TheVilfer/better-tg-cli/ci.yml?branch=main&label=ci&style=flat&colorA=000000&colorB=000000)](https://github.com/TheVilfer/better-tg-cli/actions/workflows/ci.yml)
  [![npm provenance](https://img.shields.io/badge/npm-provenance-000000?style=flat&colorA=000000&colorB=000000&logo=npm)](https://www.npmjs.com/package/better-tg-cli#provenance)
  [![install size](https://img.shields.io/npm/unpacked-size/better-tg-cli?label=size&style=flat&colorA=000000&colorB=000000)](https://www.npmjs.com/package/better-tg-cli)
  [![dependencies](https://img.shields.io/badge/dependencies-0-000000?style=flat&colorA=000000&colorB=000000)](package.json)
  [![MCP](https://img.shields.io/badge/MCP-server-000000?style=flat&colorA=000000&colorB=000000)](#mcp-server)
  [![platforms](https://img.shields.io/badge/macOS%20%C2%B7%20Linux%20%C2%B7%20Windows-000000?style=flat&colorA=000000&colorB=000000)](#install)
  [![license](https://img.shields.io/badge/license-MIT-000000?style=flat&colorA=000000&colorB=000000)](LICENSE)

  <p>
    <a href="#install">Install</a> · <a href="skills/better-tg-cli/reference.md">Reference</a> · <a href="SECURITY.md">Security</a> · <a href="https://github.com/TheVilfer/better-tg-cli/issues">Issues</a> · <a href="README.ru.md">Русский</a> · <a href="README.es.md">Español</a>
  </p>
</div>

https://github.com/user-attachments/assets/d143f7c7-7d50-439c-b373-cbcbff5a6675

**Better-TG-CLI is a Telegram CLI and MCP server for AI agents** (Claude Code, Codex, Cursor,
Claude Desktop, Gemini CLI) that works on your own Telegram account: agents read, search and send
messages and press bot buttons, and you approve every send.

It is an unofficial, open-source client (MTProto via [`teleproto`](https://www.npmjs.com/package/teleproto),
TL layer 229). The command is `telegram`: about 60 commands for reading, searching, writing, bots,
groups and exports, built so that agents can drive it cheaply and safely. It also ships as an MCP
server (`telegram mcp`) and an agent skill.

```console
$ telegram inbox -n 3
48 unread in 7 chats (showing 3)
1234567890 user unread=2 Alice @alice | see you at 7?
-1001234567 supergroup muted unread=41 Rust Seattle @rust_sea | anyone tried 1.90?
-1009876543 channel unread=5 Changelog | v2.4 is out
$ telegram read @alice -n 1 --json
{"chatTitle":"Alice","messages":[{"id":812,"date":"2026-09-27T10:02:11.000Z","sender":"Alice","senderId":"1234567890","text":"see you at 7?"}]}
```

> [!WARNING]
> **Your account, your risk.** This is an unofficial client that logs in as you. Telegram may
> limit or freeze accounts that behave like bots. The risk is highest for new accounts, bulk
> messaging, mass joins or invites, and anything that looks like spam. Use it the way you would
> use Telegram yourself, keep writes off unless you need them, and read [SECURITY.md](SECURITY.md)
> before letting an agent write. The authors are not responsible for restricted accounts.

**Contents:** [Why Better-TG-CLI](#why-better-tg-cli) · [Install](#install) · [Log in](#log-in) · [Usage](#usage) · [MCP server](#mcp-server) · [FAQ](#faq) · [Development](#development) · [Privacy](#privacy) · [License](#license)

## Why Better-TG-CLI

Made for agents:

- **Current Telegram layer.** Bot replies with rich (layer 228+) content show real text instead of
  `(no text)`. You can press bot buttons (`click`) and walk bot menus.
- **Token-efficient output.** Off a TTY you get one compact line per item with the ID first, and
  JSON on one line with empty fields dropped. `--max-text` trims long posts. `telegram help-all -g
  <word>` prints every flag, generated from the code.
- **Exact reads.** Real pagination for `--since/--until`, `--unread`, threads and channel
  comments, forum topics, `get` by ID, search filters by type, sender or date, and `me` / `Избранное`
  for Saved Messages.
- **You approve every message.** After login the agent asks you in the chat before each send,
  edit or delete, and shows the exact text; it sends only after your yes. Writing without asking
  (`write-access on`) needs your own confirmation in a terminal or a macOS dialog. Every write is
  logged to `~/.config/tg/audit.jsonl`. Secrets live in the macOS Keychain, the
  Linux Secret Service or 1Password.
- **Small and self-contained.** The npm package is one 0.3 MB file with zero runtime
  dependencies. Homebrew installs a standalone binary with no Node.

It started as a fork of [skillhq/telegram](https://github.com/skillhq/telegram) and has since been
reworked throughout.

### Compared with other Telegram MCP servers

| | Better-TG-CLI | [chigwell/telegram-mcp](https://github.com/chigwell/telegram-mcp) | [chaindead/telegram-mcp](https://github.com/chaindead/telegram-mcp) | [WireCatLabs/tg-cli](https://github.com/WireCatLabs/tg-cli) |
|---|---|---|---|---|
| What you get | CLI, MCP server, agent skill | MCP server | MCP server | CLI, MCP server, agent skill |
| Language | TypeScript (Node 20+ or a standalone binary) | Python | Go | TypeScript |
| Telegram library | teleproto (GramJS fork, layer 229) | Telethon | gotd/td | mtcute |
| Runtime dependencies | none (one file) | Python packages | one Go binary | npm packages |
| Install | Homebrew, Scoop, npm, binaries | pip, uvx, Docker | Homebrew, npx, go install | npm, bun |

Taken from each project's README and manifest in November 2026; open an issue if something here is
out of date. What sets Better-TG-CLI apart is the write model (read-only, ask, on, with approvals
bound to the exact text) and output made for agents.

## Install

```bash
brew install thevilfer/tap/better-tg-cli   # standalone binary, macOS and Linux
npm install -g better-tg-cli               # Node >= 20, any OS
```

On Windows (experimental), with [Scoop](https://scoop.sh) or npm:

```powershell
scoop bucket add thevilfer https://github.com/TheVilfer/scoop-bucket
scoop install better-tg-cli
```

The Windows `telegram.exe` is not code-signed, so SmartScreen may warn about the release download.

`telegram update` upgrades an existing install, whichever of these you used. On a terminal the CLI
checks for new versions once a day. Agents and pipes never see that notice, and
`TG_NO_UPDATE_CHECK=1` turns it off.

Downloading a binary from Releases by hand on macOS? It is not notarized, so remove the
quarantine flag once: `xattr -d com.apple.quarantine ./telegram`. Homebrew handles this for you.

Do **not** install `@skillhq/telegram`. It is the old upstream build on GramJS (layer 198).

Every channel ships the same version from one release:

| Where | What you get | Install |
|---|---|---|
| [Homebrew](https://github.com/TheVilfer/homebrew-tap) | standalone binary | `brew install thevilfer/tap/better-tg-cli` |
| [npm](https://www.npmjs.com/package/better-tg-cli) | CLI and MCP server (Node 20+) | `npm install -g better-tg-cli` |
| [Scoop](https://github.com/TheVilfer/scoop-bucket) (Windows, experimental) | standalone `telegram.exe` | `scoop bucket add thevilfer https://github.com/TheVilfer/scoop-bucket` then `scoop install better-tg-cli` |
| [GitHub Releases](https://github.com/TheVilfer/better-tg-cli/releases) | binaries and SHA256SUMS | download by hand |
| Claude Code plugin | skill (uses the installed CLI) | [see below](#claude-code-plugin) |
| Claude Desktop extension | MCP server, runs on Claude's built-in Node | [download `.mcpb`](https://github.com/TheVilfer/better-tg-cli/releases/latest/download/better-tg-cli.mcpb) and open it |
| Grok Build plugin | skill and MCP server | [see below](#grok-build-plugin) |
| Gemini CLI extension | skill and MCP server | `gemini extensions install https://github.com/TheVilfer/better-tg-cli` |
| Cursor, VS Code | MCP server | [one-click buttons](#mcp-server) |
| Grok Bot | MCP server over HTTP from your Mac | [see below](#grok-bot-remote-mcp-over-http) |
| The CLI itself | agent skill for Claude Code, Codex, Cursor, Gemini CLI and 7 more | `telegram skill install` |
| [skills.sh](https://skills.sh) | agent skill for any shell agent | `npx skills add TheVilfer/better-tg-cli` |
| [MCP Registry](https://registry.modelcontextprotocol.io/v0/servers?search=better-tg-cli) | MCP server entry `io.github.TheVilfer/better-tg-cli` | through your MCP client |

### Claude Code plugin

The skill as a plugin, kept up to date by Claude Code:

```
/plugin marketplace add TheVilfer/better-tg-cli
/plugin install better-tg-cli@better-tg-cli
```

The plugin is the skill alone, so install the CLI first (above) and log in once with
`telegram onboard` in a terminal. Claude Code then runs `telegram` itself. Anthropic's plugin
directory accepts MCP servers that run on your computer only from Verified organizations, so the MCP
server is added on its own if you want it: `claude mcp add telegram -- telegram mcp`.

### Codex plugin

The skill and the MCP server as a Codex plugin (also shows up in the ChatGPT desktop app):

```bash
codex plugin marketplace add TheVilfer/better-tg-cli
codex plugin add better-tg-cli@better-tg-cli
```

### Grok Build plugin

[Grok Build](https://x.ai/cli) installs the plugin with the skill and the MCP server together:

```bash
grok plugin install TheVilfer/better-tg-cli --trust
# or add the marketplace first, then install from the /plugins menu:
grok plugin marketplace add TheVilfer/better-tg-cli && grok plugin install better-tg-cli --trust
```

### As an agent skill

The skill ([`skills/better-tg-cli`](skills/better-tg-cli/SKILL.md)) teaches any agent with a shell
(Claude Code, Codex, Cursor, Gemini CLI, OpenCode and others) to use the CLI safely. It checks the
setup, never logs in on its own, keeps writes behind your approval, and avoids ban-prone patterns.
The CLI carries the skill and installs it itself, so it always matches your version:

```bash
telegram skill install                  # every supported agent found on this machine
telegram skill install -a claude-code codex
telegram skill status                   # installed, outdated, linked or missing, per agent
```

It knows Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, Grok Build, OpenCode, Goose,
Droid, Windsurf and Pi (`telegram skill status` lists the folders). Run it again after
`telegram update` to refresh the skill. A skill folder that is a symlink is left alone unless you
pass `--force`. For other agents, use the [skills](https://skills.sh) CLI:

```bash
npx skills add TheVilfer/better-tg-cli          # pick agents interactively
npx skills add TheVilfer/better-tg-cli -g -a claude-code -a codex -y
```

## Log in

**Through your agent** (easiest): ask it to set up Telegram, or run `telegram onboard` yourself. It
opens a page on 127.0.0.1 where you get a login key from [better-tg-cli.com](https://better-tg-cli.com)
(it comes back to the page by itself) or enter your own keys, scan a QR code and type your 2FA
password. The agent only starts the command and waits: it never sees the login key, the QR code or the
password. The page answers only on a random secret path and closes when you're done.

**With your own API keys** (the default):
1. Open https://my.telegram.org/apps, create an application, and copy its `api_id` and `api_hash`.
2. Run `telegram auth --qr` and enter them. Then, on your phone, go to Settings → Devices → Link
   Desktop Device and scan the QR code. Enter your 2FA password if you have one. Plain
   `telegram auth` asks for your phone number and a login code instead. If the QR won't scan on a
   light terminal theme, run with `TG_QR_INVERT=1`.

**With a login key.** You don't need your own keys with a login key. Get one at
[better-tg-cli.com](https://better-tg-cli.com/login) (confirm your email with a code; the page also
has an install guide), or from the maintainer.
Run `telegram auth --login-key --qr` and paste the key, or pass it as `TG_LOGIN_KEY=…` or
`--login-key -`. The login service (`broker/`) hands out the app's keys once, for this login only.
The `api_hash` is not kept on your machine. Login keys are personal, work on up to 3 computers, and
can be revoked. Before 0.37 the key was called an invite: `--invite` and `TG_INVITE` still work.

The session is stored in the macOS Keychain (service `tg-cli`), or in 1Password with
`--op-vault <vault>`. `telegram logout` removes it. On Linux it goes to the Secret Service (GNOME
Keyring, KWallet, KeePassXC) through `secret-tool`, which needs the `libsecret-tools` package. A
session already saved in the config file moves there automatically. Without any secret store, the
session is kept in `~/.config/tg/config.json5` (mode 0600). On Linux without a working Secret
Service (no `secret-tool` or no session bus, as on cloud agent computers) the write mode is then kept
in a plain file in the profile too, which guards against mistakes, not against an agent that can
edit files. On Linux, `write-access on` is confirmed at a terminal prompt. On Windows the secrets live in
`%APPDATA%\tg\secrets.dpapi`, encrypted with DPAPI for your Windows user (only you on this machine
can decrypt it), and `write-access on` asks in a Yes/No window when there's no terminal.

## Usage

```bash
telegram chats --type channel                 # one line per chat, ID first
telegram read "Chat" --since 1h               # exact range, newest first (--asc to flip)
telegram read @channel --thread 123           # comments under a post
telegram search "invoice" --chat "Work" --type document
telegram get "Chat" 812 813                   # exact messages by ID
telegram download "Chat" 812                  # save the attached file
telegram sync --chat "Chat" --output ./export --resume   # incremental markdown export

telegram drafts                               # saved but unsent drafts
telegram admin-log "Team"                     # a group's recent admin actions
telegram profile-photos @alice                # a user's profile photos
telegram read @channel -n 20 --full           # every message stat (views, reactions, flags, media)
telegram stats "Mock News" --graphs           # full channel stats + growth graphs; --message <id> / --story <id> too
telegram sessions ; telegram stars            # active login sessions; Telegram Stars balance & transactions
telegram invites "Team" --requests            # a chat's invite links, or its pending join requests

telegram write-access ask                     # each write waits for your yes in the chat (default after onboard)
telegram write-access on --for 1h             # or: writes without asking, for an hour
telegram send @alice "on my way"
printf '%s' "$text" | telegram send @alice -  # long or multi-line text: from stdin, no quoting problems
telegram reply "Chat" 812 "on it" --silent
telegram click @SomeBot 4410 "Settings"       # press an inline button
telegram pin-chat @alice                      # pin a chat; mark-unread / delete-dialog too
telegram edit-permissions "Team" @alice --mute --until 1d   # restrict a member; --lift to undo
telegram topic create "Dev Forum" "Release 2.0"            # forum topics: create/edit/close/reopen/pin

telegram api messages.GetAllDrafts            # call any raw TL method (full API reachability)
telegram api channels.GetAdminLog --params '{"channel":{"_chat":"Team"},"limit":20,"q":""}'
telegram messages get-history --peer @durov --limit 10      # or a typed command per TL method
telegram account get-authorizations           # telegram <namespace> --help lists a namespace
telegram help get-config                      # telegram ping, telegram req-pq, … for root methods
```

Read commands take `--json`, and some also take `--markdown`. `read`/`get`/`search`/`inbox`/`watch`
take `--full` (alias `--stats`) to include every per-message statistic (views, forwards, full
reaction breakdown, comments, flags, rich media metadata) — default output is unchanged without it.
See all commands with `telegram help-all`. [reference.md](skills/better-tg-cli/reference.md) covers the behaviour that the flag list can't
explain: output shapes, threads, bot buttons, admin commands and troubleshooting. The generic
`telegram api <namespace.Method>` reaches 100% of the Telegram client API, and every one of the 823
TL methods also has its own typed top-level command `telegram <namespace> <method>` (root methods
like `telegram ping` too), generated from the schema with the same refusals and read/write guard as
`api`. Where a namespace name collides with a curated command (`stats`, `stories`, `contacts`,
`auth`, `folders`, `help`), the curated command is unchanged and the generated methods attach as
subcommands, picked only when the first word is exactly a method name (quote or use an ID for a
chat literally named like a method). Each method carries its official description and per-parameter
docs (from core.telegram.org): `telegram <namespace> --help` lists a namespace's methods marked
`[read]`/`[write]`/`[refused]`, `telegram <namespace> <method> --help` shows params, an example and
the docs link, and `telegram schema [<ns> [<method>]]` prints the same as JSON.
[docs/api-coverage.md](docs/api-coverage.md) tracks coverage against the schema and Telethon.

## MCP server

For clients without a shell (Claude Desktop, Cursor, other MCP hosts), `telegram mcp` serves three
tools over stdio:
- `telegram_help`: a searchable flag reference;
- `telegram_read`: read-only, so clients may auto-approve it;
- `telegram_write`: marked destructive, and still needs `write-access on` from you.

Log in with `telegram auth` in a terminal first. Then install it in one click:

[![Install in Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en/install-mcp?name=telegram&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsImJldHRlci10Zy1jbGlAbGF0ZXN0IiwibWNwIl19)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install_MCP-000000?style=flat&colorA=000000&colorB=000000)](https://insiders.vscode.dev/redirect?url=vscode%3Amcp%2Finstall%3F%257B%2522name%2522%253A%2522telegram%2522%252C%2522command%2522%253A%2522npx%2522%252C%2522args%2522%253A%255B%2522-y%2522%252C%2522better-tg-cli%2540latest%2522%252C%2522mcp%2522%255D%257D)
[![Claude Desktop extension](https://img.shields.io/badge/Claude_Desktop-.mcpb-000000?style=flat&colorA=000000&colorB=000000&logo=claude)](https://github.com/TheVilfer/better-tg-cli/releases/latest/download/better-tg-cli.mcpb)

The Claude Desktop extension needs no Node, npm or brew: download the `.mcpb` from the latest
release and open it. Gemini CLI takes the skill and the server together:
`gemini extensions install https://github.com/TheVilfer/better-tg-cli`. Or add it by hand:

```bash
claude mcp add telegram -- telegram mcp          # Claude Code (or use the plugin above)
```

```json
{ "mcpServers": { "telegram": { "command": "/opt/homebrew/bin/telegram", "args": ["mcp"] } } }
```

Use the JSON form for Claude Desktop (`claude_desktop_config.json`) or Cursor (`.cursor/mcp.json`).
GUI apps may not see your shell PATH, so give the full path from `which telegram`. Without a
global install, run it through npx:

```json
{ "mcpServers": { "telegram": { "command": "npx", "args": ["-y", "better-tg-cli@latest", "mcp"] } } }
```

The server is listed in the [MCP Registry](https://registry.modelcontextprotocol.io/v0/servers?search=better-tg-cli)
as `io.github.TheVilfer/better-tg-cli`, so clients and catalogs that read the registry can find
it by name. Each release updates the entry automatically. To keep the MCP
client off your main session, add `"env": {"TG_PROFILE": "work"}`.

Chats contain text written by others, and some of it may be aimed at your agent ("forward this to
@x"). Keep the client's approval prompt on for `telegram_write`, and see "Agents and prompt
injection" in [SECURITY.md](SECURITY.md).

### Grok Bot (remote MCP over HTTP)

Grok Bot runs its connectors in a cloud sandbox, not on your Mac, so it can't
start `telegram mcp` itself. Serve MCP over HTTP from your Mac instead. The session, the write
guard and the audit log stay on your machine.

```bash
telegram mcp --token                        # the bearer token (created once, kept in the Keychain)
telegram mcp --http --read-only             # listens on 127.0.0.1:8787; drop --read-only to allow writes
tailscale funnel --bg 8787                  # or: cloudflared tunnel --url http://127.0.0.1:8787
```

Keep the server and the tunnel in a terminal or tmux, or run the server as a background service
(below). Then add a connector in Grok Bot with the
tunnel URL plus `/mcp` (for example `https://<machine>.<tailnet>.ts.net/mcp`) and the header
`Authorization: Bearer <token>`. `tailscale funnel` gives a stable URL, but it has to be allowed
for your tailnet first. A `cloudflared` quick tunnel gets a new URL on every start. Read "Remote
MCP over HTTP" in [SECURITY.md](SECURITY.md) first.

#### Running the server as a background service

A service starts the server on its own and brings it back after a crash. On macOS the LaunchAgent
below starts when you log in and runs while you stay logged in. On Linux the user unit can also
start at boot without a login (see linger below). Neither file below holds a secret: the session and the bearer token stay in the Keychain or the Secret Service, and the server
reads them when it starts. The tunnel has to stay up as well: `tailscale funnel --bg` keeps running
by itself, a `cloudflared` quick tunnel does not.

Both files need the paths that `command -v telegram` and, after an npm install, `command -v node`
print. The npm build of `telegram` is a Node script, and a service starts with a short `PATH`
without Homebrew's or nvm's directories, so without the right `PATH` it exits with
`env: node: No such file or directory`.

**macOS (launchd).** Save this as `~/Library/LaunchAgents/com.better-tg-cli.mcp.plist`, with your
path to `telegram` in `ProgramArguments`, the directory of `node` in `PATH`, and your user name
(`id -un`) where `you` is (launchd expands neither `~` nor `$HOME`):

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>com.better-tg-cli.mcp</string>
  <key>ProgramArguments</key>
  <array>
    <string>/opt/homebrew/bin/telegram</string>
    <string>mcp</string>
    <string>--http</string>
    <string>--read-only</string>
  </array>
  <key>EnvironmentVariables</key>
  <dict>
    <key>PATH</key>
    <string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
    <key>TG_NO_UPDATE_CHECK</key>
    <string>1</string>
  </dict>
  <key>RunAtLoad</key>
  <true/>
  <key>KeepAlive</key>
  <dict>
    <key>SuccessfulExit</key>
    <false/>
  </dict>
  <key>StandardOutPath</key>
  <string>/Users/you/Library/Logs/better-tg-cli-mcp.log</string>
  <key>StandardErrorPath</key>
  <string>/Users/you/Library/Logs/better-tg-cli-mcp.log</string>
</dict>
</plist>
```

```bash
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.better-tg-cli.mcp.plist
launchctl print gui/$(id -u)/com.better-tg-cli.mcp | grep -E 'state|last exit'
launchctl kickstart -k gui/$(id -u)/com.better-tg-cli.mcp   # restart
launchctl bootout gui/$(id -u)/com.better-tg-cli.mcp        # stop and unload
```

`KeepAlive` with `SuccessfulExit` false restarts the server whenever it fails or is killed. If macOS
asks for Keychain access on the first start, allow it.

**Linux (systemd user unit).** Save this as `~/.config/systemd/user/better-tg-cli-mcp.service`, with
the directories of `telegram` and `node` in `PATH`:

```ini
[Unit]
Description=Better-TG-CLI MCP server over HTTP
After=network-online.target

[Service]
Environment=PATH=/usr/local/bin:/usr/bin:/bin
Environment=TG_NO_UPDATE_CHECK=1
ExecStart=/usr/bin/env telegram mcp --http --read-only
Restart=on-failure
RestartSec=5

[Install]
WantedBy=default.target
```

```bash
systemctl --user daemon-reload
systemctl --user enable --now better-tg-cli-mcp.service
systemctl --user status better-tg-cli-mcp.service
journalctl --user -u better-tg-cli-mcp.service -f
```

The unit runs while you are logged in. `sudo loginctl enable-linger "$USER"` starts it at boot and
keeps it after you log out, but it does not unlock the keyring. If the Secret Service is missing or
locked for the service, the server exits with "The HTTP server needs a secret store for its token",
and systemd starts it again 5 seconds later. To see which secret store the service finds, run
`telegram check` with the unit's environment.

Where the service has no Secret Service (a headless server, say), use the 1Password store. Log in
once with `telegram auth --op-vault <vault>` under the same profile, put the directory of `op` in
the unit's `PATH`, and keep `OP_SERVICE_ACCOUNT_TOKEN=…` in a file only you can read (mode 0600),
loaded with `EnvironmentFile=%h/.config/tg/mcp.env` in `[Service]`. That token is a secret, so it
stays out of the unit file.

After `telegram mcp --rotate-token`, restart the service (`launchctl kickstart -k …` or
`systemctl --user restart better-tg-cli-mcp`): the running server keeps the old token until then.

## FAQ

**Will Telegram ban my account?**
Using your own account from a third-party client is allowed by the [Telegram API terms](https://core.telegram.org/api/terms). What gets accounts limited is behaviour that looks like a bot: bulk messaging, mass joins or invites, spam, and brand-new accounts doing a lot at once. Use it the way you would use Telegram yourself. The skill tells agents to avoid these patterns.

**Is this a bot?**
No. It logs in as you over MTProto, like Telegram Desktop, and sees exactly what you see. Bots can't read your chats; this can.

**Can an agent send messages on its own?**
Not by design. After login the profile is in mode `ask`: before each write the agent shows you the exact action in the chat and sends only after your yes, and the code it gets for that yes works once, for that exact text. Over MCP, clients that support elicitation ask you in their own dialog instead. This relies on the agent following its instructions: an agent that has a shell on your user account can read the session from the Keychain and act as you, so give such agents only chats you trust, or connect them through MCP only. `write-access on` (no questions) needs your confirmation in a terminal or a macOS dialog. Details are in [SECURITY.md](SECURITY.md).

**Does reading mark messages as read?**
No. `read`, `inbox` and `search` leave chats unread. Only `telegram mark-read` marks them.

**Do I need my own API keys?**
Yes, from [my.telegram.org/apps](https://my.telegram.org/apps). They are free and take a minute to create. With a login key from [better-tg-cli.com](https://better-tg-cli.com/login), `telegram auth --login-key --qr` logs you in without them.

**Where is my session stored, and who can see my messages?**
The session is in the macOS Keychain, the Linux Secret Service or 1Password. The CLI talks to Telegram directly and has no analytics. The only other hosts are npm for a daily version check and the login service when you log in with a login key. See [PRIVACY.md](PRIVACY.md).

**Can I use several accounts?**
Yes, with `TG_PROFILE`: each profile has its own login, config and Keychain items, for example `TG_PROFILE=work telegram auth --qr`. Switching accounts properly is tracked in [#5](https://github.com/TheVilfer/better-tg-cli/issues/5).

**Does it work on Windows?**
Yes, experimentally: `scoop install better-tg-cli` or npm. Secrets are stored with Windows DPAPI
and CI runs the tests on Windows, but live use there is less tested than on macOS and Linux. Please
report problems in the issues.

**Can I use it from Grok Bot, ChatGPT or another cloud agent?**
Cloud agents can't start a program on your computer, so run the MCP server over HTTP and reach it through a tunnel. See [Grok Bot](#grok-bot-remote-mcp-over-http).

**How do I allow writes on Grok Bot or another cloud agent?**
After `telegram onboard` it is already in mode `ask`: the agent asks you in the chat before each write. If it says writes are off, ask it to run `telegram write-access ask`; that works there as is. Only `telegram write-access on --for 1h` (no questions) needs your choice: those computers have no secret store and no terminal for you, so it opens a page in the agent's own browser where you choose. Running the command in your own terminal doesn't help: it changes the CLI on your computer, not the agent's. The agent should not try to install a keyring there; it can't work without a desktop session. On such computers the setting sits in a plain file, which protects against mistakes but not against a hostile agent.

**Login fails with "NetSocket was closed" on a cloud computer (Grok Bot and others).**
Some hosted agent computers let only HTTPS out, which drops Telegram's TCP transport. The CLI then switches to MTProto over WebSocket (`wss://*.web.telegram.org`, the transport of web.telegram.org) by itself and remembers it for the profile. `TG_TRANSPORT=tcp|wss|auto` picks the transport by hand.

**How do I update?**
Run `telegram update`. It uses the same channel you installed from: Homebrew, npm or a release binary.
If you installed the agent skill with `telegram skill install`, run that again to refresh it.

## Development

[DEVELOPMENT.md](DEVELOPMENT.md) covers:
- running from source (`scripts/tg-dev`);
- isolated dev profiles (`TG_PROFILE`) that never touch your real session;
- Telegram test servers;
- editor debugging and tests.

Releases are cut by `scripts/release.sh` (a release PR; the version comes from the PR titles), then `scripts/release.sh tag` after the merge. A tag builds the binaries and
publishes to GitHub Releases, npm (trusted publishing with provenance), the Homebrew tap and the
MCP Registry. See
[CONTRIBUTING.md](CONTRIBUTING.md).

## Uninstall

1. `telegram logout` deletes the saved session. To end it on Telegram's side too, terminate it in
   Telegram → Settings → Devices.
2. `telegram skill uninstall` removes the skill from the agents it was installed into.
3. Remove the program: `brew uninstall better-tg-cli`, `npm uninstall -g better-tg-cli` or
   `scoop uninstall better-tg-cli`. For a binary from Releases, delete `telegram` / `telegram.exe`.
4. Delete the config folder: `~/.config/tg` (`%APPDATA%\tg` on Windows), plus `~/.config/tg-<profile>`
   for any `TG_PROFILE` you used. On Windows this also removes the DPAPI secrets file. Elsewhere the
   api_hash and write-access flag stay in the secret store: delete the `tg-cli` items in Keychain
   Access (or `secret-tool clear service tg-cli`), or the item in your 1Password vault.

## Privacy

No analytics. The only service we run is the optional login service, which never sees your
messages or session. See [PRIVACY.md](PRIVACY.md) for the three hosts the CLI talks to and what it
stores locally.

## Code signing policy

Windows binaries are built from this repository by GitHub Actions only (`.github/workflows/release.yml`),
never on a personal machine. We have applied for free code signing from SignPath Foundation; once it
is in place, release binaries will carry: *Free code signing provided by
[SignPath.io](https://about.signpath.io), certificate by [SignPath Foundation](https://signpath.org).*
Until then the Windows `telegram.exe` is unsigned; macOS binaries are ad-hoc signed.

- Committers and reviewers: [@TheVilfer](https://github.com/TheVilfer)
- Approvers (each signed release): [@TheVilfer](https://github.com/TheVilfer)
- Privacy policy: [PRIVACY.md](PRIVACY.md). The CLI talks only to Telegram, to the login service
  when you log in with a login key, and to the npm registry for a daily update check in a terminal
  (off with `TG_NO_UPDATE_CHECK=1`).

## License

MIT, see [LICENSE](LICENSE). Based on [skillhq/telegram](https://github.com/skillhq/telegram) by
Derek Rein. Not affiliated with or endorsed by Telegram; "Telegram" is a trademark of its owner.
Use it in line with the [Telegram API Terms of Service](https://core.telegram.org/api/terms).