pdf_fill_form
Fill PDF form fields including text, checkboxes, dropdowns, and radio buttons. Supports non-Latin characters with custom fonts and can flatten forms to make them non-editable.
Instructions
Fill form fields in a PDF. Supports text, checkbox, dropdown, and radio fields. Provide fontPath for non-Latin text (Arabic, CJK, etc.).
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| filePath | Yes | Absolute path to the PDF file with form fields | |
| fields | Yes | Object mapping field names to values. Strings for text/dropdown/radio, booleans for checkboxes. | |
| outputPath | Yes | Absolute path for the filled output PDF | |
| flatten | No | Flatten form fields after filling (makes them non-editable). Defaults to false. | |
| fontPath | No | Absolute path to a .ttf/.otf font file for non-Latin character support. |
Implementation Reference
- src/tools/create.ts:251-367 (handler)The handler implementation for pdf_fill_form.
async ({ filePath, fields, outputPath, flatten, fontPath }) => { try { const BLOCKED_KEYS = ['__proto__', 'constructor', 'prototype']; if (Object.keys(fields).some(k => BLOCKED_KEYS.includes(k))) { return toolError('Input contains prohibited key names.'); } const resolvedPath = await validatePdfPath(filePath); await validateFileSize(resolvedPath); const resolvedOutput = await validateOutputPath(outputPath, filePath); const pdfDoc = await loadExistingPdf(resolvedPath); let form; try { form = pdfDoc.getForm(); form.deleteXFA(); } catch { return toolError( "This PDF does not contain any form fields. Use pdf_get_form_fields to inspect a PDF's form structure." ); } const allFields = form.getFields(); if (allFields.length === 0) { return toolError( "This PDF does not contain any form fields. Use pdf_get_form_fields to inspect a PDF's form structure." ); } // Check for non-Latin characters when no fontPath provided if (!fontPath) { for (const [name, value] of Object.entries(fields)) { if (typeof value === "string") { const hasNonLatin = [...value].some( (ch) => ch.charCodeAt(0) > 255 ); if (hasNonLatin) { return toolError( `Non-Latin characters detected in field '${name}'. Provide a fontPath parameter pointing to a .ttf/.otf font file that supports these characters.` ); } } } } // Load custom font if fontPath provided let customFont: PDFFont | undefined; if (fontPath) { const resolvedFontPath = await validateFontPath(fontPath); await validateFileSize(resolvedFontPath); const fontBuffer = await readFile(resolvedFontPath); const fontBytes = toUint8Array(fontBuffer); // @ts-ignore — fontkit module namespace satisfies the Fontkit interface structurally const fontkit = await import("fontkit"); pdfDoc.registerFontkit( fontkit as unknown as Parameters<typeof pdfDoc.registerFontkit>[0] ); customFont = await pdfDoc.embedFont(fontBytes); } const availableNames = allFields.map((f) => f.getName()); let filledCount = 0; for (const [name, value] of Object.entries(fields)) { const field = allFields.find((f) => f.getName() === name); if (!field) { return toolError( `Field '${name}' not found in PDF. Available fields: ${availableNames.join(", ")}` ); } if (field instanceof pdfLib.PDFTextField) { if (customFont) { field.updateAppearances(customFont); } field.setText(String(value)); } else if (field instanceof pdfLib.PDFCheckBox) { if (value) { field.check(); } else { field.uncheck(); } } else if (field instanceof pdfLib.PDFDropdown) { field.select(String(value)); } else if (field instanceof pdfLib.PDFRadioGroup) { field.select(String(value)); } else { return toolError( `Field '${name}' is of unsupported type for filling. Supported types: text, checkbox, dropdown, radio.` ); } filledCount++; } if (flatten) { form.flatten(); } await savePdf(pdfDoc, resolvedOutput); const fileSize = await getFileSize(resolvedOutput); return toolSuccess({ outputPath: resolvedOutput, filledFields: filledCount, flattened: flatten ?? false, fileSize, }); } catch (error) { return toolError( error instanceof Error ? error.message : String(error) ); } } ); - src/tools/create.ts:214-243 (schema)The input schema definition for pdf_fill_form.
inputSchema: z .object({ filePath: z .string() .max(4096) .describe("Absolute path to the PDF file with form fields"), fields: z .record(z.union([z.string(), z.boolean()])) .describe( "Object mapping field names to values. Strings for text/dropdown/radio, booleans for checkboxes." ), outputPath: z .string() .max(4096) .describe("Absolute path for the filled output PDF"), flatten: z .boolean() .optional() .describe( "Flatten form fields after filling (makes them non-editable). Defaults to false." ), fontPath: z .string() .max(4096) .optional() .describe( "Absolute path to a .ttf/.otf font file for non-Latin character support." ), }) .strict(), - src/tools/create.ts:209-250 (registration)The registration block for pdf_fill_form in src/tools/create.ts.
server.registerTool( "pdf_fill_form", { description: "Fill form fields in a PDF. Supports text, checkbox, dropdown, and radio fields. Provide fontPath for non-Latin text (Arabic, CJK, etc.).", inputSchema: z .object({ filePath: z .string() .max(4096) .describe("Absolute path to the PDF file with form fields"), fields: z .record(z.union([z.string(), z.boolean()])) .describe( "Object mapping field names to values. Strings for text/dropdown/radio, booleans for checkboxes." ), outputPath: z .string() .max(4096) .describe("Absolute path for the filled output PDF"), flatten: z .boolean() .optional() .describe( "Flatten form fields after filling (makes them non-editable). Defaults to false." ), fontPath: z .string() .max(4096) .optional() .describe( "Absolute path to a .ttf/.otf font file for non-Latin character support." ), }) .strict(), annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false, }, },