关联主题::
同级:: 2026-02-09_星期一
下一级::


用 Claude Code 实现 Obsidian 文档秒级同步飞书

背景:一个真实的痛点

我日常用 Obsidian 写文章、做笔记,但团队协作和对外分享用的是飞书。每次要把 Obsidian 里写好的文档分享到飞书,就面临一个尴尬的问题:

手动复制粘贴——格式全乱,标题层级丢失,代码块变成纯文本,Mermaid 流程图直接消失。

导出再导入——步骤繁琐,每次都要导出 → 打开飞书 → 导入 → 手动调格式。

我想要的很简单:在终端里说一句”把这篇文档上传到飞书”,几秒钟后拿到飞书链接,格式完好,流程图也在。

这篇文章记录了我从调研、踩坑到最终实现的完整过程。

方案调研:三条路的取舍

方案一:Feishu-MCP 逐块写入

Feishu-MCP 是一个飞书的 MCP Server,能通过 Claude Code 直接操作飞书文档的每一个”块”——段落、标题、代码块、Mermaid 图表等。

优点很明显:精确控制每一个块的类型,Mermaid 代码可以直接创建为飞书原生图表块。

但实际测试下来,一篇 400 行的文档,逐块写入需要几十次 API 调用,整个过程需要好几分钟。对于一篇写好的文章来说,这个速度不可接受。

方案二:飞书 Import API 整篇导入

飞书提供了 Import API,可以直接把一个 Markdown 文件导入为飞书文档。整篇文档一次性导入,速度是秒级的。

但 Import API 有一个硬伤:不认识 Mermaid。它会把 ```mermaid 代码块当作普通代码块处理,在飞书文档里只是一段看不懂的代码文本。

方案三:ObShare 插件

ObShare 是一个 Obsidian 社区插件,也是用 Import API 上传,但它做了额外处理——把 Mermaid 代码块在本地渲染成 PNG 图片,再作为图片上传。

这解决了”Mermaid 在飞书里不显示”的问题,但图片毕竟不是原生图表,不能编辑、不能交互、在飞书里也不够美观。

最终方案:Import API + MCP 补刀

三条路各有长短,最终我取了个混合方案:

阶段工具作用
主力上传Python 脚本 (Import API)秒级导入整篇文档
Mermaid 补刀Feishu-MCP将占位符替换为原生 Mermaid 块

思路是:

  1. 上传前,把 Mermaid 代码块提取出来,用占位符 [FEISHU_MERMAID_0] 替换
  2. 用 Import API 秒级上传(占位符变成普通文本)
  3. 上传后,用 Feishu-MCP 找到占位符文本块,删除,在同位置插入原生 Mermaid 块

这样,无 Mermaid 的文档秒级完成,有 Mermaid 的文档也只多几秒钟

实现过程

第一步:搞定飞书应用权限

飞书开放平台创建一个企业自建应用,开通以下权限:

  • drive:drive — 云空间文件读写
  • drive:drive:import — 文件导入
  • docx:document — 文档读写
  • drive:drive:permission:transfer — 权限转移
  • wiki:wiki — 知识库读写(知识库模式需要)

记下 App IDApp Secret,配置到 .claude/feishu-config.env

FEISHU_APP_ID = "cli_xxxxxxxx"
FEISHU_APP_SECRET = "xxxxxxxxxxxxxxxx"
FEISHU_API_DOMAIN = "https://open.feishu.cn"
FEISHU_WEB_DOMAIN = "https://my.feishu.cn"
FEISHU_DEFAULT_FOLDER = "xxxxxxxx"   # 云空间上传目标文件夹
FEISHU_OPEN_ID = "ou_xxxxxxxx"       # 你的 open_id(用于所有权转移)
 
# 知识库配置(可选,使用 --wiki 模式时需要)
FEISHU_WIKI_SPACE_ID = "xxxxxxxx"       # 知识空间 ID
FEISHU_WIKI_PARENT_NODE = "xxxxxxxx"    # 默认父节点 token

第二步:Python 导入脚本

脚本的核心流程:

读取 MD → 剥离 frontmatter → 剥离 Obsidian 元数据 → 提取 Mermaid
→ 上传文件 → 创建导入任务 → 等待完成 → 清理 → 转移所有权
→ (知识库模式)移动到知识库 → 回写 feishu_url

几个关键的坑,都是对照 ObShare 源码才发现的:

坑 1:Import API 的 file_name 字段

飞书的 Import API 文档里没有强调这个字段,但它是必需的。缺少这个字段,简单文档能导入成功,复杂文档会直接报 job_status: 2(失败),没有任何错误信息。

# 错误写法(缺少 file_name)
payload = {
    "file_extension": "md",
    "file_token": file_token,
    "type": "docx",
}
 
# 正确写法
payload = {
    "file_extension": "md",
    "file_name": "文档标题",   # 不含扩展名!
    "file_token": file_token,
    "type": "docx",
}

这个坑让我折腾了很久。一开始做二分法排查——把文档切成两半分别上传,两半都成功,合起来就失败。后来阅读 ObShare 的 feishu-api.ts 源码才发现这个差异。

坑 2:上传文件时 filename 不要带扩展名

upload_all 接口的 multipart 请求中,Content-Dispositionfilename 字段不能带 .md 扩展名,否则导入可能出问题:

# file_name 字段 → 带 .md
'file_name': (None, '文档标题.md')
 
# file 部分的 filename → 不带 .md
'file': ('文档标题', md_bytes, 'application/octet-stream')

坑 3:所有权转移的 member_type

飞书文档通过应用创建后,所有者是应用本身,需要转移给用户。ObShare 用的是 member_type: "userid",但我实测 userid 报错 Invalid parameter,换成 openid 才成功:

payload = {
    "member_id": open_id,
    "member_type": "openid"    # 不是 userid
}

这可能跟应用的权限范围有关,不同配置下行为可能不同。

坑 4:知识库 Wiki API 的异步/同步双模式

将云空间文档移动到知识库时,飞书的 move_docs_to_wiki API 有两种响应模式:

  • 文档所有者是用户时(已完成所有权转移):同步返回 data.wiki_token
  • 文档所有者是应用时:异步返回 data.task_id,需要轮询 /wiki/v2/tasks/{task_id} 获取结果

这意味着所有权转移必须在移动到知识库之前执行,否则会走异步路径,还需要额外处理轮询逻辑。

# 情况1: 同步返回(推荐,先转移所有权再移动)
data = {"wiki_token": "Naa8wywOOiO0YvkaOZEcdWGNn3g"}
 
# 情况2: 异步返回(需要轮询)
data = {"task_id": "7471234567890123456"}
# 轮询时必须带 task_type 参数
GET /wiki/v2/tasks/{task_id}?task_type=move

我的脚本同时处理了两种情况,并加了一个 _get_wiki_node_by_obj() 回退方案——如果轮询也失败,通过 obj_token 反查 node_token

第三步:Mermaid 补刀(Feishu-MCP)

这一步交给 Claude Code + Feishu-MCP 自动完成。

上传脚本输出 JSON 结果,包含每个 Mermaid 代码块的原始代码。Claude Code 拿到结果后:

1. 获取飞书文档的块结构

mcp__feishu-mcp__get_feishu_document_blocks({
  documentId: "D86Wd559ZogKXLxkEuicevW8nIf"
})

2. 找到占位符文本块

在返回的块列表中搜索文本内容为 [FEISHU_MERMAID_0][FEISHU_MERMAID_1] 的块,记录它们的 block_id 和在父块中的 index

3. 从后往前替换

从后往前是为了避免删除前面的块后,后面块的 index 发生偏移:

# 先删除占位符块
delete_feishu_document_blocks(startIndex=65, endIndex=66)

# 在同位置插入原生 Mermaid
batch_create_feishu_blocks(index=65, blocks=[{
  blockType: "mermaid",
  options: { mermaid: { code: "flowchart TD\n  A-->B" } }
}])

替换完成后,飞书文档里就是可交互的原生 Mermaid 图表了。

第四步:Obsidian 内容清理

上传前,脚本会自动处理两类 Obsidian 特有的内容:

YAML Frontmatter 剥离:Obsidian 的 frontmatter(--- 包裹的元数据)不需要出现在飞书文档中,但标题会从 title 字段提取。

Dataview 关联字段剥离:Obsidian 的 dataview 内联字段(如 关联主题::同级::下一级::)是本地知识管理用的元数据,上传飞书时自动去除,包括紧随其后的分隔线 ---

# 这些内容会被自动剥离,不会出现在飞书文档中:
关联主题::
同级:: [[2026-02-08_星期六]]
下一级::
---

第五步:feishu_url 自动回写

上传成功后,脚本会自动把飞书文档的 URL 写回 Obsidian 文件的 frontmatter:

---
title: 人生行动力六要素模型
feishu_url: https://my.feishu.cn/wiki/Jb7ewxyRmitdWIkN5b8cgC57niA
---

如果 frontmatter 中已有 feishu_url 字段,会更新为最新的 URL;如果没有,会新增。这样在 Obsidian 里就能直接看到对应的飞书链接,方便后续查阅和分享。

第六步:封装为 Claude Code Skill

把整个流程封装为一个 Skill,用自然语言触发:

.claude/skills/同步飞书/
├── skill.md                 ← SOP 定义
└── scripts/
    └── feishu_import.py     ← Import API 脚本

skill.md 定义了触发条件和执行步骤,Claude Code 读取后会严格按照 SOP 执行。

使用效果

在 Claude Code 里说一句话就行:

> 把"人生行动力六要素模型"这篇文章上传到飞书

Claude Code 自动执行:

[1/3] 执行导入脚本...
  ✓ 文件上传成功
  ✓ 导入完成: D86Wd559ZogKXLxkEuicevW8nIf
  ✓ 所有权已转移
  ✓ 已移动到知识库
  ✓ 已回写 feishu_url

[2/3] Mermaid 补刀(2 个图表)...
  ✓ [FEISHU_MERMAID_1] → 原生 Mermaid 块
  ✓ [FEISHU_MERMAID_0] → 原生 Mermaid 块

[3/3] 完成
  文档 URL: https://my.feishu.cn/wiki/Jb7ewxyRmitdWIkN5b8cgC57niA

对比一下各方案的实际表现:

方案400 行文档Mermaid 支持知识库操作方式
Feishu-MCP 逐块数分钟原生图表支持CLI
Import API 纯导入3-4 秒变成代码文本不支持CLI
ObShare 插件3-4 秒PNG 图片不支持Obsidian GUI
本方案(混合)5-8 秒原生图表支持CLI

架构总结

┌─────────────────────────────────────────────────┐
│                 用户说"上传到飞书"                  │
└───────────────────────┬─────────────────────────┘
                        │
                        ▼
┌─────────────────────────────────────────────────┐
│  Python 脚本 (feishu_import.py)                  │
│                                                  │
│  1. 剥离 YAML frontmatter                        │
│  2. 剥离 Obsidian dataview 关联字段               │
│  3. 提取 Mermaid → 替换为占位符                    │
│  4. Upload API → Import API → 轮询等待            │
│  5. 清理临时文件 + 转移所有权                       │
│  6. (知识库模式)移动到知识库                       │
│  7. 回写 feishu_url 到 Obsidian 文件               │
│  8. 输出 JSON(含 mermaid 代码列表)               │
└───────────────────────┬─────────────────────────┘
                        │
                   有 Mermaid?
                   │        │
                  否        是
                   │        │
                   ▼        ▼
              返回 URL   ┌────────────────────────┐
                         │  Feishu-MCP 补刀        │
                         │                         │
                         │  1. 获取文档块结构        │
                         │  2. 定位占位符块          │
                         │  3. 删除占位符            │
                         │  4. 插入原生 Mermaid 块   │
                         └────────────┬────────────┘
                                      │
                                      ▼
                                  返回 URL

前置配置清单

如果你也想搭建这套方案,需要准备:

  1. 飞书开放平台应用 — 创建企业自建应用,开通文档、云空间和知识库权限
  2. Python 3 + requests 库 — 脚本运行环境
  3. Feishu-MCP — 安装到 .mcp.json,用于 Mermaid 补刀
  4. Claude Code — Anthropic 的 CLI 工具,用于编排整个流程

已知限制

  • 图片不会上传:Import API 导入 Markdown 时,本地图片引用(![](image.png))不会自动上传。如果文档包含本地图片,需要先上传到图床替换为远程 URL。
  • Callout 语法:Obsidian 的 > [!note] Callout 语法,Import API 会当作普通引用块处理。
  • Wiki Links[[双链]] 语法在飞书中没有对应概念,会变成纯文本。

这些限制在大部分分享场景下影响不大。如果需要完美还原,可以在 MCP 补刀阶段增加更多处理逻辑。

写在最后

这个项目最有意思的地方不是某个单一技术的使用,而是混合架构的思路:用最合适的工具做它最擅长的事。

Import API 擅长快速导入整篇文档,但处理不了 Mermaid;Feishu-MCP 能精确操作每个块,但逐块写入太慢。把两者组合起来,取长补短,就得到了一个既快又全的方案。

这种”主力 + 补刀”的模式,在很多自动化场景里都适用——先用粗粒度的方式完成 80% 的工作,再用细粒度的方式处理剩下的 20%