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:

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed1 schema field changedv1.0.0
    • addedInput schema / title
      Added value: +"pdf_combine_pages_to_singleArguments"
  2. First observed

TDQS

C2.9/5.0
Behavior2/5

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

With no annotations provided, the description carries the full burden of behavioral disclosure. It does not mention side effects (e.g., whether it creates a new file or modifies the original), handling of output_path, or behavior when page_numbers are omitted. 'Specified layout' is vague and does not disclose available layout options or formatting behavior.

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

Conciseness4/5

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

The description is a single sentence with no wasted words, front-loaded with the primary action. However, it is too brief to be fully effective—important operational details are omitted, but this is not a bloat issue.

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

Completeness2/5

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

The tool has four parameters and no annotations, so a sparse description is inadequate. It does not explain layout options, page selection semantics, output path behavior, or what the output schema represents. Though an output schema exists, the description still leaves too much unexplained for an agent to confidently invoke the 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?

Description provides only a vague reference to 'specified layout' but does not explain any of the four schema parameters (pdf_path, layout, output_path, page_numbers). It does not clarify what page_numbers means, what output_path defaults to, or how layout values affect the output. Schema description coverage is 0%, so the description fails to compensate.

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

Purpose5/5

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

The description states a specific action ('Combine multiple pages from a PDF into a single page') with a clear resource and result. This differentiates it from sibling tools like pdf_merge_files, which merges entire PDF files rather than combining pages within a single PDF.

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?

No guidance is given on when to use this tool versus alternatives such as pdf_merge_files or pdf_split. There are no explicit use cases, prerequisites, or exclusion statements.

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