autoform-mcp-server 1.2.0 → 1.3.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/README.md +15 -1
- package/dist/services/batchGenerator.js +26 -3
- package/dist/services/pdfService.js +18 -2
- package/dist/services/templateStore.js +3 -1
- package/dist/tools/fillAtCoordinates.js +9 -2
- package/dist/tools/fillBatchAtCoordinates.js +10 -2
- package/dist/tools/fillPdf.js +3 -2
- package/package.json +1 -3
- package/output/.gitkeep +0 -0
- package/templates/.gitkeep +0 -0
package/README.md
CHANGED
|
@@ -123,9 +123,23 @@ When a template has repeated fields (e.g., 3 fields named "nombre"):
|
|
|
123
123
|
- **`sequential`** (default): Each field consumes a different data row. 9 rows + 3 fields = 3 documents.
|
|
124
124
|
- **`repeat`**: One row fills ALL fields with the same name. 9 rows = 9 documents.
|
|
125
125
|
|
|
126
|
+
## Storage
|
|
127
|
+
|
|
128
|
+
All data is stored in `~/.autoform-mcp/` on the user's machine:
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
~/.autoform-mcp/
|
|
132
|
+
├── templates/ ← Saved templates (JSON + PDF files)
|
|
133
|
+
└── output/ ← Generated PDFs
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
- **Windows:** `C:\Users\<name>\.autoform-mcp\`
|
|
137
|
+
- **macOS/Linux:** `~/.autoform-mcp/`
|
|
138
|
+
|
|
139
|
+
You can override the output path in any tool using the `output_path` or `output_dir` parameter.
|
|
140
|
+
|
|
126
141
|
## How It Works
|
|
127
142
|
|
|
128
|
-
- Templates and generated PDFs are stored locally in the MCP server directory
|
|
129
143
|
- Uses `pdf-lib` for PDF manipulation (no external services)
|
|
130
144
|
- All processing happens locally — no data leaves your machine
|
|
131
145
|
- Zero vulnerabilities — only 3 dependencies
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import { PdfService } from './pdfService.js';
|
|
4
|
-
|
|
4
|
+
import * as os from 'os';
|
|
5
|
+
const AUTOFORM_HOME = path.join(os.homedir(), '.autoform-mcp');
|
|
6
|
+
const OUTPUT_DIR = path.join(AUTOFORM_HOME, 'output');
|
|
5
7
|
/**
|
|
6
8
|
* Batch PDF generation with distribution logic.
|
|
7
9
|
* Adapts web app's distributionEngine + pdfGenerator for Node.js.
|
|
@@ -21,11 +23,15 @@ export class BatchGenerator {
|
|
|
21
23
|
}
|
|
22
24
|
const outputPaths = [];
|
|
23
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;
|
|
24
30
|
for (let docNum = 0; docNum < documentsNeeded; docNum++) {
|
|
25
31
|
try {
|
|
26
32
|
const mappings = this.createMappings(data, template, docNum, mode);
|
|
27
33
|
const docNumStr = String(docNum + 1).padStart(3, '0');
|
|
28
|
-
const outputPath = path.join(
|
|
34
|
+
const outputPath = path.join(workingDir, `${safeName}_${docNumStr}_${timestamp}.pdf`);
|
|
29
35
|
await PdfService.generateSingleDocument(basePdf, mappings, outputPath);
|
|
30
36
|
outputPaths.push(outputPath);
|
|
31
37
|
}
|
|
@@ -33,11 +39,28 @@ export class BatchGenerator {
|
|
|
33
39
|
errors.push(`Doc ${docNum + 1}: ${err instanceof Error ? err.message : String(err)}`);
|
|
34
40
|
}
|
|
35
41
|
}
|
|
36
|
-
// Merge if requested
|
|
42
|
+
// Merge if requested — and clean up individuals
|
|
37
43
|
let mergedPath;
|
|
38
44
|
if (mergeIntoSingle && outputPaths.length > 0) {
|
|
39
45
|
mergedPath = path.join(OUTPUT_DIR, `${safeName}_merged_${timestamp}.pdf`);
|
|
40
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
|
+
};
|
|
41
64
|
}
|
|
42
65
|
return {
|
|
43
66
|
documentsGenerated: outputPaths.length,
|
|
@@ -202,11 +202,14 @@ export class PdfService {
|
|
|
202
202
|
* Each entry in `dataRows` produces one PDF using the same field positions.
|
|
203
203
|
*/
|
|
204
204
|
static async batchFillAtCoordinates(pdfPath, fieldDefinitions, dataRows, outputDir, baseName, merge = false) {
|
|
205
|
-
const bytes = fs.readFileSync(pdfPath);
|
|
206
205
|
const outputPaths = [];
|
|
207
206
|
const errors = [];
|
|
208
207
|
const timestamp = new Date().toISOString().slice(0, 10);
|
|
209
208
|
fs.mkdirSync(outputDir, { recursive: true });
|
|
209
|
+
// If merging, write individuals to a temp dir that will be cleaned up
|
|
210
|
+
const workingDir = merge
|
|
211
|
+
? fs.mkdtempSync(path.join(outputDir, '.tmp_batch_'))
|
|
212
|
+
: outputDir;
|
|
210
213
|
for (let i = 0; i < dataRows.length; i++) {
|
|
211
214
|
try {
|
|
212
215
|
const row = dataRows[i];
|
|
@@ -221,7 +224,7 @@ export class PdfService {
|
|
|
221
224
|
overflowMode: def.overflowMode
|
|
222
225
|
})).filter(f => f.text.trim() !== '');
|
|
223
226
|
const num = String(i + 1).padStart(3, '0');
|
|
224
|
-
const outPath = path.join(
|
|
227
|
+
const outPath = path.join(workingDir, `${baseName}_${num}_${timestamp}.pdf`);
|
|
225
228
|
await this.fillAtCoordinates(pdfPath, fields, outPath);
|
|
226
229
|
outputPaths.push(outPath);
|
|
227
230
|
}
|
|
@@ -233,6 +236,19 @@ export class PdfService {
|
|
|
233
236
|
if (merge && outputPaths.length > 0) {
|
|
234
237
|
mergedPath = path.join(outputDir, `${baseName}_merged_${timestamp}.pdf`);
|
|
235
238
|
await this.mergePdfs(outputPaths, mergedPath);
|
|
239
|
+
// Clean up temp files and dir
|
|
240
|
+
for (const p of outputPaths) {
|
|
241
|
+
try {
|
|
242
|
+
fs.unlinkSync(p);
|
|
243
|
+
}
|
|
244
|
+
catch { /* ignore */ }
|
|
245
|
+
}
|
|
246
|
+
try {
|
|
247
|
+
fs.rmdirSync(workingDir);
|
|
248
|
+
}
|
|
249
|
+
catch { /* ignore */ }
|
|
250
|
+
// Clear individual paths from response since they no longer exist
|
|
251
|
+
return { outputPaths: [], mergedPath, errors };
|
|
236
252
|
}
|
|
237
253
|
return { outputPaths, mergedPath, errors };
|
|
238
254
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
|
-
|
|
3
|
+
import * as os from 'os';
|
|
4
|
+
const AUTOFORM_HOME = path.join(os.homedir(), '.autoform-mcp');
|
|
5
|
+
const TEMPLATES_DIR = path.join(AUTOFORM_HOME, 'templates');
|
|
4
6
|
/**
|
|
5
7
|
* Filesystem-based template storage.
|
|
6
8
|
* Templates are stored as individual JSON files in the templates/ directory.
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import * as path from 'path';
|
|
2
|
+
import * as os from 'os';
|
|
2
3
|
import { PdfService } from '../services/pdfService.js';
|
|
3
|
-
const OUTPUT_DIR = path.
|
|
4
|
+
const OUTPUT_DIR = path.join(os.homedir(), '.autoform-mcp', 'output');
|
|
4
5
|
export const fillAtCoordinatesSchema = {
|
|
5
6
|
name: 'autoform_fill_at_coordinates',
|
|
6
7
|
description: `Escribe texto en posiciones exactas de un PDF usando coordenadas en puntos PDF (origen: esquina inferior-izquierda).
|
|
7
8
|
Ideal cuando Claude analiza visualmente un PDF y determina dónde colocar el texto.
|
|
8
9
|
|
|
10
|
+
IMPORTANTE — Codificación de texto:
|
|
11
|
+
- El texto se pasa EXACTAMENTE como lo envías. NO simplifiques ni modifiques caracteres.
|
|
12
|
+
- Acentos españoles (á é í ó ú ñ Ñ ü) están soportados completamente.
|
|
13
|
+
- Nombres como "Sofía", "Benítez", "Peña" deben pasarse con sus tildes intactas.
|
|
14
|
+
- Solo se filtran: emojis, caracteres de control invisibles, y patrones de script malicioso.
|
|
15
|
+
|
|
9
16
|
FLUJO RECOMENDADO:
|
|
10
17
|
1. Primero usa autoform_get_pdf_info para obtener las dimensiones de cada página
|
|
11
18
|
2. Basándote en el análisis visual del PDF, calcula las coordenadas en puntos PDF
|
|
@@ -44,7 +51,7 @@ SISTEMA DE COORDENADAS:
|
|
|
44
51
|
},
|
|
45
52
|
output_path: {
|
|
46
53
|
type: 'string',
|
|
47
|
-
description: '(Opcional) Ruta donde guardar el PDF.
|
|
54
|
+
description: '(Opcional) Ruta donde guardar el PDF. Default: ~/.autoform-mcp/output/'
|
|
48
55
|
}
|
|
49
56
|
},
|
|
50
57
|
required: ['pdf_path', 'fields']
|
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
import * as path from 'path';
|
|
2
|
+
import * as os from 'os';
|
|
2
3
|
import { PdfService } from '../services/pdfService.js';
|
|
3
|
-
const OUTPUT_DIR = path.
|
|
4
|
+
const OUTPUT_DIR = path.join(os.homedir(), '.autoform-mcp', 'output');
|
|
4
5
|
export const fillBatchAtCoordinatesSchema = {
|
|
5
6
|
name: 'autoform_fill_batch_at_coordinates',
|
|
6
7
|
description: `Genera MULTIPLES PDFs a partir de un mismo PDF base, escribiendo datos diferentes en las mismas posiciones de cada copia.
|
|
7
8
|
|
|
8
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.
|
|
9
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
|
+
|
|
10
18
|
FLUJO:
|
|
11
19
|
1. Usa autoform_get_pdf_info para obtener las dimensiones de la pagina
|
|
12
20
|
2. Analiza visualmente el PDF para determinar donde van los campos
|
|
@@ -54,7 +62,7 @@ SISTEMA DE COORDENADAS: Origen (0,0) = esquina inferior-izquierda. Y aumenta hac
|
|
|
54
62
|
},
|
|
55
63
|
output_dir: {
|
|
56
64
|
type: 'string',
|
|
57
|
-
description: '(Opcional) Directorio de salida. Default: output/'
|
|
65
|
+
description: '(Opcional) Directorio de salida. Default: ~/.autoform-mcp/output/'
|
|
58
66
|
}
|
|
59
67
|
},
|
|
60
68
|
required: ['pdf_path', 'field_definitions', 'data_rows']
|
package/dist/tools/fillPdf.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as path from 'path';
|
|
2
|
+
import * as os from 'os';
|
|
2
3
|
import { PdfService } from '../services/pdfService.js';
|
|
3
4
|
import { TemplateStore } from '../services/templateStore.js';
|
|
4
|
-
const OUTPUT_DIR = path.
|
|
5
|
+
const OUTPUT_DIR = path.join(os.homedir(), '.autoform-mcp', 'output');
|
|
5
6
|
export const fillPdfSchema = {
|
|
6
7
|
name: 'autoform_fill_pdf',
|
|
7
8
|
description: `Llena UN PDF con valores. Detecta automaticamente si tiene AcroForm o usa un template guardado.
|
|
@@ -23,7 +24,7 @@ NO USAR SI: Quieres generar MULTIPLES documentos → usa autoform_generate_batch
|
|
|
23
24
|
},
|
|
24
25
|
output_path: {
|
|
25
26
|
type: 'string',
|
|
26
|
-
description: '(Opcional) Ruta donde guardar el PDF llenado.
|
|
27
|
+
description: '(Opcional) Ruta donde guardar el PDF llenado. Default: ~/.autoform-mcp/output/'
|
|
27
28
|
},
|
|
28
29
|
template_name: {
|
|
29
30
|
type: 'string',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "autoform-mcp-server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.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",
|
|
@@ -36,8 +36,6 @@
|
|
|
36
36
|
},
|
|
37
37
|
"files": [
|
|
38
38
|
"dist",
|
|
39
|
-
"templates/.gitkeep",
|
|
40
|
-
"output/.gitkeep",
|
|
41
39
|
"README.md"
|
|
42
40
|
],
|
|
43
41
|
"dependencies": {
|
package/output/.gitkeep
DELETED
|
File without changes
|
package/templates/.gitkeep
DELETED
|
File without changes
|