pdf-mcp-server
v2.3.0
Published
A production-ready MCP server for PDF operations - count pages, extract text, get metadata, summarize, answer questions, add watermarks and headers/footers, analyze page layouts, detect text positions, and replace text at specific positions with intellige
Maintainers
Readme
📄 PDF MCP Server
A production-ready Model Context Protocol (MCP) server for PDF operations. This server enables AI assistants like Claude Desktop and VS Code Copilot to interact with PDF files through a standardized interface.
🆕 What's New in v2.1
🎯 Advanced PDF Analysis Tools
Two powerful new tools for precise PDF analysis:
🔍 analyze-pdf-page - Extract page dimensions, margins, and layout information
- Get dimensions in points, inches, and millimeters
- Automatic margin detection for standard page sizes
- MediaBox and CropBox information
- Essential for layout-aware PDF operations
📍 detect-text-position - OCR-level text position detection
- Precise bounding boxes for every text element
- Font name and size information
- Text search and filtering capabilities
- Perfect for intelligent watermark placement
Use Cases:
- 🎯 Smart watermark positioning that avoids text
- 📐 Layout-aware content addition
- 🔍 Content analysis and structure detection
- 📊 Automated document processing
See advanced tools documentation →
🎉 What's New in v2.0
🚀 Major Improvements
🔍 Intelligent File Path Resolution
- Just use the filename! No need for full paths
- Automatically searches Downloads, Documents, Desktop
- Supports relative paths, absolute paths, and file:// URIs
- Example:
"document.pdf"instead of"/Users/you/Downloads/document.pdf"
🎯 Perfect Watermark Centering
- Watermarks now appear exactly in the center of pages
- Fixed rotation-aware positioning
- All position presets (corners, center) work flawlessly
📥 Automatic File Downloads
- Tools return downloadable PDF files
- Works seamlessly in Claude Desktop and VS Code
- Base64-encoded for instant access
🔒 Strict PDF Validation
- Only accepts .pdf files (rejects .docx, .txt, etc.)
- Clear, helpful error messages
- Security validations
📝 Enhanced Documentation
- Comprehensive tool descriptions
- Clear examples in every tool
- Better error guidance
✨ Features
This MCP server provides 10 powerful PDF tools:
Core PDF Tools
- 📊 count-pdf-pages - Count total pages in a PDF file
- 📝 extract-pdf-text - Extract all text content from a PDF
- ℹ️ extract-pdf-metadata - Get PDF metadata (title, author, dates, etc.)
- 🤖 summarize-pdf - AI-powered PDF summarization using LLM sampling
- ❓ answer-pdf-question - Answer questions about PDF content using AI
Content Modification Tools
- 🏷️ add-pdf-watermark - Add custom watermarks to PDF pages with full control
- 📑 add-pdf-header-footer - Add headers and footers with dynamic variables
- ✏️ replace-text-at-position - Replace text at specific positions with precise control
🆕 Advanced Analysis Tools (v2.1)
- 🔍 analyze-pdf-page - Extract page dimensions, margins, and layout information
- 📍 detect-text-position - Detect precise text positions with OCR-level accuracy
See advanced tools documentation →
New: Watermark & Header/Footer Features
Watermark Tool
- Position Options: Top-left, top-right, top-center, bottom-left, bottom-right, bottom-center, center, or custom coordinates
- Full Customization: Font size, color (RGB), opacity, rotation angle, margin control
- Memory Efficient: Optimized for large PDFs (300+ pages) with batch processing
- Smart Defaults: Works out-of-the-box with sensible defaults
Header/Footer Tool
- Dynamic Variables:
{page},{totalPages},{date},{title}automatically replaced - Flexible Alignment: Left, center, or right alignment
- Page Range Support: Apply to specific pages or all pages
- Customization: Font size, color, margins
- Memory Efficient: Handles large PDFs (300+ pages) with batch processing
🚀 Quick Start
Prerequisites
- Node.js 18.0.0 or higher
- npm or yarn
- An MCP client (Claude Desktop or VS Code Copilot)
Installation
# Clone or download this repository
cd pdf-mcp-server
# Install dependencies
npm install
# Build the project
npm run build
# Test the server (optional)
npm start🔧 Configuration
For Claude Desktop
Add the following to your Claude Desktop configuration file:
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"pdf-operations": {
"command": "node",
"args": [
"/absolute/path/to/pdf-mcp-server/dist/index.js"
],
"env": {}
}
}
}Important: Replace /absolute/path/to/pdf-mcp-server with the actual absolute path to this project directory.
For VS Code Copilot
Add to your VS Code settings.json:
{
"github.copilot.mcp.servers": {
"pdf-operations": {
"command": "node",
"args": [
"/absolute/path/to/pdf-mcp-server/dist/index.js"
]
}
}
}Important: Replace /absolute/path/to/pdf-mcp-server with the actual absolute path to this project directory.
📖 Usage Examples
Once configured, you can use natural language to interact with PDF files:
Count Pages
How many pages are in /path/to/document.pdf?The server will use the count-pdf-pages tool to return the page count.
Extract Text
Extract all text from /path/to/report.pdfThe server will use the extract-pdf-text tool to return the full text content.
Get Metadata
What are the metadata details of /path/to/paper.pdf?The server will use the extract-pdf-metadata tool to return title, author, creation date, etc.
Summarize PDF
Summarize the main points of /path/to/research.pdfThe server will use the summarize-pdf tool to generate an AI-powered summary.
Answer Questions
Based on /path/to/manual.pdf, how do I install the software?The server will use the answer-pdf-question tool to provide an answer based on the PDF content.
Add Watermark
Add a watermark "CONFIDENTIAL" to /path/to/document.pdfThe server will use the add-pdf-watermark tool to add a watermark to all pages.
Advanced watermark example:
Add a watermark "DRAFT" to /path/to/report.pdf with:
- Position: bottom-right corner
- Font size: 36
- Light gray color (RGB: 0.7, 0.7, 0.7)
- 50% opacity
- No rotationAdd Headers and Footers
Add "My Document" as header and "Page {page} of {totalPages}" as footer to /path/to/file.pdfThe server will use the add-pdf-header-footer tool to add headers and footers with dynamic page numbers.
Advanced header/footer example:
Add headers and footers to /path/to/contract.pdf:
- Header: "{title} - {date}" aligned left
- Footer: "Page {page} of {totalPages}" aligned right
- Apply only to pages 2-20
- Font size: 10pt🛠️ Available Tools
1. count-pdf-pages
Description: Count the total number of pages in a PDF file.
Input:
{
filePath: string // Absolute or relative path to PDF file
}Output:
{
pageCount: number,
filePath: string
}Example:
Input: { filePath: "/docs/report.pdf" }
Output: { pageCount: 42, filePath: "/docs/report.pdf" }2. extract-pdf-text
Description: Extract all text content from a PDF file.
Input:
{
filePath: string, // Absolute or relative path to PDF file
maxLength?: number // Optional: max characters to return (default: 50000)
}Output:
{
text: string, // Extracted text content
characterCount: number, // Total characters extracted
pageCount: number, // Number of pages
filePath: string
}3. extract-pdf-metadata
Description: Extract metadata from a PDF file (title, author, dates, etc.).
Input:
{
filePath: string // Absolute or relative path to PDF file
}Output:
{
title: string,
author: string,
subject: string,
creationDate: string,
modificationDate: string,
pageCount: number,
pdfVersion: string,
filePath: string
}4. summarize-pdf
Description: Generate an AI-powered summary of PDF content.
Input:
{
filePath: string, // Absolute or relative path to PDF file
maxTokens?: number // Optional: max tokens for summary (default: 500)
}Output:
{
summary: string, // Generated summary
pageCount: number,
originalLength: number, // Original text length
summaryLength: number, // Summary length
filePath: string
}Note: Requires MCP client to support LLM sampling feature.
5. answer-pdf-question
Description: Answer questions about PDF content using AI.
Input:
{
filePath: string, // Absolute or relative path to PDF file
question: string, // Question to answer
maxTokens?: number // Optional: max tokens for answer (default: 500)
}Output:
{
answer: string, // Answer to the question
question: string, // Original question
context: string, // Relevant PDF context used
filePath: string
}Note: Requires MCP client to support LLM sampling feature.
6. add-pdf-watermark
Description: Add custom watermark to all pages of a PDF with full control over appearance.
Input:
{
filePath: string, // Absolute or relative path to PDF file
text: string, // Watermark text
position?: string, // Position: 'top-left', 'top-right', 'top-center',
// 'bottom-left', 'bottom-right', 'bottom-center',
// 'center', 'custom' (default: 'center')
x?: number, // X coordinate (required if position is 'custom')
y?: number, // Y coordinate (required if position is 'custom')
fontSize?: number, // Font size in points (default: 48)
color?: { // RGB color (0-1 range, default: gray)
r: number,
g: number,
b: number
},
opacity?: number, // Opacity 0-1 (default: 0.3)
rotation?: number, // Rotation in degrees (default: -45)
margin?: number, // Margin from edges in points (default: 50)
outputPath?: string, // Output file path (default: input_watermarked.pdf)
useBatchProcessing?: boolean // Use batch processing for large PDFs (default: false)
}Output:
{
outputPath: string, // Path to watermarked PDF
pagesProcessed: number, // Number of pages watermarked
originalPath: string, // Original file path
config: WatermarkConfig // Configuration used
}Example:
Input: {
filePath: "/docs/report.pdf",
text: "CONFIDENTIAL",
position: "center",
opacity: 0.3
}
Output: {
outputPath: "/docs/report_watermarked.pdf",
pagesProcessed: 42,
originalPath: "/docs/report.pdf",
config: { text: "CONFIDENTIAL", position: "center", ... }
}7. add-pdf-header-footer
Description: Add headers and/or footers to PDF pages with dynamic variables.
Input:
{
filePath: string, // Absolute or relative path to PDF file
headerText?: string, // Header text (supports variables)
footerText?: string, // Footer text (supports variables)
alignment?: string, // Text alignment: 'left', 'center', 'right' (default: 'center')
fontSize?: number, // Font size in points (default: 12)
color?: { // RGB color (0-1 range, default: black)
r: number,
g: number,
b: number
},
margin?: number, // Margin from top/bottom in points (default: 30)
pageRange?: [number, number], // Page range [start, end] (default: all pages)
outputPath?: string, // Output file path (default: input_with_headers_footers.pdf)
useBatchProcessing?: boolean // Use batch processing for large PDFs (default: false)
}Supported Variables:
{page}- Current page number (1-based){totalPages}- Total number of pages{date}- Current date (YYYY-MM-DD format){title}- PDF document title
Output:
{
outputPath: string, // Path to PDF with headers/footers
pagesProcessed: number, // Number of pages processed
originalPath: string, // Original file path
config: HeaderFooterConfig // Configuration used
}Example:
Input: {
filePath: "/docs/manual.pdf",
headerText: "{title}",
footerText: "Page {page} of {totalPages}",
alignment: "center"
}
Output: {
outputPath: "/docs/manual_with_headers_footers.pdf",
pagesProcessed: 42,
originalPath: "/docs/manual.pdf",
config: { headerText: "{title}", footerText: "Page {page} of {totalPages}", ... }
}8. analyze-pdf-page
Description: Extract comprehensive page layout information including dimensions, margins, and page boxes. Essential for understanding page structure before adding watermarks or other elements.
Input:
{
filePath: string, // Absolute or relative path to PDF file
pageNumber?: number // Page number to analyze (1-indexed, default: 1)
}Output:
{
filePath: string,
pageNumber: number,
dimensions: {
width: number, // Width in points
height: number, // Height in points
rotation: number, // Rotation in degrees (0, 90, 180, 270)
inchDimensions: {
width: number, // Width in inches
height: number // Height in inches
},
mmDimensions: {
width: number, // Width in millimeters
height: number // Height in millimeters
}
},
margins: {
top: number, // Top margin in points
bottom: number, // Bottom margin in points
left: number, // Left margin in points
right: number, // Right margin in points
detected: boolean // Whether margins were detected from content
},
mediaBox: {
x: number,
y: number,
width: number,
height: number
},
cropBox?: { // Optional: visible page area
x: number,
y: number,
width: number,
height: number
}
}Example:
Input: {
filePath: "document.pdf",
pageNumber: 1
}
Output: {
filePath: "/Users/you/Downloads/document.pdf",
pageNumber: 1,
dimensions: {
width: 595.28,
height: 841.89,
rotation: 0,
inchDimensions: { width: 8.27, height: 11.69 },
mmDimensions: { width: 210.0, height: 297.0 }
},
margins: { top: 71.0, bottom: 71.0, left: 71.0, right: 71.0, detected: true },
mediaBox: { x: 0, y: 0, width: 595.28, height: 841.89 }
}9. detect-text-position
Description: Detect precise text positions in a PDF page using advanced OCR-level text extraction. Returns exact bounding boxes, font information, and coordinates for each text element.
Input:
{
filePath: string, // Absolute or relative path to PDF file
pageNumber?: number, // Page number to analyze (1-indexed, default: 1)
searchQuery?: string, // Optional: filter text items by content
maxResults?: number // Optional: maximum number of results to return
}Output:
{
filePath: string,
pageNumber: number,
textItems: Array<{
text: string, // The text content
fontName: string, // Font name used for this text
fontSize: number, // Font size in points
bounds: {
x: number, // X coordinate of top-left corner
y: number, // Y coordinate of top-left corner
width: number, // Width of the bounding box
height: number // Height of the bounding box
},
transform: number[], // PDF transformation matrix [a, b, c, d, e, f]
direction: number // Text direction in degrees (0=horizontal, 90=vertical)
}>,
totalTextItems: number, // Total number of text items found
pageDimensions: {
width: number,
height: number,
rotation: number,
inchDimensions: { width: number, height: number },
mmDimensions: { width: number, height: number }
},
searchQuery?: string, // Search query used (if filtering was applied)
filtered: boolean // Whether results were filtered
}Example:
Input: {
filePath: "document.pdf",
pageNumber: 1,
searchQuery: "watermark"
}
Output: {
filePath: "/Users/you/Downloads/document.pdf",
pageNumber: 1,
textItems: [
{
text: "WATERMARK",
fontName: "Helvetica-Bold",
fontSize: 48.0,
bounds: { x: 200.0, y: 400.0, width: 195.0, height: 48.0 },
transform: [48, 0, 0, 48, 200, 400],
direction: 0
}
],
totalTextItems: 1,
pageDimensions: { width: 595.28, height: 841.89, rotation: 0, ... },
searchQuery: "watermark",
filtered: true
}Use Cases:
- Intelligent watermark placement (avoid text overlap)
- Content-aware PDF operations
- Text position analysis for layout understanding
- Finding optimal positions for annotations
See advanced tools documentation →
10. replace-text-at-position
Description: Replaces text at specific positions in PDF documents. Works in conjunction with detect-text-position to find and replace text with precise control over positioning, fonts, and colors. Perfect for bulk text replacements, typo corrections, and content updates.
Input:
{
filePath: string, // Absolute or relative path to PDF file
replacements: Array<{
pageNumber: number, // Page number where text should be replaced (1-indexed)
originalText: string, // Original text to find and replace
replacementText: string, // New text to insert (can be empty to remove text)
bounds: {
x: number, // X coordinate of text position
y: number, // Y coordinate of text position
width: number, // Width of text bounding box
height: number // Height of text bounding box
},
fontName?: string, // Font name (defaults to Helvetica)
fontSize?: number, // Font size in points
color?: { // Text color in RGB (0-1 range)
r: number,
g: number,
b: number
},
rotation?: number, // Text rotation angle in degrees
backgroundColor?: { // Background color to cover old text (defaults to white)
r: number,
g: number,
b: number
}
}>,
preserveFonts?: boolean, // Preserve original fonts when possible (default: true)
preserveColors?: boolean, // Preserve original colors when possible (default: true)
outputPath?: string, // Output file path (default: input_with_replacements.pdf)
createBackup?: boolean // Create backup of original file (default: false)
}Output:
{
outputPath: string, // Path to the output PDF with replacements
replacementCount: number, // Number of successful replacements made
pagesModified: number, // Number of pages modified
originalPath: string, // Original file path
replacements: Array<{
pageNumber: number,
originalText: string,
replacementText: string,
success: boolean, // Whether the replacement was successful
error?: string // Error message if replacement failed
}>
}Example Use Cases:
- Replace all occurrences of "Hello" with "Hi":
// Step 1: Find all "Hello" text positions
Input: {
filePath: "document.pdf",
searchQuery: "Hello"
}
// Use detect-text-position to get positions
// Step 2: Replace all occurrences
Input: {
filePath: "document.pdf",
replacements: [
{
pageNumber: 1,
originalText: "Hello",
replacementText: "Hi",
bounds: { x: 100, y: 200, width: 50, height: 15 },
fontSize: 12
}
// ... more occurrences
]
}
Output: {
outputPath: "/path/to/document_with_replacements.pdf",
replacementCount: 3,
pagesModified: 1,
originalPath: "/path/to/document.pdf",
replacements: [
{ pageNumber: 1, originalText: "Hello", replacementText: "Hi", success: true }
// ... more results
]
}- Replace year "2023" with "2024" in first occurrence:
Input: {
filePath: "contract.pdf",
replacements: [
{
pageNumber: 1,
originalText: "2023",
replacementText: "2024",
bounds: { x: 150, y: 700, width: 40, height: 12 },
fontSize: 10
}
]
}- Replace whitespace with underscores:
// First detect text with spaces, then replace
Input: {
filePath: "form.pdf",
replacements: [
{
pageNumber: 1,
originalText: "First Name",
replacementText: "First_Name",
bounds: { x: 50, y: 100, width: 80, height: 15 },
fontSize: 12
}
]
}- Batch replacement with custom styling:
Input: {
filePath: "report.pdf",
replacements: [
{
pageNumber: 1,
originalText: "DRAFT",
replacementText: "FINAL",
bounds: { x: 50, y: 50, width: 100, height: 20 },
fontSize: 16,
color: { r: 1, g: 0, b: 0 }, // Red text
backgroundColor: { r: 1, g: 1, b: 0.8 } // Light yellow background
},
{
pageNumber: 2,
originalText: "Confidential",
replacementText: "Public",
bounds: { x: 400, y: 50, width: 120, height: 15 },
fontSize: 12
}
],
preserveFonts: false,
createBackup: true
}Use Cases:
- Replace all occurrences of specific text
- Update dates, years, or version numbers
- Correct typos at specific locations
- Replace whitespace or special characters
- Batch update product names, prices, or codes
- Content updates without recreating PDFs
- Automated document corrections
Workflow:
- Use
detect-text-positionto find text locations - Prepare replacement specifications with new text
- Call
replace-text-at-positionto perform replacements - Get detailed results with success/failure status
🔒 Security Features
- ✅ Path validation: Prevents path traversal attacks
- ✅ File size limits: Default 50MB maximum to prevent memory exhaustion
- ✅ File type validation: Ensures only PDF files are processed
- ✅ Error handling: Comprehensive error messages for debugging
- ✅ Input sanitization: All inputs validated with Zod schemas
📁 Project Structure
pdf-mcp-server/
├── src/
│ ├── index.ts # Main server entry point
│ ├── types.ts # TypeScript type definitions
│ ├── utils/
│ │ ├── path-resolver.ts # File path resolution utilities
│ │ ├── pdf-utils.ts # PDF processing utilities
│ │ ├── watermark-utils.ts # Watermark utilities
│ │ ├── header-footer-utils.ts # Header/footer utilities
│ │ ├── page-analysis-utils.ts # Page dimension & margin analysis
│ │ ├── text-position-utils.ts # Text position detection (PDF.js)
│ │ └── text-replacement-utils.ts # Text replacement utilities (NEW)
│ └── tools/
│ ├── count-pages.tool.ts # Page counting tool
│ ├── extract-text.tool.ts # Text extraction tool
│ ├── extract-metadata.tool.ts # Metadata extraction tool
│ ├── summarize-pdf.tool.ts # AI summarization tool
│ ├── answer-question.tool.ts # AI Q&A tool
│ ├── add-watermark.tool.ts # Watermark tool
│ ├── add-header-footer.tool.ts # Header/footer tool
│ ├── analyze-pdf-page.tool.ts # Page analysis tool (v2.1)
│ ├── detect-text-position.tool.ts # Text position detection tool (v2.1)
│ └── replace-text-at-position.tool.ts # Text replacement tool (NEW)
├── scripts/
│ ├── test-new-tools.ts # Test suite for new tools
│ └── test-text-replacement.ts # Test suite for text replacement (NEW)
├── dist/ # Compiled JavaScript output
├── package.json # Project dependencies
├── tsconfig.json # TypeScript configuration
├── README.md # This file
├── ADVANCED_TOOLS.md # Advanced tools documentation (v2.1)
└── EXAMPLES.md # Detailed usage examples🧪 Development
Build
npm run buildWatch Mode
npm run watchRun Server
npm startThe server will start and wait for MCP client connections via stdio transport.
🐛 Troubleshooting
Server not appearing in Claude Desktop
- Check configuration path: Ensure the path in
claude_desktop_config.jsonis absolute and correct - Rebuild the project: Run
npm run buildto ensure latest code is compiled - Restart Claude Desktop: Close and reopen Claude Desktop completely
- Check logs: Look for error messages in Claude Desktop's developer console
"File not found" errors
- Use absolute paths: Provide absolute paths to PDF files
- Check file permissions: Ensure the PDF file is readable
- Verify file exists: Double-check the file path is correct
"PDF is password protected" errors
This server does not support password-protected PDFs. Remove the password before processing.
Memory issues with large PDFs
The default file size limit is 50MB. For larger files, you may need to:
- Split the PDF into smaller chunks
- Modify the
MAX_FILE_SIZE_BYTESconstant insrc/utils/pdf-utils.ts
📚 Dependencies
- @modelcontextprotocol/sdk: Official MCP TypeScript SDK
- pdf-parse: Pure JavaScript PDF text extraction
- zod: TypeScript-first schema validation
🤝 Contributing
Contributions are welcome! Please feel free to submit issues or pull requests.
📄 License
MIT License - feel free to use this in your own projects!
🙏 Acknowledgments
- Built with the Model Context Protocol
- Uses pdf-parse for PDF text extraction
- Follows MCP best practices and TypeScript strict mode
📞 Support
For issues or questions:
- Check the Troubleshooting section
- Review the MCP documentation
- Open an issue on GitHub
Made with ❤️ for the MCP community
