Skip to main content
Glama
andr3medeiros

PDF Manipulation MCP Server

pdf_combine_pages_to_single

Combine multiple PDF pages into a single page with customizable layout options for simplified viewing and printing.

Instructions

Combine multiple pages from a PDF into a single page with specified layout.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
pdf_pathYes
page_numbersNo
layoutNovertical
output_pathNo

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultYes

Implementation Reference

  • The complete handler function for 'pdf_combine_pages_to_single' tool, including @mcp.tool() decorator for registration, type-annotated parameters serving as input schema, docstring description, and full implementation using PyMuPDF (fitz) to combine PDF pages into a single page with vertical, horizontal, or grid layouts.
    @mcp.tool()
    async def pdf_combine_pages_to_single(
        pdf_path: str,
        page_numbers: Optional[List[int]] = None,
        layout: str = "vertical",
        output_path: Optional[str] = None
    ) -> str:
        """Combine multiple pages from a PDF into a single page with specified layout."""
        if not os.path.exists(pdf_path):
            return f"Error: PDF file not found: {pdf_path}"
        
        if not validate_pdf_file(pdf_path):
            return f"Error: Invalid PDF file: {pdf_path}"
        
        if layout not in ["vertical", "horizontal", "grid"]:
            return f"Error: Invalid layout. Must be 'vertical', 'horizontal', or 'grid'."
        
        try:
            # Open PDF document
            doc = fitz.open(pdf_path)
            
            # Determine pages to combine
            if page_numbers is None:
                pages_to_combine = list(range(len(doc)))
            else:
                # Validate page numbers
                for page_num in page_numbers:
                    if not validate_page_number(doc, page_num):
                        doc.close()
                        return f"Error: Invalid page number {page_num}. Document has {len(doc)} pages."
                pages_to_combine = page_numbers
            
            if len(pages_to_combine) < 2:
                doc.close()
                return "Error: Need at least 2 pages to combine."
            
            # Get dimensions of all pages
            page_rects = [doc[page_num].rect for page_num in pages_to_combine]
            page_widths = [rect.width for rect in page_rects]
            page_heights = [rect.height for rect in page_rects]
            
            # Calculate new page dimensions based on layout
            if layout == "vertical":
                new_width = max(page_widths)
                new_height = sum(page_heights)
            elif layout == "horizontal":
                new_width = sum(page_widths)
                new_height = max(page_heights)
            else:  # grid layout
                import math
                num_pages = len(pages_to_combine)
                cols = math.ceil(math.sqrt(num_pages))
                rows = math.ceil(num_pages / cols)
                new_width = max(page_widths) * cols
                new_height = max(page_heights) * rows
            
            # Create new document with single page
            new_doc = fitz.open()
            new_page = new_doc.new_page(width=new_width, height=new_height)
            
            # Place pages according to layout
            if layout == "vertical":
                y_offset = 0
                for page_num in pages_to_combine:
                    page_rect = fitz.Rect(0, y_offset, page_rects[pages_to_combine.index(page_num)].width, 
                                        y_offset + page_rects[pages_to_combine.index(page_num)].height)
                    new_page.show_pdf_page(page_rect, doc, page_num)
                    y_offset += page_rects[pages_to_combine.index(page_num)].height
                    
            elif layout == "horizontal":
                x_offset = 0
                for page_num in pages_to_combine:
                    page_rect = fitz.Rect(x_offset, 0, x_offset + page_rects[pages_to_combine.index(page_num)].width,
                                        page_rects[pages_to_combine.index(page_num)].height)
                    new_page.show_pdf_page(page_rect, doc, page_num)
                    x_offset += page_rects[pages_to_combine.index(page_num)].width
                    
            else:  # grid layout
                for i, page_num in enumerate(pages_to_combine):
                    row = i // cols
                    col = i % cols
                    x = col * max(page_widths)
                    y = row * max(page_heights)
                    page_rect = fitz.Rect(x, y, x + page_rects[i].width, y + page_rects[i].height)
                    new_page.show_pdf_page(page_rect, doc, page_num)
            
            # Generate output filename
            if not output_path:
                output_path = generate_output_filename(pdf_path, "combined")
            
            # Save the combined PDF
            new_doc.save(output_path)
            new_doc.close()
            doc.close()
            
            return f"Successfully combined {len(pages_to_combine)} pages using {layout} layout. Output saved to: {output_path}"
            
        except Exception as e:
            return f"Error combining pages: {str(e)}"
  • Input schema defined by function parameters with type hints: pdf_path (str), page_numbers (Optional[List[int]]), layout (str, default 'vertical'), output_path (Optional[str]); returns str (status message with output path).
    async def pdf_combine_pages_to_single(
        pdf_path: str,
        page_numbers: Optional[List[int]] = None,
        layout: str = "vertical",
        output_path: Optional[str] = None
    ) -> str:
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden of behavioral disclosure. It states the tool combines pages but doesn't describe what happens to the original PDF (e.g., if it's modified or a new file is created), permissions needed, error conditions, or output behavior. For a mutation tool with zero annotation coverage, this is a significant gap in transparency.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient sentence: 'Combine multiple pages from a PDF into a single page with specified layout.' It's front-loaded with the core purpose and has zero wasted words, making it highly concise and well-structured for quick understanding.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool has an output schema (which likely describes the return value), the description doesn't need to explain output details. However, with 4 parameters, 0% schema coverage, and no annotations, the description is incomplete—it lacks behavioral context and parameter guidance. It's minimally adequate but has clear gaps for a mutation tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate by explaining parameters. It mentions 'specified layout' which hints at the 'layout' parameter but doesn't detail other parameters like 'pdf_path', 'page_numbers', or 'output_path'. With 4 parameters and no schema descriptions, the description adds minimal semantic value beyond what's inferred from the tool name.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Combine multiple pages from a PDF into a single page with specified layout.' It specifies the verb ('combine'), resource ('pages from a PDF'), and outcome ('single page with specified layout'), making it easy to understand. However, it doesn't explicitly differentiate from sibling tools like pdf_merge_files or pdf_split, which also manipulate PDF pages, so it doesn't reach the highest score.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. It doesn't mention sibling tools like pdf_merge_files (which merges entire files) or pdf_split (which splits pages), leaving the agent to infer usage context. There's no explicit when/when-not advice or prerequisites, resulting in minimal guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/andr3medeiros/pdf-manipulation-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server