Skip to main content
Glama
README.md
# tg-mcp

MCP server for a personal Telegram account. Read, search, download media, and send
messages or files, from any MCP client.

Credentials stay on the machine in `~/.tg/` and are never returned by any tool.

## Setup on a new machine

Needs [uv](https://docs.astral.sh/uv/) and nothing else. Python, dependencies and the
package are all fetched on demand.

**1. Sign in** (once per machine, interactive because Telegram sends a code):

```
uvx --from git+https://github.com/Abdk4Moura/tg-mcp tg-mcp-login
```

It prompts for `api_id` / `api_hash` from https://my.telegram.org (API development
tools), then your phone number, then the code Telegram sends to the app. It writes
`~/.tg/credentials.json` and `~/.tg/session.session`, both mode 600, and prints your
recent chats with their ids.

**2. Register the server:**

```
claude mcp add telegram -- uvx --from git+https://github.com/Abdk4Moura/tg-mcp tg-mcp
```

That is the whole setup. `claude mcp list` should show `telegram ✔ Connected`.

## Setting it up for other agents

**The sign-in is per machine, not per agent.** Run `tg-mcp-login` once and every agent on
that machine shares `~/.tg/`. Adding a second or third client is config only.

There is no single config format across MCP clients, so this package ships an installer
that writes each one in its own shape:

```
uvx --from git+https://github.com/Abdk4Moura/tg-mcp tg-mcp-install
```

It configures every supported client it finds and leaves the rest alone. `--list` shows
what it detected, `--dry-run` prints the changes without making them, `--name` sets the
server name, and naming clients (`tg-mcp-install opencode cursor`) limits it to those.
Existing config is merged rather than replaced, a `.bak` is kept, and a config file it
cannot parse is skipped with a warning rather than overwritten.

| Client | How it is registered |
|---|---|
| Claude Code | `claude mcp add` |
| Codex | `codex mcp add` |
| opencode | `~/.config/opencode/opencode.json` |
| Claude Desktop | `claude_desktop_config.json` |
| Cursor | `~/.cursor/mcp.json` |
| Windsurf | `~/.codeium/windsurf/mcp_config.json` |

The rest of this section is the same thing by hand. Under every format it is one command
and three arguments:

| | |
|---|---|
| command | `uvx` |
| args | `--from`, `git+https://github.com/Abdk4Moura/tg-mcp`, `tg-mcp` |

### Claude Code

```
claude mcp add telegram -- uvx --from git+https://github.com/Abdk4Moura/tg-mcp tg-mcp
```

Add `--scope user` to make it available in every project rather than just the current one.

### Claude Desktop, Cursor, Windsurf, Continue, and most others

These share the same `mcpServers` block. Add:

```json
{
  "mcpServers": {
    "telegram": {
      "command": "uvx",
      "args": ["--from", "git+https://github.com/Abdk4Moura/tg-mcp", "tg-mcp"]
    }
  }
}
```

to the client's config file:

| Client | File |
|---|---|
| Claude Desktop (macOS) | `~/Library/Application Support/Claude/claude_desktop_config.json` |
| Claude Desktop (Windows) | `%APPDATA%\Claude\claude_desktop_config.json` |
| Cursor | `~/.cursor/mcp.json`, or `.cursor/mcp.json` for one project |
| Windsurf | `~/.codeium/windsurf/mcp_config.json` |
| Continue | `~/.continue/config.json` |

Restart the client afterwards. Most only read MCP config at startup.

### opencode

opencode does not use `mcpServers`. Its key is `mcp`, the command is a single array rather
than a command plus args, and each server declares its `type`. Add to
`~/.config/opencode/opencode.json` (or `opencode.json` / `.opencode/opencode.json` in a
project, for that project only):

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "telegram": {
      "type": "local",
      "command": ["uvx", "--from", "git+https://github.com/Abdk4Moura/tg-mcp", "tg-mcp"],
      "enabled": true
    }
  }
}
```

Environment variables go in an `environment` object, not `env`. `opencode mcp list` shows
the server and whether it connected.

opencode reads `opencode.json` and `opencode.jsonc` and merges them, so if your config is
a commented `.jsonc` you can drop the block above into a plain `opencode.json` beside it
and leave the commented file alone. That is what `tg-mcp-install` does.

`opencode mcp add telegram` also works, but it prompts for the command interactively, so
it is not scriptable for a local server the way `claude mcp add` is. The `--url` flag on
that command is for remote servers only.

### Codex

```
codex mcp add telegram -- uvx --from git+https://github.com/Abdk4Moura/tg-mcp tg-mcp
```

Codex stores this in `~/.codex/config.toml`. `codex mcp list` shows it.

### Anything else

Give the client the same command and args from the table at the top of this section, in
whatever shape its own MCP documentation specifies.

### If `uvx` is not on PATH

Some clients launch with a minimal environment and will not find `uvx`. Use its absolute
path as the command (`which uvx` to find it, commonly `~/.local/bin/uvx`).

## Tools

| Tool | What it does |
|---|---|
| `tg_whoami` | Which account is signed in |
| `tg_list_chats` | Recent chats with their numeric ids |
| `tg_read_chat` | Read messages; `peer` takes an id, `@username`, or `me` for Saved Messages; optional `since` / `until` as `YYYY-MM-DD` |
| `tg_search` | Search message text across all chats or within one peer |
| `tg_download_media` | Fetch a message's attachment to disk |
| `tg_send_message` | **Sends** text. Real, immediate, not recallable by this server |
| `tg_send_file` | **Sends** a file with an optional caption |

## Sessions

Each machine that runs `tg-mcp-login` creates its own Telegram session, listed
separately under Settings → Devices and revocable on its own.

Copying `session.session` between machines also works, but both then share one session
identity: revoking it logs out both, and a login from a very different IP can trip
Telegram's security checks. Prefer a fresh login per machine.

That file is full account access. Anyone holding it can read and send as you.

## Configuration

| Variable | Default | Purpose |
|---|---|---|
| `TG_DIR` | `~/.tg` | Where `credentials.json` and `session.session` live |
| `TG_DOWNLOAD_DIR` | `$TG_DIR/downloads` | Where `tg_download_media` writes |

## Local development

```
uv run --directory /path/to/tg-mcp tg-mcp
```

TDQS

A4.1/5.0

Scored across 7 tools

Disambiguation5/5

Each tool targets a distinct action: listing chats, reading messages, searching, downloading media, sending text, sending files, and identifying the account. There is no overlap, and the boundaries between tools are clear.

Naming Consistency4/5

All tools share the 'tg_' prefix and most follow a verb_noun pattern (list_chats, read_chat, download_media, send_message, send_file). Two exceptions are 'search' (verb only) and 'whoami' (question format), which are minor deviations from the otherwise consistent pattern.

Tool Count5/5

Seven tools is well-scoped for a Telegram server, covering core operations without being either too sparse or overly heavy. Each tool has a clear purpose and earns its place in the set.

Completeness4/5

The set covers the primary Telegram workflows: discovering chats, reading and searching messages, downloading media, sending text and files, and checking account identity. Minor gaps like message editing/deletion or chat management exist, but they are not critical for the server's apparent purpose.

Maintenance

ActivitySlowing
ResponsivenessNo issues