telegram-send-mcp
v0.2.0
Published
MCP server for Telegram Bot API: send text/photo, edit and delete messages. Multi-bot, multi-group, purpose-based targeting baked into config.
Maintainers
Readme
telegram-send-mcp
MCP server:通过 Telegram Bot API 发送文本 / 图片、编辑与删除消息。目标固化在配置里(支持多 bot、每个 bot 多群、按用途选群),Agent 调用时只传内容,不需要每次给 chat_id。
由 lark-tg-mcp 拆分而来:本包只管 Telegram;Lark 建档见姊妹包
lark-send-mcp。
安装
# 推荐 pnpm
pnpm add -g telegram-send-mcp # 全局安装
pnpm dlx telegram-send-mcp # 免装、一次性执行
# 或 npm
npm install -g telegram-send-mcp
npx telegram-send-mcp接入 MCP 客户端
~/.claude.json(Claude Code)或 ~/.cursor/mcp.json(Cursor):
{
"mcpServers": {
"telegram-send": {
"command": "npx",
"args": ["-y", "telegram-send-mcp@latest"],
"env": {
"TG_BOT_TOKEN": "1234567890:AA...",
"TG_CHAT_ID": "-1001234567890"
}
}
}
}配好即用:TG_CHAT_ID 是全局默认群,所有机器共用这一份配置模板,发消息只传文本。
配置:env 凭据 + 项目文件
固定凭据走 环境变量,项目专属参数走 cwd 的 ./.telegram.json(与 lark-send-mcp / apifox-url-mcp 一致)。
env(跨项目共用一份)
| env 变量 | 必填 | 说明 |
|---|---|---|
| TG_BOT_TOKEN | ✅* | 单 bot 写法的 token;多 bot 写法里给 default_bot 兜底(也可写进 .telegram.json 兜底,env 优先) |
| TG_BOT_TOKEN_<BOT> | ⬜ | 多 bot 写法的 per-bot token,<BOT> 是 bots 键名大写、非字母数字转 _。例:bots.alert → TG_BOT_TOKEN_ALERT |
| TG_CHAT_ID | ⬜ | 默认目标群(仅单 bot 写法、且无配置文件时生效) |
| TG_CONFIG | ⬜ | 配置文件的显式路径,优先于 cwd 的 .telegram.json |
* 每个 bot 必须能从「文件内 bot_token → 对应 env」拿到 token,缺任何一个启动即报错。
目标配置文件(与 airouting-mcp 的 AIROUTING_CONFIG 同款约定)
| 优先级 | 来源 | 说明 |
|---|---|---|
| 1 | env TG_CONFIG | 显式指定配置文件路径,任何 cwd 都生效——想全局共用一份配置,就在 MCP 客户端 env 里指到那个文件 |
| 2 | ./.telegram.json(cwd) | 项目级点文件 |
不做合并、没有其他隐式落点;TG_CONFIG 指向的文件不存在时启动即报错。全局用法示例:
"env": { "TG_CONFIG": "/Users/you/path/to/.telegram.json" }多 bot 写法(一对多:每个 bot 管自己的一批群):
{
"default_bot": "notify",
"bots": {
"notify": {
"title": "通知机器人",
"purpose": "日常通知、进度同步",
"tg_chat_id": "-1000000000000",
"tg_groups": {
"daily_report": {
"chat_id": "-1001111111111",
"title": "工作日报群",
"purpose": "提交每日/每周工作汇报、进度同步。Agent 在用户说『发日报』『写周报』时应使用此群。"
}
}
},
"alert": {
"title": "告警机器人",
"bot_token": "987654321:BB...",
"tg_groups": {
"prod_alerts": {
"chat_id": "-1002222222222",
"title": "线上告警群",
"purpose": "生产环境告警、故障通报。Agent 在用户说『发告警』『线上出事了』时应使用此群。"
}
}
}
}
}| 字段 | 必填 | 说明 |
|---|---|---|
| default_bot | ⬜ | 多 bot 时不传 bot/group 的默认落点;只有一个 bot 时自动就是它 |
| bots.<key>.bot_token | ⬜ | 该 bot 的 token;不写则找 env TG_BOT_TOKEN_<KEY>(default_bot 还能用 TG_BOT_TOKEN) |
| bots.<key>.tg_chat_id | ⬜ | 该 bot 的默认群(没配 tg_groups 时用) |
| bots.<key>.tg_groups | ⬜ | 该 bot 的多群按用途配置,值为 { chat_id, title, purpose },purpose 给 Agent 看 |
单 bot 写法(旧版扁平结构,继续兼容):
{
"tg_chat_id": "-1001234567890",
"tg_groups": {
"daily_report": { "chat_id": "-1001111111111", "title": "工作日报群", "purpose": "…" }
}
}token 走 env TG_BOT_TOKEN,内部等价于一个名为 default 的 bot。
Telegram 侧前置准备
- 私聊 @BotFather →
/newbot创建,拿bot_token - 把 Bot 拉进群(或让接收用户先私聊
/start过它) - 取
chat_id:发一条消息后访问https://api.telegram.org/bot<token>/getUpdates,看chat.id
工具
| 名称 | 说明 |
|---|---|
| tg_send(text, url?, title?, bot?, group?, chat_id?) | 发文本(HTML parse mode),可附带链接。返回 {message_id, chat_id, bot},留着可编辑/删除 |
| tg_send_photo(photo, caption?, bot?, group?, chat_id?) | 发图片。photo 支持本地文件路径(multipart 上传)、http(s) URL、Telegram file_id |
| tg_edit(message_id, text, bot?, group?, chat_id?) | 按 message_id 编辑。文本消息走 editMessageText,图片消息自动降级改 caption。Bot 只能编辑自己发的消息 |
| tg_delete(message_id, bot?, group?, chat_id?) | 按 message_id 删除。Telegram 限制:Bot 只能删 48 小时内的消息 |
四个工具的目标解析规则一致(见下);tg_edit / tg_delete 要传消息当初发出时的那组 bot / group / chat_id。
多群 / 按用途选群
tg_groups 让 Agent 按"语义用途"选群,而不是死记 chat_id。三种触发路径:
- Agent 自动选(推荐):Agent 看
tg_groups[*].purpose描述,自己决定group参数。例:你说「把今天的进度发一下」→ Agent 调tg_send(text=..., group="daily_report")。 - 用户显式选:调用时传
group="daily_report"或chat_id="-100xxx",跳过弹窗。 - 弹窗选群(fallback):多群配置且没传
group/chat_id时,server 通过 MCP elicitation 弹一个单选对话框,让用户当场选。
弹窗的限制(⚠️ 关键)
弹窗能力依赖 MCP client 实现 elicitation。当前情况:
| Client | 支持? | |---|---| | Claude Code ≥ 2.1.76 | ✅ | | Cursor / Trae / 其他 | ❌(多数还未实现) |
在不支持 elicitation 的 client 上,多群配置 + 没传 group 会直接报错,提示用户必须显式传 group 或 chat_id。如果你只在 Cursor 用,配置上要么只配一个群、要么始终显式传 group。
兜底逻辑(resolveTarget 优先级)
- 调用时显式传
chat_id→ 直接发(bot 取显式bot>default_bot> 唯一 bot),最高优先级 - 调用时显式传
group→ 在所有 bot(或显式bot限定的那个)里找该群名;跨 bot 唯一时自动定位到对应 bot;多个 bot 撞名则报错要求传bot - 都没传 → 收敛到
default_bot(或唯一 bot);该 bot 没配tg_groups时用它的tg_chat_id兜底 - 收敛后只剩一个候选目标 → 自动选中,不弹窗
- 仍有多个候选 → 弹窗选群(client 支持时,多 bot 下选项形如
bot/group)/ 报错(不支持时)
自检
# 只读诊断:逐 bot 验身份(getMe)+ 每个配置目标的可达性(getChat)
pnpm diagnose
# 端到端:发文本 → 编辑 → 发图 → 删图(会真实发消息)
pnpm e2e [chat_id] [photo_url_or_path] [bot_key]常见错误
| 报错 | 原因 |
|---|---|
| 403 bot can't initiate conversation with a user | 接收用户没 /start 过 Bot |
| 403 bot was blocked by the user | 用户屏蔽了 Bot |
| 400 chat not found | chat_id 错误,或 Bot 没被拉进群 |
| 400 message can't be deleted | 超过 48 小时,或 Bot 无删除权限(群里需管理员或删自己的消息) |
| 400 message can't be edited | 不是 Bot 自己发的消息,或消息过旧 |
| 400 can't parse entities | 文本走 HTML parse mode,含未转义的 < > & 时会被当成非法标签;需转义成 < > & 或避免尖括号 |
License
MIT
