Shadow-tg
by ulinycoin
README.md
<!-- badges -->
[](https://pypi.org/project/shadow-tg/)
[](https://pypi.org/project/shadow-tg/)
[](https://pypi.org/project/shadow-tg/)
[](LICENSE)
[](https://github.com/ulinycoin/shadow-tg)
# 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.
```text
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
```bash
pip install shadow-tg
```
From source:
```bash
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 shadow-tg` (or `pip install -e .` from source), `shadow-tg` is available as a command:
```json
{
"mcpServers": {
"tg": {
"command": "shadow-tg"
}
}
}
```
Or point to the project venv directly:
```json
{
"mcpServers": {
"tg": {
"command": ".venv/bin/python",
"args": ["-m", "tg_mcp.server"]
}
}
}
```
### Claude Code (Anthropic)
After `pip install shadow-tg`, add to `~/.claude/settings.json`:
```json
{
"mcpServers": {
"tg": {
"command": "shadow-tg"
}
}
}
```
Claude Code also picks up the included `CLAUDE.md` for project context.
### Hermes (`config.yaml`)
After `pip install shadow-tg`:
```yaml
mcp_servers:
tg:
command: shadow-tg
enabled: true
```
Or module form (pointing to the project venv):
```yaml
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:
```text
Find the top 5 Telegram channels about AI agents,
check their size and engagement, and recommend the best one
```
```text
Read the last 10 posts from @durov and summarize
what he's talking about this week
```
```text
Inspect these channels for quality: @techcrunch, @techmeme, @verge
Drop any that are stale or have inflated subscribers
```
```text
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 from `data-post` (e.g. `@durov` stays `@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 size
- **`median_views` / `engagement_ratio` / `flags`** — from `inspect_channels` / verified search; `stale` = no posts newer than `stale_after_days` (default 30)
- **`engagement_flags` / `likely_inflated`** — fake-sub signals: `low_engagement`, `low_ratio`, `dead_reach`
- **`has_more` / `next_before`** — history cursor; **`next_after`** — live-tail comments (~3s poll)
---
## Examples (`@durov`)
```text
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):
```text
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_posts` is a recent-post filter, not Telegram full-text search
- `search_channels` depends 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
```bash
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
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues