关于 Sezzlee PDF
Sezzlee PDF 是面向本地 PDF 的只读 MCP 服务,可在指定目录沙箱中提取文本、按页读取并搜索内容。它默认不启用 OCR,可通过注入的组件按需处理扫描页,服务本身不访问网络。
主要功能
- 01列出并描述 PDF 文档
- 02按页提取文本并以 Markdown 返回
- 03搜索文本并返回页码和上下文
- 04通过可插拔组件按需执行 OCR
- 05使用游标分页并缓存提取结果
适用场景
- 01读取本地 PDF 的指定页面
- 02在文档中定位关键词及其上下文
- 03为扫描页面配置 OCR 识别
- 04在受限目录中处理文档
MCP 相关
它向 MCP 客户端提供列出、描述、读取和搜索本地 PDF 的工具。访问受指定目录限制;OCR 默认关闭,启用后由外部注入的组件处理,服务自身不会访问网络。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
@sezzlee/pdf-mcp
用于本地 PDF 文档的只读、沙盒化 MCP 服务。它基于 @sezzlee/file-core 构建,由后者提供沙盒路径解析、响应预算、错误封装、游标和文档缓存。文本提取使用 @firecrawl/pdf-inspector。此服务绝不会连接网络:OCR 为可选功能;启用后,连接决策完全由提供这两个注入端口的一方决定。
快速开始
{
"mcpServers": {
"pdf": {
"command": "npx",
"args": ["-y", "@sezzlee/pdf-mcp", "/path/to/documents"]
}
}
}
要启用 OCR,请将 --ocr 指向一个默认导出为 OcrBinding 的模块(参见配置和 OCR):
{
"mcpServers": {
"pdf": {
"command": "npx",
"args": [
"-y",
"@sezzlee/pdf-mcp",
"/path/to/documents",
"--ocr",
"/path/to/ollama-binding.js"
]
}
}
}
工具
| 工具 | 功能 | 关键参数 |
|---|---|---|
list_documents | 列出根目录下可读取的 PDF 候选文件;不会打开文件 | subdirectory、pattern、maxResults |
describe_document | 页面数量、文档类型、每页是否需要 OCR、服务功能 | filePath |
read_pages | 将选定页面读取为 Markdown,页码从 1 开始 | filePath、pages、maxPages、ocr、cursor |
find_in_document | 在提取的文本中进行字面搜索,并返回页码和匹配上下文 | filePath、query、matchMode、caseSensitive、ocr、cursor |
如果已知文件路径,describe_document 是可选的:read_pages 和
find_in_document 只需提供 filePath 即可使用。
配置
CLI(sezzlee-pdf <pdf-source-root> [--ocr <module>]):
| 参数 | 默认值 | 含义 |
|---|---|---|
<pdf-source-root> | 必填 | 沙盒根目录;每个可读取路径都会解析并检查是否位于该目录下。 |
--ocr <module> | 无 | 模块的路径或包说明符,该模块的默认导出为 { rasterizer, provider }。未提供时不启用 OCR:read_pages/find_in_document 会拒绝 ocr: true,且 describe_document 会报告 capabilities.ocr: false。 |
程序化配置(createPdfMcpServer(root, options)):
| 选项 | 默认值 | 含义 |
|---|---|---|
documentCacheSize | 2 | 同时保留在内存中的已提取文档数量,范围为 1–16。这是预算,而非保护措施:每个缓存文档都会将所有页面的 Markdown 保存在堆内存中。 |
maxConcurrentListings | 4 | 拒绝请求前允许并发执行的 list_documents 调用数。 |
maxConcurrentExtractions | 2 | 拒绝请求前允许并发执行的 PDF 提取数。 |
ocr | 无 | 一个 OcrBinding({ rasterizer, provider, dpi?, maxPagesPerCall?, timeoutMs? })。此包不依赖这两个端口。 |
OCR
服务器不会执行 OCR;它只提供支持。服务器会注入两个端口——栅格库和模型客户端都不是此软件包的依赖项。
interface PageRasterizer {
render(job: {
bytes: Buffer;
pages: readonly number[];
dpi: number;
signal?: AbortSignal;
}): Promise<readonly RenderedPage[]>; // { page, image, mediaType }
}
interface OcrProvider {
readonly name: string;
recognize(job: {
pages: readonly RenderedPage[];
signal?: AbortSignal;
}): Promise<readonly RecognizedPage[]>; // { page, markdown, confidence? }
}
服务器只会执行以下操作:对文本层无法解答的页面进行 render,识别生成的图像,用结果替换该页面,并将其标记为 source: "ocr"。
程序化绑定:
createPdfMcpServer(root, {
ocr: { rasterizer: pdfjsRasterizer, provider: ollamaProvider, dpi: 200 },
});
CLI 绑定——加载默认导出与 OcrBinding 匹配的模块,并使用 --ocr <module>(有关可用示例,请参阅此软件包中的 examples/ollama-binding.ts,该示例基于 @sezzlee/pdf-raster-pdfjs 和 @sezzlee/ocr-ollama 构建):
import { createPdfjsRasterizer } from "@sezzlee/pdf-raster-pdfjs";
import { createOllamaOcrProvider } from "@sezzlee/ocr-ollama";
import type { OcrBinding } from "@sezzlee/pdf-mcp";
const binding: OcrBinding = {
rasterizer: createPdfjsRasterizer(),
provider: createOllamaOcrProvider({
baseUrl: process.env["SEZZLEE_PDF_OCR_URL"] ?? "http://127.0.0.1:11434",
model: process.env["SEZZLEE_PDF_OCR_MODEL"] ?? "deepseek-ocr:3b",
}),
dpi: 200,
};
export default binding;
OCR 规则
- 设置
ocr: true却未绑定提供程序时会报错(ocr_unavailable),绝不会静默回退到文本层。否则,请求 OCR 的 Agent 可能会把“没人转录此文档”误认为“没有匹配项”。describe_document→capabilities.ocr会报告是否已绑定提供程序。 - 空转录内容不算作答案。 如果提供程序返回空文本,该页面仍会保持
needsOcr——“模型没有返回内容”不等于“此页面为空白”。 - 未请求的页面会被忽略。 端口可能会重排、跳过或虚构页面;结果会与请求的页码匹配,因此提供程序无法覆盖文本层已信任的页面。
- 数据会离开进程。 绑定提供程序意味着这些页面的像素会被发送到其他地方。因此,OCR 默认关闭,且每次调用都必须显式请求。
- 每次调用不会重复转录同一页面。 转录内容按文档指纹和页码缓存。缓存不是进度日志:搜索只会将游标之后的页面发送给 OCR,并在游标中延续覆盖范围,因此即使是大型文档,最终也能通过缓存遍历至末尾。
- 作业过期不会释放其槽位。 引擎和注入的端口都无法取消已经开始的工作;槽位会一直被占用,直到工作实际完成。对于始终不返回的提供程序,只能通过重启服务器来恢复——通过计时器回收槽位只会延后触及限制的时间。
规则
- **公共接口中的所有页码均从 1 开始。**底层库在同一个结果对象中返回的字段,有些从 0 开始,有些从 1 开始。所有转换都在
src/engine/中进行,并且 lint 禁止在该目录之外导入@firecrawl/pdf-inspector——第二个导入位置会再次引入这个问题(在 1.23.0 中测得:PageMarkdownResult.page从 0 开始,同级字段pagesNeedingOcr从 1 开始)。 - **页码选择在传入库之前就会进行验证。**否则,超出范围的索引会以标记为
needsOcr的虚假页面返回;负索引则会通过 u32 回绕。两种情况都会让 Agent 误以为这是真实页面。 describe_document绝不会根据分类器构建 OCR 页面列表。classifyPdf会将包含单个扫描页面的文档中的每一页都标记出来。页面列表来自逐页提取结果。classificationConfidence表示分类器对自身判定的置信度,而不是对任何提取文本准确性的置信度。- **无法读取的页面绝不会被呈现为空白页。**扫描页面会返回
needsOcr:true;只有引擎确认并判定确实为空白的页面,empty才为true。 - **每个响应都会报告搜索覆盖情况。**只要有页面需要 OCR,
coverageComplete就为false;匹配列表为空并不能证明整个文档中都没有该查询内容。 - **文档会完整提取一次,响应则采用分页形式。**测量结果显示,只请求单页只能将提取成本降低三分之一,而不是 200 倍——固定成本占主导。游标标识取决于文档内容和 OCR 模式,而非页面大小。显式的
pages选择会在游标中保留:游标会继续处理该选择中剩余的页面,不会延伸到未选页面。若游标从某页中间恢复,它会携带该页的文本指纹;如果转录后文本发生变化,响应会返回stale_cursor,而不是静默跳过内容。 - 服务绝不会访问网络。
fetch、node:http、node:net及其同类项在src/中都被 lint 禁止,并且每一层的 override 中也重复设置了此禁令——后续的 oxlint override 会替换no-restricted-imports,而不是与之合并,因此仅在顶层设置一次禁令无法覆盖所有位置。 - **平台支持范围有限。**引擎不提供
darwin-x64二进制文件,且其 wasm 回退版本尚未发布。在不受支持的主机上,服务会在启动时停止,并输出一行提示信息(退出代码为 3)。
开发
pnpm turbo run build --filter=@sezzlee/pdf-mcp
node packages/servers/pdf-mcp/dist/cli.js /path/to/documents
测试:pnpm turbo run test --filter=@sezzlee/pdf-mcp(Vitest)。实时 Ollama 测试套件(test/ocr-live.spec.ts)只有在设置了 SEZZLEE_PDF_OCR_URL 时才会运行;它还会读取 SEZZLEE_PDF_OCR_MODEL(默认值为 deepseek-ocr:3b)。test/ocr.spec.ts 中的其他所有测试都使用模拟端口运行;实时测试套件仅用于验证两个真实适配器能够组合使用。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。