lark-tg-mcp
v0.8.0
Published
MCP server for creating Lark/Feishu wiki documents and pushing notifications to Telegram.
Maintainers
Readme
lark-tg-mcp
MCP server:在 Lark / Feishu 知识库自动建文档(含 Markdown 正文写入),并把链接推送到 Telegram。一把梭工具 lark_publish 一行搞定「建文档 → 写正文 → 推 TG」。
安装
# 推荐 pnpm
pnpm add -g lark-tg-mcp # 全局安装
pnpm dlx lark-tg-mcp # 免装、一次性执行
# 或 npm
npm install -g lark-tg-mcp
npx lark-tg-mcp快速开始(4 步)
# 1. 准备配置 —— 凭据走 env(接入客户端时填),项目参数写 ./.lark.json
vim ./.lark.json # 项目级参数:space_id / tg_chat_id 等
# 2. 飞书后台开权限并发布版本(见「飞书侧前置准备」)
# 3. OAuth 登录,浏览器自动打开
npx lark-tg-mcp login
# 4. 启动 MCP server,或接入 Claude Code / Cursor
npx lark-tg-mcp成功标志:第 3 步浏览器显示 ✅,token 文件出现在 ~/.claude/config/lark-tg-mcp.user.json(无项目配置时)或当前目录 .lark_user.json(有项目配置时)。
接入 MCP 客户端
~/.claude.json(Claude Code)或 ~/.cursor/mcp.json(Cursor):
{
"mcpServers": {
"lark-tg": {
"command": "npx",
"args": ["-y", "lark-tg-mcp@latest"],
"env": {
"LARK_APP_ID": "cli_xxxxxxxxxxxx",
"LARK_APP_SECRET": "xxxxxxxxxxxxxxxx",
"LARK_BOT_TOKEN": "1234567890:AA...",
"LARK_DOMAIN": "https://open.larksuite.com"
}
}
}
}配置:env 凭据 + 项目文件
固定凭据走 环境变量,项目专属参数走 cwd 的 ./.lark.json(与 apifox-url-mcp 一致)。
固定凭据(env,跨项目共用一份)
在 MCP 客户端配置的 env 里设置:
| env 变量 | 必填 | 说明 |
|---|---|---|
| LARK_APP_ID | ✅ | 飞书自建应用 app_id |
| LARK_APP_SECRET | ✅ | 飞书自建应用 app_secret |
| LARK_BOT_TOKEN | ✅ | Telegram BotFather 颁发的 bot token |
| LARK_DOMAIN | ⬜ | 国际版 https://open.larksuite.com,国内 https://open.feishu.cn(默认国内) |
这四项也可写进项目
.lark.json(字段名app_id/app_secret/bot_token/lark_domain)作为兜底;env 优先。
项目参数(./.lark.json,从 cwd 读,可选)
OAuth token (.lark_user.json / lark-tg-mcp.user.json) 同样项目优先:
| 优先级 | 路径 |
|---|---|
| 1 | ./.lark_user.json(项目级) |
| 2 | ~/.claude/config/lark-tg-mcp.user.json(全局级) |
lark-tg-mcp login 写哪一边:项目目录存在 .lark.json → 项目;否则 → 全局。
项目文件示例(凭据已在 env,这里只放项目参数;如需兜底也可把 app_id/app_secret/bot_token/lark_domain 一并写入):
{
"tg_chat_id": "-1001234567890",
"tg_groups": {
"daily_report": {
"chat_id": "-1001111111111",
"title": "工作日报群",
"purpose": "提交每日/每周工作汇报、进度同步。Agent 在用户说『发日报』『写周报』『汇报进度』时应使用此群。"
},
"backend_bugs": {
"chat_id": "-1002222222222",
"title": "后端对接群",
"purpose": "向后端团队提交 bug、API 对接问题。Agent 在用户说『提 bug』『后端有问题』『接口报错』时应使用此群。"
}
},
"space_id": "7234567890123456789",
"parent_node": "",
"import_folder_token": "fldxxxxxxxxxxxxxx",
"wiki_host": "https://your-tenant.larksuite.com"
}| 字段 | 必填 | 说明 |
|---|---|---|
| tg_chat_id | ⬜ | 单群兜底。tg_groups 没配时用它做默认;同时配置时,工具调用没传 group / chat_id 才用它 |
| tg_groups | ⬜ | 多群按用途配置。键随便起(daily_report、backend_bugs 等),值为 { chat_id, title, purpose }。purpose 给 Agent 看,决定怎么选群。详见下文「多群 / 按用途选群」 |
| space_id | ✅ | 知识库 ID。从知识库 URL …/wiki/settings/<id> 获取(用 lark_create_wiki_doc / lark_publish 时必填) |
| import_folder_token | ✅ | Lark Drive 文件夹 token。import_tasks 临时落 md 文件用。打开任意 Drive 文件夹,URL 末段 …/drive/folder/<token> 就是它 |
| parent_node | ⬜ | 可选,建文档放哪个父节点下;空 = 知识库根 |
| wiki_host | ⬜ | 你企业的飞书域,如 https://abc.larksuite.com,决定生成的文档链接 |
凭据字段(
app_id/app_secret/bot_token/lark_domain)现在走 env,见上一节;写进本文件仅作 env 缺失时的兜底。
飞书侧前置准备
1. 自建应用 + 权限申请
https://open.larksuite.com → 创建企业自建应用 → Permissions & Scopes 勾选并发布版本:
wiki:wikiwiki:node:createwiki:node:readdocx:documentdrive:drive(import_tasks链路必须)drive:file:upload(同上,上传 md 源文件用)offline_access(OAuth refresh token 必须)
2. 把应用加进目标知识库
进知识库 → Settings → Permissions / Members → Add Member → 切到 Bot / Apps Tab → 搜应用名 → 角色选 Full Access / 可管理。
如果你的租户禁用了「Bot 加入 wiki」功能、或者 UI 找不到该 Tab,跳到下面「生成 user token(OAuth 登录)」用你自己的身份调 API。
生成 user token(OAuth 登录)
很多场景"把应用加进知识库成员"会失败(租户限制、UI 找不到 Bot Tab、企业策略禁用等)。用你自己作为知识库 admin 的身份做一次 OAuth,绕开成员问题——user token 就是这一步的产物,lark-tg-mcp 跑起来强烈建议有它。
落地位置由 lark-tg-mcp login 决定:当前 cwd 有 ./.lark.json → 写 ./.lark_user.json;没有 → 写 ~/.claude/config/lark-tg-mcp.user.json。
前置
- 飞书后台开权限并发布版本(见上一节「权限申请」),尤其需要:
offline_access← 没这个,refresh token 拿不到,2 小时后要重新登录
- 配置回调 URL:开发者后台 → Security Settings → Redirect URLs → 添加:
http://localhost:3000/callback - 你本人在目标知识库里至少是「可编辑」角色(admin 最佳)
操作
login 需要 app_id / app_secret —— 从 env 读(缺省时回退项目 .lark.json),所以先把凭据导入环境变量:
export LARK_APP_ID=cli_xxxxxxxxxxxx
export LARK_APP_SECRET=xxxxxxxxxxxxxxxx
export LARK_DOMAIN=https://open.larksuite.com # 国内省略,默认 feishu.cn
# 全局登录(推荐,跨项目共用):从任意目录跑,token 落到 ~/.claude/config/
npx lark-tg-mcp login
# 项目级登录:cd 到包含 ./.lark.json 的目录再跑,token 落到当前目录
cd <项目目录>
npx lark-tg-mcp login会发生什么:
- 终端打印授权 URL 并自动调系统命令打开浏览器
- 你在浏览器里登录 Lark / 同意授权
- 飞书 302 跳回
http://localhost:3000/callback?code=xxx&state=yyy - 本地脚本接到 code,用 tenant_access_token 兑换 user_access_token
- 写入对应位置的 user token 文件,浏览器显示
✅ Login OK.,命令自动退出
文件结构(生成后大致长这样,别提交 git):
{
"access_token": "u-xxx...",
"refresh_token": "ur-xxx...",
"expires_at": 1735000000000,
"refresh_expires_at": 1738000000000,
"scope": "wiki:wiki docx:document offline_access ..."
}自动续期
access_token 2 小时过期,但 lark-tg-mcp 在每次调用 API 前会检查;过期就用 refresh_token 自动续,并把新 token 回写到加载时的同一文件(项目级或全局级,不会跨层迁移)。refresh_token 通常 30 天有效,过期了再 npx lark-tg-mcp login 重做一次即可。
常见报错
| 报错 | 原因 |
|---|---|
| redirect_uri mismatch | 上面前置第 2 步的回调 URL 没加,或拼错 |
| scope not allowed | 前置第 1 步缺 scope 或没发布版本 |
| Login OK 但 token 文件没生成 | 目标目录无写权限,看终端报错 |
| 浏览器打不开 | 终端有授权 URL,手动复制粘贴到浏览器即可 |
Telegram 侧前置准备
- 私聊 @BotFather →
/newbot创建,拿bot_token - 把 Bot 拉进群(或让接收用户先私聊
/start过它) - 取
chat_id:发一条消息后访问https://api.telegram.org/bot<token>/getUpdates,看chat.id
工具
| 名称 | 说明 |
|---|---|
| lark_create_wiki_doc(title, markdown?, space_id?, parent_node?) | 建知识库 docx 节点 + 写 Markdown 正文,返回 {document_id, node_token, url} |
| tg_send(text, url?, title?, group?, chat_id?) | TG 推送,可附带文档链接。group 指定 tg_groups 中的 key;chat_id 是裸 chat id 覆盖;都不传且多群时弹窗让用户选 |
| lark_publish(title, markdown, tg_text, group?, chat_id?, ...) | 一把梭:建文档 + 写正文 + 推 TG。选群规则同 tg_send |
多群 / 按用途选群
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。
兜底逻辑(resolveTgTarget 优先级)
- 调用时显式传
chat_id→ 直接发,最高优先级 - 调用时显式传
group→ 查tg_groups[group].chat_id发 tg_groups没配 → 用tg_chat_id兜底tg_groups只配了一个群 → 自动选这一个,不弹窗tg_groups配了多个且没传 → 弹窗选群(client 支持时)/ 报错(不支持时)
子命令
| 命令 | 说明 |
|---|---|
| npx lark-tg-mcp | 启动 stdio MCP server |
| npx lark-tg-mcp login | OAuth 登录,写 user token(项目优先,否则全局) |
Markdown 转换
走飞书 import_tasks 接口把 markdown 整段交给服务端转 docx,支持范围由飞书侧决定(标题、列表、代码块、表格、粗斜体、行内链接、图片引用等常规 GFM 语法都覆盖)。本项目不再做客户端转换,所以 md 不需要手工降级到子集。
常见错误
| 报错 | 原因 |
|---|---|
| 131006 permission denied: tenant needs edit permission | 应用未加入知识库成员,或仅有「可阅读」角色。改用 OAuth 用户身份兜底 |
| 131005 not found | space_id / node_token 错或无访问权 |
| 99991663 / scope not allowed | OpenAPI 权限未开 / 版本未发布 |
| TG 403 bot can't initiate conversation with a user | 接收用户没 /start 过 Bot |
| TG 403 bot was blocked by the user | 用户屏蔽了 Bot |
| redirect_uri mismatch | 飞书后台没加 http://localhost:3000/callback |
License
MIT
