fill_hwp_template
Fill placeholders in an HWPX template by providing a JSON object of replacements. Specify the file path and optional output path to generate the filled document.
Instructions
Fill multiple placeholders in an HWPX template. replacements is a JSON object string, e.g. {"{{name}}":"Kim","{{company}}":"Acme"}. v0.2: .hwpx only. Args: file_path, replacements, output_path (optional).
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| file_path | Yes | ||
| replacements | Yes | ||
| output_path | No |
Implementation Reference
- src/tools/write.ts:93-138 (handler)The main handler function that executes the 'fill_hwp_template' tool logic. Parses replacements JSON, validates file existence and format, computes output path, calls mutateHwpxText to perform the text replacements in the .hwpx zip XML, and returns a summary string.
export async function fillHwpTemplate(args: FillTemplateArgs): Promise<string> { let map: Record<string, string>; try { map = JSON.parse(args.replacements); if (typeof map !== "object" || map === null || Array.isArray(map)) { throw new Error("replacements must be a JSON object of string→string"); } } catch (e) { return `replacements JSON 파싱 오류 (JSON parse error): ${(e as Error).message}`; } if (!existsSync(args.file_path)) { return `파일을 찾을 수 없습니다 (file not found): ${args.file_path}`; } try { getFormatFromPath(args.file_path); } catch (e) { return (e as Error).message; } const outputPath = args.output_path && args.output_path.length > 0 ? args.output_path : defaultOutputPath(args.file_path, "filled"); try { ensureSameFormat(args.file_path, outputPath); } catch (e) { return (e as Error).message; } const reject = rejectIfHwp(args.file_path); if (reject) return reject; try { const r = await mutateHwpxText(args.file_path, outputPath, map); const lines = [ `저장 완료 (saved): ${outputPath}`, `총 ${r.total}건 치환 (${r.total} replacements)`, "", ]; for (const [k, n] of Object.entries(r.perKey)) { lines.push(` '${k}' → ${n}건`); } return lines.join("\n"); } catch (e) { return `치환 오류 (replace error): ${(e as Error).message}`; } } - src/core/hwpx-mutate.ts:19-85 (helper)Core helper that reads a .hwpx file (zip), applies text replacements inside <hp:t> XML text nodes in section*.xml and header.xml, then writes the modified .hwpx. This is the low-level mutation engine used by fillHwpTemplate.
export async function mutateHwpxText( inputPath: string, outputPath: string, replacements: Record<string, string> ): Promise<MutationResult> { const bytes = await readFile(inputPath); const zip = await JSZip.loadAsync(bytes); const counts: Record<string, number> = {}; let total = 0; // Mutate every XML carrying body content: section*.xml, header.xml (HF blocks), // and master pages. mimetype/content.hpf are excluded. const sectionFiles = Object.keys(zip.files).filter((n) => /^Contents\/(section\d+|header)\.xml$/i.test(n) ); for (const fname of sectionFiles) { const file = zip.files[fname]; let xml = await file.async("string"); for (const [key, value] of Object.entries(replacements)) { const escapedXml = xmlEscape(value); // Only replace inside <hp:t>...</hp:t> text nodes, to avoid touching // tag names or attribute values. const pattern = new RegExp( "(<hp:t(?:\\s[^>]*)?>)([^<]*)(" + escapeRegex(key) + ")([^<]*)(</hp:t>)", "g" ); let didReplace = true; while (didReplace) { didReplace = false; xml = xml.replace(pattern, (_match, open, pre, _hit, post, close) => { counts[key] = (counts[key] ?? 0) + 1; total += 1; didReplace = true; return open + pre + escapedXml + post + close; }); // Loop because a single node may contain multiple occurrences; // String.replace with /g consumes from current position, so one pass // catches all non-overlapping. Set didReplace=false after one pass. break; } } zip.file(fname, xml); } // mimetype must remain stored (uncompressed); JSZip preserves per-file // compression options if we re-set them. if (zip.files["mimetype"]) { const mt = await zip.files["mimetype"].async("string"); zip.file("mimetype", mt, { compression: "STORE" }); } const out = await zip.generateAsync({ type: "uint8array", compression: "DEFLATE", compressionOptions: { level: 6 }, }); await writeFile(outputPath, out); for (const k of Object.keys(replacements)) { if (counts[k] === undefined) counts[k] = 0; } return { total, perKey: counts }; } - src/tools/write.ts:20-24 (schema)TypeScript interface defining the input arguments for fillHwpTemplate: file_path (string), replacements (JSON string), and optional output_path.
export interface FillTemplateArgs { file_path: string; replacements: string; output_path?: string; } - src/server.ts:107-120 (registration)Registration of the fill_hwp_template tool in the TOOLS array with name, description, and JSON Schema input schema. Required args: file_path, replacements.
{ name: "fill_hwp_template", description: "Fill multiple placeholders in an HWPX template. `replacements` is a JSON object string, e.g. {\"{{name}}\":\"Kim\",\"{{company}}\":\"Acme\"}. v0.2: .hwpx only. Args: file_path, replacements, output_path (optional).", inputSchema: { type: "object", properties: { file_path: { type: "string" }, replacements: { type: "string" }, output_path: { type: "string" }, }, required: ["file_path", "replacements"], }, }, - src/server.ts:516-516 (registration)Maps the tool name 'fill_hwp_template' to the fillHwpTemplate handler function in the HANDLERS record used by the CallToolRequestSchema handler.
fill_hwp_template: fillHwpTemplate,