Telegram OSINT
One line: An MCP server for your own Telegram account that lets an AI assistant read your chats and manage a local two-stage monitor (rules → Claude judge) over incoming messages.
Login & auth —
auth_status,set_api_credentials(API ID/hash →~/.telegram-mcp/.env, mode 0600),login_request_code,login_submit_code(with optional 2FA password), for either thewatcherormcpsession.Reading Telegram —
list_dialogs(find numeric chat ids),read_messages,search_messages(searches history, which rules never see),unread_summary, andpreview_ruleto test a candidate rule against real history before creating it.Prefilter rules —
add_alert_rule(substring / whole-word / regex, case sensitivity,chat_idsscoping,exclude_chat_ids,include_outgoing, macOSnotify), pluslist_alert_rules,set_alert_rule_enabled,delete_alert_rule. Live within ~5s, no restart; forward-looking only.Judge criteria —
add_criterionwith plain-language prose saying what qualifies and what doesn't, pluslist_criteria,set_criterion_enabled,delete_criterion; Claude scores each rule hit against them.Alerts triage —
list_alertsfiltering by verdict (relevant/irrelevant/pending/skipped/error), severity, rule, chat,since_hours, andinclude_irrelevantto audit rejections;ack_alertsby ids, rule, or all.Health —
monitor_statusreports watcher liveness via heartbeat, counts, and judge throughput (stale > 90s = watcher down).Skills (plugin install) —
/telegram-osint:setup,:review,:watcherfor guided setup, coverage tuning, and daemon troubleshooting.Not possible — no Telegram write operations at all: nothing can send, delete, or leave anything.
Integrates with a user's Telegram account to list dialogs, read and search message history, summarize unread messages, and monitor incoming messages via local rules and judging criteria. It can generate alerts for matching messages, with no Telegram write operations.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Telegram OSINT search my telegram chats for anything about the project deadline"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Unofficial Telegram OSINT
An unofficial, independent plugin. Not affiliated with, endorsed by or sponsored by Telegram.
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 inuv.lockfrom 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_KEYis 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.Login happens in the chat. You type your Telegram API ID and hash, phone number, login codes and (if you have one) two-step verification password into the conversation, and Claude passes them to the local server. They become part of that conversation's history in your Claude app. The password is passed to Telegram once to sign in and is never stored.
Local files. Session files, your API credentials (
.env, mode0600), 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.
Related MCP server: Dotiphant
Where it works
Claude app | Works |
Claude Code (terminal, IDE, desktop app Code tab) | Yes |
Cowork in the desktop app | Yes, when the task runs on your computer — not in a cloud task |
Chat on claude.ai web, desktop, or mobile | Skills only — chat can't start a local server, so the Telegram tools aren't available |
Operating systems. Developed and tested on macOS. On Windows and Linux the tools, login and monitoring work, but desktop notifications are macOS-only: elsewhere alerts are still recorded and listed, they just don't pop up.
Requirements
Check these before installing — without uv the plugin's server cannot start, and the Telegram tools simply never appear.
What | Needed for | |
Required | uv on the computer running Claude | Starting the plugin's server. uv installs Python 3.10+ and the pinned packages itself. |
Required | A Telegram account and an app API ID + API hash from my.telegram.org (API development tools) | Logging in |
Required | Claude Code, or Cowork running on your computer | Local servers don't run in claude.ai chat or cloud tasks |
Optional | An Anthropic API key (a workspace key from the Console) | Judging alerts against your criteria. Without it, every rule match becomes an alert. |
Optional | macOS | Desktop notifications |
Installing uv
macOS with Homebrew:
brew install uvWindows:
winget install --id=astral-sh.uv -eLinux, or macOS without Homebrew: use your package manager, or uv's standalone installer from its installation guide (it installs to
~/.local/bin)
Then check with uv --version, and fully quit and reopen your Claude app.
Two pitfalls:
Apps launched from the Dock or Start menu may not see
~/.local/binor a conda/virtualenv. Ifuv --versionworks in a terminal but the plugin still fails to connect, put uv somewhere the app can see (on macOS,sudo ln -s ~/.local/bin/uv /usr/local/bin/uv) — don't install it withpipinto a conda environment.The first start downloads Python and ~50 packages, which can outlast the app's connection timeout. If the first connection fails, retry once.
Network access
Host | When |
| First start, and after updates |
Telegram's servers | Always |
| Only with judging on |
Managed or corporate networks that block PyPI or inspect TLS will stop the first start; ask IT to allow the hosts above.
Python packages are declared in pyproject.toml and pinned exactly in
uv.lock (53 packages); uv installs them automatically. Direct
dependencies: mcp, telethon, and — for judging — anthropic and pydantic.
Install
As a Claude Code plugin — this is the easy path, and gives you the setup skill:
claude plugin marketplace add alamri-intel/telegram-mcp
claude plugin install telegram-osint@telegram-osintOr 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:
uv tool install "telegram-monitor-mcp[judge] @ git+https://github.com/alamri-intel/telegram-mcp"
claude mcp add telegram -- telegram-mcpThe 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.
export TELEGRAM_API_ID=...
export TELEGRAM_API_HASH=...
export ANTHROPIC_API_KEY=... # only if you want judgingStart the watcher
telegram-mcp-watcherThe 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:
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:
claude mcp add telegram -- telegram-mcpClaude Desktop — add to claude_desktop_config.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 | |
| Create a prefilter rule. Live within ~5s. |
| Rules with hit counts. |
| Pause or resume a rule. |
| Delete a rule and its alerts. |
| Add a plain-language judging criterion. |
| Criteria with hit counts. |
| Pause or resume a criterion. |
| Delete a criterion. |
| What matched and what the judge decided. |
| Clear alerts from the default view. |
| Watcher liveness, counts, judge throughput. |
Skills (plugin install only)
/telegram-osint:setup walks you through a full setup: it connects Telegram,
interviews you in multiple-choice rounds (with "suggest for me", "widen
coverage" and "describe it in my own words" at every step), learns what you
want from real messages in your channels, engineers and tests the keywords
across languages, calibrates the judge on real examples, and only then creates
the rules and criteria. /telegram-osint:review reports how your coverage is
performing — gaps, noise, silent rules, questionable verdicts — and adjusts it
in the same format, any time. /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 | |
| Whether credentials are saved and each session is logged in. |
| Save your Telegram app credentials to |
| Ask Telegram to send a login code. |
| 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, sodeploywon't fire onredeployment), orregexcase_sensitive— off by defaultchat_ids/exclude_chat_ids— scope it; omitchat_idsto watch everything. Exclusions apply first.include_outgoing— off by default, so your own messages don't matchnotify— 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_messagesfor 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
errorand 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 | |
| — | required |
| — | required for judging |
|
| database and session files |
|
| database path |
|
| server's session name |
|
| watcher's session name |
|
|
|
|
| judging model |
|
|
|
|
|
|
|
| watcher log level |
Security
Session files under
~/.telegram-mcp/(mode0700) 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_idsif 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
Country names in skills/setup/data/country-names/ come from the
Unicode CLDR project, © Unicode, Inc., under the
Unicode License v3 (included alongside the data). Regenerate them with
python3 scripts/build_country_names.py.
Available Tools
20 toolsack_alertsA
Mark alerts as acknowledged so they drop out of the default list_alerts view. Pass exactly one of the three selectors.
Args: alert_ids: specific alert ids to acknowledge. rule_id: acknowledge every unacked alert from this rule. all_alerts: acknowledge every unacked alert.
Inputs: rule_id, alert_ids, all_alerts.
add_alert_ruleA
Create an alert rule. The watcher picks it up within a few seconds — no restart needed. Rules only match messages that arrive after they are created; use search_messages to look backwards.
Args: name: unique label for the rule, used in alerts and notifications. pattern: what to look for. match_type: 'substring' (default), 'word' (whole-word only), or 'regex'. case_sensitive: match case exactly (default: case-insensitive). chat_ids: only watch these chats; omit to watch every chat. exclude_chat_ids: never match in these chats; applied before chat_ids. include_outgoing: also match messages you send (default: incoming only). notify: fire a macOS desktop notification on match.
Inputs: name (required), pattern (required), notify, chat_ids, +4 more.
add_criterionA
Add a natural-language criterion the judge uses to decide whether a prefiltered post actually matters. The watcher picks it up on the next judged alert — no restart needed.
Rules decide what gets looked at; criteria decide what gets surfaced. Write a criterion the way you'd brief someone doing the triage for you: say what qualifies and what doesn't. "A specific open remote backend role that names the company" beats "jobs".
Args: name: short unique label, shown on matching alerts. description: what qualifies, in plain language.
Inputs: name (required), description (required).
auth_statusA
Check whether Telegram is logged in, and as whom.
Reports both sessions: the watcher's (the one that must be logged in for monitoring to work) and this server's (needed only for the read tools). Call this before anything else if alerts or reads are failing.
Inputs: no named parameters.
delete_alert_ruleA
Delete an alert rule and every alert it produced. Irreversible — prefer set_alert_rule_enabled(rule_id, False) to just pause it.
Args: rule_id: id from list_alert_rules.
Inputs: rule_id (required).
delete_criterionA
Delete a criterion. Alerts already judged against it keep their verdicts. Prefer set_criterion_enabled to pause one.
Args: criterion_id: id from list_criteria.
Inputs: criterion_id (required).
list_alert_rulesB
List alert rules with their hit counts.
Args: include_disabled: include rules that are currently turned off.
Inputs: include_disabled. Returns: result
list_alertsA
List recorded alerts, newest first.
By default this hides alerts the judge ruled irrelevant — that filtering is the whole point of the judge. Pass include_irrelevant=True to audit what it rejected and why (each one keeps its reasoning).
Args: limit: max number of alerts to return. unacked_only: only alerts you haven't acknowledged yet (default). rule_id: restrict to one prefilter rule. chat_id: restrict to one chat. since_hours: only alerts matched within this many hours. verdict: exact verdict — relevant, irrelevant, pending, skipped, error. min_severity: lowest severity to include (low, medium, high, critical). include_irrelevant: include alerts the judge rejected.
Inputs: limit, chat_id, rule_id, verdict, +4 more. Returns: result
list_criteriaA
List the judging criteria and how often each has matched.
Args: include_disabled: include criteria that are currently turned off.
Inputs: include_disabled. Returns: result
list_dialogsA
List your Telegram chats/channels, most recently active first.
Use this to find the numeric chat ids you need for alert rule scopes.
Args: limit: max number of dialogs to return. unread_only: only return dialogs with unread messages. include_archived: include archived chats (excluded by default).
Inputs: limit, unread_only, include_archived. Returns: result
login_request_codeA
Start logging in: ask Telegram to send a login code.
Telegram delivers the code in the Telegram app itself if the user is signed in elsewhere, otherwise by SMS. Follow with login_submit_code.
Ask the user for their phone number rather than guessing it.
Args: phone: phone number in international format, e.g. +14155550123. session: which session to log in — 'watcher' (default, the one monitoring needs) or 'mcp' (for this server's read tools).
Inputs: phone (required), session.
login_submit_codeA
Finish logging in with the code Telegram sent.
If the account has two-factor authentication, this returns an error asking for the password; call it again with both.
Args: code: the login code the user received. password: two-factor password, only if the account has one. session: the session being logged in; must match login_request_code.
Inputs: code (required), session, password.
monitor_statusA
Check whether the watcher daemon is running and what it has seen.
Reports liveness from the daemon's heartbeat, so a 'stale' result means watcher.py has stopped and no new alerts are being recorded.
Inputs: no named parameters.
preview_ruleA
Test a candidate rule against real message history before creating it.
Shows what the rule would have matched, so the user can judge whether it is too broad or too narrow while they are still writing it. Creates nothing. Use this during setup, before add_alert_rule, and show the user the samples — a rule that looks sensible in the abstract often turns out to match mostly noise.
The matched samples are also the right input for testing draft criteria: read them and decide which ones a criterion should keep, then tell the user which would have been surfaced and which filtered out.
Args: pattern: the candidate pattern. match_type: 'substring', 'word', or 'regex'. case_sensitive: match case exactly. chat_ids: restrict to these chats; omit to search everywhere. search_hint: a plain word to search Telegram for when match_type is 'regex' — Telegram cannot search by regex, so this narrows what gets scanned locally. Required for an unscoped regex preview. limit: max samples to return. scan: how many messages to pull and test.
Inputs: pattern (required), scan, limit, chat_ids, +3 more.
read_messagesA
Read the most recent messages from a chat/channel.
Args: chat_id: numeric dialog id, as returned by list_dialogs. limit: max number of messages to return (most recent first).
Inputs: chat_id (required), limit. Returns: result
search_messagesA
Search your Telegram messages for a keyword.
Searches history, unlike alert rules which only see new messages.
Args: query: text to search for. chat_id: restrict search to one chat; omit to search all chats. limit: max number of results.
Inputs: query (required), limit, chat_id. Returns: result
set_alert_rule_enabledA
Turn an alert rule on or off without deleting it or its alerts.
Args: rule_id: id from list_alert_rules. enabled: True to resume matching, False to pause.
Inputs: enabled (required), rule_id (required).
set_api_credentialsA
Save the Telegram app credentials needed before any login.
The user gets these from https://my.telegram.org under "API development tools" — they identify the application, not the account, and are not secret in the way a login code is. Stored owner-only in /.env.
Args: api_id: numeric App api_id from my.telegram.org. api_hash: 32-character App api_hash from my.telegram.org.
Inputs: api_id (required), api_hash (required).
set_criterion_enabledA
Turn a criterion on or off without deleting it.
Args: criterion_id: id from list_criteria. enabled: True to resume judging against it, False to pause.
Inputs: enabled (required), criterion_id (required).
unread_summaryA
Summarize unread chats: for each, the unread count and the last few unread message previews.
Args: limit_per_chat: how many recent messages to preview per chat. max_chats: max number of unread chats to include.
Inputs: max_chats, limit_per_chat. Returns: result
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 20 tools
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.
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.
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.
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
Related MCP Connectors
Your Telegram, WhatsApp, Instagram, mail and calendar in Claude or ChatGPT. Never sends messages.
Let AI assistants post to your Telegram chats, groups and channels. Not affiliated with Telegram.
Read, search and reply to your WhatsApp chats from your AI, using your own number.
Hosted Telegram MCP server for Claude, ChatGPT and Cursor, with each action switched per account.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceA read-only Telegram MCP server that retrieves messages from your DMs, groups, and channels, enabling Claude to generate executive briefings from Telegram conversations.MIT
- AlicenseAqualityBmaintenanceEnables AI assistants to search and read a private, local archive of your own Telegram chats, groups and media, so they can answer questions about past conversations. It keeps access read-only toward Telegram, treats message text as untrusted, and lets you choose which chats AI apps may see.63MIT
- AlicenseNot gradedqualityBmaintenanceA read-only server that lets any MCP client or model list, search, and read your own Telegram chats, including chat details, forum topics, message history, and surrounding context. It never sends, edits, or deletes anything, and stores your Telegram credentials encrypted at rest.MIT
- AlicenseNot gradedqualityCmaintenanceConnects your own Telegram account or a bot to Claude and other AI assistants so they can read, search, send, and wait for Telegram messages, with everything stored in a local database.MIT