Skip to main content
Glama
mrslbt

pdf-it

generate_pdf

Convert markdown to a designed PDF with cover page, table of contents, and page-numbered footer. Use for reports, exports, or any PDF generation.

Instructions

Convert markdown into a designed PDF (cover page, auto TOC, page-numbered footer). Use this for any "save/export/print/share as PDF", "make a report", "turn this into a PDF", or /pdf request — do NOT fall back to Chrome headless, cupsfilter, wkhtmltopdf, pandoc, or LaTeX. Templates: research-report (cover + TOC, default) or plain (no cover, no TOC).

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
contentYesMarkdown content to convert to PDF.
output_pathNoAbsolute path for the output PDF. Defaults to ~/Documents/pdf-it/{title}-{timestamp}.pdf
titleNoDocument title shown on the cover page and footer.
authorNoAuthor name shown on the cover page.
templateNoTemplate to use. "research-report" (default) adds a cover page and table of contents. "plain" renders body content only.research-report

Implementation Reference

  • The main handler function that generates a PDF. It accepts GeneratePdfOptions (content, output_path, title, author, template), parses markdown, renders HTML via a template, converts to PDF using Puppeteer, adds footers with pdf-lib, and saves the result.
    export async function generatePdf(options: GeneratePdfOptions): Promise<GeneratePdfResult> {
      const {
        content,
        output_path,
        title,
        author,
        template: templateName = 'research-report',
      } = options;
    
      const template = getTemplate(templateName);
      if (!template) {
        throw new Error(
          `Template "${templateName}" not found. Available templates: research-report, plain`
        );
      }
    
      const { html: contentHtml, toc } = parseMarkdown(content);
      const tocHtml = generateTocHtml(toc);
      const date = new Date().toLocaleDateString('en-US', {
        year: 'numeric',
        month: 'long',
        day: 'numeric',
      });
    
      const rendered = template.render(contentHtml, { title, author, date, tocHtml });
      const outputPath = resolveOutputPath(output_path, title);
    
      // Render the full document and (in parallel, if there's any) the front
      // matter just to count pages. Single render of the full HTML keeps internal
      // anchor links (TOC → headings) intact.
      const [mainBuffer, frontMatterPageCount] = await Promise.all([
        renderHtmlToPdf(rendered.html),
        rendered.frontMatter
          ? renderHtmlToPdf(rendered.frontMatter).then(countPdfPages)
          : Promise.resolve(0),
      ]);
    
      const pdfDoc = await PDFDocument.load(mainBuffer);
    
      await drawFooters(pdfDoc, {
        startPage: frontMatterPageCount,
        footerTitle: title ?? '',
      });
    
      if (title) pdfDoc.setTitle(title);
      if (author) pdfDoc.setAuthor(author);
      pdfDoc.setCreator('pdf-it');
      pdfDoc.setProducer('pdf-it');
      pdfDoc.setCreationDate(new Date());
      pdfDoc.setModificationDate(new Date());
    
      const bytes = await pdfDoc.save();
      writeFileSync(outputPath, bytes);
    
      return { path: outputPath, page_count: pdfDoc.getPageCount() };
    }
  • TypeScript interface defining the input options for generate_pdf: content (required string), output_path, title, author, and template.
    export interface GeneratePdfOptions {
      content: string;
      output_path?: string;
      title?: string;
      author?: string;
      template?: string;
    }
  • TypeScript interface defining the result of generate_pdf: path (string) and page_count (number).
    export interface GeneratePdfResult {
      path: string;
      page_count: number;
    }
  • src/index.ts:56-101 (registration)
    Tool registration in the MCP ListToolsRequestSchema handler. Defines the tool name 'generate_pdf', description, and input JSON schema with properties: content (required string), output_path, title, author, and template (enum: research-report or plain).
      tools: [
        {
          name: 'generate_pdf',
          description:
            'Convert markdown into a designed PDF (cover page, auto TOC, page-numbered footer). Use this for any "save/export/print/share as PDF", "make a report", "turn this into a PDF", or /pdf request — do NOT fall back to Chrome headless, cupsfilter, wkhtmltopdf, pandoc, or LaTeX. Templates: research-report (cover + TOC, default) or plain (no cover, no TOC).',
          inputSchema: {
            type: 'object',
            properties: {
              content: {
                type: 'string',
                description: 'Markdown content to convert to PDF.',
              },
              output_path: {
                type: 'string',
                description:
                  'Absolute path for the output PDF. Defaults to ~/Documents/pdf-it/{title}-{timestamp}.pdf',
              },
              title: {
                type: 'string',
                description: 'Document title shown on the cover page and footer.',
              },
              author: {
                type: 'string',
                description: 'Author name shown on the cover page.',
              },
              template: {
                type: 'string',
                enum: ['research-report', 'plain'],
                description:
                  'Template to use. "research-report" (default) adds a cover page and table of contents. "plain" renders body content only.',
                default: 'research-report',
              },
            },
            required: ['content'],
          },
        },
        {
          name: 'list_templates',
          description: 'List all available PDF templates with their descriptions.',
          inputSchema: {
            type: 'object',
            properties: {},
          },
        },
      ],
    }));
  • src/index.ts:103-139 (registration)
    Tool call handler in the MCP CallToolRequestSchema. Dispatches to generatePdf when name === 'generate_pdf', extracts args, validates content, calls the handler, and returns the result or an error.
    server.setRequestHandler(CallToolRequestSchema, async (request) => {
      const { name, arguments: args } = request.params;
    
      if (name === 'generate_pdf') {
        const { content, output_path, title, author, template } = args as {
          content: string;
          output_path?: string;
          title?: string;
          author?: string;
          template?: string;
        };
    
        if (!content || typeof content !== 'string') {
          return {
            content: [{ type: 'text', text: 'Error: content is required and must be a string.' }],
            isError: true,
          };
        }
    
        try {
          const result = await generatePdf({ content, output_path, title, author, template });
          return {
            content: [
              {
                type: 'text',
                text: `PDF created successfully.\n\nPath: ${result.path}\nPages: ${result.page_count}`,
              },
            ],
          };
        } catch (err) {
          const message = err instanceof Error ? err.message : String(err);
          return {
            content: [{ type: 'text', text: `Error generating PDF: ${message}` }],
            isError: true,
          };
        }
      }
Behavior4/5

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

No annotations provided, so description must carry behavioral disclosure. It describes output features (cover, TOC, footer) and template effects. Could mention overwrite behavior or directory requirements, but conversion behavior is mostly implied by the task.

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?

Two sentences efficiently cover purpose, usage guidelines, and template options. No redundant information, front-loaded with key details.

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

Completeness4/5

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

Covers main functionality, output features, and templates. Lacks details on error handling or file overwrite, but for a conversion tool with no output schema, it sufficiently prepares the agent to select and invoke the tool.

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

Parameters4/5

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

Schema coverage is 100% so baseline is 3. Description adds value by explaining template behavior (research-report vs plain) and reinforcing that title appears on cover and footer. Not all parameters get extra context, but overall it enhances understanding.

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 explicitly states the tool converts markdown to a designed PDF with cover page, auto TOC, and page-numbered footer. It distinguishes from the only sibling, list_templates, which is clearly different.

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

Usage Guidelines5/5

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

Provides clear when-to-use scenarios (save/export/print/share as PDF, make a report, /pdf request) and explicitly lists alternatives to avoid (Chrome headless, cupsfilter, wkhtmltopdf, pandoc, LaTeX).

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/mrslbt/pdf-it'

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