PDF Processor Server

PDF Processor Server

An MCP server for PDF utilities including text extraction, metadata, merge/split/rotate, and PDF-to-image conversion.

Category
访问服务器

README

FastMCP PDF Processing Server

An MCP server built with FastMCP (STDIO transport) offering PDF utilities: text extraction, metadata, merge/split/rotate, and PDF↔image conversion.

SPANISH VERSION [README.es.md]

Quick Start (Windows PowerShell)

python -m venv .venv
\.\.venv\Scripts\Activate.ps1
pip install -r requirements.txt
Copy-Item .env.example .env
python -m fastmcp_pdf_server

If installed as a package, you may also run:

fastmcp-pdf-server

Quick Start (Linux/macOS)

python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env
python -m fastmcp_pdf_server

MCP Integration

  • Transport: STDIO. Do not print to stdout/stderr; logs go to file.
  • Server name/version: from config (server_name, server_version).
  • Tools are registered using @app.tool() and return structured outputs with a meta block containing operation_id and execution_ms.

Claude Desktop config example

Add to claude_desktop_config.json (Update with your own File System Path):

{
  "mcpServers": {
     "pdf-processor-server": {
      "command": "D:\\Github Projects\\mcp_pdf_server\\.venv\\Scripts\\python.exe",
      "args": [
        "-m",
        "fastmcp_pdf_server"
      ],
      "env": {
        "MAX_FILE_SIZE_MB": "50",
        "TEMP_DIR": "D:\\Github Projects\\mcp_pdf_server\\temp_files",
        "LOG_LEVEL": "DEBUG",
        "LOG_FILE_PATH": "D:\\Github Projects\\mcp_pdf_server\\logs\\fastmcp_pdf_server.log",
        "SERVER_NAME": "pdf-processor-server",
        "SERVER_VERSION": "1.0.0",
        "PATH": "%PATH%;C:\\poppler-25.07.0\\Library\\bin"
      }
    }
}

Note: If you update dependencies (e.g., we added requests for URL uploads), reinstall with:

pip install -r requirements.txt

Claude Desktop config example (Linux)

Add to claude_desktop_config.json:

{
  "mcpServers": {
    "pdf-processor-server": {
      "command": "python3",
      "args": [
        "-m",
        "fastmcp_pdf_server"
      ],
      "env": {
        "MAX_FILE_SIZE_MB": "50",
        "TEMP_DIR": "/home/you/dev/mcp_pdf_server/temp_files",
        "LOG_LEVEL": "DEBUG"
      }
    }
  }
}

Claude Desktop config example (macOS)

Add to claude_desktop_config.json:

{
  "mcpServers": {
    "pdf-processor-server": {
      "command": "python3",
      "args": [
        "-m",
        "fastmcp_pdf_server"
      ],
      "env": {
        "MAX_FILE_SIZE_MB": "50",
        "TEMP_DIR": "/Users/you/dev/mcp_pdf_server/temp_files",
        "LOG_LEVEL": "DEBUG"
      }
    }
  }
}

Programmatic usage (Python)

import asyncio
from fastmcp import Client

async def main():
    client = Client(command="python", args=["-m", "fastmcp_pdf_server"])
    await client.start()
    try:
        info = await client.call_tool("server_info")
        print(info)
    finally:
        await client.close()

asyncio.run(main())

Exposed Tools (API)

All tools return structured data; many responses include meta.operation_id and meta.execution_ms. Some tools return lists (arrays). These are marked with FastMCP's x-fastmcp-wrap-result, so clients receive { "result": [...] } at the RPC layer.

MCP Tools Reference

Each tool lists: purpose, inputs, outputs, behavior, examples, and notes about errors and usage.


Utilities / Server

  • server_info()

    • Purpose: Return basic server info and configuration snapshot (non-secret).
    • Inputs: None
    • Returns: dict with keys:
      • name (str): server name from settings
      • version (str): server version from settings
      • max_file_size_mb (int): maximum configured file size in megabytes
      • temp_dir (str): absolute path to temporary files directory
      • log_file (str): absolute path to the log file
      • meta (dict): operation metadata: operation_id (hex), execution_ms (int)
    • Errors: none expected; if configuration missing, underlying access may raise exceptions.
    • Example:
      • Call: { "name": "server_info" }
      • Response: { "name": "mcp-pdf", "version": "1.0.0", "meta": { ... } }
  • list_temp_resources(content_type: Optional[str] = None, max_items: Optional[int] = 100) -> list[dict]

    • Purpose: List files currently in the server temp directory with optional filtering by content type.
    • Inputs:
      • content_type (optional str): MIME filter; supported examples: application/pdf, image/png, image/jpeg.
      • max_items (optional int): maximum number of entries to return (default 100). If set to null or 0, defaults to 100.
    • Returns: list of resource dicts (each):
      • path (str): absolute path to the temp file
      • size (int): size in bytes
      • created (str): creation timestamp (ISO or file-manager-specific format)
      • content_type (str): MIME type of resource
      • filename (str): filename only
      • extension (str): lowercased file extension (e.g. .pdf)
      • directory (str): parent directory of the file
    • Behavior: Cleans up expired temp files before listing. Result list is sliced to max_items.
    • Errors: Raises ValueError if internal listing fails.
    • Example call:
      • { "name": "list_temp_resources", "arguments": { "content_type": "application/pdf" } }
  • get_pdf_info(file_path: str) -> dict

    • Purpose: Read a PDF headers and basic info without extracting pages/text.
    • Inputs:
      • file_path (str): path to an existing file on disk (absolute or relative). Must be accessible to the server.
    • Returns: dict:
      • pages (int): number of pages
      • size (int): file size in bytes
      • version (str|None): PDF header/version info (if available)
      • encrypted (bool): whether the PDF is encrypted
      • meta (dict): operation_id, execution_ms
    • Errors:
      • Raises ValueError if file not found.
      • May raise other errors if the file is not a PDF or is corrupted.
  • get_resource_base64(file_path: str) -> dict

    • Purpose: Return base64-encoded contents of a file inside the server temp directory.
    • Inputs:
      • file_path (str): path; must be inside the configured temp directory. The function enforces this.
    • Returns: dict:
      • path (str): resolved path inside temp
      • base64 (str): Base64-encoded content of the file
      • meta (dict): operation metadata
    • Errors:
      • Raises ValueError if the path is outside temp or file missing.
    • Notes: Use this to fetch content for download via MCP where direct file transfers aren't available.

Uploads

  • upload_file(file: Any, filename: Optional[str] = None) -> dict

    • Purpose: Persist an uploaded file into the server temp directory.
    • Inputs:
      • file (Any): Accepts:
        • a full path string to a local file
        • a short filename that refers to a file already stored in temp
        • bytes or file-like object
        • dicts containing base64 and filename (will be saved to temp)
      • filename (Optional[str]): optional filename hint used when saving raw bytes.
    • Returns: dict:
      • path (str): absolute path to the saved file
      • filename (str): saved filename
      • directory (str): directory containing the file
      • meta (dict): operation metadata
    • Errors:
      • Raises ValueError with a descriptive message on failure (network, decoding, IO).
    • Example:
      • To upload base64: call upload_file with file = { "base64": "<...>", "filename": "my.pdf" }.
  • upload_file_base64(base64: str, filename: str) -> dict

    • Purpose: Upload raw Base64 content and persist to temp storage.
    • Inputs:
      • base64 (str): Base64 string
      • filename (str): filename to use when saving
    • Returns: dict:
      • path, filename, directory, size (int), meta
    • Errors: Raises ValueError on decoding or write errors.
  • upload_file_url(url: str, filename: Optional[str] = None) -> dict

    • Purpose: Download a remote file (HTTP/HTTPS) and save to temp storage.
    • Inputs:
      • url (str): direct URL to file
      • filename (Optional[str]): optional override filename
    • Returns: dict with path, filename, directory, meta.
    • Notes: Requires requests package to be available in the environment.

Text Extraction

  • extract_text(file: Any, encoding: Optional[str] = "utf-8") -> dict

    • Purpose: Extract all text from a PDF and return summary metrics.
    • Inputs:
      • file (Any): same resolver rules as upload_file (path, temp filename, bytes, base64 dict).
      • encoding (str|None): encoding used when returning text (default utf-8).
    • Returns: dict:
      • text (str): full extracted text
      • page_count (int): number of pages processed
      • char_count (int): number of characters in text
      • meta (dict): includes resolved_path pointing to saved temp file
    • Errors:
      • Raises ValueError with helpful hint explaining how to provide the file if extraction fails.
    • Example usage:
      • Upload a file with upload_file, then call extract_text with the returned path.
  • extract_text_by_page(file: Any, pages: Optional[List[int]] = None, page_range: Optional[str] = None, encoding: Optional[str] = "utf-8") -> list[dict]

    • Purpose: Extract text from specific pages or a page range.
    • Inputs:
      • file (Any): resolver rules as above
      • pages (Optional[List[int]]): list of 1-based page indices to extract (e.g., [1,3,5]).
      • page_range (Optional[str]): range expression like "1-3,5" (parser in utils.parsers will be used).
      • encoding (Optional[str]): text encoding
    • Returns: list of page result dicts; each dict typically contains:
      • page_number (int)
      • text (str)
      • char_count (int)
    • Behavior: If both pages and page_range are provided, pages takes precedence. The tool returns a list directly (framework wraps list results).
    • Errors: Raises ValueError on invalid pages or extraction failures.
  • extract_metadata(file: Any) -> dict

    • Purpose: Extract detailed PDF metadata (author, title, producer, creation/mod dates, custom metadata, etc.).
    • Inputs: file same as above.
    • Returns: dict containing metadata keys found in the PDF plus meta operation info.

Conversion

  • pdf_to_images(file_path: str, output_dir: str, format: str = "png", dpi: int = 150, pages: Optional[List[int]] = None) -> list[dict]

    • Purpose: Convert one or more PDF pages to image files.
    • Inputs:
      • file_path (str): path to the PDF on disk (absolute or temp path).
      • output_dir (str): directory where generated images will be written.
      • format (str): image format, e.g., png, jpeg.
      • dpi (int): resolution for conversion (default 150).
      • pages (Optional[List[int]]): list of 1-based pages to render; None for all pages.
    • Returns: list of dicts for each generated image:
      • path (str), page_number (int), size (int), format (str)
    • Notes: Implementation uses pdf2image and PIL; ensure dependencies and poppler are installed on the host.
  • images_to_pdf(image_paths: List[str], output_path: str, page_size: str = "A4", orientation: str = "portrait") -> dict

    • Purpose: Create a PDF document from multiple images.
    • Inputs:
      • image_paths (List[str]): list of image file paths in order
      • output_path (str): path for the generated PDF
      • page_size (str): e.g., A4, Letter (processor maps to physical sizes)
      • orientation (str): portrait or landscape
    • Returns: dict with success info and meta including operation timing.

PDF Manipulation

  • merge_pdfs(input_files: List[str], output_path: str) -> dict

    • Purpose: Merge multiple PDF files into a single PDF.
    • Inputs:
      • input_files (List[str]): file paths
      • output_path (str): destination path
    • Returns: dict with details (e.g., path) and meta.
  • split_pdf(file_path: str, split_ranges: List[Dict[str, Any]]) -> list[dict]

    • Purpose: Split a PDF into multiple files by page ranges.
    • Inputs:
      • file_path (str): source PDF
      • split_ranges (List[Dict]): each dict should describe start and end pages and optional filename.
    • Returns: list of generated files info dicts.
  • rotate_pages(file_path: str, rotations: List[Dict[str, int]], output_path: str) -> dict

    • Purpose: Rotate specific pages in a PDF and write to output_path.
    • Inputs:
      • file_path (str): source PDF
      • rotations (List[Dict]): each dict should include page (1-based) and degrees (e.g., 90, 180, 270).
      • output_path (str): target PDF path
    • Returns: dict with path and meta.

Notes:

  • All tools log an operation_id and execution time in ms in the returned meta object.
  • Tools that return lists set x-fastmcp-wrap-result=true for the framework so they are returned as bare lists.
  • Tools will raise ValueError for user-facing errors; internal exceptions are logged.
  • For file inputs, prefer uploading first via upload_file to ensure files are in the server temp directory.
  • page_range syntax uses utils.parsers.parse_page_range: e.g., "1-3,5,7-9".
  • If both pages and page_range are passed, pages takes precedence.
  • Image conversion requires Poppler (see below).

Example JSON: extract_text (simple)

  • Request arguments:
{
  "file": "C:/path/to/input.pdf",
  "encoding": "utf-8"
}
  • Response shape:
{
  "text": "... full extracted text ...",
  "page_count": 3,
  "char_count": 1234,
  "meta": { "operation_id": "<hex>", "execution_ms": 42 }
}

Uploading files (Claude Desktop and clients)

Claude may not automatically send binary file contents. Use one of these upload tools to persist a file to the server temp directory, then reference it by short filename in subsequent calls.

  1. Upload a file (generic)
  • Tool: upload_file
  • Request:
{
  "name": "upload_file",
  "arguments": {
    "file": { "base64": "<BASE64_DATA>", "filename": "document.pdf" }
  }
}
  • Response contains filename and absolute path under the server temp_dir.
  1. Upload a file as base64 (explicit schema)
  • Tool: upload_file_base64
  • Request:
{
  "name": "upload_file_base64",
  "arguments": { "base64": "<BASE64_DATA>", "filename": "document.pdf" }
}
  1. Upload a file from URL (explicit schema)
  • Tool: upload_file_url
  • Request:
{
  "name": "upload_file_url",
  "arguments": { "url": "https://example.com/document.pdf", "filename": "document.pdf" }
}
  1. Extract text using the saved short filename
  • Request:
{
  "name": "extract_text",
  "arguments": { "file": "document.pdf" }
}

Alternative: provide a URL to upload_file (requires requests installed):

{
  "name": "upload_file",
  "arguments": {
    "file": { "url": "https://example.com/document.pdf", "filename": "document.pdf" }
  }
}

Manual option: run server_info to get temp_dir, copy your file into that directory, then call tools with the short filename.

Example JSON: merge_pdfs

  • Request arguments:
{
  "input_files": [
    "C:/path/a.pdf",
    "C:/path/b.pdf"
  ],
  "output_path": "C:/path/merged.pdf"
}
  • Response shape:
{
  "output_path": "C:/path/merged.pdf",
  "page_count": 10,
  "size": 456789,
  "meta": { "operation_id": "<hex>", "execution_ms": 87 }
}

Configuration

Configuration is loaded via pydantic-settings from .env and environment variables.

Env vars (case-insensitive):

  • MAX_FILE_SIZE_MB (int, default 50): Max file size for inputs.
  • LOG_LEVEL (str, default INFO): Logging level.
  • LOG_FILE_PATH (str, default logs/pdf-processor-server.log): Log file path.
  • TEMP_DIR (str, default temp_files): Working temp storage directory.
  • SERVER_NAME (str, default pdf-processor-server): Server name.
  • SERVER_VERSION (str, default 1.0.0): Server version.

Path helpers:

  • TEMP_DIR resolves to absolute settings.temp_path.
  • LOG_FILE_PATH resolves to absolute settings.log_path.

Storage & Security

  • Temp files are stored under TEMP_DIR and cleaned up automatically after 24h of inactivity.
  • ensure_within_temp(path) prevents reading files outside TEMP_DIR for base64 retrieval.
  • Validators enforce allowed extensions and size limits for PDFs and images.

Logging & Telemetry

  • Rotating logs at LOG_FILE_PATH (10MB x 5). No stdout/stderr prints.
  • Each tool returns meta.operation_id and meta.execution_ms for traceability.
  • Server banner and lifecycle logs are emitted by FastMCP at startup/shutdown.

Windows: Poppler for pdf2image

pdf2image requires Poppler binaries.

  • Download: https://github.com/oschwartz10612/poppler-windows/releases/
  • Extract, add poppler-*/Library/bin to your PATH.
  • Verify: pdftoppm -v prints a version. If not available, pdf_to_images tools will raise helpful errors.

Linux: Poppler for pdf2image

pdf2image requires Poppler binaries. Install via your package manager:

  • Debian/Ubuntu: sudo apt update && sudo apt install -y poppler-utils
  • Fedora: sudo dnf install -y poppler-utils
  • Arch: sudo pacman -S --noconfirm poppler
  • Verify: pdftoppm -v prints a version.

macOS: Poppler for pdf2image

Install Poppler with Homebrew:

brew install poppler

If Homebrew is in /opt/homebrew/bin (Apple Silicon), ensure your shell PATH includes it. Verify: pdftoppm -v.

Developer Guide

Project layout

  • src/fastmcp_pdf_server/
    • main.py: Builds FastMCP app, registers tools, runs via STDIO.
    • config.py: Pydantic settings for env and paths.
    • utils/: Logger, validators, parsers.
    • services/: PDF and image operations, file manager.
    • tools/: Thin async wrappers exposing services as MCP tools.

Install & Run

python -m venv .venv
\.\.venv\Scripts\Activate.ps1
pip install -r requirements.txt
Copy-Item .env.example .env
python -m fastmcp_pdf_server

Linux/macOS:

python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env
python -m fastmcp_pdf_server

Tests

pytest -q

Conversion tests are skipped if Poppler (pdftoppm) is not found.

Troubleshooting

  • Startup hangs after banner: normal for STDIO mode (waiting for an MCP client).
  • pdf2image errors: ensure Poppler on PATH; retry shell after updating PATH.
  • ValueError: File not found or Invalid file extension: check inputs and validators.
  • Large files slow/timeout: reduce dpi, use page-range, or increase resources.

Performance Notes

  • Max file size is enforced; adjust MAX_FILE_SIZE_MB if needed.
  • Prefer page-scoped ops for large PDFs.
  • Lower dpi for faster PDF→image conversions.

Optional HTTP Mode (advanced)

FastMCP supports a streamable HTTP transport. This server defaults to STDIO. For experimentation, you can run an HTTP endpoint:

# run_http.py
import asyncio
from fastmcp_pdf_server.main import build_app

async def main():
  app = build_app()
  await app.run_http_async(host="127.0.0.1", port=8000, path="mcp")

asyncio.run(main())

Happy Coding!

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选