Read public Telegram channels from AI agents — no MTProto, no Telethon, no api_id.
Part of the Shadow product line: tools that give AI agents eyes on the open web.
MCP server that scrapes Telegram's public web preview (t.me/s/…): channel metadata, posts, media URLs, comments, and DuckDuckGo-backed channel search. Stack: httpx + lxml + FastMCP. No browser.
get_channel("durov")
# → title, subscribers_raw ("2.54M"), description, avatar…
get_posts("durov", limit=3)
# → recent posts + views_raw + next_before cursor
search_posts("durov", "TON")
# → filter ~100 recent posts client-side (not Telegram search)
Agents need Telegram signal (channel size, engagement, fresh posts). Official Bot API can't read arbitrary public channels. MTProto/Telethon means app registration, session files, and ban risk.
shadow-tg stays on the public HTML surface Telegram already exposes. Same data a browser sees — structured for MCP tools.
pip install shadow-tgFrom source:
git clone https://github.com/ulinycoin/shadow-tg.git
cd shadow-tg
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"After pip install shadow-tg (or pip install -e . from source), shadow-tg is available as a command:
{
"mcpServers": {
"tg": {
"command": "shadow-tg"
}
}
}Or point to the project venv directly:
{
"mcpServers": {
"tg": {
"command": ".venv/bin/python",
"args": ["-m", "tg_mcp.server"]
}
}
}After pip install shadow-tg, add to ~/.claude/settings.json:
{
"mcpServers": {
"tg": {
"command": "shadow-tg"
}
}
}Claude Code also picks up the included CLAUDE.md for project context.
After pip install shadow-tg:
mcp_servers:
tg:
command: shadow-tg
enabled: trueOr module form (pointing to the project venv):
mcp_servers:
tg:
command: python3
args: ["-m", "tg_mcp.server"]
enabled: trueCopy-paste these into your AI agent's chat after adding shadow-tg as an MCP server:
Find the top 5 Telegram channels about AI agents,
check their size and engagement, and recommend the best one
Read the last 10 posts from @durov and summarize
what he's talking about this week
Inspect these channels for quality: @techcrunch, @techmeme, @verge
Drop any that are stale or have inflated subscribers
Search for Telegram channels about Rust programming.
Verify their subscriber counts and show me the 3 most active ones
| Tool | What it does |
|---|---|
resolve_channel(query) |
Exists? title + canonical username |
get_channel(channel) |
Metadata: subscribers / subscribers_raw, description, avatar |
inspect_channels(channels, …) |
Batch size + median_views + last_post_at + quality flags |
get_posts(channel, limit?, before?) |
Feed page; paginate with next_before |
get_post(channel, post_id) |
Single post (+ media) |
get_media(channel, post_id) |
CDN photo/video URLs + document cards |
get_comments(channel, post_id, …) |
Comments: history (before) / live-tail (after) |
search_posts(channel, query, limit?) |
Scan ~100 recent posts + filter |
search_channels(query, limit?, verify?) |
DDG site:t.me/s/; verify=true pulls subs + median views |
requested/canonical— alias vs real username fromdata-post(e.g.@durovstays@durov; alias channels like@some_news→@original_name)subscribers/subscribers_raw— int + display string (2.54M). Use raw for size; don't drop the decimal (2.54M≠54M)views/views_raw— post views, not channel sizemedian_views/engagement_ratio/flags— frominspect_channels/ verified search;stale= no posts newer thanstale_after_days(default 30)engagement_flags/likely_inflated— fake-sub signals:low_engagement,low_ratio,dead_reachhas_more/next_before— history cursor;next_after— live-tail comments (~3s poll)
resolve_channel("durov")
get_channel("durov")
get_posts("durov", limit=3)
get_post("durov", 513)
get_media("durov", 532)
search_posts("durov", "TON")
inspect_channels("durov, telegram")
search_channels("durov ton", limit=5, verify=true)
Comments (only if the channel has a discussion group):
get_comments("durov", 513) # first page → next_before + next_after
get_comments("durov", 513, before=…) # older
get_comments("durov", 513, after=…) # live-tail; empty = wait, reuse after
Do not pass before and after together.
| Need | Source |
|---|---|
| Posts / channel page | https://t.me/s/{channel} |
| Single post / media | https://t.me/s/{channel}/{id} (+ ?embed=1 fallback) |
| Comments | discussion widget + POST t.me/api/method (loadComments) |
| Channel search | DuckDuckGo site:t.me/s/ |
- Public channels only (web preview). Private / "Contact" pages →
exists: false - No reactions, members list, DMs, or cross-channel user comment history
search_postsis a recent-post filter, not Telegram full-text searchsearch_channelsdepends on DuckDuckGo indexing- Comments require a linked discussion group
- CDN media URLs can expire
src/tg_mcp/
server.py # FastMCP tools
tme_parser.py # t.me/s posts / channel / media
comment_parser.py # discussion widget + loadComments auth
channel_search.py # ddgs
channel_quality.py # median views + inflation / stale flags
cache.py # TTL cache under /tmp/tg-mcp-cache
CLAUDE.md # Claude Code project context
tests/
fixtures/ # HTML fixtures
PYTHONPATH=src python3 -m pytest tests/ -vMIT. Free for anything.
#telegram #mcp #ai-agents #llm-tools #web-scraping #python #channel-analytics #no-mtproto