关于 TransformPipe
TransformPipe 可将文档转换为 Markdown,也能反向转换,并支持保存、搜索和分享为链接页面。它面向处理文件、网页和助手生成内容的用户;转换在浏览器中进行,可不上传文件。
主要功能
- 01将办公文件和导出内容转为 Markdown
- 02将 Markdown 导出为 HTML、Word 等格式
- 03合并多个文件为一份文档
- 04保存文档并搜索和查看修订
- 05通过访问控制分享页面
适用场景
- 01将 Word 文件或 Notion 导出内容转为 Markdown
- 02将 Markdown 发布为可分享页面
- 03将助手生成的文档保留在聊天之外
- 04从网页提取文章内容
MCP 相关
TransformPipe 可作为 MCP 应用连接 Claude 和 ChatGPT,让助手保存、发布和查找账户文档。助手访问范围仅限用户自己的文档。
配置
远程服务无需本地安装。选择你使用的客户端,复制配置或使用一键安装。
https://transformpipe.com/api/mcpclaude mcp add --transport http raudarlabs-transformpipe https://transformpipe.com/api/mcp{"mcpServers":{"raudarlabs-transformpipe":{"type":"http","url":"https://transformpipe.com/api/mcp"}}}在 Cursor 中打开{"servers":{"raudarlabs-transformpipe":{"type":"http","url":"https://transformpipe.com/api/mcp"}}}在 VS Code 中打开{"mcpServers":{"raudarlabs-transformpipe":{"type":"http","url":"https://transformpipe.com/api/mcp"}}}{"mcpServers":{"raudarlabs-transformpipe":{"serverUrl":"https://transformpipe.com/api/mcp"}}}命令在终端中运行;JSON 粘贴到对应客户端的 MCP 配置后重启。需要授权的服务会在首次连接时提示登录。
工具
详细介绍
TransformPipe
将 Word 文件、网页或完整的 Notion 导出内容转换为整洁的文档,保存下来,并用一个链接将其作为页面发送。

文档的格式总是不合适。你需要 Markdown 格式的 Word 文件。一个打包在 zip 里的 Notion 导出文件,长达 40 页。助手写出的规范,却在聊天记录滚动过去后消失。粘贴到电子邮件里的 Markdown,最后变成满屏星号。
TransformPipe 会将这些内容都转换成同一种格式:整洁的 Markdown 文档。你可以将其作为排版完成的页面查看,导出为 Markdown、HTML、Word、PDF、Confluence、Jira、Slack 或 EPUB,也可以保存文档并发送链接,而不是附件。打开 transformpipe.com,拖入文件即可。转换无需账号。
在浏览器中完成 20 种转换
支持 Word、PowerPoint、Excel、EPUB、OpenDocument、富文本、HTML、CSV、JSON、纯文本、Evernote,以及完整的 Notion、Confluence 或 Obsidian 导出内容。转换结果均为 Markdown,标题、列表、链接和表格都会完整保留;Markdown 也可以反向转换为排版完成的页面、Word 文档、Confluence 页面、Jira 工单、Slack 消息或 EPUB 电子书。
转换在你的设备上进行。退出登录时,不会上传任何内容,也无需联网。拖入多个文件,它们会按你选择的顺序合并为一个文档。

输入 Markdown,输出排版完成的页面
表格、代码、任务列表、数学公式和 Mermaid 图表都会按预期呈现。下载的内容是一个独立的 .html 文件,打开时不会获取任何内容:没有脚本,也没有网页字体。保存的文档也可以下载为 Word,任何文档都可以打印为 PDF。

发送链接,而不是附件
你可以与任何拥有链接的人分享已保存的文档,也可以仅与指定地址分享。如果有需要,还可以为链接设置密码或截止日期,并查看文档被打开的次数。设为私密后,一步即可停止分享,旧链接也将永久失效。
通过 API、命令行、助手或 Obsidian 发布新内容后,已有链接的人看到的就是更新后的内容。被替换的文本会作为修订版本保留。
保存助手写下的内容
TransformPipe 已收录在 Claude 的连接器目录中:点击连接,登录,然后用日常语言提出请求。“把这个保存为 Q3 计划。”“发布它并给我链接。”“查找上周的迁移规范。”文档会保存到你的账号中,并带有可供搜索的标题,即使聊天记录早已滚动过去,文档仍会保留。
ChatGPT 也可以连接,支持通过地址连接或作为 MCP 应用使用;Obsidian 则提供了一个社区插件。这三者都能查看相同的文档。

一个资料库,而非四十个聊天
你转换、保存或让助手保存的所有内容,都会归入同一个列表:可搜索,可按格式筛选,可并排查看各版本,还能逐行比较差异。登录后,所有设备上看到的都是同一个列表。

任意网页,转换为 Markdown
适用于 Chrome 和 Firefox 的浏览器扩展会抓取你当前浏览的网页,并返回不含导航栏和 Cookie 提示的文章。你可以复制、下载,或将网页保存为独立的 .html 文件。未登录时,它绝不会与我们通信。

你的文档,你的账户
助手会以你的身份操作,且只能访问你的文档。它无法更改账户、查看密码或创建 API 密钥;未经明确确认,也不会删除任何内容。授权可以是只读的。服务免费,使用限额也有明确说明:每个账户最多 5,000 份文档、250 MB,每份文档最多 4 MB。达到限额后,保存操作会被拒绝;不会为了腾出空间而删除任何内容。

多种接入方式
- Web 应用:transformpipe.com:转换、预览、保存和分享。
- Claude:可通过 claude.ai 上的连接器目录和 Claude Desktop 使用,也可在 Claude Code 中通过地址接入。
- ChatGPT:作为 MCP 应用使用:三个步骤。
- 浏览器扩展:适用于 Chrome、基于 Chromium 的浏览器,以及桌面版和 Android 版 Firefox。
- Obsidian 版 TransformPipe:将笔记发布为网页,每次编辑后链接保持不变。
- 脚本:API、命令行和 GitHub Action。
转换
现已上线 transformpipe.com。旧地址 md-2-html.vercel.app 仍可访问,因此已分享的链接继续有效;规范 URL、站点地图以及 CLI 和 Action 中的所有默认值都指向新域名。server/ 中完全不包含任何域名——来源地址通过 selfOrigin 从请求中获取,因此迁移只需信任一个来源并重新构建。
上传文件,查看转换结果,然后下载。共有二十种转换,每种都有自己的页面和地址:
| 转换 | 输入 | 输出 |
|---|---|---|
| Markdown → HTML | .md .markdown .mdown .mkd | 独立的 .html 文件 |
| Markdown → Confluence | .md | Confluence 存储格式(代码宏、面板、任务列表) |
| Markdown → Jira | .md | Jira wiki 标记语言 |
| Markdown → Slack | .md | Slack mrkdwn(表格转换为对齐的代码块) |
| Markdown → EPUB | .md | .epub 电子书,每个顶级标题对应一章 |
| Markdown → Word | .md | 使用 Word 自带样式的 .docx 文件(标题 1–6、列表、脚注) |
| HTML → Markdown | .html .htm .xhtml | .md |
| 纯文本 → Markdown | .txt | .md |
| CSV → Markdown 表格 | .csv .tsv | .md |
| JSON → Markdown | .json | .md |
| Notion 导出 → Markdown | .zip(导出为 Markdown 和 CSV) | .md |
| Confluence 导出 → Markdown | .zip(导出 → HTML) | .md |
| Obsidian 知识库 → Markdown | .zip(压缩后的知识库文件夹) | .md |
| Word → Markdown | .docx | .md |
| Excel → Markdown 表格 | .xlsx | .md |
| PowerPoint → Markdown | .pptx | .md(每张幻灯片对应一个章节,保留演讲者备注) |
| EPUB → Markdown | .epub | .md(每章对应一个章节,按阅读顺序排列) |
| OpenDocument → Markdown | .odt | .md |
| 富文本 → Markdown | .rtf | .md |
| Evernote → Markdown | .enex | .md(每条笔记对应一个章节,保留标签) |
它们都会统一转换为 Markdown,因为文档以这种形式存储、预览、分享,并由脚本访问——一种格式,而非十四种。之后,任何文档都可以作为 Markdown、HTML、纯文本、Word、Confluence、Jira、Slack、EPUB 传递,也可以打印为 PDF。shared/conversions.ts 是唯一的列表;页眉菜单、页面、历史记录标签、徽章和预渲染页面都读取它,因此新增转换只需在其中添加一项并提供转换器。
过去,Markdown → HTML 页面会把 .txt 当作 Markdown 接受;现在它有了自己的转换,因为纯文本文件并不是 Markdown,即使看起来像也不是——直接输入的星号和用作强调的星号是同一个字符,而这两种转换不应以相同方式处理它们。
Notion、Confluence 和 Obsidian 是三种无法从单个文件生成单份文档的格式:每种导出内容都是 .zip 中的多个页面(或笔记),转换后则合并为一份 Markdown 文档——先是目录,然后是按顺序排列的所有页面,每页各自成为一个标题(shared/from-notion.ts、shared/from-confluence.ts、shared/from-obsidian.ts;它们共享 shared/zip-import.ts 中的 ZIP 读取和合并约定)。导出内容中从一个页面指向另一个页面的链接会保留链接文字,但移除链接地址——包括 Obsidian 自带的 [[wikilinks]]:当每个页面都成为同一文档中的一个章节后,链接便无处可指。
转换在浏览器中进行——.docx 读取器和 HTML 解析器只会在使用对应页面时加载,因此它们不会被打包进首页的资源中。登录——使用 Google,或电子邮件和密码——即可将文档保存在账户中,并从任何设备访问。
本地运行
npm install
cp .env.example .env.local # then fill in the values (see Configuration)
npm run db:init # creates the table in Neon
npm run dev
应用会在 http://127.0.0.1:5180 启动——/api/* 由与生产环境中作为 Vercel 函数运行的同一个 Hono 应用提供服务,因此无需额外进程。
其他脚本:npm run build、npm run preview、npm run check-types、
npm run db:init。
未设置 NEON_AUTH_BASE_URL 时,应用仍可转换文件:登录会返回 503,历史记录则会回退到此浏览器的 localStorage。
配置
| 变量 | 说明 |
|---|---|
DATABASE_URL | Neon 池化连接字符串 |
NEON_AUTH_BASE_URL | 项目的 Neon Auth 端点 |
两者都仅供服务端使用;浏览器与 /api/* 通信,不会直接连接 Neon。
登录使用 Neon Auth(由 Neon 端点提供的 Better Auth),它已内置 Google 登录——无需单独的 Google OAuth 客户端,此处也无需客户端密钥。/api/auth/* 会将请求转发给认证服务,并重写 Set-Cookie,使会话 Cookie 成为此网站的第一方 Cookie;/api/auth/finish 会将一次性验证器兑换为该 Cookie。请参阅 server/auth.ts。
Neon Auth 只会为其信任的来源启动登录,因此每个来源都必须添加一次——网站迁移到新域名时,只有这项配置需要更改:其他所有内容都会从请求中推导 URL,只有登录需要手动告知:
npm run auth:origin # list what is trusted
npm run auth:origin -- https://transformpipe.com # the live site
npm run auth:origin -- http://127.0.0.1:5180 # for local work
Neon——TransformPipe 有自己的 Neon 项目(tp):独立的数据库和 Neon Auth,不与任何其他应用共享账户或表。该项目通过 Vercel Marketplace 配置,它也会连接项目并写入 DATABASE_URL / NEON_AUTH_BASE_URL:
vercel integration add neon --name m2h
vercel env pull .env.local --environment=production
npm run env:setup # prunes .env.local down to this app's two keys
npm run db:init # creates m2h_document
Vercel——上述变量会在连接步骤中为生产、预览和开发环境设置;在设置这些变量之前创建的部署需要重新部署才能读取它们。构建配置会自动检测(Vite → dist),并由 vercel.json 将 /api/* 路由到 Hono 函数。
功能
- 上传 — 拖放文件或使用文件选择器,支持
.md / .markdown / .mdown / .mkd / .txt,最大 10 MB。 - 串联多个文件 — 拖入多个文件,它们会按到达顺序合并为一个文档,并以分隔线隔开;在历史记录中选择任意多个文件也能进行同样操作。
- 预览 — 支持 GitHub Flavored Markdown(表格、任务列表、删除线、自动链接),使用 DOMPurify 清理,并采用设计系统的令牌设置样式。
- 分享 — 账号中的文档可供任何持有链接的人打开,也可以仅供你列出的地址访问(对方需使用该地址登录)。
/s/<token>是只读页面:只有文档和下载功能,没有其他内容。撤销分享会移除令牌,因此已发送的链接也会失效。你列出的每个地址都会收到一封包含链接的邮件。 - 与我共享 — 历史记录中的标签会列出其他人分享给你的文档,并标明分享者。此类文档为只读:可以打开和下载,但不能删除或再次分享。这里只显示指定地址的分享;链接分享属于持有链接的人,不属于某个列表。
- 全屏阅读 — 预览会占满整个屏幕,文档区域保持适宜的阅读宽度并可滚动;按 Escape 返回。长文档在普通视图和全屏视图中都会显示返回顶部按钮。
- HTML source 选项卡 — 显示实际下载的完整独立文档。
- 下载 / 复制 — 导出为带内联样式、可直接打印的独立
.html文件。打开时不会获取任何内容:没有脚本,也没有网络字体。过去会从 Google 加载 DM Sans,这让实际交付给他人的单个文件不再真正“独立”;因此导出会依次回退到ui-sans-serif和系统字体,不会请求任何资源。 - 下载为 Word — 已保存文档的下载菜单中会提供
.docx,由服务器使用与预览相同的 HTML 生成(@turbodocx/html-to-docx— 纯 JavaScript,无需无头浏览器)。需先保存文档:端点读取的是账号中的副本,而不是浏览器中的副本。 - 通过 API 获取 .pdf —
GET /api/v1/documents/:id.pdf使用pdfmake根据相同的 HTML 排版,而不是使用无头 Chrome — 应用中有意不提供此按钮:“打印或另存为 PDF”已经能让登录用户通过浏览器生成像素级精确的 PDF;此 API 则适用于无法使用该按钮的情况 — 脚本、CI 作业,或完全没有浏览器的 GitHub Action 步骤。 - 历史记录 — 已登录:文档保存在账号中(最多 5,000 个文档,每个源文件最大 4 MB),可在所有设备上访问;未登录:最近 25 次转换保存在
localStorage中。首次登录时,之前收集在本地的内容会迁移到账户中。此页面有自己的投放区、用于搜索文件名的搜索框、Markdown/HTML 标签和可排序的表头;点击行即可打开文档,也可批量选择多行来合并、下载或删除(选择栏和标签均来自设计系统)。 这些切换按钮决定列表显示文档的哪种形式:生成它的 Markdown 源文件,或转换得到的 HTML。行名称、类型徽标和下载内容都会随之变化。系统只存储源文件;HTML 会即时生成,因此大小列会注明测量的对象。 - 版本 — 通过
?replaces=<id>(API)、--replaces(CLI)或replaces(Action)推送,可将文档关联到较早的文档,作为它的新版本。此操作需主动选择并明确指定,绝不会自动推断;除非另有说明,新推送的文档始终是独立文档。关联的文档会在历史记录中显示链条图标,并提供与其前一版本的逐行差异。 - 登录 — 通过 Neon Auth 使用 Google,或使用邮箱和密码登录。
- 摘要 — 已保存的文档会多出第三个标签页:先用一段话说明文档内容及其用途,再以简短列表列出具体信息。摘要由 Gemini 生成,在撰写时流式传输到标签页,并通过 Google AI Studio 密钥直接调用(
GOOGLE_GENERATIVE_AI_API_KEY— 免费套餐,无需银行卡)。摘要只生成一次并缓存到该行,因此再次打开标签页无需付费;点击“重新生成”会再次发起请求。摘要单独计量,不计入常规 API 限额——每个账户每天 20 次——因为与此应用的其他功能不同,每次调用都要付费。未设置密钥时,标签页会说明这一点,而不会导致保存失败。
UI
界面基于我们的内部设计系统构建:设计令牌(浅色和深色)、DM Sans 以及 React 组件库都存放在 src/ui 下,并通过 tailwind.ui.config.ts / tailwind.config.ts 扩展,再由 src/index.css 加载。应用默认使用深色主题(<html class="dark">);移除该类即可切换为浅色主题。
Markdown 文档样式(src/lib/md-doc-css.ts)只需针对 --md-* 变量编写一次,即可同时用于预览和导出的文件,因此两者始终保持一致,连页面本身的背景色也相同。文档会跟随应用当前主题,下载的文件也会保留相应的配色。打印时始终切换为浅色值,因为深色页面打印出来会铺满墨水。
主题(默认深色)位于账户菜单中,并按浏览器保存在 localStorage;退出登录后,页眉中的太阳/月亮按钮可实现相同功能。
API
应用能做的所有操作,脚本都能用密钥完成:账户菜单 → API keys。密钥只显示一次,仅以哈希形式存储,并可随时撤销。它可以访问文档和共享内容,但不能访问账户或密钥本身,因此即使密钥泄露,也无法生成替代密钥或将你锁在账户之外。
# publish a file in one request
curl -H "Authorization: Bearer tp_live_…" --data-binary @README.md "https://transformpipe.com/api/v1/documents?name=README.md&share=link"
# → { "document": { "id": "…", "share": { "mode": "link", "url": "https://…/s/…" } } }
POST /api/v1/documents | 请求正文为 Markdown(?name=),或 JSON {name, markdown};?share=link|people 会在同一次调用中发布文档,?expires_at= 为该链接设置失效时间;?kind=html-to-markdown|csv-to-markdown|json-to-markdown|word-to-markdown|notion-to-markdown|confluence-to-markdown|obsidian-to-markdown|text-to-markdown|excel-to-markdown|powerpoint-to-markdown|epub-to-markdown|odt-to-markdown|rtf-to-markdown|evernote-to-markdown 会先转换正文——对于所有非纯文本格式,请将文件本身(.docx、.zip、.xlsx、.pptx、.epub、.odt 或 .rtf)作为正文发送;?replaces=<id> 会将其作为新版本关联到较早的文档,此选项默认关闭 |
GET /api/v1/documents | 最新的 500 个文档;?q= 搜索内容和名称,并按相关性排序 |
GET /api/v1/documents/:id | 元数据和源内容 |
GET /api/v1/documents/:id.html | 独立文档,可选使用 ?theme=dark |
GET /api/v1/documents/:id.docx | Word 文档,由同一份 HTML 转换生成 |
GET /api/v1/documents/:id.pdf | PDF,由 pdfmake 根据同一份 HTML 排版生成——不使用无头浏览器 |
GET /api/v1/documents/:id/versions | 同一版本链中的所有文档,按从旧到新的顺序排列 |
PUT /api/v1/documents/:id | 替换同一文档的文本——请求正文为 Markdown,或 JSON {markdown, name}。ID、链接以及文档上的所有设置均保持不变;被替换的文本会作为修订版本保留(最多保留最新的十个,按字节数而非文档数计算)。若文本未变化,则返回 changed: false |
GET /api/v1/documents/:id/revisions | 按从新到旧的顺序返回这些修订版本;/revisions/:revision 可读取其中一个修订版本及其 Markdown |
DELETE /api/v1/documents/:id | 删除该记录、源内容及其修订版本 |
GET | PUT /api/v1/documents/:id/share | {mode, emails[], expires_at};private 会移除令牌,因此已发送的链接将无法使用。expires_at 是链接失效时间,格式为 ISO 8601 日期时间(失效后返回 410);设为 null 表示永不过期,省略则保留现有设置。响应还包含 views 和 last_viewed_at:这些是通过共享页面自带的单像素图片记录的链接打开次数和最近打开时间。password 可为链接设置密码,也可在文档仍为私有时提前设置(8 至 200 个字符,仅放在请求正文中);设为 null 则移除密码;面向特定人员的共享不支持密码;响应会返回 has_password,不会返回哈希值 |
GET /api/v1/documents/:id/views | 链接最近的打开记录,按从新到旧的顺序排列:{views, last_viewed_at, events: [{at, via}]}——via 的值为 page 或 app。链接不会记录打开者的身份;面向特定人员的共享会在每次打开记录中添加 who,并返回 people 列表——其中包含每个指定地址、其打开次数及最近一次打开时间 |
POST /api/v1/documents/:id/summary | 获取缓存的摘要;若尚无摘要,则先生成。每个账户每天最多可调用 20 次 |
Cookie 也可用,因此登录后的浏览器也能调用相同的端点。GET /api/v1/usage 可查看账户的用量。错误响应格式为 { "error": "…" },状态码含义直观:401 表示密钥未知,404 表示资源不属于你,413 表示文档超过 4 MB,403 表示账户空间不足,429 表示请求过快,410 表示源内容已不存在。
Webhooks
账户菜单 → Webhooks 可注册一个 URL:当文档创建或分享给指定人员时,该 URL 会收到带签名的 POST 请求。它仅在会话期间可用,这是刻意设计的——与 API 密钥不同,webhook 不会通过 /api/v1 公开:如果 API 密钥也能注册 webhook,那么密钥一旦泄露,就会变成持续推送所有未来文档的渠道,而不像现在这样仅能获取某个时间点的内容。
{
"event": "document.created",
"created_at": "2026-09-11T12:00:00.000Z",
"data": { "id": "…", "name": "notes.md", "kind": "markdown-to-html", "size": 512 }
}
请求正文使用 HMAC-SHA256 对 {timestamp}.{body} 进行签名,签名位于 x-transformpipe-signature: t=<unix-seconds>,v1=<hex> 标头中——格式与 Stripe 和 GitHub 使用的相同,因此现有验证代码通常只需更换密钥:
const expected = crypto
.createHmac('sha256', secret)
.update(`${timestamp}.${rawBody}`)
.digest('hex');
创建 webhook 时会显示密钥,也可以在对话框中再次查看——与 API 密钥不同,webhook 密钥是由这个应用提供给外部,而不是提供给这个应用的,因此账户所有者可能确实需要再次获取它,以配置或调试接收端。投递尽力而为:仅发送一次请求,超时为五秒,不重试,也不排队——接收端停机时会错过这次投递,对话框会显示上次投递失败的时间。
从终端使用
cli/tp.mjs 提供了同一套 API,只是使用起来更方便。它没有依赖项——只有一次 fetch 和一些输出,因为在 CI 中运行的工具不应拖着一整棵包依赖树。
node cli/tp.mjs login tp_live_… # remembers the key in ~/.config/tp/config.json
node cli/tp.mjs push README.md --share # prints the link
node cli/tp.mjs push docs/*.md --merge --share --name handbook.md
node cli/tp.mjs push notes.md --share --expires 7d # a link that stops working in a week
TP_SHARE_PASSWORD=… node cli/tp.mjs push notes.md --share --password # a link that asks for it
node cli/tp.mjs list
node cli/tp.mjs rm <id>
node cli/tp.mjs summary <id> # generated once, cached; --force to regenerate
node cli/tp.mjs push v2.md --replaces <id> # links it to an earlier document as a new version
node cli/tp.mjs push notes.md --update <id> # new text, same link; the old text is kept as a revision
node cli/tp.mjs versions <id> # every document in the chain, oldest first
node cli/tp.mjs usage # 65.8 kB of 250.0 MB · 3 of 5000 documents
node cli/tp.mjs folders # the tree: ids, documents in each, the link if shared
node cli/tp.mjs push notes.md --folder "Team/Notes" # filed there; missing folders are made
node cli/tp.mjs list --folder "Team/Notes" # that folder and everything under it; - for unfiled
node cli/tp.mjs mkdir "Team/Notes/2026" # every part of the path not there yet
node cli/tp.mjs mv <id>… "Team/Archive" # documents, or folders, into a folder; - for none
node cli/tp.mjs share-folder "Team/Notes" --link --expires 7d # prints <host>/f/<token>
node cli/tp.mjs share-folder "Team/Notes" --people [email protected],[email protected]
node cli/tp.mjs share-folder "Team/Notes" --private
node cli/tp.mjs rmdir "Team/Old" # its documents move up a level
node cli/tp.mjs rmdir "Team/Old" --with-documents # and deletes everything under it
密钥的获取顺序依次为 --key、TP_API_KEY 和该配置文件;TP_HOST 可将其指向其他部署。--json 会输出 API 的原始响应,Action 会读取该响应。文件夹可通过 tp folders 返回的 ID 或路径指定——路径从顶层开始,各级名称之间用 / 分隔,且不区分大小写。
作为 GitHub Action 使用
本仓库根目录下的 action.yml 可通过工作流发布 Markdown。如果未提供文件列表,它会选取拉取请求中发生变更的文件;如果设置了 pull-requests: write,它还会评论并附上链接,让审阅者打开渲染后的文档,而不是阅读满是星号的差异内容。
- uses: raudarlabs/transformpipe@main
with:
api-key: ${{ secrets.TP_API_KEY }}
examples/publish-markdown.yml 是一个可直接复制的完整工作流。输入项:api-key、files、share(link / people / none)、merge、name、replaces、comment、host;输出项:urls 和 documents。Checkout 需要设置 fetch-depth: 0,以便获取计算文件列表时所依据的基础提交。
每次推送都创建一份新文档是刻意设计的——旧评论中的链接会继续显示该次提交所表达的内容。如果链接不应公开,可使用 share: none 私密发布。replaces 是用于声明两次推送属于同一内容的不同版本,而非无关文档的唯一方式——传入上次推送的 ID,这两份文档便会在账户历史记录中关联显示,并附带差异对比。
在助手中
TransformPipe 是一个位于 /api/mcp 的 MCP 服务,因此可以将其添加到 Claude 作为连接器,或添加到 ChatGPT 作为 MCP 应用(Plugins → Add → Create MCP App,输入地址,然后登录),并在一个账户中转换、保存、分享、总结、版本管理和删除文档。无需粘贴密钥:
claude mcp add --transport http transformpipe https://transformpipe.com/api/mcp
首次调用会收到 401 响应,其中的 WWW-Authenticate 标头指向 /.well-known/oauth-protected-resource;客户端会跟随该地址前往 /.well-known/oauth-authorization-server,标识自身,然后将用户引导至 /api/oauth/authorize。客户端有两种标识自身的方式:使用 Client ID Metadata Document——一个指向 client_id 的 https URL,由 server/cimd.ts 获取、检查并缓存;或者进行注册(RFC 7591)并获取一个 id。Claude 更倾向于使用该文档,MCP 规范也是如此,因为每次连接时注册都会生成一个新的客户端;不支持该机制的客户端仍可使用注册方式。用户使用同一个 Google 账户登录,并在一个页面上批准一个具名客户端——这是一个 POST 请求,因此单靠链接无法授权——随后客户端会用自己的授权码向我们换取令牌。server/oauth.ts 是授权服务器,server/mcp.ts 是端点;src/lib/mcp-facts.ts 以带类型的形式保存工具名称,因此如果服务器上的工具改了名,记录该工具的页面就无法通过构建。
我们必须使用自己的授权服务器:协议禁止资源接受由其他任何人签发的令牌,因此不能把 Neon Auth 会话交给客户端。客户端获得的凭据可以访问文档和分享内容,但绝不能访问账户、登录信息或 API 密钥——这是原本应用于密钥的规则,现在也应用于最重要的凭据。连接会与密钥一同列在账户菜单中,可在那里撤销;撤销操作会一次性撤销某个客户端的所有令牌,因为只撤销访问令牌,客户端仍能在一分钟内再生成一个。
这些工具会在进程内调用此应用自身的 /api/v1,并转发调用者的凭据,因此对话和脚本会通过同一份代码获得相同的结果。有八个工具需要传入 confirm: true,否则会被拒绝:tp_save_document、tp_share_document 和 tp_share_folder 会发布链接或与他人分享;tp_save_document、tp_move_documents 和 tp_update_folder 会将内容放入共享文件夹;tp_update_document 会修改已分享的文档;tp_delete_document 和 tp_delete_folder 则始终需要确认。授权可以是只读的,此时写入工具会用一句话说明并拒绝操作。每个工具都带有全部四个 MCP 注释提示标记,且值均为布尔值;每个工具都会返回 MCP Apps 卡片,供支持绘制卡片的客户端使用——包括文档、文档列表、账户及其文件夹、单个文件夹、文档,以及带有确认按钮的拒绝调用。tp_help 和 tp_convert_markdown 是两个工具在 2026 年 10 月之前使用的名称,现在仍分别作为 tp_search_documentation 和 tp_convert_from_markdown 的别名响应,但不会列出。
npm run mcp:check 会针对本地服务器和真实数据库运行完整流程——发现、401、注册、未注册的 redirect_uri、PKCE、已失效的授权码、刷新令牌轮换、撤销及每个工具——并在结束后清理。需要浏览器的部分(Google 登录,然后按下 Connect)是唯一由它直接生成的内容,完全按照 /approve 的方式执行。
限制
| 每个账户 | 250 MB,5,000 篇文档 |
| 每次转换 | 10 MB — 约 150 万个单词。转换在浏览器中运行,因此这取决于机器性能,而不是平台限制 |
| 每篇保留的文档 | 4 MB。Vercel Function 会直接以 413 拒绝超过 4.5 MB 的请求或响应正文,应用无法获知,因此更大的文档既无法保存,也无法读回。若要提高此限制,就必须避免在请求中传递 Markdown:由客户端直接上传到 blob 存储,并将读取请求重定向到签名 URL。这两个数值及其原因都记录在 shared/limits.ts 中 |
| 每个调用方 | 每分钟 60 个请求,按密钥或会话计数;连接器调用一次工具计为一次 |
达到限制时会拒绝请求,而不是悄悄清除内容:这个应用以前会删除最早的文档以满足上限,这会在用户毫不知情的情况下毁掉他们选择保留的内容。拒绝时会说明应删除什么。server/limits.ts 保存着这些数值;速率计数器在 Postgres 中按每位调用方每分钟一行记录。在高并发下,这种方式不够精确,但就当前规模而言,比在数据库旁再运行缓存更合适。
滥用
共享页面会在我们的域名下承载他人的内容,因此会通过
script-src 'self' 和 frame-ancestors 'none' 提供 — 即使注入内容 somehow 通过了净化器,也无法运行;文档也无法被嵌入成他人页面。自 2026 年 10 月 5 日起使用 'self' 而不是
'none':页面会加载一个由我们提供的脚本 /share/diagrams.js
(src/share-diagrams.ts),用于绘制 Mermaid 图表,并将图表或宽表格显示为全屏。不会运行任何内联内容 — 文档中没有 <script>,也没有事件处理程序。每个共享页面都会链接到 /report/<token>,这是一个无需 JavaScript 的表单,举报会存入 m2h_report。
不会自动撤销任何内容。npm run reports 会列出尚未处理的举报,-- --revoke <id> 会使链接失效并将举报标记为已处理,-- --dismiss <id> 则只会将其标记为已处理。举报是陌生人对他人文档提出的指控,而两种误判 — 放任钓鱼页面存在、误杀无辜链接 — 都值得先由人来审阅。
博客和文档
/docs 是使用手册,/blog 则是 70 篇关于 Markdown 转换的文章;它们都是这个应用的页面,使用同一套设计系统构建。文章是 content/blog/ 中的 Markdown 文件,由转换器本身渲染。无需维护第二条同步流程,而且渲染器中的错误会先出现在我们自己的页面上,而不是先出现在别人的文档中。
content/keywords.md 是撰写文章时依据的关键词研究 — 按意图划分关键词组,每组都注明了针对该组撰写的文章,其中四组标记为尚无内容涵盖的空缺。文件中没有搜索量数据:我们无法从这里测量这些数据,而编造的数字会比得出它的猜测存在得更久。
单页应用对爬虫不可见 — 每条路由返回的都是相同的外壳和标题 — 因此 npm run build 会在最后执行 scripts/prerender.ts。它在 Node 中运行,使用服务端转换器渲染每篇文章,并为每条路由写入一个实际文件:dist/blog/<slug>/index.html,其中包含各自的标题、描述、规范链接和 BlogPosting 数据;首页包含 FAQPage,此外还会生成 sitemap.xml 和 robots.txt(共享文档通过 /s/ 下每个响应中的 X-Robots-Tag: noindex 标头排除在搜索结果之外,而不是依靠 robots.txt — 被 robots.txt 禁止访问页面的爬虫不会读取该页面上的 noindex)。Vercel 会先提供匹配的文件,再查看重写规则,因此这些页面是静态的;应用包仍会加载并接管页面,而应用内导航不会触及这些页面。
常见问题解答集中在 src/lib/faq.ts 的一个列表中,会显示在转换器的文件拖放区下方,也会再次显示在文档中。ArticleCard、SectionHeading 和 Faq 与设计系统的其余部分一起放在 src/ui/components 中,因为它们各自在三个地方使用;三份几乎相同的副本看起来就像三个不同的产品。
地址
/ 是转换器,/history 是列表(使用 ?filter=html|md|shared 指定当前显示的标签),/docs 是手册,/blog 和 /blog/<slug> 是文章,/s/<token> 是共享文档。它们直接从 location 读取,而不是通过路由器——只有三个路由,不需要路由器——因此重新加载后会回到原来的位置,后退按钮也能正常工作。每个路由都需要在 vercel.json 中重写到 index.html。
文档存放位置
Postgres 保存应用查询的内容——名称、大小、统计数据、共享令牌、收件人。无论有多少文档,这些行都保持精简。Markdown 源文件不会用于筛选或排序,只会整份读取,而且它是唯一会增长的部分,因此改存到 Vercel Blob 存储中:sources/<user>/<document>.md。
该存储是私有的。源文件由服务器使用存储令牌读取,其 URL 永远不会传到浏览器——共享文档由我们自己的路由提供,因此访问权限由该路由决定。
将存储连接到项目后,函数会获得 OIDC 身份和 BLOB_STORE_ID,而不是长期密钥,这是标准方式;如果存在 BLOB_READ_WRITE_TOKEN,仍会使用它。两者都没有时,源文件会像以前一样写入 markdown 列——因此没有存储访问权限的代码检出仍可正常工作,之前写入的行也仍能打开。
这些旧行不需要一次性全部迁移:每当在存储可访问的地方读取某行时,它就会迁移到存储中,读取方无论如何都能拿到文本。若想一次完成迁移,可运行 npm run blob:migrate;npm run blob:reconcile 会报告存储与数据库不一致的情况——孤立文件可以安全删除,文件缺失的行只会被报告。
存储连接必须包含 Development 环境(Storage → 存储 → Configure → Environments),否则本地拉取的 OIDC 令牌会被拒绝,npm run dev 会将源文件写入列中,而生产环境会将其写入存储。
渲染
解析、渲染器覆盖项和允许列表都位于 shared/ 中,并在两个环境中运行:浏览器渲染预览,函数渲染通过共享链接打开的页面。无论是谁请求,都是同一份文档。
只有清理器不同,因为两个运行时中只有一个有 DOM:src/lib/markdown.ts 使用 DOMPurify 清理浏览器自带的 DOM,server/render.ts 使用 xss,由它根据同一份允许列表自行解析 HTML。最初尝试过模拟 DOM,这是值得记录的陷阱——jsdom 直接导致 Node 运行时无法运行(CJS 依赖要求使用 ESM 模块),而更轻量的替代方案更糟:DOMPurify 报告成功,却原样返回输入内容,连 <script> 也不处理。sanitize-html 和 jsdom 一样失败,还导致登录功能不可用——因此现在改为在共享路由中延迟导入渲染器,这样渲染器出错只会影响该页面。标题 ID 使用 doc- 前缀,这样两个清理器都会保留它们;裸露的 id="title" 会导致 DOM 混淆,浏览器会将其剥除,而解析器不会。
GET /s/<token> 由函数提供,而不是由应用提供:
- 链接共享 — 生成的 HTML 使用
s-maxage=60, stale-while-revalidate=600,因此重复访问者由 CDN 响应,数据库每份文档每分钟大约只需读取一次。窗口时间刻意设得较短:撤销共享后必须在大约一分钟内生效。 - 定向共享 — 从不缓存。已登录且在名单中的读者会看到同一页面;其他人会被重定向到应用自己的
/open/<token>页面,该页面会提示他们登录。 ?download— 将同一份文档作为附件提供下载。
结构
api/index.ts Vercel entry point (wraps the Hono app)
shared/ the converter and document styles, used by both runtimes
server/ API: routes, Neon Auth proxy, Neon client, dev middleware
db/schema.sql m2h_document and its sharing tables
scripts/init-db.mjs applies the schema
scripts/auth-origin.mjs manages Neon Auth's trusted origins
server/mcp.ts the MCP endpoint and its tools
server/oauth.ts the authorization server, and the consent page
server/wellknown.ts the two discovery documents
scripts/prerender.ts a real HTML file per route, after the bundle is built
scripts/screenshots.mjs the documentation screenshots, captured from the running app
content/blog/ the articles; content/keywords.md is what they were written against
cli/tp.mjs the command line client
action.yml the GitHub Action (examples/ has a workflow to copy)
src/
App.tsx app shell and state
components/ header, user menu, dropzone, preview, stats
features/ ConverterPage, HistoryPage, DocsPage, BlogPage, ArticlePage
lib/ conversion, doc styles, auth, api client, history
ui/ design system (vendored)
许可证
MIT — 参见 LICENSE。品牌不包含在许可中:brand/mark.svg、brand/logo.svg 和名称归 Raudar Labs 所有,因此可以自由分叉代码,并使用你自己的标识。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。