Skip to main content
Glama
alamri-intel

Telegram OSINT

by alamri-intel
README.md
# Telegram OSINT

An MCP server for your own Telegram account. Read your chats through an AI
assistant, and run a background monitor that watches every incoming message
for things you care about.

The monitor works in two stages: cheap **rules** (substring / whole-word /
regex) decide what's worth looking at, then Claude judges each hit against
your plain-language **criteria** and decides whether it's worth surfacing.
On a firehose of busy channels, stage one keeps the cost down and stage two
keeps the noise down. The judge is optional — without it, every rule hit
becomes an alert.

Everything runs locally. Nothing leaves your machine except calls to
Telegram's API and, if you enable judging, Anthropic's.

## What it runs and sends

- **Local server.** The plugin starts a Python MCP server on your computer
  with `uv run --locked`. On first start, uv downloads Python and the exact
  package versions pinned in `uv.lock` from PyPI.
- **Telegram.** The server and the watcher log in to *your* Telegram
  account (a user session, not a bot) and talk to Telegram's servers to
  list chats, read and search messages, and receive new ones. Nothing is
  ever sent, deleted, or left on Telegram — there are no write tools.
- **Anthropic.** Only when judging is on and `ANTHROPIC_API_KEY` is set, the
  watcher sends the text of each rule-matched message, plus your criteria,
  to Anthropic's API for a verdict. Messages that match no rule are never
  sent anywhere.
- **Local files.** Session files, your API credentials (`.env`, mode
  `0600`), the alerts database, and the watcher log live in
  `~/.telegram-mcp/`. Nothing is uploaded elsewhere.
- **Background watcher.** A separate process you start yourself; the plugin
  never starts it for you. On macOS it can show desktop notifications via
  `osascript`.

## Where it works

| Claude app | Works |
|---|---|
| Claude Code (terminal, IDE, desktop app Code tab) | Yes |
| Cowork in the desktop app, on your computer | Yes |
| Chat on claude.ai web, desktop, or mobile | Skills only — chat can't start a local server, so the Telegram tools aren't available |

## Install

**Requires [uv](https://docs.astral.sh/uv/).** It provisions the right Python
version itself, so you don't need to manage one. On macOS, `brew install uv`;
for other systems, see uv's
[installation guide](https://docs.astral.sh/uv/getting-started/installation/).

As a Claude Code plugin — this is the easy path, and gives you the setup skill:

```bash
claude plugin marketplace add alamri-intel/telegram-mcp
claude plugin install telegram-osint@telegram-osint
```

Or from inside Claude Code: `/plugin marketplace add alamri-intel/telegram-mcp`,
then `/plugin install telegram-osint@telegram-osint`.

Start a new session, then run `/telegram-osint:setup` and answer the
questions — it writes your rules and criteria for you.

Or as a plain Python package, if you'd rather not use the plugin:

```bash
uv tool install "telegram-monitor-mcp[judge] @ git+https://github.com/alamri-intel/telegram-mcp"
claude mcp add telegram -- telegram-mcp
```

The `judge` extra pulls in the Anthropic SDK. Omit it if you only want rule
matching.

You need a Telegram `API_ID` / `API_HASH` pair from
<https://my.telegram.org> (API development tools). These identify the *app*,
not your account.

```bash
export TELEGRAM_API_ID=...
export TELEGRAM_API_HASH=...
export ANTHROPIC_API_KEY=...   # only if you want judging
```

## Start the watcher

```bash
telegram-mcp-watcher
```

The first run prompts for your phone number and the login code Telegram
sends. After that the saved session is reused and startup is silent. Leave
it running — alerts are only recorded while it's up.

To run it in the background:

```bash
nohup telegram-mcp-watcher > ~/.telegram-mcp/watcher.log 2>&1 &
```

## Registering the server by hand

The plugin does this for you. If you installed the plain package instead:

```bash
claude mcp add telegram -- telegram-mcp
```

Claude Desktop — add to `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "telegram": {
      "command": "telegram-mcp",
      "env": {
        "TELEGRAM_API_ID": "...",
        "TELEGRAM_API_HASH": "..."
      }
    }
  }
}
```

The server and the watcher use **separate** Telegram sessions on purpose:
one session file can only be used by one process at a time. A side effect
worth knowing is that the monitor tools work even if the server itself is
never logged in — they only read the local database.

## Tools

**Monitoring** — local database only, no Telegram login needed

| Tool | |
|---|---|
| `add_alert_rule(name, pattern, …)` | Create a prefilter rule. Live within ~5s. |
| `list_alert_rules(include_disabled)` | Rules with hit counts. |
| `set_alert_rule_enabled(rule_id, enabled)` | Pause or resume a rule. |
| `delete_alert_rule(rule_id)` | Delete a rule and its alerts. |
| `add_criterion(name, description)` | Add a plain-language judging criterion. |
| `list_criteria(include_disabled)` | Criteria with hit counts. |
| `set_criterion_enabled(id, enabled)` | Pause or resume a criterion. |
| `delete_criterion(id)` | Delete a criterion. |
| `list_alerts(limit, verdict, min_severity, …)` | What matched and what the judge decided. |
| `ack_alerts(alert_ids \| rule_id \| all_alerts)` | Clear alerts from the default view. |
| `monitor_status()` | Watcher liveness, counts, judge throughput. |

**Skills** (plugin install only)

`/telegram-osint:setup` interviews you about what you watch for and writes the
rules and criteria. `/telegram-osint:watcher` covers starting the daemon and
working out why alerts aren't arriving.

**Reading** — talks to Telegram

`list_dialogs`, `read_messages`, `search_messages`, `unread_summary`,
`preview_rule`. `list_dialogs` is how you find the numeric chat ids for rule
scopes; `search_messages` searches history, which rules never see;
`preview_rule` tests a candidate rule against real message history before you
create it.

**Login** — sets up your Telegram session

| Tool | |
|---|---|
| `auth_status()` | Whether credentials are saved and each session is logged in. |
| `set_api_credentials(api_id, api_hash)` | Save your Telegram app credentials to `~/.telegram-mcp/.env` (mode `0600`). |
| `login_request_code(phone)` | Ask Telegram to send a login code. |
| `login_submit_code(code, password)` | Finish login; the session file is saved locally. |

## Rules and criteria

They answer different questions. A **rule** asks "does this string appear?"
A **criterion** asks "does this matter?"

Rules:

- `match_type` — `substring` (default), `word` (whole words only, so
  `deploy` won't fire on `redeployment`), or `regex`
- `case_sensitive` — off by default
- `chat_ids` / `exclude_chat_ids` — scope it; omit `chat_ids` to watch
  everything. Exclusions apply first.
- `include_outgoing` — off by default, so your own messages don't match
- `notify` — desktop notification on a surfaced alert (macOS)

Criteria are prose. Write them the way you'd brief a person doing the
triage for you — say what qualifies *and* what doesn't, because near-misses
are where a judge earns its keep:

> **remote-backend-roles** — A specific open role for a backend engineer
> that is remote or remote-friendly in Europe, posted by someone hiring for
> it. Must name the company and the role. Excludes: recruiter cold-calls
> with no named company, roles requiring relocation, and anyone advertising
> their own availability.

Nothing about the subject matter is built in — the criteria supply it. The
same machinery works for job leads, release announcements, a research
field, or a resale market.

Each alert keeps the judge's verdict, severity, one-line summary, and its
reasoning — including why it rejected something. `list_alerts` hides
rejected alerts by default; pass `include_irrelevant=True` to audit them.

## Gotchas

- **Rules are forward-looking.** The watcher only sees messages that arrive
  while it's running. Use `search_messages` for history.
- **Check `monitor_status()` first.** If the watcher died, the alert tools
  keep answering from a stale database. Status is what tells you. It reports
  the watcher down when its heartbeat is over 90 seconds old.
- **Judging is best-effort.** If the API call fails, the alert is marked
  `error` and surfaced anyway — dropping a possible hit is worse than a
  false positive. Repeated auth failures disable judging for that run rather
  than retrying on every message.
- **A broken rule is skipped, not fatal.** An invalid regex logs a warning;
  the other rules keep running.

## Configuration

| Variable | Default | |
|---|---|---|
| `TELEGRAM_API_ID` / `TELEGRAM_API_HASH` | — | required |
| `ANTHROPIC_API_KEY` | — | required for judging |
| `TELEGRAM_MCP_HOME` | `~/.telegram-mcp` | database and session files |
| `TELEGRAM_MCP_DB` | `$TELEGRAM_MCP_HOME/monitor.db` | database path |
| `TELEGRAM_MCP_SESSION` | `mcp` | server's session name |
| `TELEGRAM_MCP_WATCHER_SESSION` | `watcher` | watcher's session name |
| `TELEGRAM_MCP_JUDGE` | `1` | `0` disables judging |
| `TELEGRAM_MCP_JUDGE_MODEL` | `claude-opus-5` | judging model |
| `TELEGRAM_MCP_JUDGE_EFFORT` | `low` | `low`…`max`; raise if verdicts look shallow |
| `TELEGRAM_MCP_NOTIFY` | `1` | `0` silences desktop notifications |
| `TELEGRAM_MCP_LOG` | `INFO` | watcher log level |

## Security

- Session files under `~/.telegram-mcp/` (mode `0700`) are equivalent to
  login credentials for your Telegram account. Never commit or share one.
- The database holds the full text of every message that matched a rule.
- These are **user** sessions, not bots — the watcher sees everything you
  see. Scope rules with `chat_ids` if you'd rather not store message text
  from every chat you're in.
- The server exposes **no** Telegram write operations: nothing here can
  send, delete, or leave anything. Add write tools deliberately and
  narrowly if you need them.

## License

MIT

TDQS

A3.9/5.0

Scored across 20 tools

Disambiguation5/5

Each tool targets a distinct action/resource: auth flow (status, credentials, request/submit code), dialog reading, search, alert-rule CRUD, criterion CRUD, alert listing/acking, and a monitor status check. The two-tier design (rules prefilter, criteria judge, alerts result) is explicitly documented, so even similar-sounding pairs like list_alert_rules/list_alerts and set_alert_rule_enabled/set_criterion_enabled are clearly separated.

Naming Consistency4/5

The vast majority follow a clean verb_noun pattern (list_alert_rules, add_alert_rule, delete_criterion, search_messages, ack_alerts). A few deviate to noun-phrases with no verb (auth_status, unread_summary, monitor_status), which is a minor inconsistency but still readable and predictable.

Tool Count4/5

At 20 tools this is on the heavier side, but the surface spans several genuinely distinct concerns (auth, reading, rules, criteria, alerts, monitoring), and each tool maps to a real operation without obvious redundancy. It sits just above the ideal band rather than being bloated.

Completeness4/5

Coverage is strong: full login/auth flow, dialog listing, reading, searching, preview-before-create, and CRUD for both rules and criteria, plus alert listing/acking and daemon status. Minor gaps remain (no delete/prune or un-ack for alerts, no fetch-by-id), but these are workaroundable.

Maintenance

ActivityMaintained
ResponsivenessNo issues