关于 MintPDF
MintPDF 通过 API 或托管服务将 HTML、Markdown 和公开网页转换为样式化 PDF,适合需要生成文档的 Agent 和开发者。它支持分页控制,也可依据 MIT 许可自行部署。
主要功能
- 01将 HTML 和 Markdown 转为 PDF
- 02渲染公开 URL
- 03分页与重复表格标题
- 04页面尺寸、边距和方向
- 05页眉、页脚与页码选项
- 06自行托管部署
适用场景
- 01将 Markdown 简报生成 PDF
- 02生成带页码的 HTML 发票
- 03将公开网页转换为 PDF
- 04在私有部署中运行文档渲染
MCP 相关
通过无状态的 streamable-HTTP MCP 端点提供 `generate_pdf`(HTML 或 Markdown)和 `pdf_from_url`(公开网页)。两者均返回临时下载链接。
配置
远程服务无需本地安装。选择你使用的客户端,复制配置或使用一键安装。
https://mintpdf.dev/mcpclaude mcp add --transport http trendtweekers-mintpdf https://mintpdf.dev/mcp{"mcpServers":{"trendtweekers-mintpdf":{"type":"http","url":"https://mintpdf.dev/mcp"}}}在 Cursor 中打开{"servers":{"trendtweekers-mintpdf":{"type":"http","url":"https://mintpdf.dev/mcp"}}}在 VS Code 中打开{"mcpServers":{"trendtweekers-mintpdf":{"type":"http","url":"https://mintpdf.dev/mcp"}}}{"mcpServers":{"trendtweekers-mintpdf":{"serverUrl":"https://mintpdf.dev/mcp"}}}命令在终端中运行;JSON 粘贴到对应客户端的 MCP 配置后重启。需要授权的服务会在首次连接时提示登录。
工具
2| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| html | string | 否 | 要渲染的完整 HTML 文档或片段 |
| format | enum | 否 | 纸张尺寸,默认为 A4A4 | Letter | Legal | A3 | A5 |
| margin | string | 否 | 四周的页边距,例如 "18mm" |
| markdown | string | 否 | 使用默认样式表渲染的 Markdown 内容 |
| landscape | boolean | 否 | 横向页面方向,默认为 false |
| footerText | string | 否 | 每页显示的小号页脚文本 |
| headerText | string | 否 | 每页上的小型页眉文本 |
| pageNumbers | boolean | 否 | 在页脚显示页码 |
详细介绍
MintPDF
HTML 和 Markdown → PDF,提供 REST API 和 MCP 服务。
没有模板编辑器。没有模板 ID。没有控制面板。试用无需注册。
发送 HTML 或 Markdown,即可获取 PDF。底层使用 Chromium,并已配置好打印 CSS,因此表格不会跨页拆分、表头会重复,Markdown 的呈现效果也更像文档,而不是文本文件。
- 分页是核心。
break-inside、重复的thead、孤行和寡行控制,以及能真正继承你的样式的页眉和页脚。参见工作原理。 - 原生支持 MCP — 通过可流式传输的 HTTP 提供
generate_pdf和pdf_from_url,让 Agent 能在对话过程中生成文档。 - 不保留文档 — 渲染的文件会在一小时后删除,其内容绝不会被记录。密钥、电子邮件和使用量计数当然会被保存。参见安全。
- 自行运行 — 采用 MIT 许可证,并提供已发布的镜像。提供托管服务,是为了让你无需自行运行 Chromium,而不是因为渲染器是机密。
快速入门
无需注册,也无需密钥:
curl -X POST https://mintpdf.dev/v1/pdf \
-H "Content-Type: application/json" \
-d '{"markdown":"# Invoice #42\n\n| Item | Price |\n|---|---|\n| Widget | $9.00 |","pageNumbers":true}' \
--output invoice.pdf
每天渲染次数超过 3 次?免费密钥(仅需电子邮件,无需银行卡)可将额度提高到每月 100 次:
curl -X POST https://mintpdf.dev/v1/keys \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]"}'
# → {"key":"pm_…","daily_limit":100} # 100 renders per month
然后在请求中发送 Authorization: Bearer pm_…。
从 Claude(或任何 MCP 客户端)使用
{
"mcpServers": {
"mintpdf": {
"command": "npx",
"args": ["-y", "mintpdf-mcp"]
}
}
}
想直接使用托管端点?改用 mcp-remote:
{
"mcpServers": {
"mintpdf": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://mintpdf.dev/mcp"]
}
}
}
重启客户端,然后直接提出请求:
“把这段讨论总结成一页简报,添加页码,并生成 PDF。”
| 工具 | 输入 | 返回 |
|---|---|---|
generate_pdf | html 或 markdown,以及选项 | 下载 URL,有效期 1 小时 |
pdf_from_url | url(公开的 http/https 地址),以及选项 | 下载 URL,有效期 1 小时 |
API
POST /v1/pdf
请求正文必须且只能包含一个来源,以及选项:
| 字段 | 类型 | 说明 |
|---|---|---|
html | string | 完整文档或片段 |
markdown | string | 使用默认样式表进行渲染 |
url | string | 要渲染的公开页面。私有或内部地址会被阻止 |
format | string | A4(默认)、Letter、Legal、A3、A5 |
landscape | boolean | 默认值为 false |
margin | string | 四边边距,例如 "18mm" |
headerText / footerText | string | 显示在每页上的小字 |
pageNumbers | boolean | 在页脚添加 3 / 7 |
output | "pdf" | "url" | 默认返回 PDF 字节;"url" 返回包含链接的 JSON |
POST /v1/keys
{"email":"[email protected]"} → 获取免费密钥。无需银行卡,也无需反复验证。
POST /mcp
MCP 可流式传输的 HTTP 端点,无状态。功能与 REST API 相同。
Postman
postman/ 中提供了一个现成的集合,涵盖所有端点和选项。通过链接导入:
https://raw.githubusercontent.com/TrendTweekers/mintpdf/main/postman/mintpdf.postman_collection.json
第一个请求完全无需密钥,获取免费密钥时会自动将其保存到集合变量中,因此集合中的其余请求随后即可直接使用。
限额
| 套餐 | 限额 | 价格 |
|---|---|---|
| 匿名 | 每个 IP 每天 3 次渲染 | 免费,无需注册 |
| 免费密钥 | 每月 100 次渲染 | 免费,仅需电子邮件 |
| Solo | 每月 3,000 次渲染 | $19/月 |
| Team | 每月 12,000 次渲染 | $49/月 |
| Scale | 每月 50,000 次渲染 | $129/月 |
自托管
MintPDF 采用 MIT 许可证;如果你更愿意,也可以运行自己的实例。
npm install
npm run build
npm start # http://localhost:3000
node dist/smoke.js # end-to-end render check
或者拉取已发布的镜像,其中已打包 Chromium 和字体:
docker run -p 3000:3000 \
-e BASE_URL=http://localhost:3000 \
-e DATA_DIR=/data -v mintpdf-data:/data \
ghcr.io/trendtweekers/mintpdf:latest
这样,你在自己的机器上就能使用相同的 API,没有限制,也不会有任何数据离开本机:
curl -X POST http://localhost:3000/v1/pdf \
-H "Content-Type: application/json" \
-d '{"markdown":"# Local","pageNumbers":true}' --output local.pdf
CI 会在每次变更时构建并发布镜像,并在打标签前通过启动容器并从中渲染真实 PDF 来进行冒烟测试。标签为 latest 和短提交 SHA。你也可以自行构建:
docker build -t mintpdf .
docker run -p 3000:3000 -e BASE_URL=http://localhost:3000 mintpdf
环境变量:BASE_URL(用于下载链接)、DATA_DIR(默认为 /tmp/mintpdf;挂载卷以持久保存密钥)、ANON_DAILY_LIMIT、FREE_MONTHLY_LIMIT、SOLO_MONTHLY_LIMIT、TEAM_MONTHLY_LIMIT、SCALE_MONTHLY_LIMIT、OVERAGE_FACTOR、RENDER_CONCURRENCY、RENDER_QUEUE、RENDER_QUEUE_WAIT_MS。
负载与准入控制
每次渲染都会占用一个 Chromium 标签页,因此内存会远早于 CPU 或成本限制并发量。如果不加限制,流量激增时,每个请求都会打开一个标签页,直到 OOM 回收器终止容器,导致所有请求失败,包括那些几乎已完成的请求。在此环境中的实测结果:30 个并发渲染在没有限制的情况下留下了 30 个孤立的 Chrome 进程,机器也无法使用。
RENDER_CONCURRENCY 个渲染任务可同时运行,另有 RENDER_QUEUE 个任务可以等待;超出部分会立即被拒绝,返回 503 和 Retry-After,而不是让请求不断堆积。与其让所有人都等待到超时,不如在一秒内拒绝部分调用。
代码默认值较为保守(3 和 20)。在一个小型 Railway 实例上测得,设置为 10 和 70 时:
| 突发请求数 | 已处理 | 已拒绝 | 中位数 | 总耗时 | 实例状态 |
|---|---|---|---|---|---|
| 45 | 45 | 0 | 1.5s | 2.3s | 健康 |
| 120 | 81 | 39 | 3.0s | 4.0s | 健康 |
| 250 | 80 | 170 | 3.4s | 4.4s | 健康,主页响应时间为 0.24s |
持续负载下,每秒大约可处理 18 次渲染,被拒绝的请求会在 2.7s 内得到响应。只有在自己的实例规格上进行实测后,才提高这些数值,切勿凭空猜测。
如果你需要更完整的自托管 PDF 工具链(Office 格式、合并、拆分),Gotenberg 是很棒的选择,功能也比这里更丰富。
安全
本服务会渲染任何人提供的 HTML 和 URL,因此需要关注的是这些内容能访问什么。
提交的 HTML 会执行 JavaScript。 这是必需的:页面会渲染 Mermaid 图表和 KaTeX 数学公式。因此,应将渲染器视为在运行不受信任的代码,这也是下文的网络限制比静态转换器更重要的原因。
SSRF 在两层防护下会被阻止。
- 系统会解析提交的
url,将协议限制为http/https,并解析其地址。如果解析出的地址中 有任何一个 是私有地址,就会在启动浏览器之前拒绝请求并返回 400。 - Chromium 发出的每个请求都会被单独拦截,并在请求发出时再次解析目标地址;如果地址是私有地址,就会阻止请求。这不仅适用于你请求的 URL,也涵盖嵌入的图片、样式表、字体、重定向,以及提交的 JavaScript 中的
fetch()。
第二层会解析地址,而不是信任主机名,并且只缓存拒绝结果,从不缓存通过结果:缓存“此主机是公共地址”会重新打开这项检查旨在堵住的漏洞。无法解析的名称会被默认拒绝。
准确来说,这些措施的效果有其边界:DNS 重绑定攻击无法再等待缓存的通过结果过期,因此必须在每次请求中与 Chromium 自身的解析抢先完成。这比固定的时间窗口更难利用,但只是缩小了竞速范围,并未彻底关闭漏洞。要彻底消除该风险,就必须在套接字层固定解析出的地址,目前尚未实现。
会被阻止的地址包括:回环地址、0.0.0.0、RFC1918、CGNAT(100.64/10)、链路本地地址和云元数据地址(169.254.169.254);IPv6 回环地址、未指定地址、链路本地地址、站点本地地址、唯一本地地址、组播地址、NAT64 和 Teredo;两种写法的 IPv4 映射和 IPv4 兼容地址,因此 ::ffff:10.0.0.1 和 ::ffff:a00:1 是同一地址,都会被拒绝;localhost/.local/.internal 名称;解析到私有地址的任何公共主机名;以及除 http、https、data 和 blob 之外的所有协议。
地址会根据其字节进行判断,而不是匹配文本,因为同一地址有多种写法,文本匹配只能识别其中一种,遗漏其余写法。
我们为此编写了专门的测试套件,应该实际运行,而不是只相信它:
BASE=https://mintpdf.dev node scratchpad/ssrf_suite.mjs
它会检查上述绕过方式,也会检查常规渲染仍然正常,因为连网页字体也一起拦截的防护措施只是另一个 bug,并非修复。
IPv6 解析器还有一组专门针对恶意字面量的测试,因为在这类代码中,失效情形是无效字符串悄然变成有效地址:
node scratchpad/ipv6_table_test.mjs
下载链接使用 crypto.randomUUID() 标识符,且未经身份验证:在文件存在的一小时内,任何拥有链接的人都能获取文件。这是有意为之,因此可以通过电子邮件发送链接,或将链接交给浏览器;但这也意味着链接本身就是秘密。
日志记录。 会记录请求元数据(方法、路径、状态、耗时)。绝不记录请求正文,因此你发送的 HTML 和 Markdown 除临时文件外不会写入任何地方。分析表会存储事件类型、路径、来源页、国家/地区以及使用每日盐值生成的 IP 哈希值。不会存储文档内容,也无法据此还原文档。
局限
使用前值得了解。
- 文件会在一小时后删除。 没有文档库,也无法稍后再次获取已渲染的文件。生成、使用、完成。如果需要永久保存,请在你自己的设备上保存文件内容。
- 不支持 Office 格式、合并或拆分。 此工具仅转换 HTML、Markdown 和网页,不支持其他格式。如果你要可自托管且需要更多功能的方案,Gotenberg 更成熟,支持的功能也多得多。
- 仅支持一个实例。 密钥和配额存储在挂载卷中的 SQLite 数据库里,因此多个副本共用一个卷无法正常工作。要进行水平扩展,必须先接入真正的数据库。
- 渲染请求受准入控制。 超出容量时,API 会返回带有
Retry-After的503,而不是无限排队。实测表现请参见上表。 - 写作时仅上线两天,尚无付费用户。
工作原理
使用 TypeScript、Fastify 和 Puppeteer,通过一个共享的 Chromium 为每个请求打开一个页面。node:sqlite
用于存储密钥、配额和事件,因此无需构建原生依赖。
实际花费精力的部分并不起眼:
- 打印 CSS。 对表格、行、列表项、代码块、引用和图表使用
break-inside: avoid;设置thead { display: table-header-group }以便重复显示表头;使用orphans/widows;对标题使用break-after: avoid,避免标题孤零零地留在页面底部。 - 页眉和页脚模板与页面是分开的文档:它们会忽略页面 CSS,如果样式未内联,字体大小几乎为零;并且位于内容边距之外。
- 准入控制,因为每个并发请求对应一个 Chromium 标签页,容器很容易因此耗尽内存。
- 网络隔离,用于运行不受信任 JavaScript 的渲染器。请参见“安全性”。
许可证
MIT——请参见 LICENSE。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。