shadow-tg
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)
Pain point
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.
Quick install (from 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]"
Cursor
After pip install -e ., 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"]
}
}
}
Claude Code (Anthropic)
After pip install -e ., add to ~/.claude/settings.json:
{
"mcpServers": {
"tg": {
"command": "shadow-tg"
}
}
}
Claude Code also picks up the included CLAUDE.md for project context.
Hermes (config.yaml)
After pip install -e .:
mcp_servers:
tg:
command: shadow-tg
enabled: true
Or module form (pointing to the project venv):
mcp_servers:
tg:
command: python3
args: ["-m", "tg_mcp.server"]
enabled: true
Example prompts
Copy-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
Tools
| 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 |
Important response fields
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)
Examples (@durov)
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.
How data is fetched
| 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/ |
Limitations
- 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
Layout
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
Tests
PYTHONPATH=src python3 -m pytest tests/ -v
License
MIT. Free for anything.
#telegram #mcp #ai-agents #llm-tools #web-scraping #python #channel-analytics #no-mtproto
Metadata
Release files for shadow-tg 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| shadow_tg-0.1.0.tar.gz | 25.7 kB | Without provenance | Details for shadow_tg-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| shadow_tg-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for shadow_tg-0.1.0-py3-none-any.whl |
Total release size: 51.6 kB
Release files / shadow_tg-0.1.0.tar.gz
| Download URL | shadow_tg-0.1.0.tar.gz |
|---|---|
| Size | 25.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f0adbf81f0f02465f4c1635897e50d4b7a5b2dbde3e05ae7758416ce16a50422
|
|
BLAKE2b-256 checksum How to use checksums |
6708a3a23664d08be3aba45de8166bbf82e54831e4b74454169958f36499eb1c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.8
|
Release files / shadow_tg-0.1.0-py3-none-any.whl
| Download URL | shadow_tg-0.1.0-py3-none-any.whl |
|---|---|
| Size | 25.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d08b3ad7881f15b80c70592cc0ca6d0722481c2bab9601531b3dc21593f8f68d
|
|
BLAKE2b-256 checksum How to use checksums |
10001133fd42e3ec116f998ab383f94953a092bd71751d494654a2710b7f3d05
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.8
|
Security for shadow-tg 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.