agents-guide-to-telegram
教 agent 使用 Telegram / Teach your agent to use Telegram — native rich messages (tables, LaTeX, collapsible blocks), a live progress window for Claude Code, and a field guide of hard-won pitfalls.
Install / Use
claude mcp add Circe22 -- npx -y github:Circe22/agents-guide-to-telegramIf the server publishes to npm under a different name, use that package instead — check the repo README.
MCP Server
Model Context Protocol server
Quality Score
Category
Development & EngineeringSupported Platforms
Tags
Skill content
View source on GitHubThe Agent's Guide to Telegram
教 agent 使用 Telegram:发原生表格、LaTeX 公式、折叠块、真贴纸,让你在手机上看着它干活——外加一整本真机踩出来的坑谱。
https://github.com/Circe22/agents-guide-to-telegram · MIT · 只依赖
requests发现 bug 或者 Telegram 又更新了,欢迎开 issue / 提 PR。 (前名tg-rich-mcp,旧链接与旧 git remote 均自动跳转。名字致敬你猜到的那本书—— 面对一个陌生星球的 API,手册比勇气有用,Don't Panic.)
Telegram 在 Bot API 10.1(2026-06-11)加了 Rich Messages,10.2(07-14)补齐发送侧。
官方 telegram 插件的 reply 够不着这些,这个包直投 Bot API 把它接进来。
两件东西,可以只用一件:
| 文件 | 是什么 | 通用性 |
|---|---|---|
| tg_rich_mcp.py | MCP server,六个工具:发 / 原地改 / 推草稿 / 贴纸挑发 / 贴纸入库 / 按钮选择题(实验性) | ✅ 走握手式 MCP 的 host(Claude Code、Claude Desktop、Cursor、自己写的 agent)——协议版本见下 |
| tg_sticker.py | 贴纸车道:库 / 认领 / 交集挑选 / 各 bot 懒迁移 / 句内标记(挂在上面那个 server 里) | 跟着走 |
| tg_ask.py | 按钮问答机制层:inline keyboard + getUpdates 同步等点击(挂在上面那个 server 里) | 跟着走;⚠️ 要专用 bot,见「按钮问答要专用 bot」 |
| tg_progress_hook.py | 进度窗 hook:每次调工具前推一行 | ⚠️ 仅 Claude Code(靠它的 PreToolUse 钩子,别的 host 没有这个机制),且需要 Linux / macOS / WSL |
| tg_sticker_hook.py | 入站贴纸识别 hook:认识的注入标签(agent 不用看图)、不认识的提醒归档 | ⚠️ 仅 Claude Code(UserPromptSubmit 钩子);零网络、fail-silent |
| secret_redaction.py | 密钥形态的单一真源,上面的都用它 | 跟着走,别单独删 |
| sticker-spec/ | 贴纸标记语法的规格真源:共享 golden fixtures,多实现各自跑同一份止漂移(见 COOKBOOK 贴纸章末节) | ✅ 任何实现这套标记语法的都该跑 |
| test_*.py | 一批测试(含 test_conformance.py 跑 sticker-spec),python3 -m unittest discover -v,无需 Telegram 凭证、不发网络;耗时取决于环境 | — |
外加一份 COOKBOOK.md —— Telegram 富消息能玩什么的全景清单: 行内公式、剧透、上下标、脚注、锚点跳转、任务清单、表格高级字段、地图、拼贴轮播、 按读者时区渲染的时间……包括你大概率用不上的那些。 列出来不是让你都用,是让你知道有这条路——不知道的能力等于不存在。
进度窗长这样,在手机上一行行自己长出来:
┌ 正在干活…
│ 📖 Read · server.py
│ 🔍 Grep · handleRequest
│ ⚡ Bash · 跑一遍测试
└ 已经做了 12 步
它默认会把长得像密钥的摘要替换成「(内容隐去)」。不喜欢这种防御?
TG_PROGRESS_REDACT=0一把关掉 —— 详见下面「安全闸,以及怎么关」。
TL;DR (English) — Teach your agent to use Telegram. An MCP server exposing Telegram's Rich Message API (native tables, LaTeX, collapsible blocks, in-place edits, streaming drafts, an emoji-indexed sticker lane the agent curates itself, plus experimental synchronous choice questions answered with one button tap) to any handshake-based stdio MCP host (protocol 2024-11-05 … 2025-11-25), plus a Claude Code hook that streams your agent's tool calls into a live Telegram window. Config via
~/.tg-rich-mcp.json. Only dependency:requests. Redaction is on by default —TG_PROGRESS_REDACT=0disables it.⚠️ Android caveat: while a streaming draft is active, Telegram Android replaces the user's send button with an ellipsis — they cannot send anything, and text already typed gets wiped when the input recovers (bugs.telegram.org/c/62189, closed by Telegram as expected behaviour). Bot API 10.3 added a partial fix: drafts sent with
can_stopshow a Stop button on up-to-date clients — pressing it dismisses the draft and unlocks the composer — but the bot can't hear the press unless its inbound side handlesstopped_message_generation, and older clients never draw the button. The progress hook therefore still defaults tosendRichMessage+editMessageTextand deletes the window when done; drafts (frames sent withcan_stop) are opt-in viaTG_PROGRESS_MODE=draft.
装
1. 配置
不走代理的最小配置(可直接复制):
cat > ~/.tg-rich-mcp.json <<'JSON'
{
"bot_token": "123456:AA...",
"chat_id": "你的 chat id"
}
JSON
chmod 600 ~/.tg-rich-mcp.json
要走代理就加一行 "proxy"——JSON 不允许尾逗号,所以给它前面那行(chat_id)
补上一个逗号:
{
"bot_token": "123456:AA...",
"chat_id": "你的 chat id",
"proxy": "http://127.0.0.1:7897"
}
反过来,要删掉末尾某个字段(如 proxy),记得连同上一行的逗号一起删,别留下
"chat_id": "…", 这种悬着的尾逗号——那是不合法的 JSON。也支持环境变量
TG_BOT_TOKEN / TG_CHAT_ID / TG_PROXY(优先级更高)。
配置在进程启动时读一次并缓存,改了要重启 MCP server 才生效。
配置文件不存在时静默按"没配"处理;存在但格式错误会往 stderr 打一行脱敏诊断
(不吞掉、也不把 token 带出来),免得只看到"没找到 token"却不知道是 JSON 写坏了。
想用进度窗 hook 的话,必须用配置文件(或把变量 export 进编辑器的启动环境)—— hook 是编辑器另起的进程,拿不到你写在 MCP server 那段
env里的变量。 这是最容易卡住的一步,第一次装的人十有八九栽在这。
2. 挂 MCP server
.mcp.json(或你的 host 对应的配置文件):
{
"mcpServers": {
"tg-rich": {
"command": "python3",
"args": ["/绝对路径/tg_rich_mcp.py"]
}
}
}
只依赖 requests,协议是手写的 JSON-RPC over stdio,不需要 mcp SDK。完整安装:
git clone https://github.com/Circe22/agents-guide-to-telegram.git
cd agents-guide-to-telegram
python3 -m venv .venv && . .venv/bin/activate # Python 3.9+
pip install requests
⚠️ .mcp.json 里的 command 要指向同一个解释器——上面装 requests 用的 venv
里的 python(即 /绝对路径/.venv/bin/python),别写成系统 python3,否则 MCP
起的进程用的是另一套环境、import requests 失败。用哪个解释器装、就用哪个解释器起。
支持哪几版 MCP 协议
实现的是握手式(initialize / notifications/initialized)的 MCP,协商这四版:
2025-11-25 · 2025-06-18 · 2025-03-26 · 2024-11-05
客户端要的版本在这里面就回同一个,不在就回最新的那个、由它决定断不断。
⚠️ 2026-07-28 那版不支持,而且不是"再加一个字符串"就能支持的——它把 MCP 改成了 无状态协议:移除
initialize握手,协议版本和客户端能力改为每个请求放进_meta; 服务器 MUST 实现server/discover;所有 result 必须带resultType;ping/logging/setLevel被移除。 (Key Changes)好在规范留了向后兼容的路:新客户端可以先拿
server/discover探测、 失败再按旧握手回退——本 server 对它回method not found,这条回退路即成立 (本 server 的响应实测是这个)。但"是否真去探测、真回退"取决于客户端实现, 不是所有同时支持两代协议的 host 都一定这么做;只实现了 2026-07-28 的客户端更 不在此列,连不上这个 server——这不是 bug,是两代协议的分界。 真要支持新协议是另一个工程,欢迎提 PR。
3. 挂进度窗 hook(可选,仅 Claude Code)
⚠️ 进度窗 hook 需要 Linux / macOS / WSL。 它用
fcntl给状态文件加锁 (并发的工具调用会同时写同一个文件),而fcntl是 Unix-only —— 原生 Windows 的 Python 一 import 就报错。 Windows 用户请在 WSL 里跑 Claude Code,或者只用 MCP server 那半边(那半边全平台都行)。Progress hook requires Linux, macOS, or WSL (
fcntlis Unix-only). The MCP server itself runs anywhere.
.claude/settings.json:
{
"hooks": {
"PreToolUse": [{"hooks": [{"type": "command",
"command": "python3 /绝对路径/tg_progress_hook.py",
"timeout": 5}]}],
"Stop": [{"hooks": [{"type": "command",
"command": "python3 /绝对路径/tg_progress_hook.py --finish",
"timeout": 15}]}]
}
}
改完要重开会话才生效。
前一条是每一帧,后一条是收工。只挂前一条也能用,只是窗口会停在最后一帧、不会自己收拾。
Stop 那条的 15 秒只是够用、不是靠它保证收拾干净。 收工分两步:先在状态锁里一笔 关账(清空活动窗口之前把「哪扇窗还没收」落进
pending_cleanup),再 best-effort 地有界等推送锁去即时删窗/定格。万一在飞的慢请求握着推送锁、宿主又把 Stop 超时杀了, 清理责任已经落盘——下一轮推送或下一次 Stop 持同一把推送锁时会补做,那扇窗不会因为 这次没收成就永远留在聊天里。所以调大 Stop 的 timeout 只是让「当场收拾」更常发生, 不是收拾干净的前提。
4. 挂贴纸识别 hook(可选,仅 Claude Code)
收贴纸不该靠 agent「记得」。挂上这个 hook 之后:用户发来库里认识的贴纸,
agent 直接收到标题/emoji/标签/描述,不用下载看图;没见过的,注入一行提醒
(file_id 已带好),得空调一次 tg_sticker_import 就归档。
{
"hooks": {
"UserPromptSubmit": [{"hooks": [{"type": "command",
"command": "python3 /绝对路径/tg_sticker_hook.py",
"timeout": 5}]}]
}
}
它零网络(识别只查本地库,下载留给 import 工具)、fail-silent(自己挂了
最多少一行提示,绝不挡用户说话)。前提:入站消息里有 attachment_kind="sticker"
和 attachment_file_id(官方 telegram 插件的 tag 格式);只有 file_id 时靠
import 时记下的各 bot 缓存反查身份,所以导入过的才认得出。
它默认怎么干活
发一条正式消息,然后每帧原地改它(sendRichMessage → editMessageText),
收工时把这条消息撤掉——聊天记录里一条工具调用都不留。
⚠️ 为什么默认不是流式草稿:
sendRichMessageDraft活跃期间, Telegram Android 会把用户的发送键换成省略号,用户发不出消息, 而且这期间在输入框里打的字会在恢复时被清空。 官方缺陷记录 https://bugs.telegram.org/c/62189 已被关闭,称是"当前预期行为"。 Bot API 10.3 起本 hook 的草稿帧都带can_stop——新客户端有停止按钮, 按停=草稿消失+输入框解锁(2026-09-04 Android 实测)。但发布出去的 hook 没法预知用户拿的是哪版客户端:旧客户端不画这颗按钮、锁死照旧; 且 hook 收不到按停事件(stopped_message_generation走收信侧)—— 好在进度窗瞎推无害,按停后客户端会把同 draft_id 的后续帧直接扔掉。长任务里用户最需要插话的时刻(补条件、喊停、纠方向、回答 agent 的提问), 恰好就是草稿最活跃的时刻。所以草稿仍只在你显式打开时才走—— 确认你的用户客户端够新(或在桌面端)再开。 桌面端据用户反馈不锁输入框——那是用户反馈,不是官方的跨平台保证。
| 想要什么 | 怎么设 |
|---|---|
| 默认(持久窗 + 收工撤掉) | 什么都不用设 |
| 干完把窗口留下来当记录 | TG_PROGRESS_END=keep |
| 就要那种流式动画+自动蒸发(帧自带 can_stop;旧客户端仍锁输入框) | TG_PROGRESS_MODE=draft |
| 换标题 | TG_PROGRESS_TITLE=… / TG_PROGRESS_DONE_TITLE=… |
不用改 bot、不用升级什么——Rich Message 是 Telegram 服务端的能力, 你的 bot 直接调新方法就有。客户端得是支持 10.1 的版本才看得到渲染效果。
安全闸,以及怎么关
进度窗要把工具调用的摘要发进 Telegram,所以默认带两道闸。 它们都可以关,而且关得干脆——这是你的机器。
| 想干什么 | 怎么做 |
|---|---|
| 整个进度窗都不要 | TG_PROGRESS=0 |
| 要进度窗,但不要任何脱敏(摘要原样推) | TG_PROGRESS_REDACT=0 |
| 想知道哪些东西被隐过 | 看 ~/.tg-progress/redacted.log |
| 换掉窗口标题 | TG_PROGRESS_TITLE="你的标题" / TG_PROGRESS_DONE_TITLE="…" |
| 干完别删、留一条记录 | TG_PROGRESS_END=keep |
| 要流式草稿(会锁安卓输入框) | TG_PROGRESS_MODE=draft |
两道闸分别是:
- 关键词闸 —— 摘要里出现
token/secret/password/.env/id_rsa… 就整条隐去。 - 形态闸 —— 认长相不认词:
sk-*、AKIA*、ghp_*、xox?-*、JWT、PEM 头、 长 hex / base64、URL 里的user:pass@。 只有关键词闸是不够的:deploy sk-live-ABC123XYZ一个关键词都没有,照样是把密钥递出去。
另外几处是硬编码的保守取舍(不受开关影响之外的行为,代码里改也就一行):
Bash只发description(人话说明),从不发command全文。Grep/Glob的 pattern 只在长得像普通标识符时才发,否则一个字不说。WebFetch的 URL 只留 host + path,丢掉 query / userinfo / fragment。
关了会怎样:Bash 的说明、文件名、搜索词会原样发进 Telegram。 如果你的 agent 会碰到真的生产密钥,想清楚再关。
redacted.log 刻意不记原文——记了就等于把密钥抄进另一个文件,闸就白设了。
它只记时间、哪个工具、命中哪道闸、原摘要多长,够回头对账。
用
日常:markdown 一行字
tg_rich_send(markdown="## 今日进度\n\n- [x] 修完 bug\n- [ ] 写测试")
进阶:把它接成 agent 的默认出口(渲染器模式)
「记得挑富消息工具、选对格式」不该是 agent 每条消息的负担。更稳的接法是反过来:
正式文字回复默认走 tg_rich_send(markdown=…),把它当渲染器用——
没有 Markdown 语法的消息渲染出来就是普通文本,写了 **重点**、列表、代码块的
自动长成原生样式,agent 不用每条都想"这条要不要富格式"。
⚠️ 这是"调用方需要自己实现"的接入策略,不是本工具内置的行为:
tg_rich_send本身没有 sendMessage 自动降级;server instructions 也仍让纯文字聊天走原来的 发送工具。下面两条护栏是给"要照这个思路接"的调用方的接入约定,得你自己在外层写。
接入示例(伪码):
try:
tg_rich_send(markdown=reply)
except err:
if 是"格式/方法不支持"类拒收: # 见护栏 1 的辨别
sendMessage(text=reply) # 降级重发同一段
else:
raise # 其它错不降级
两条护栏(这个接法实跑出来的,别省):
- 降级只认"格式/方法不支持"类拒收,
400 / 404只是候选范围不是判据:Telegram 因富格式/能力问题拒收时才退回普通sendMessage重发同一段。但400是杂物袋——chat not found、reply message not found、message text is empty也报 400,404也可能是 token/路径错;这些不该降级成纯文本(降了也发不出、还盖掉真错因)。 要辨别的是"这段富消息本身不被接受",不是"这次请求有别的毛病"。 网络错、超时、5xx、429 一律原样抛错,不自动补发——这些状态下 Telegram 可能 已经收到了第一条,自动补发=制造重复消息。 - 只包纯文字出口:引用回复、附件、贴纸等旁路照走原来的路,别把整条发送链 都塞进渲染器——包的面越大,降级时要复原的状态越多。
长任务:持久进度窗(推荐)
tg_rich_send(blocks=[...]) → 返回 message_id
tg_rich_edit(blocks=[...]) ← 每帧原地改;message_id 可省,
默认改本会话最后发的那条(簿记归脚本)
editMessageText 收 rich_message(10.1 加的),所以进度窗不必用 30 秒草稿:
发一条正式消息、之后原地编辑,留在聊天记录里、编辑还不响铃。
tg_rich_draft 只在你要那种 30 秒动画质感时才用(私聊限定,不进聊天记录,
定稿必须补一条正式消息)。它会锁住安卓用户的发送框——新客户端可以
can_stop: true 给用户一颗解锁按钮,但旧客户端不画按钮、bot 也收不到按停
事件(完整账见坑 3),所以仍别拿它当长任务的默认进度方案。
进度窗 hook 的两种形态用 TG_PROGRESS_MODE 切:edit(默认·持久窗)/
draft(流式动画·帧自带 can_stop·收工自动消失)——各有拥趸,都留着。
表格 / 公式:用 blocks
[
{"type": "heading", "size": 3, "text": "本周开销"},
{"type": "table", "is_bordered": true, "is_striped": true,
"cells": [
[{"text": "项目", "is_header": true}, {"text": "金额", "is_header": true}],
[{"text": "服务器"}, {"text": "¥128"}],
[{"text": "域名"}, {"text": "¥55"}]
]},
{"type": "mathematical_expression", "expression": "\\sum_{i=1}^{n} x_i = 183"},
{"type": "details", "summary": "明细", "blocks": [
{"type": "paragraph", "text": "折叠起来只占一行。"}
]}
]
mathematical_expression 的 expression 是裸 LaTeX,不要包 $$。
本地图片 / 九宫格:media_paths + attach://
tg_rich_send(
media_paths=["/pics/1.jpg", "/pics/2.jpg", "/pics/3.jpg"],
blocks=[{"type": "collage", "blocks": [
{"type": "photo", "photo": {"type": "photo", "media": "attach://f0"}},
{"type": "photo", "photo": {"type": "photo", "media": "attach://f1"}},
{"type": "photo", "photo": {"type": "photo", "media": "attach://f2"}}
]}]
)
- 第 i 个路径=
attach://f{i}。collage换成slideshow就是左右翻页; 单个photo块就是普通发图。一条消息最多 50 个媒体。三个"上限"不是一回事,别混:- 本地拦截阈值:本工具对每个文件按 50 MiB 拦(
MEDIA_MAX_BYTES),是内存/误传保护,不是 Telegram 的承诺; - 累计输入预算:一次调用所有文件读进内存拼 multipart 的总量,默认 200 MiB(
TG_RICH_MEDIA_TOTAL_MB可调)。读取本身有界——每个文件最多只读「单文件上限与剩余预算取小」再 +1 字节(多那一字节用来判超限),所以就算文件在 stat 之后被撑大,也不会
- 本地拦截阈值:本工具对每个文件按 50 MiB 拦(
Truncated for display — read the full file on GitHub.
Related Skills
Agent-Reach
78.5kGive your AI agent eyes to see the entire internet. Read & search Twitter, Reddit, YouTube, GitHub, Bilibili, XiaoHongShu — one CLI, zero API fees.
ruflo
71.1k🌊 The original agent meta-harness. Deploy intelligent multi-player swarms, coordinate autonomous workflows, and build conversational AI systems. Features adaptive memory, self-learning intelligence, RAG integration, and native Claude Code / Codex / Hermes and many more Integrated
headroom
69.2kCompress tool outputs, logs, files, and RAG chunks before they reach the LLM. 20% fewer tokens for coding agents, 60-95% fewer tokens for JSON, same answers. Library, proxy, MCP server.
CowAgent
46.8kOpen-source super AI assistant & Agent Harness. Plans tasks, runs tools and skills, self-evolves with memory and knowledge. Multi-model, multi-channel. Lightweight, extensible, one-line install. (formerly chatgpt-on-wechat)
