Sezzlee PDF について
Sezzlee PDFは、指定フォルダー内のローカルPDFからテキストを抽出・検索する読み取り専用MCPサーバーです。OCRは既定で無効で、スキャンページには注入したコンポーネントを使えます。サーバー自体はネットワークに接続しません。
主な機能
- 01PDF文書の一覧表示と説明
- 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 は任意で有効にできます。有効にした場合、接続するかどうかの判断は、2 つの注入されたポートを提供する側に完全に委ねられます。
クイックスタート
{
"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を実行しませんが、OCRをサポートしています。2つのポートが注入されます。ラスタライブラリもモデルクライアントも、このパッケージの依存関係ではありません。
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し、生成された画像をrecognizeして、結果をそのページの内容に置き換え、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を要求したエージェントが「このドキュメントは誰も文字起こししていない」ことを「一致するものがない」と誤認するおそれがあります。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としてラップされます。どちらの場合も、エージェントには実在するページのように見えてしまいます。 describe_documentは、分類器からOCR対象ページのリストを作成しません。classifyPdfは、1ページでもスキャンページを含む文書の場合、すべてのページにマークを付けます。リストはページごとの抽出結果から作成します。classificationConfidenceは、分類器自身の判定に対する信頼度であり、抽出テキストの正確性に対する信頼度ではありません。- 読み取れないページを空ページとして扱うことはありません。 スキャンページでは
needsOcr: trueが返されます。emptyがtrueになるのは、エンジンが信頼できると判断し、実際に空白だと確認したページだけです。 - すべてのレスポンスで検索対象範囲が報告されます。 OCRが必要なページが1つでもある場合、
coverageCompleteはfalseです。検索結果が空でも、クエリが文書全体に存在しない証拠にはなりません。 - 文書全体を一度だけ抽出し、レスポンスをページ分割します。 計測の結果、単一ページをリクエストしても抽出コストは200分の1ではなく、3分の1に減るだけでした。固定コストが大部分を占めるためです。カーソルの識別情報は、ページサイズではなく文書の内容とOCRモードに依存します。明示的な
pages選択はカーソル使用時も保持されます。選択範囲の残りのページを引き継ぎ、未選択のページに移ることはありません。ページの途中から再開するカーソルには、そのページのテキストフィンガープリントが含まれます。文字起こし後にテキストが変更されている場合、コンテンツを黙ってスキップする代わりに、レスポンスとしてstale_cursorを返します。 - サーバーがネットワークにアクセスすることはありません。
fetch、node:http、node:netとその関連項目はsrc/内でlintにより禁止されており、この禁止設定は各レイヤーのoverrideでも繰り返されています。後続のoxlint overrideはno-restricted-importsを統合せずに置き換えるため、トップレベルでの禁止設定だけではすべての箇所に適用されないからです。 - プラットフォームのサポート対象は限定されています。 エンジンには
darwin-x64バイナリが含まれておらず、wasmフォールバックも公開されていません。サポート対象外のホストでは、起動時にサーバーが1行のメッセージを表示して停止します(終了コード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内のその他のテストはすべてフェイクポートを使用します。実テストスイートで確認できるのは、2つの実アダプターが連携することだけです。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。