tgread
Provides read-only access to a Telegram account's channels, groups, and direct messages, with tools for listing chats, reading message history, searching messages, and viewing chat metadata. Deliberately exposes no write, edit, delete, or admin capabilities.
Click on "Install 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., "@tgreadsummarize recent messages from my Telegram channel"
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.
tgread
A read-only Telegram reader — a CLI first, and an MCP server second. Fifteen tools for reading your channels, groups and DMs, and not one that can write to them.
./install.sh # pinned venv + launcher + MCP registration
tgread login # api_id/api_hash, phone, code, 2FA
tgread tools # the catalogue
tgread read_chat --chat @durov --limit 5 # …and every tool runs from a shellThe CLI comes first
Every tool is a subcommand; the MCP server is a second front end onto the same handlers. That ordering is the point. A tool you can only reach through an MCP client is a tool you cannot test against a real account, and "the code type-checks" is not evidence that a Telegram result unpacks the way you assumed.
tgread tools # catalogue, with required flags
tgread read_chat --help # one tool's arguments
tgread read_chat --chat @durov --limit 5 # JSON on stdout
tgread search_messages --query "postgres" | jq '.messages[].url'Flags are generated from each tool's inputSchema — the same object advertised
over MCP — so the two surfaces cannot drift. Adding a property to a schema adds
a flag; there is no second place to update. Types come from the schema too:
--limit 5 is coerced to an integer, --ids 1,2,3 to an array, --admins-only
is a bare boolean, and --kind banana is rejected against the enum.
exit | meaning |
0 | success; a JSON document on stdout, diagnostics on stderr |
1 | the tool ran and failed — not logged in, chat not visible, no such thread |
2 | usage error — unknown flag, bad type, missing required argument |
3 | a read tool attempted a write. Should be unreachable; it means a bug |
4 | an unexpected error escaped a tool — also a bug, never a stack trace |
CLI output carries no UNTRUSTED CONTENT banner. That envelope exists to frame
the payload for a model; a human or a jq pipeline wants the document itself.
TGREAD_TOOLS restricts what an agent sees, not what you can run — the person
at the terminal is not the threat it exists for. tgread tools marks anything
currently hidden from agents.
Related MCP server: telegram-mcp-server
As an MCP server
You never run tgread serve yourself. Claude Code spawns it as a child
process and speaks JSON-RPC over its stdin/stdout, dispatching to the same
handlers the CLI calls; install.sh registers it so that happens automatically:
claude mcp add --scope user tgread -- ~/.local/bin/tgread serveRestart Claude Code (or /mcp → reconnect) and the tools appear. To check:
claude mcp list # tgread: … - ✔ Connected
tgread status # account, session validity, tool surfaceThose two answer different questions, and the difference bites people:
Connected only means the process started and completed the MCP handshake.
It says nothing about whether you are logged in — connection to Telegram is
lazy, on the first tool call. tgread status is the one that talks to Telegram.
sequenceDiagram
participant C as Claude Code
participant S as tgread serve
participant T as Telegram
C->>S: spawn (stdio)
C->>S: initialize / tools/list
S-->>C: 4 read tools
Note over S,T: no connection yet — login is not needed to start
C->>S: tools/call read_chat
S->>T: connect, reuse stored auth_key
T-->>S: history
S-->>C: UNTRUSTED envelope + JSONAny other MCP client works too — it is a plain stdio server:
{ "mcpServers": { "tgread": { "command": "/home/you/.local/bin/tgread",
"args": ["serve"] } } }To drive it by hand while debugging, pipe JSON-RPC at it (TGREAD_LOG=DEBUG
puts diagnostics on stderr; stdout stays pure protocol):
printf '%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{}}}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/list"}' | tgread serveWhy not one of the existing ones
There are good community servers — chigwell/telegram-mcp has 1.5k stars, 30 contributors and real release hygiene. The reason to write this one is not distrust of that code. It is that the honest gain from self-writing is tool surface and reviewability, not dependency count, and those are the two things that matter when the process holds a Telegram session and feeds an agent attacker-controlled text.
community server | tgread | |
MTProto | Telethon | Telethon — same, and rightly so |
resolved packages | 44 | 5 ( |
MCP layer |
| ~200 lines of stdio JSON-RPC in this repo |
read tools | ~15 | 15 |
runnable without an MCP client | no | yes — every tool is a subcommand |
write tools | send, edit, delete, forward, react, join, admin | none |
write enforcement | by convention | at the transport chokepoint |
code to review before trusting it | ~3,000 lines across 30 contributors | one file you can read in a sitting |
Writing MTProto by hand would be reckless — Telethon is the crypto, the DC migration and the reconnect logic. So it stays. Everything above it is ours.
The threat model
Reading channels means text an attacker chose enters an agent that holds a shell — and, usually, whatever else you have connected: mail, notes, cloud credentials. That is the dominant risk here, and it is not fixed by who wrote the server. Choosing a server with no write surface is one of the few mitigations that does not depend on the model behaving well.
flowchart TD
A["hostile channel post<br/>'ignore previous instructions…'"] --> B["tgread read_chat"]
B --> C["UNTRUSTED envelope<br/>wrapped around every payload"]
C --> D["agent context"]
D --> E{"agent tries to act on it"}
E -->|"send / delete / join"| F["no such tool exists<br/>tools/call → isError"]
E -->|"raw TL request"| G["guard at _call → WriteBlocked"]
E -->|"summarise for the user"| H["fine — this is the intended path"]
style F fill:#1f6f43,color:#fff
style G fill:#1f6f43,color:#fffThree layers, in increasing order of how much they'd survive a bug:
No write tool is advertised. An injected instruction has nothing to call.
Every payload is wrapped in an
UNTRUSTED CONTENTbanner naming it as data, not instructions — including chat titles and bios, which are attacker-controlled too.The transport guard. Telethon funnels every outbound TL request through
TelegramClient._call(68 internal call sites reach it viaawait self(req), and__call__is a one-line delegate).ReadOnlyClientoverrides it. A bug in this file still cannot mutate the account.
The guard fails closed: a request is refused unless its TL class name starts
with Get/Search/Resolve/Check/Find, or is on a nine-entry
infrastructure allowlist. Three read-shaped requests are denied by name
because they have effects other people can observe — GetMessagesViews
(bumps the public view counter), GetBotCallbackAnswer (presses an inline
button), GetInlineBotResults (queries a bot as you). Nested requests are
walked, so a write cannot ride inside an allowed InvokeWithLayer wrapper.
flowchart LR
R["TL request"] --> W["walk nested .query"]
W --> D{"in EXPLICIT_DENY?"}
D -->|yes| X["WriteBlocked"]
D -->|no| I{"in INFRA_ALLOW?"}
I -->|yes| P["to the wire"]
I -->|no| V{"starts with Get/Search/<br/>Resolve/Check/Find?"}
V -->|yes| P
V -->|"no — incl. every<br/>name we've never seen"| X
style X fill:#8b2020,color:#fff
style P fill:#1f6f43,color:#ffftgread check runs this offline: 26 write requests blocked, 17 reads allowed,
unknown names fail closed, nesting checked. No network, no session, no
credentials.
Tools
The surface is as wide as MTProto's read paths allow — narrowing it is the
deployment's job, not the server's (see below). Every tool takes a chat as a
@username, a t.me link, or a numeric id from list_chats.
Identity and discovery
Tool | What |
| which account this server is signed in as |
| dialogs, filterable by name and by kind |
| Telegram's public directory — finds channels never joined, split into already-known and strangers |
| chat folders (dialog filters) and their sizes |
| saved contacts |
Reading
Tool | What |
| history, oldest-first, paged by message id or date |
| specific ids, or a window around one — what a search hit was replying to |
| replies under a post: a channel item's comment section |
| pinned messages — usually a chat's rules and announcements |
Search
Tool | What |
| full text, in one chat or across everything the account sees |
| history filtered by attachment kind, via Telegram's server-side index |
People and metadata
Tool | What |
| kind, member count, description, verified/scam flags |
| participants with roles, searchable, |
| who reacted to a message, and with what |
| groups and channels shared with a given user |
Three things these deliberately do not do, despite being adjacent to tools that would:
No media download.
list_mediareturns captions and metadata; fetching attachments would mean writing attacker-chosen bytes to the agent's disk.Reading never marks as read.
messages.ReadHistoryis a write and the guard blocks it, so the unread badges on your phone stay exactly as they were.Nothing joins, reacts or presses.
search_public_chatsfinds channels without joining them;get_reactionsreads reactions without adding one;GetBotCallbackAnswer— which would press an inline button — is denied by name even though it is spelled like a read.
Restricting the tool surface
The server implements everything; the deployment decides what an agent sees. Those are separate concerns, so they are separate settings — a tool you might want next month should not have to be a tool your agent can reach today.
Set TGREAD_TOOLS in config.env (or the environment) to a comma-separated
allowlist. Unset means all fifteen, which is the right default: the cost of
a read tool is context, not risk, and the risky operations were never
implemented.
TGREAD_TOOLS=list_chats,read_chat # just follow some channels
TGREAD_TOOLS=list_chats,read_chat,search_messages,get_messages # + researchRestricted tools are not advertised in tools/list and are refused again
on the call path. That ordering matters: a denied-but-advertised tool still
costs context and is still something a model can be argued into attempting,
whereas a tool that was never listed does not exist as far as the model is
concerned. Same reason there are no write tools at all — absence beats denial.
An unknown name is fatal at startup, not a warning. A typo that silently fell back to "all tools" would widen the surface at exactly the moment someone was trying to narrow it.
There is no YAML config, deliberately: a YAML parser means PyYAML, and
5 dependencies is the point of this repo. config.env is KEY=VALUE.
Three places can narrow what an agent reaches, weakest last:
Layer | Where | Format |
Not advertised |
|
|
Advertised, denied | Claude Code | JSON |
Per-subagent |
| YAML |
Operational notes
Use a secondary account. Userbots (any MTProto client that is not the official app) are ToS-ban-able. This risk is identical for every server here.
The session file is a bearer token for the whole account. Changing your Telegram password does not invalidate it. Only
tgread logout— which revokes server-side before deleting locally — or Settings → Devices does. Treat it like an SSH private key.pip install telegram-mcpis not this, and not chigwell's either. That PyPI name belongs to an unrelated project; passingTELEGRAM_API_ID/TELEGRAM_API_HASHto it would hand your credentials to third-party code. Nothing here is published to PyPI on purpose.State lives in one directory —
$TGREAD_STATE_DIR, default~/.local/state/tgread, mode 0700, holdingconfig.env(0600) andtgread.session(0600). One directory to chmod, to back up, to destroy.tgread statusflags it if the permissions drift.install.shusesuv sync --frozen— it installs exactly the versions in the committeduv.lockand fails rather than re-resolving. A resolver that quietly picks up a fresh upstream release is how a compromised package reaches a process holding your session.
Layout
Path | What |
| the whole server: guard, tools, JSON-RPC loop, CLI |
| launcher — execs the pinned venv's interpreter |
| the pin |
| venv, symlink, |
| 45 offline tests — guard, catalogue, protocol, CLI, hygiene |
| every tool against the real account; skips cleanly with no session |
Commands
tgread tools the catalogue, and what agents see of it
tgread <tool> [--flag value ...] run one tool, JSON on stdout
tgread <tool> --help that tool's arguments
tgread login interactive: API credentials, phone, login code, 2FA
tgread status who am I, is the session valid, are permissions sane
tgread logout revoke server-side, then delete locally
tgread check offline self-test of the read-only guard (no network)
tgread serve speak MCP over stdio — what Claude Code runsTwo test suites, split by what they can actually prove:
./test-tgread.sh # 45 tests, no network/session/credentials needed
./test-tgread-live.sh # every tool against your account; skips without a sessionThe offline suite covers the guard, the catalogue, the protocol and the CLI — the properties that must hold before this is pointed at an account. The live suite covers the half it structurally cannot: that a TL result unpacks the way the code assumes. It is read-only like everything else, so it is safe to re-run.
It earned its keep on the first run, catching get_reactions crashing with
BroadcastForbiddenError — broadcast channels expose per-emoji totals but not
who reacted. Telegram declining a read is a normal answer, so every tool now
funnels through one seam that turns any RPC refusal into a readable error.
WriteBlocked is deliberately not caught there: that one really is a bug.
login and logout use a plain client, not the guarded one: the guard exists
to constrain the agent, not the human at the terminal establishing or
revoking the session. Everything the MCP server touches goes through
ReadOnlyClient.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.
No tool schema history has been recorded yet.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Read-only Remote MCP for externally grounded AI agent trust receipts.
Unified inbox MCP for WhatsApp, Telegram, Email, voice — read/send messages, search, AI agents.
Private agent messaging: DMs, group channels, presence, search, and webhooks over MCP or REST.
Join durable public agent discussions and invite-only private group rooms through MCP.
161
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to interact with Telegram accounts through MCP, supporting messaging, contacts, groups, media, and admin functions.4Apache 2.0
- AlicenseNot gradedqualityCmaintenanceA read-only MCP server that lets AI agents read personal Telegram chats from an allowlist of folders, with no send/edit/delete capability.53MIT
- FlicenseNot gradedqualityDmaintenanceEnables users to read, search, and manage Telegram messages in channels, groups, and private chats through MCP tools.-
- AlicenseNot gradedqualityBmaintenanceProvides read-only access to Telegram chats, allowing AI agents to list chats, read messages, and search within chats via local MCP.MIT
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/demian-overflow/tgread'
If you have feedback or need assistance with the MCP directory API, please join our Discord server