autoform-mcp-server 1.2.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 +135 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +69 -0
- package/dist/services/batchGenerator.d.ts +10 -0
- package/dist/services/batchGenerator.js +105 -0
- package/dist/services/pdfService.d.ts +65 -0
- package/dist/services/pdfService.js +412 -0
- package/dist/services/templateStore.d.ts +20 -0
- package/dist/services/templateStore.js +137 -0
- package/dist/tools/detectFields.d.ts +20 -0
- package/dist/tools/detectFields.js +41 -0
- package/dist/tools/fillAtCoordinates.d.ts +94 -0
- package/dist/tools/fillAtCoordinates.js +113 -0
- package/dist/tools/fillBatchAtCoordinates.d.ts +88 -0
- package/dist/tools/fillBatchAtCoordinates.js +102 -0
- package/dist/tools/fillPdf.d.ts +42 -0
- package/dist/tools/fillPdf.js +97 -0
- package/dist/tools/generateBatch.d.ts +46 -0
- package/dist/tools/generateBatch.js +106 -0
- package/dist/tools/importTemplate.d.ts +20 -0
- package/dist/tools/importTemplate.js +36 -0
- package/dist/tools/manageTemplates.d.ts +15 -0
- package/dist/tools/manageTemplates.js +44 -0
- package/dist/tools/saveCoordinatesAsTemplate.d.ts +78 -0
- package/dist/tools/saveCoordinatesAsTemplate.js +141 -0
- package/dist/types.d.ts +94 -0
- package/dist/types.js +2 -0
- package/output/.gitkeep +0 -0
- package/package.json +52 -0
- package/templates/.gitkeep +0 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { TemplateStore } from '../services/templateStore.js';
|
|
2
|
+
import { BatchGenerator } from '../services/batchGenerator.js';
|
|
3
|
+
export const generateBatchSchema = {
|
|
4
|
+
name: 'autoform_generate_batch',
|
|
5
|
+
description: `Genera MULTIPLES PDFs usando un TEMPLATE GUARDADO + datos.
|
|
6
|
+
|
|
7
|
+
CUANDO USAR: Ya tienes un template guardado (via autoform_import_template o autoform_save_coordinates_as_template) y quieres generar muchos documentos.
|
|
8
|
+
NO USAR SI: No tienes template guardado → usa autoform_fill_batch_at_coordinates (no requiere template).
|
|
9
|
+
|
|
10
|
+
DATOS: Pasa los datos como array de objetos JSON en el parametro "data". Si el usuario tiene un archivo (Excel, CSV, etc.), leelo primero y convierte a JSON.
|
|
11
|
+
|
|
12
|
+
Modos de distribucion:
|
|
13
|
+
- "sequential": campos repetidos consumen filas diferentes (para tablas/listas)
|
|
14
|
+
- "repeat": una fila se repite en todos los campos con el mismo nombre (para certificados)`,
|
|
15
|
+
inputSchema: {
|
|
16
|
+
type: 'object',
|
|
17
|
+
properties: {
|
|
18
|
+
template_name: {
|
|
19
|
+
type: 'string',
|
|
20
|
+
description: 'Nombre del template guardado a usar'
|
|
21
|
+
},
|
|
22
|
+
data: {
|
|
23
|
+
type: 'array',
|
|
24
|
+
description: 'Datos como array de objetos. Ejemplo: [{"nombre": "Juan", "dni": "123"}, {"nombre": "Maria", "dni": "456"}]',
|
|
25
|
+
items: {
|
|
26
|
+
type: 'object',
|
|
27
|
+
additionalProperties: { type: 'string' }
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
distribution_mode: {
|
|
31
|
+
type: 'string',
|
|
32
|
+
enum: ['sequential', 'repeat'],
|
|
33
|
+
description: 'Modo de distribucion: "sequential" o "repeat". Default: sequential.'
|
|
34
|
+
},
|
|
35
|
+
merge_into_single: {
|
|
36
|
+
type: 'boolean',
|
|
37
|
+
description: 'Si es true, une todos los PDFs en uno solo. Default: false.'
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
required: ['template_name', 'data']
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
export async function handleGenerateBatch(args) {
|
|
44
|
+
const { template_name, distribution_mode = 'sequential', merge_into_single = false } = args;
|
|
45
|
+
// data puede llegar como string JSON o como array
|
|
46
|
+
let data = args.data;
|
|
47
|
+
if (typeof data === 'string') {
|
|
48
|
+
try {
|
|
49
|
+
data = JSON.parse(data);
|
|
50
|
+
}
|
|
51
|
+
catch { /* keep */ }
|
|
52
|
+
}
|
|
53
|
+
if (!data || !Array.isArray(data) || data.length === 0) {
|
|
54
|
+
return {
|
|
55
|
+
content: [{ type: 'text', text: JSON.stringify({ error: true, message: 'Proporcione "data" como array de objetos. Si tiene un archivo, lealo primero y convierta a JSON.' }, null, 2) }],
|
|
56
|
+
isError: true
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
// Load template
|
|
60
|
+
const template = TemplateStore.load(template_name);
|
|
61
|
+
if (!template) {
|
|
62
|
+
return {
|
|
63
|
+
content: [{ type: 'text', text: JSON.stringify({ error: true, message: `Template "${template_name}" no encontrado. Use autoform_list_templates para ver los disponibles.` }, null, 2) }],
|
|
64
|
+
isError: true
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
// Convert to ParsedData
|
|
68
|
+
const parsedData = data.map((row) => {
|
|
69
|
+
const p = {};
|
|
70
|
+
for (const [k, v] of Object.entries(row)) {
|
|
71
|
+
if (v === null || v === undefined || v === '')
|
|
72
|
+
p[k] = null;
|
|
73
|
+
else {
|
|
74
|
+
const num = Number(v);
|
|
75
|
+
p[k] = !isNaN(num) && isFinite(num) ? num : String(v);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return p;
|
|
79
|
+
});
|
|
80
|
+
// Validate columns match
|
|
81
|
+
const requiredCols = template.analysis.requiredColumns;
|
|
82
|
+
const providedCols = Object.keys(parsedData[0] || {});
|
|
83
|
+
const missing = requiredCols.filter((c) => !providedCols.includes(c));
|
|
84
|
+
if (missing.length > 0) {
|
|
85
|
+
return {
|
|
86
|
+
content: [{ type: 'text', text: JSON.stringify({ error: true, message: `Columnas faltantes: ${missing.join(', ')}. El template requiere: ${requiredCols.join(', ')}. Los datos tienen: ${providedCols.join(', ')}` }, null, 2) }],
|
|
87
|
+
isError: true
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
// Generate
|
|
91
|
+
const result = await BatchGenerator.generate(template, parsedData, distribution_mode, merge_into_single);
|
|
92
|
+
return {
|
|
93
|
+
content: [{
|
|
94
|
+
type: 'text',
|
|
95
|
+
text: JSON.stringify({
|
|
96
|
+
documents_generated: result.documentsGenerated,
|
|
97
|
+
output_paths: result.outputPaths,
|
|
98
|
+
merged_path: result.mergedPath,
|
|
99
|
+
errors: result.errors,
|
|
100
|
+
message: result.documentsGenerated > 0
|
|
101
|
+
? `Generados ${result.documentsGenerated} documentos.${result.mergedPath ? ` PDF unificado: ${result.mergedPath}` : ''}`
|
|
102
|
+
: `Error: ${result.errors.join(', ')}`
|
|
103
|
+
}, null, 2)
|
|
104
|
+
}]
|
|
105
|
+
};
|
|
106
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare const importTemplateSchema: {
|
|
2
|
+
name: string;
|
|
3
|
+
description: string;
|
|
4
|
+
inputSchema: {
|
|
5
|
+
type: "object";
|
|
6
|
+
properties: {
|
|
7
|
+
json_file_path: {
|
|
8
|
+
type: string;
|
|
9
|
+
description: string;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
required: string[];
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
export declare function handleImportTemplate(args: any): Promise<{
|
|
16
|
+
content: {
|
|
17
|
+
type: "text";
|
|
18
|
+
text: string;
|
|
19
|
+
}[];
|
|
20
|
+
}>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { TemplateStore } from '../services/templateStore.js';
|
|
2
|
+
export const importTemplateSchema = {
|
|
3
|
+
name: 'autoform_import_template',
|
|
4
|
+
description: `Importa una plantilla JSON exportada desde la app web de AutoForm.
|
|
5
|
+
|
|
6
|
+
CUANDO USAR: El usuario creo la plantilla visualmente en la app web y exporto el JSON. Este JSON contiene el PDF en base64 + coordenadas de campos.
|
|
7
|
+
ALTERNATIVA: Si no usas la app web, usa autoform_save_coordinates_as_template para crear templates directamente desde Claude.`,
|
|
8
|
+
inputSchema: {
|
|
9
|
+
type: 'object',
|
|
10
|
+
properties: {
|
|
11
|
+
json_file_path: {
|
|
12
|
+
type: 'string',
|
|
13
|
+
description: 'Ruta absoluta al archivo JSON exportado desde la app web de AutoForm'
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
required: ['json_file_path']
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
export async function handleImportTemplate(args) {
|
|
20
|
+
const { json_file_path } = args;
|
|
21
|
+
const template = TemplateStore.importFromWebApp(json_file_path);
|
|
22
|
+
return {
|
|
23
|
+
content: [{
|
|
24
|
+
type: 'text',
|
|
25
|
+
text: JSON.stringify({
|
|
26
|
+
success: true,
|
|
27
|
+
template_name: template.name,
|
|
28
|
+
fields_count: template.fields.length,
|
|
29
|
+
pages: template.pdfInfo.pageCount,
|
|
30
|
+
required_columns: template.analysis.requiredColumns,
|
|
31
|
+
pdf_saved_at: template.pdfPath,
|
|
32
|
+
message: `Template "${template.name}" importado exitosamente. ${template.fields.length} campos en ${template.pdfInfo.pageCount} pagina(s). Columnas requeridas: ${template.analysis.requiredColumns.join(', ')}`
|
|
33
|
+
}, null, 2)
|
|
34
|
+
}]
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare const listTemplatesSchema: {
|
|
2
|
+
name: string;
|
|
3
|
+
description: string;
|
|
4
|
+
inputSchema: {
|
|
5
|
+
type: "object";
|
|
6
|
+
properties: {};
|
|
7
|
+
required: never[];
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
export declare function handleListTemplates(): Promise<{
|
|
11
|
+
content: {
|
|
12
|
+
type: "text";
|
|
13
|
+
text: string;
|
|
14
|
+
}[];
|
|
15
|
+
}>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { TemplateStore } from '../services/templateStore.js';
|
|
2
|
+
export const listTemplatesSchema = {
|
|
3
|
+
name: 'autoform_list_templates',
|
|
4
|
+
description: `Lista las plantillas guardadas en AutoForm. Muestra nombre, campos requeridos, paginas y fecha.
|
|
5
|
+
|
|
6
|
+
CUANDO USAR: Antes de autoform_generate_batch para verificar que el template existe y ver sus columnas requeridas.`,
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: 'object',
|
|
9
|
+
properties: {},
|
|
10
|
+
required: []
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
export async function handleListTemplates() {
|
|
14
|
+
const templates = TemplateStore.list();
|
|
15
|
+
if (templates.length === 0) {
|
|
16
|
+
return {
|
|
17
|
+
content: [{
|
|
18
|
+
type: 'text',
|
|
19
|
+
text: JSON.stringify({
|
|
20
|
+
total: 0,
|
|
21
|
+
templates: [],
|
|
22
|
+
message: 'No hay plantillas guardadas. Importe una con autoform_import_template o cree una desde la app web de AutoForm.'
|
|
23
|
+
}, null, 2)
|
|
24
|
+
}]
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
content: [{
|
|
29
|
+
type: 'text',
|
|
30
|
+
text: JSON.stringify({
|
|
31
|
+
total: templates.length,
|
|
32
|
+
templates: templates.map(t => ({
|
|
33
|
+
name: t.name,
|
|
34
|
+
description: t.description,
|
|
35
|
+
pages: t.pageCount,
|
|
36
|
+
fields: t.fieldCount,
|
|
37
|
+
required_columns: t.requiredColumns,
|
|
38
|
+
created: t.createdAt
|
|
39
|
+
})),
|
|
40
|
+
message: `${templates.length} plantilla(s) disponibles.`
|
|
41
|
+
}, null, 2)
|
|
42
|
+
}]
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
export declare const saveCoordinatesAsTemplateSchema: {
|
|
2
|
+
name: string;
|
|
3
|
+
description: string;
|
|
4
|
+
inputSchema: {
|
|
5
|
+
type: "object";
|
|
6
|
+
properties: {
|
|
7
|
+
pdf_path: {
|
|
8
|
+
type: string;
|
|
9
|
+
description: string;
|
|
10
|
+
};
|
|
11
|
+
template_name: {
|
|
12
|
+
type: string;
|
|
13
|
+
description: string;
|
|
14
|
+
};
|
|
15
|
+
description: {
|
|
16
|
+
type: string;
|
|
17
|
+
description: string;
|
|
18
|
+
};
|
|
19
|
+
fields: {
|
|
20
|
+
type: string;
|
|
21
|
+
description: string;
|
|
22
|
+
items: {
|
|
23
|
+
type: string;
|
|
24
|
+
properties: {
|
|
25
|
+
label: {
|
|
26
|
+
type: string;
|
|
27
|
+
description: string;
|
|
28
|
+
};
|
|
29
|
+
page: {
|
|
30
|
+
type: string;
|
|
31
|
+
description: string;
|
|
32
|
+
};
|
|
33
|
+
x: {
|
|
34
|
+
type: string;
|
|
35
|
+
description: string;
|
|
36
|
+
};
|
|
37
|
+
y: {
|
|
38
|
+
type: string;
|
|
39
|
+
description: string;
|
|
40
|
+
};
|
|
41
|
+
width: {
|
|
42
|
+
type: string;
|
|
43
|
+
description: string;
|
|
44
|
+
};
|
|
45
|
+
height: {
|
|
46
|
+
type: string;
|
|
47
|
+
description: string;
|
|
48
|
+
};
|
|
49
|
+
fontSize: {
|
|
50
|
+
type: string;
|
|
51
|
+
description: string;
|
|
52
|
+
};
|
|
53
|
+
overflowMode: {
|
|
54
|
+
type: string;
|
|
55
|
+
enum: string[];
|
|
56
|
+
description: string;
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
required: string[];
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
required: string[];
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
export declare function handleSaveCoordinatesAsTemplate(args: any): Promise<{
|
|
67
|
+
content: {
|
|
68
|
+
type: "text";
|
|
69
|
+
text: string;
|
|
70
|
+
}[];
|
|
71
|
+
isError: boolean;
|
|
72
|
+
} | {
|
|
73
|
+
content: {
|
|
74
|
+
type: "text";
|
|
75
|
+
text: string;
|
|
76
|
+
}[];
|
|
77
|
+
isError?: undefined;
|
|
78
|
+
}>;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { PDFDocument } from 'pdf-lib';
|
|
4
|
+
import { TemplateStore } from '../services/templateStore.js';
|
|
5
|
+
export const saveCoordinatesAsTemplateSchema = {
|
|
6
|
+
name: 'autoform_save_coordinates_as_template',
|
|
7
|
+
description: `Guarda un PDF + coordenadas de campos como plantilla reutilizable.
|
|
8
|
+
|
|
9
|
+
CUANDO USAR: Despues de que Claude analizo visualmente un PDF y determino las coordenadas de los campos. Guardar como template permite reutilizar esas posiciones con autoform_generate_batch sin recalcular cada vez.
|
|
10
|
+
|
|
11
|
+
FLUJO TIPICO:
|
|
12
|
+
1. Claude analiza el PDF visualmente + autoform_get_pdf_info
|
|
13
|
+
2. Determina coordenadas de campos
|
|
14
|
+
3. Llama esta tool para guardar como template
|
|
15
|
+
4. Futuras generaciones usan autoform_generate_batch con el template guardado`,
|
|
16
|
+
inputSchema: {
|
|
17
|
+
type: 'object',
|
|
18
|
+
properties: {
|
|
19
|
+
pdf_path: {
|
|
20
|
+
type: 'string',
|
|
21
|
+
description: 'Ruta absoluta al PDF base'
|
|
22
|
+
},
|
|
23
|
+
template_name: {
|
|
24
|
+
type: 'string',
|
|
25
|
+
description: 'Nombre para la plantilla (ej: "certificado_seminario")'
|
|
26
|
+
},
|
|
27
|
+
description: {
|
|
28
|
+
type: 'string',
|
|
29
|
+
description: '(Opcional) Descripcion de la plantilla'
|
|
30
|
+
},
|
|
31
|
+
fields: {
|
|
32
|
+
type: 'array',
|
|
33
|
+
description: 'Campos con sus coordenadas en puntos PDF. El "label" se convierte en el nombre de columna requerido.',
|
|
34
|
+
items: {
|
|
35
|
+
type: 'object',
|
|
36
|
+
properties: {
|
|
37
|
+
label: { type: 'string', description: 'Nombre del campo (sera el nombre de la columna en el Excel/datos)' },
|
|
38
|
+
page: { type: 'number', description: 'Pagina (1-indexed)' },
|
|
39
|
+
x: { type: 'number', description: 'X en puntos desde la izquierda' },
|
|
40
|
+
y: { type: 'number', description: 'Y en puntos desde abajo (origen PDF)' },
|
|
41
|
+
width: { type: 'number', description: 'Ancho en puntos' },
|
|
42
|
+
height: { type: 'number', description: 'Alto en puntos' },
|
|
43
|
+
fontSize: { type: 'number', description: '(Opcional) Tamaño de fuente deseado' },
|
|
44
|
+
overflowMode: { type: 'string', enum: ['shrink', 'wrap'], description: '(Opcional) Default: shrink' }
|
|
45
|
+
},
|
|
46
|
+
required: ['label', 'page', 'x', 'y', 'width', 'height']
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
required: ['pdf_path', 'template_name', 'fields']
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
export async function handleSaveCoordinatesAsTemplate(args) {
|
|
54
|
+
const { pdf_path, template_name, description = '' } = args;
|
|
55
|
+
let fields = args.fields;
|
|
56
|
+
if (typeof fields === 'string') {
|
|
57
|
+
try {
|
|
58
|
+
fields = JSON.parse(fields);
|
|
59
|
+
}
|
|
60
|
+
catch { /* keep */ }
|
|
61
|
+
}
|
|
62
|
+
if (!fields || !Array.isArray(fields) || fields.length === 0) {
|
|
63
|
+
return { content: [{ type: 'text', text: JSON.stringify({ error: true, message: 'fields es requerido' }, null, 2) }], isError: true };
|
|
64
|
+
}
|
|
65
|
+
// Read PDF to get page info
|
|
66
|
+
const pdfBytes = fs.readFileSync(pdf_path);
|
|
67
|
+
const pdfDoc = await PDFDocument.load(pdfBytes, { ignoreEncryption: true });
|
|
68
|
+
const pageCount = pdfDoc.getPageCount();
|
|
69
|
+
const pageSize = pdfDoc.getPage(0).getSize();
|
|
70
|
+
// Copy PDF to templates directory
|
|
71
|
+
const TEMPLATES_DIR = path.resolve(import.meta.dirname, '../../templates');
|
|
72
|
+
fs.mkdirSync(TEMPLATES_DIR, { recursive: true });
|
|
73
|
+
const safeName = template_name.replace(/[^a-zA-Z0-9_\-]/g, '_');
|
|
74
|
+
const pdfDest = path.join(TEMPLATES_DIR, `${safeName}.pdf`);
|
|
75
|
+
fs.copyFileSync(pdf_path, pdfDest);
|
|
76
|
+
// Convert coordinate-based fields to FieldBox format (percentage-based for web app compatibility)
|
|
77
|
+
const fieldBoxes = fields.map((f, i) => ({
|
|
78
|
+
id: `field_${Date.now()}_${i}`,
|
|
79
|
+
// Convert points to percentages relative to page size
|
|
80
|
+
x: (f.x / pageSize.width) * 100,
|
|
81
|
+
y: ((pageSize.height - f.y - f.height) / pageSize.height) * 100, // Flip Y (PDF bottom-left → top-left)
|
|
82
|
+
width: (f.width / pageSize.width) * 100,
|
|
83
|
+
height: (f.height / pageSize.height) * 100,
|
|
84
|
+
pageNumber: f.page,
|
|
85
|
+
name: `Campo_${i + 1}`,
|
|
86
|
+
fieldName: f.label,
|
|
87
|
+
fieldType: 'text',
|
|
88
|
+
fontSize: f.fontSize,
|
|
89
|
+
overflowMode: f.overflowMode
|
|
90
|
+
}));
|
|
91
|
+
// Build analysis
|
|
92
|
+
const groups = {};
|
|
93
|
+
for (const f of fieldBoxes) {
|
|
94
|
+
if (!groups[f.fieldName])
|
|
95
|
+
groups[f.fieldName] = { fieldName: f.fieldName, count: 0, fields: [], pageDistribution: {} };
|
|
96
|
+
groups[f.fieldName].count++;
|
|
97
|
+
groups[f.fieldName].fields.push(f);
|
|
98
|
+
groups[f.fieldName].pageDistribution[f.pageNumber] = (groups[f.fieldName].pageDistribution[f.pageNumber] || 0) + 1;
|
|
99
|
+
}
|
|
100
|
+
const fieldGroups = Object.values(groups);
|
|
101
|
+
const counts = fieldGroups.map((g) => g.count);
|
|
102
|
+
const template = {
|
|
103
|
+
id: `template_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
|
|
104
|
+
name: template_name,
|
|
105
|
+
description,
|
|
106
|
+
createdAt: new Date().toISOString(),
|
|
107
|
+
updatedAt: new Date().toISOString(),
|
|
108
|
+
version: '1.0.0',
|
|
109
|
+
pdfPath: pdfDest,
|
|
110
|
+
pdfInfo: {
|
|
111
|
+
name: path.basename(pdf_path),
|
|
112
|
+
size: pdfBytes.length,
|
|
113
|
+
pageCount
|
|
114
|
+
},
|
|
115
|
+
fields: fieldBoxes,
|
|
116
|
+
analysis: {
|
|
117
|
+
totalFields: fieldBoxes.length,
|
|
118
|
+
fieldGroups,
|
|
119
|
+
documentCapacity: fieldBoxes.length,
|
|
120
|
+
maxFieldCount: Math.max(...counts, 0),
|
|
121
|
+
minFieldCount: Math.min(...counts, 0),
|
|
122
|
+
requiredColumns: fieldGroups.map((g) => g.fieldName),
|
|
123
|
+
isBalanced: new Set(counts).size <= 1
|
|
124
|
+
},
|
|
125
|
+
metadata: { totalUsages: 0 }
|
|
126
|
+
};
|
|
127
|
+
TemplateStore.save(template);
|
|
128
|
+
return {
|
|
129
|
+
content: [{
|
|
130
|
+
type: 'text',
|
|
131
|
+
text: JSON.stringify({
|
|
132
|
+
success: true,
|
|
133
|
+
template_name: template.name,
|
|
134
|
+
fields_count: fieldBoxes.length,
|
|
135
|
+
required_columns: template.analysis.requiredColumns,
|
|
136
|
+
pages: pageCount,
|
|
137
|
+
message: `Template "${template_name}" guardado con ${fieldBoxes.length} campo(s). Columnas: ${template.analysis.requiredColumns.join(', ')}. Ahora puede usar autoform_generate_batch con template_name="${template_name}" para generacion masiva.`
|
|
138
|
+
}, null, 2)
|
|
139
|
+
}]
|
|
140
|
+
};
|
|
141
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
export interface FieldBox {
|
|
2
|
+
id: string;
|
|
3
|
+
x: number;
|
|
4
|
+
y: number;
|
|
5
|
+
width: number;
|
|
6
|
+
height: number;
|
|
7
|
+
pageNumber: number;
|
|
8
|
+
name: string;
|
|
9
|
+
fieldName: string;
|
|
10
|
+
fieldType?: string;
|
|
11
|
+
fontSize?: number;
|
|
12
|
+
overflowMode?: 'shrink' | 'wrap';
|
|
13
|
+
}
|
|
14
|
+
export interface FieldGroup {
|
|
15
|
+
fieldName: string;
|
|
16
|
+
count: number;
|
|
17
|
+
fields: FieldBox[];
|
|
18
|
+
pageDistribution: {
|
|
19
|
+
[pageNumber: number]: number;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export interface TemplateAnalysis {
|
|
23
|
+
totalFields: number;
|
|
24
|
+
fieldGroups: FieldGroup[];
|
|
25
|
+
documentCapacity: number;
|
|
26
|
+
maxFieldCount: number;
|
|
27
|
+
minFieldCount: number;
|
|
28
|
+
requiredColumns: string[];
|
|
29
|
+
isBalanced: boolean;
|
|
30
|
+
}
|
|
31
|
+
export interface SavedTemplate {
|
|
32
|
+
id: string;
|
|
33
|
+
name: string;
|
|
34
|
+
description: string;
|
|
35
|
+
createdAt: string;
|
|
36
|
+
updatedAt: string;
|
|
37
|
+
version: string;
|
|
38
|
+
pdfPath: string;
|
|
39
|
+
pdfInfo: {
|
|
40
|
+
name: string;
|
|
41
|
+
size: number;
|
|
42
|
+
pageCount: number;
|
|
43
|
+
dataUrl?: string;
|
|
44
|
+
};
|
|
45
|
+
fields: FieldBox[];
|
|
46
|
+
analysis: TemplateAnalysis;
|
|
47
|
+
metadata: {
|
|
48
|
+
totalUsages: number;
|
|
49
|
+
lastUsedAt?: string;
|
|
50
|
+
tags?: string[];
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
export interface TemplatePreview {
|
|
54
|
+
name: string;
|
|
55
|
+
description: string;
|
|
56
|
+
createdAt: string;
|
|
57
|
+
fieldCount: number;
|
|
58
|
+
pageCount: number;
|
|
59
|
+
requiredColumns: string[];
|
|
60
|
+
}
|
|
61
|
+
export interface DetectedField {
|
|
62
|
+
name: string;
|
|
63
|
+
type: string;
|
|
64
|
+
page: number;
|
|
65
|
+
x: number;
|
|
66
|
+
y: number;
|
|
67
|
+
width: number;
|
|
68
|
+
height: number;
|
|
69
|
+
value?: string;
|
|
70
|
+
}
|
|
71
|
+
export interface ParsedData {
|
|
72
|
+
[columnName: string]: string | number | null;
|
|
73
|
+
}
|
|
74
|
+
export type DistributionMode = 'sequential' | 'repeat';
|
|
75
|
+
export interface FieldDataMapping {
|
|
76
|
+
fieldId: string;
|
|
77
|
+
fieldName: string;
|
|
78
|
+
fieldIndex: number;
|
|
79
|
+
pageNumber: number;
|
|
80
|
+
dataValue: any;
|
|
81
|
+
dataRowIndex: number;
|
|
82
|
+
isEmpty: boolean;
|
|
83
|
+
}
|
|
84
|
+
export interface DataChunk {
|
|
85
|
+
chunkNumber: number;
|
|
86
|
+
fieldsMapping: FieldDataMapping[];
|
|
87
|
+
isComplete: boolean;
|
|
88
|
+
}
|
|
89
|
+
export interface GenerationResult {
|
|
90
|
+
documentsGenerated: number;
|
|
91
|
+
outputPaths: string[];
|
|
92
|
+
mergedPath?: string;
|
|
93
|
+
errors: string[];
|
|
94
|
+
}
|
package/dist/types.js
ADDED
package/output/.gitkeep
ADDED
|
File without changes
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "autoform-mcp-server",
|
|
3
|
+
"version": "1.2.0",
|
|
4
|
+
"description": "MCP server for bulk PDF form filling. Detect fields, fill templates, and generate hundreds of PDFs from data — directly from Claude.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"autoform-mcp-server": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"start": "node dist/index.js",
|
|
13
|
+
"prepublishOnly": "npm run build"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"mcp",
|
|
17
|
+
"mcp-server",
|
|
18
|
+
"model-context-protocol",
|
|
19
|
+
"claude",
|
|
20
|
+
"pdf",
|
|
21
|
+
"pdf-fill",
|
|
22
|
+
"form-filling",
|
|
23
|
+
"bulk-pdf",
|
|
24
|
+
"autoform",
|
|
25
|
+
"template",
|
|
26
|
+
"certificate-generator"
|
|
27
|
+
],
|
|
28
|
+
"author": "YeferAndersson",
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "https://github.com/YeferAndersson/auto-form"
|
|
33
|
+
},
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=18"
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"dist",
|
|
39
|
+
"templates/.gitkeep",
|
|
40
|
+
"output/.gitkeep",
|
|
41
|
+
"README.md"
|
|
42
|
+
],
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
45
|
+
"jszip": "^3.10.1",
|
|
46
|
+
"pdf-lib": "^1.17.1"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/node": "^20.0.0",
|
|
50
|
+
"typescript": "^5.2.2"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
File without changes
|