autoform-mcp-server 1.2.1 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -6,11 +6,12 @@ import { handleDetectFields, detectFieldsSchema } from './tools/detectFields.js'
6
6
  import { handleFillPdf, fillPdfSchema } from './tools/fillPdf.js';
7
7
  import { handleFillAtCoordinates, fillAtCoordinatesSchema, handleGetPdfInfo, getPdfInfoSchema } from './tools/fillAtCoordinates.js';
8
8
  import { handleFillBatchAtCoordinates, fillBatchAtCoordinatesSchema } from './tools/fillBatchAtCoordinates.js';
9
+ import { handleFillBatchAcroForm, fillBatchAcroFormSchema } from './tools/fillBatchAcroForm.js';
9
10
  import { handleSaveCoordinatesAsTemplate, saveCoordinatesAsTemplateSchema } from './tools/saveCoordinatesAsTemplate.js';
10
11
  import { handleGenerateBatch, generateBatchSchema } from './tools/generateBatch.js';
11
12
  import { handleListTemplates, listTemplatesSchema } from './tools/manageTemplates.js';
12
13
  import { handleImportTemplate, importTemplateSchema } from './tools/importTemplate.js';
13
- const server = new Server({ name: 'autoform', version: '1.2.0' }, { capabilities: { tools: {} } });
14
+ const server = new Server({ name: 'autoform', version: '1.4.0' }, { capabilities: { tools: {} } });
14
15
  // Register all tools
15
16
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
16
17
  tools: [
@@ -20,7 +21,8 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
20
21
  // Single fill
21
22
  fillPdfSchema,
22
23
  fillAtCoordinatesSchema,
23
- // Batch fill
24
+ // Batch fill (preferred for multiple documents)
25
+ fillBatchAcroFormSchema,
24
26
  fillBatchAtCoordinatesSchema,
25
27
  generateBatchSchema,
26
28
  // Template management
@@ -44,6 +46,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
44
46
  return await handleFillAtCoordinates(args);
45
47
  case 'autoform_fill_batch_at_coordinates':
46
48
  return await handleFillBatchAtCoordinates(args);
49
+ case 'autoform_fill_batch_acroform':
50
+ return await handleFillBatchAcroForm(args);
47
51
  case 'autoform_save_coordinates_as_template':
48
52
  return await handleSaveCoordinatesAsTemplate(args);
49
53
  case 'autoform_generate_batch':
@@ -66,4 +70,4 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
66
70
  // Start server
67
71
  const transport = new StdioServerTransport();
68
72
  await server.connect(transport);
69
- console.error('[AutoForm MCP] Server v1.2.0 started — 9 tools registered');
73
+ console.error('[AutoForm MCP] Server v1.4.0 started — 10 tools registered');
@@ -23,11 +23,15 @@ export class BatchGenerator {
23
23
  }
24
24
  const outputPaths = [];
25
25
  const errors = [];
26
+ // If merging, write individuals to temp dir (will be cleaned up)
27
+ const workingDir = mergeIntoSingle
28
+ ? fs.mkdtempSync(path.join(OUTPUT_DIR, '.tmp_batch_'))
29
+ : OUTPUT_DIR;
26
30
  for (let docNum = 0; docNum < documentsNeeded; docNum++) {
27
31
  try {
28
32
  const mappings = this.createMappings(data, template, docNum, mode);
29
33
  const docNumStr = String(docNum + 1).padStart(3, '0');
30
- const outputPath = path.join(OUTPUT_DIR, `${safeName}_${docNumStr}_${timestamp}.pdf`);
34
+ const outputPath = path.join(workingDir, `${safeName}_${docNumStr}_${timestamp}.pdf`);
31
35
  await PdfService.generateSingleDocument(basePdf, mappings, outputPath);
32
36
  outputPaths.push(outputPath);
33
37
  }
@@ -35,11 +39,28 @@ export class BatchGenerator {
35
39
  errors.push(`Doc ${docNum + 1}: ${err instanceof Error ? err.message : String(err)}`);
36
40
  }
37
41
  }
38
- // Merge if requested
42
+ // Merge if requested — and clean up individuals
39
43
  let mergedPath;
40
44
  if (mergeIntoSingle && outputPaths.length > 0) {
41
45
  mergedPath = path.join(OUTPUT_DIR, `${safeName}_merged_${timestamp}.pdf`);
42
46
  await PdfService.mergePdfs(outputPaths, mergedPath);
47
+ // Delete temp files and dir
48
+ for (const p of outputPaths) {
49
+ try {
50
+ fs.unlinkSync(p);
51
+ }
52
+ catch { /* ignore */ }
53
+ }
54
+ try {
55
+ fs.rmdirSync(workingDir);
56
+ }
57
+ catch { /* ignore */ }
58
+ return {
59
+ documentsGenerated: outputPaths.length,
60
+ outputPaths: [],
61
+ mergedPath,
62
+ errors
63
+ };
43
64
  }
44
65
  return {
45
66
  documentsGenerated: outputPaths.length,
@@ -11,6 +11,16 @@ export declare class PdfService {
11
11
  }>;
12
12
  /** Fill AcroForm fields and save */
13
13
  static fillAcroForm(pdfPath: string, values: Record<string, string>, outputPath: string): Promise<number>;
14
+ /**
15
+ * Batch fill AcroForm: generate N PDFs from the same base PDF, one per data row.
16
+ * If merge=true, all outputs are concatenated into a single PDF and temp files are cleaned.
17
+ * Optionally accepts a fieldMap to translate logical data keys → technical AcroForm field names.
18
+ */
19
+ static batchFillAcroForm(pdfPath: string, dataRows: Array<Record<string, string>>, outputDir: string, baseName: string, merge?: boolean, fieldMap?: Record<string, string>): Promise<{
20
+ outputPaths: string[];
21
+ mergedPath?: string;
22
+ errors: string[];
23
+ }>;
14
24
  /** Fill PDF using template coordinates (drawText), same logic as web app's pdfGenerator */
15
25
  static fillWithTemplate(pdfPath: string, fields: FieldBox[], values: Record<string, string>, outputPath: string): Promise<number>;
16
26
  /**
@@ -98,6 +98,86 @@ export class PdfService {
98
98
  fs.writeFileSync(outputPath, savedBytes);
99
99
  return filled;
100
100
  }
101
+ /**
102
+ * Batch fill AcroForm: generate N PDFs from the same base PDF, one per data row.
103
+ * If merge=true, all outputs are concatenated into a single PDF and temp files are cleaned.
104
+ * Optionally accepts a fieldMap to translate logical data keys → technical AcroForm field names.
105
+ */
106
+ static async batchFillAcroForm(pdfPath, dataRows, outputDir, baseName, merge = false, fieldMap) {
107
+ const outputPaths = [];
108
+ const errors = [];
109
+ const timestamp = new Date().toISOString().slice(0, 10);
110
+ fs.mkdirSync(outputDir, { recursive: true });
111
+ const workingDir = merge
112
+ ? fs.mkdtempSync(path.join(outputDir, '.tmp_batch_'))
113
+ : outputDir;
114
+ // Load the base PDF once
115
+ const bytes = fs.readFileSync(pdfPath);
116
+ for (let i = 0; i < dataRows.length; i++) {
117
+ try {
118
+ const row = dataRows[i];
119
+ // Translate logical keys to technical field names if a map is provided
120
+ const resolvedValues = {};
121
+ for (const [key, value] of Object.entries(row)) {
122
+ const technicalName = fieldMap?.[key] ?? key;
123
+ resolvedValues[technicalName] = String(value ?? '');
124
+ }
125
+ const num = String(i + 1).padStart(3, '0');
126
+ const outPath = path.join(workingDir, `${baseName}_${num}_${timestamp}.pdf`);
127
+ // Re-load each iteration to get a fresh form instance
128
+ const pdfDoc = await PDFDocument.load(bytes);
129
+ const form = pdfDoc.getForm();
130
+ for (const [name, value] of Object.entries(resolvedValues)) {
131
+ try {
132
+ const field = form.getField(name);
133
+ if (field instanceof PDFTextField) {
134
+ field.setText(value);
135
+ }
136
+ else if (field instanceof PDFCheckBox) {
137
+ if (['true', '1', 'yes', 'si', 'sí'].includes(value.toLowerCase()))
138
+ field.check();
139
+ else
140
+ field.uncheck();
141
+ }
142
+ else if (field instanceof PDFDropdown) {
143
+ field.select(value);
144
+ }
145
+ else if (field instanceof PDFRadioGroup) {
146
+ field.select(value);
147
+ }
148
+ }
149
+ catch {
150
+ // Field not found — skip silently
151
+ }
152
+ }
153
+ form.flatten();
154
+ const savedBytes = await pdfDoc.save();
155
+ fs.writeFileSync(outPath, savedBytes);
156
+ outputPaths.push(outPath);
157
+ }
158
+ catch (err) {
159
+ errors.push(`Fila ${i + 1}: ${err instanceof Error ? err.message : String(err)}`);
160
+ }
161
+ }
162
+ let mergedPath;
163
+ if (merge && outputPaths.length > 0) {
164
+ mergedPath = path.join(outputDir, `${baseName}_merged_${timestamp}.pdf`);
165
+ await this.mergePdfs(outputPaths, mergedPath);
166
+ // Clean up temp files and dir
167
+ for (const p of outputPaths) {
168
+ try {
169
+ fs.unlinkSync(p);
170
+ }
171
+ catch { /* ignore */ }
172
+ }
173
+ try {
174
+ fs.rmdirSync(workingDir);
175
+ }
176
+ catch { /* ignore */ }
177
+ return { outputPaths: [], mergedPath, errors };
178
+ }
179
+ return { outputPaths, mergedPath, errors };
180
+ }
101
181
  /** Fill PDF using template coordinates (drawText), same logic as web app's pdfGenerator */
102
182
  static async fillWithTemplate(pdfPath, fields, values, outputPath) {
103
183
  const bytes = fs.readFileSync(pdfPath);
@@ -202,11 +282,14 @@ export class PdfService {
202
282
  * Each entry in `dataRows` produces one PDF using the same field positions.
203
283
  */
204
284
  static async batchFillAtCoordinates(pdfPath, fieldDefinitions, dataRows, outputDir, baseName, merge = false) {
205
- const bytes = fs.readFileSync(pdfPath);
206
285
  const outputPaths = [];
207
286
  const errors = [];
208
287
  const timestamp = new Date().toISOString().slice(0, 10);
209
288
  fs.mkdirSync(outputDir, { recursive: true });
289
+ // If merging, write individuals to a temp dir that will be cleaned up
290
+ const workingDir = merge
291
+ ? fs.mkdtempSync(path.join(outputDir, '.tmp_batch_'))
292
+ : outputDir;
210
293
  for (let i = 0; i < dataRows.length; i++) {
211
294
  try {
212
295
  const row = dataRows[i];
@@ -221,7 +304,7 @@ export class PdfService {
221
304
  overflowMode: def.overflowMode
222
305
  })).filter(f => f.text.trim() !== '');
223
306
  const num = String(i + 1).padStart(3, '0');
224
- const outPath = path.join(outputDir, `${baseName}_${num}_${timestamp}.pdf`);
307
+ const outPath = path.join(workingDir, `${baseName}_${num}_${timestamp}.pdf`);
225
308
  await this.fillAtCoordinates(pdfPath, fields, outPath);
226
309
  outputPaths.push(outPath);
227
310
  }
@@ -233,6 +316,19 @@ export class PdfService {
233
316
  if (merge && outputPaths.length > 0) {
234
317
  mergedPath = path.join(outputDir, `${baseName}_merged_${timestamp}.pdf`);
235
318
  await this.mergePdfs(outputPaths, mergedPath);
319
+ // Clean up temp files and dir
320
+ for (const p of outputPaths) {
321
+ try {
322
+ fs.unlinkSync(p);
323
+ }
324
+ catch { /* ignore */ }
325
+ }
326
+ try {
327
+ fs.rmdirSync(workingDir);
328
+ }
329
+ catch { /* ignore */ }
330
+ // Clear individual paths from response since they no longer exist
331
+ return { outputPaths: [], mergedPath, errors };
236
332
  }
237
333
  return { outputPaths, mergedPath, errors };
238
334
  }
@@ -5,7 +5,18 @@ export const detectFieldsSchema = {
5
5
 
6
6
  CUANDO USAR: El PDF es un formulario donde puedes hacer click y escribir. Si el PDF es una imagen/diseño estatico (certificado, diploma), esta tool dira "sin campos" — en ese caso usa autoform_get_pdf_info + analisis visual.
7
7
 
8
- DESPUES DE DETECTAR: Usa autoform_fill_pdf para llenar los campos encontrados.`,
8
+ ⚠️ IMPORTANTE Los nombres tecnicos NO son los labels visibles:
9
+ - Esta tool devuelve nombres TECNICOS internos del PDF (ej: "Text1", "fld_001", "untitled_field"), que pueden NO coincidir con los labels visibles ("Nombre:", "Apellido:", "DNI:").
10
+ - NO asumas el significado de cada campo por su nombre tecnico ni por su posicion.
11
+ - DESPUES de detectar los campos, DEBES mirar el PDF visualmente (ya puedes ver PDFs como imagenes) y correlacionar cada campo tecnico con su label visible usando las coordenadas (x, y, page) que devuelve esta tool.
12
+ - Solo DESPUES de esa correlacion visual procede a llenar con los datos correctos.
13
+
14
+ FLUJO RECOMENDADO:
15
+ 1. autoform_detect_fields → obtienes lista de campos tecnicos con coordenadas
16
+ 2. MIRAS EL PDF VISUALMENTE → asocias cada coordenada con el label visible cercano
17
+ 3. Decides que dato va en que campo tecnico
18
+ 4. Para UN documento: autoform_fill_pdf con field_values={"nombre_tecnico": "valor"}
19
+ 5. Para MULTIPLES documentos (batch): autoform_fill_batch_acroform con data_rows y field_map si las claves son logicas.`,
9
20
  inputSchema: {
10
21
  type: 'object',
11
22
  properties: {
@@ -4,9 +4,17 @@ import { PdfService } from '../services/pdfService.js';
4
4
  const OUTPUT_DIR = path.join(os.homedir(), '.autoform-mcp', 'output');
5
5
  export const fillAtCoordinatesSchema = {
6
6
  name: 'autoform_fill_at_coordinates',
7
- description: `Escribe texto en posiciones exactas de un PDF usando coordenadas en puntos PDF (origen: esquina inferior-izquierda).
7
+ description: `Escribe texto en posiciones exactas de UN PDF usando coordenadas en puntos PDF (origen: esquina inferior-izquierda).
8
8
  Ideal cuando Claude analiza visualmente un PDF y determina dónde colocar el texto.
9
9
 
10
+ ⚠️ ESTA TOOL ES PARA UN UNICO DOCUMENTO. Si necesitas generar multiples PDFs con los mismos campos pero datos diferentes, USA autoform_fill_batch_at_coordinates (mas eficiente, una sola llamada).
11
+
12
+ IMPORTANTE — Codificación de texto:
13
+ - El texto se pasa EXACTAMENTE como lo envías. NO simplifiques ni modifiques caracteres.
14
+ - Acentos españoles (á é í ó ú ñ Ñ ü) están soportados completamente.
15
+ - Nombres como "Sofía", "Benítez", "Peña" deben pasarse con sus tildes intactas.
16
+ - Solo se filtran: emojis, caracteres de control invisibles, y patrones de script malicioso.
17
+
10
18
  FLUJO RECOMENDADO:
11
19
  1. Primero usa autoform_get_pdf_info para obtener las dimensiones de cada página
12
20
  2. Basándote en el análisis visual del PDF, calcula las coordenadas en puntos PDF
@@ -0,0 +1,52 @@
1
+ export declare const fillBatchAcroFormSchema: {
2
+ name: string;
3
+ description: string;
4
+ inputSchema: {
5
+ type: "object";
6
+ properties: {
7
+ pdf_path: {
8
+ type: string;
9
+ description: string;
10
+ };
11
+ data_rows: {
12
+ type: string;
13
+ description: string;
14
+ items: {
15
+ type: string;
16
+ additionalProperties: {
17
+ type: string;
18
+ };
19
+ };
20
+ };
21
+ field_map: {
22
+ type: string;
23
+ description: string;
24
+ additionalProperties: {
25
+ type: string;
26
+ };
27
+ };
28
+ merge_into_single: {
29
+ type: string;
30
+ description: string;
31
+ };
32
+ output_dir: {
33
+ type: string;
34
+ description: string;
35
+ };
36
+ };
37
+ required: string[];
38
+ };
39
+ };
40
+ export declare function handleFillBatchAcroForm(args: any): Promise<{
41
+ content: {
42
+ type: "text";
43
+ text: string;
44
+ }[];
45
+ isError: boolean;
46
+ } | {
47
+ content: {
48
+ type: "text";
49
+ text: string;
50
+ }[];
51
+ isError?: undefined;
52
+ }>;
@@ -0,0 +1,96 @@
1
+ import * as path from 'path';
2
+ import * as os from 'os';
3
+ import { PdfService } from '../services/pdfService.js';
4
+ const OUTPUT_DIR = path.join(os.homedir(), '.autoform-mcp', 'output');
5
+ export const fillBatchAcroFormSchema = {
6
+ name: 'autoform_fill_batch_acroform',
7
+ description: `Genera MULTIPLES PDFs a partir de un PDF con campos AcroForm (formulario interactivo) + array de datos. Una sola llamada genera N documentos.
8
+
9
+ CUANDO USAR: El PDF base tiene campos AcroForm (detectados con autoform_detect_fields) y el usuario quiere llenarlos con datos de varias personas/filas. Escalable a cientos de documentos en una sola llamada.
10
+
11
+ NO USAR SI: El PDF es estatico (sin AcroForm) → usa autoform_fill_batch_at_coordinates.
12
+ NO USAR SI: Solo necesitas llenar UN documento → usa autoform_fill_pdf.
13
+
14
+ IMPORTANTE sobre nombres de campos:
15
+ - Los campos AcroForm tienen nombres TECNICOS (ej: "Text1", "fld_nombre", "field_001") que NO siempre coinciden con los labels visibles del PDF.
16
+ - DESPUES de llamar autoform_detect_fields, DEBES MIRAR EL PDF VISUALMENTE para correlacionar cada campo tecnico con su label visible segun sus coordenadas.
17
+ - Si las claves de tus data_rows NO coinciden con los nombres tecnicos, usa el parametro field_map para traducir: { "nombre_logico": "nombre_tecnico" }
18
+ - Ejemplo: si el campo tecnico es "Text1" pero visualmente corresponde al label "Nombre:", pasa field_map={"nombre": "Text1"} y data_rows=[{"nombre": "Juan"}]
19
+
20
+ MERGE: Si merge_into_single=true, se genera SOLO el PDF unificado (los individuales son temporales y se borran automaticamente).
21
+
22
+ CODIFICACION: Los datos se escriben EXACTAMENTE como los envias. Acentos espanoles (a e i o u n N u con tildes) estan completamente soportados. NO simplifiques caracteres.`,
23
+ inputSchema: {
24
+ type: 'object',
25
+ properties: {
26
+ pdf_path: {
27
+ type: 'string',
28
+ description: 'Ruta absoluta al PDF con AcroForm'
29
+ },
30
+ data_rows: {
31
+ type: 'array',
32
+ description: 'Array de objetos, cada objeto es un documento. Claves = nombre del campo (tecnico o logico si usas field_map), valores = texto a escribir.',
33
+ items: {
34
+ type: 'object',
35
+ additionalProperties: { type: 'string' }
36
+ }
37
+ },
38
+ field_map: {
39
+ type: 'object',
40
+ description: '(Opcional) Mapeo de nombres logicos a nombres tecnicos de AcroForm. Formato: {"nombre_logico": "nombre_tecnico"}. Usalo cuando las claves de data_rows son mas legibles que los nombres tecnicos reales del PDF.',
41
+ additionalProperties: { type: 'string' }
42
+ },
43
+ merge_into_single: {
44
+ type: 'boolean',
45
+ description: '(Opcional) Si true, une todos los PDFs en uno solo y elimina los individuales. Default: false.'
46
+ },
47
+ output_dir: {
48
+ type: 'string',
49
+ description: '(Opcional) Directorio de salida. Default: ~/.autoform-mcp/output/'
50
+ }
51
+ },
52
+ required: ['pdf_path', 'data_rows']
53
+ }
54
+ };
55
+ export async function handleFillBatchAcroForm(args) {
56
+ const { pdf_path, output_dir, merge_into_single = false } = args;
57
+ let data_rows = args.data_rows;
58
+ if (typeof data_rows === 'string') {
59
+ try {
60
+ data_rows = JSON.parse(data_rows);
61
+ }
62
+ catch { /* keep */ }
63
+ }
64
+ let field_map = args.field_map;
65
+ if (typeof field_map === 'string') {
66
+ try {
67
+ field_map = JSON.parse(field_map);
68
+ }
69
+ catch { /* keep */ }
70
+ }
71
+ if (!data_rows || !Array.isArray(data_rows) || data_rows.length === 0) {
72
+ return {
73
+ content: [{ type: 'text', text: JSON.stringify({ error: true, message: 'data_rows es requerido (array de objetos con datos)' }, null, 2) }],
74
+ isError: true
75
+ };
76
+ }
77
+ const outDir = output_dir || OUTPUT_DIR;
78
+ const baseName = path.basename(pdf_path, '.pdf').replace(/[^a-zA-Z0-9_\-]/g, '_');
79
+ const result = await PdfService.batchFillAcroForm(pdf_path, data_rows, outDir, baseName, merge_into_single, field_map);
80
+ return {
81
+ content: [{
82
+ type: 'text',
83
+ text: JSON.stringify({
84
+ documents_generated: merge_into_single ? data_rows.length - result.errors.length : result.outputPaths.length,
85
+ output_paths: result.outputPaths,
86
+ merged_path: result.mergedPath,
87
+ errors: result.errors,
88
+ message: result.mergedPath
89
+ ? `Generados ${data_rows.length - result.errors.length} documentos unidos en: ${result.mergedPath}`
90
+ : result.outputPaths.length > 0
91
+ ? `Generados ${result.outputPaths.length} documentos en ${outDir}`
92
+ : `Error: ${result.errors.join(', ')}`
93
+ }, null, 2)
94
+ }]
95
+ };
96
+ }
@@ -8,6 +8,13 @@ export const fillBatchAtCoordinatesSchema = {
8
8
 
9
9
  CUANDO USAR: El usuario pasa un PDF (certificado, diploma, constancia) y una LISTA de datos (nombres, fechas, etc.) y quiere generar un PDF por cada fila de datos.
10
10
 
11
+ IMPORTANTE — Codificación de texto:
12
+ - Los datos se escriben EXACTAMENTE como los envías. NO simplifiques ni modifiques caracteres.
13
+ - Acentos españoles (á é í ó ú ñ Ñ ü) están soportados completamente.
14
+ - Nombres como "Sofía", "Benítez", "Peña" deben pasarse con sus tildes intactas.
15
+
16
+ MERGE: Si merge_into_single=true, se genera SOLO el PDF unificado (los individuales son temporales y se borran automáticamente).
17
+
11
18
  FLUJO:
12
19
  1. Usa autoform_get_pdf_info para obtener las dimensiones de la pagina
13
20
  2. Analiza visualmente el PDF para determinar donde van los campos
@@ -5,11 +5,16 @@ import { TemplateStore } from '../services/templateStore.js';
5
5
  const OUTPUT_DIR = path.join(os.homedir(), '.autoform-mcp', 'output');
6
6
  export const fillPdfSchema = {
7
7
  name: 'autoform_fill_pdf',
8
- description: `Llena UN PDF con valores. Detecta automaticamente si tiene AcroForm o usa un template guardado.
8
+ description: `Llena UN SOLO PDF con valores. Detecta automaticamente si tiene AcroForm o usa un template guardado.
9
9
 
10
- CUANDO USAR: Quieres llenar UN SOLO documento y ya tienes AcroForm o un template guardado.
11
- NO USAR SI: El PDF es estatico sin template usa autoform_fill_at_coordinates.
12
- NO USAR SI: Quieres generar MULTIPLES documentos usa autoform_generate_batch o autoform_fill_batch_at_coordinates.`,
10
+ ⚠️ ESTA TOOL ES PARA UN UNICO DOCUMENTO. Si necesitas generar 2 o mas documentos, USA UNA TOOL BATCH (mas eficiente, menos llamadas):
11
+ - PDF con AcroForm + multiples filasautoform_fill_batch_acroform
12
+ - PDF estatico + multiples filas → autoform_fill_batch_at_coordinates
13
+ - Template guardado + multiples filas → autoform_generate_batch
14
+
15
+ CUANDO USAR: Solo cuando el usuario pide llenar UN documento especifico.
16
+ NO USAR PARA BATCH: Nunca llames esta tool en loop para generar multiples PDFs — usa las tools batch que existen para eso.
17
+ NO USAR SI: El PDF es estatico sin template → usa autoform_fill_at_coordinates.`,
13
18
  inputSchema: {
14
19
  type: 'object',
15
20
  properties: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autoform-mcp-server",
3
- "version": "1.2.1",
3
+ "version": "1.4.0",
4
4
  "description": "MCP server for bulk PDF form filling. Detect fields, fill templates, and generate hundreds of PDFs from data — directly from Claude.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",