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
| Name | Required | Description | Default |
|---|---|---|---|
| content | Yes | Markdown content to convert to PDF. | |
| output_path | No | Absolute path for the output PDF. Defaults to ~/Documents/pdf-it/{title}-{timestamp}.pdf | |
| title | No | Document title shown on the cover page and footer. | |
| author | No | Author name shown on the cover page. | |
| template | No | Template to use. "research-report" (default) adds a cover page and table of contents. "plain" renders body content only. | research-report |
Implementation Reference
- src/generator.ts:210-265 (handler)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() }; } - src/types.ts:1-7 (schema)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; } - src/types.ts:9-12 (schema)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, }; } }