Skip to content

ulinycoin/shadow-tg

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

PyPI version PyPI downloads Python License: MIT Stars

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

pip install shadow-tg

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 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"]
    }
  }
}

Claude Code (Anthropic)

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.

Hermes (config.yaml)

After pip install shadow-tg:

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 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.54M54M)
  • views / views_rawpost 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)

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_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

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

About

MCP server for public Telegram channels via t.me web preview — no MTProto, no api_id

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages