@zincapp/mcp-server 1.9.0 → 1.11.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/tools/index.js
CHANGED
|
@@ -17,6 +17,7 @@ import { registerLinkGestorAuthorizations } from './link-gestor-authorizations.j
|
|
|
17
17
|
import { registerProposeNt } from './propose-nt.js';
|
|
18
18
|
import { registerLookupAuthorization } from './lookup-authorization.js';
|
|
19
19
|
import { registerNtFinalGestor } from './nt-final-gestor.js';
|
|
20
|
+
import { registerUpsertGestor } from './upsert-gestor.js';
|
|
20
21
|
export function registerAllTools(server, client) {
|
|
21
22
|
registerSearchDocs(server, client);
|
|
22
23
|
registerReadDoc(server, client);
|
|
@@ -37,4 +38,5 @@ export function registerAllTools(server, client) {
|
|
|
37
38
|
registerProposeNt(server, client);
|
|
38
39
|
registerLookupAuthorization(server, client);
|
|
39
40
|
registerNtFinalGestor(server, client);
|
|
41
|
+
registerUpsertGestor(server, client);
|
|
40
42
|
}
|
|
@@ -9,4 +9,15 @@ export declare function validatePdf(bytes: Buffer): {
|
|
|
9
9
|
ok: false;
|
|
10
10
|
error: string;
|
|
11
11
|
};
|
|
12
|
+
interface LinkDto {
|
|
13
|
+
orden: number;
|
|
14
|
+
clientId: number;
|
|
15
|
+
centerId: number;
|
|
16
|
+
operacion: string;
|
|
17
|
+
}
|
|
12
18
|
export declare function registerNtFinalGestor(server: McpServer, client: ZincAppClient): void;
|
|
19
|
+
/** Una línea por eslabón, en orden documental. Puro y exportado para test. */
|
|
20
|
+
export declare function formatChain(cadena: LinkDto[]): string;
|
|
21
|
+
/** Los defectos de forma que E3L reportará al emitir. NO bloquean el vínculo. Puro y exportado para test. */
|
|
22
|
+
export declare function formatWarnings(avisos: string[]): string;
|
|
23
|
+
export {};
|
|
@@ -44,10 +44,11 @@ function resolvePdfBase64(pdfPath, pdfBase64) {
|
|
|
44
44
|
}
|
|
45
45
|
const errorContent = (text) => ({ content: [{ type: 'text', text }] });
|
|
46
46
|
export function registerNtFinalGestor(server, client) {
|
|
47
|
-
server.tool('preview_nt_final_gestor', 'DRY-RUN: extrae por IA
|
|
48
|
-
'
|
|
49
|
-
'
|
|
50
|
-
'(
|
|
47
|
+
server.tool('preview_nt_final_gestor', 'DRY-RUN: extrae por IA la CADENA COMPLETA de gestores finales (hasta 3 eslabones, p.ej. ' +
|
|
48
|
+
'D15→D13→D9), la resuelve contra las entidades/centros de tu tenant (fail-closed sólo por ' +
|
|
49
|
+
'IDENTIDAD: el gestor debe estar dado de alta — si no, usa upsert_entidad/upsert_centro), avisa ' +
|
|
50
|
+
'(sin bloquear) de lo que le falte a cada centro para exportar E3L, y devuelve una PROPUESTA ' +
|
|
51
|
+
'opaca (con proposalId) SIN escribir nada. Aporta el PDF con pdfPath (ruta local) o pdfBase64 ' +
|
|
51
52
|
'(exactamente uno). Revisa la propuesta y, si es correcta, aplícala con apply_nt_final_gestor. ' +
|
|
52
53
|
'Corre bajo tu elevación de operador (company derivada de ella).', {
|
|
53
54
|
ntNumero: z.string().describe('El número de la NT objetivo (p.ej. "NT30280008283420220064303")'),
|
|
@@ -66,20 +67,26 @@ export function registerNtFinalGestor(server, client) {
|
|
|
66
67
|
`**Razón:** ${p.razon ?? 'desconocida'}\n\n` +
|
|
67
68
|
`Terna actual en la NT: ${fmtTriple(p.ternaActual)}. No se ha persistido nada.`);
|
|
68
69
|
}
|
|
70
|
+
const cadena = p.cadena ?? [];
|
|
71
|
+
const avisos = p.avisosE3l ?? [];
|
|
69
72
|
const text = `# Propuesta · NT ${p.ntNumero}\n\n` +
|
|
70
73
|
`- **proposalId:** \`${p.proposalId}\`\n` +
|
|
71
74
|
`- **Nº de NT en el documento:** ${p.ntNumeroDocumento ?? '—'}\n` +
|
|
72
|
-
`- **Gestor final propuesto:** ${fmtTriple(p.gestorFinal)}\n` +
|
|
73
75
|
`- **Terna actual en la NT:** ${fmtTriple(p.ternaActual)}\n\n` +
|
|
74
|
-
|
|
76
|
+
`**Cadena de gestores finales propuesta (${cadena.length} eslabón/es):**\n` +
|
|
77
|
+
`${formatChain(cadena)}\n\n` +
|
|
78
|
+
`El **último eslabón** es el que verá el libro de registro.` +
|
|
79
|
+
formatWarnings(avisos) +
|
|
80
|
+
`\n\nSi es correcto, aplica con \`apply_nt_final_gestor({ proposalId: "${p.proposalId}" })\`. ` +
|
|
75
81
|
`La propuesta caduca (TTL) — aplícala pronto.`;
|
|
76
82
|
return { content: [{ type: 'text', text }] };
|
|
77
83
|
});
|
|
78
84
|
server.tool('apply_nt_final_gestor', 'Aplica una propuesta de gestor final previamente revisada (por proposalId de preview_nt_final_gestor). ' +
|
|
79
|
-
'NO re-ejecuta la IA ni re-transporta el PDF. Escribe la
|
|
80
|
-
'
|
|
81
|
-
'
|
|
82
|
-
'sobrescribe). Sólo aplica
|
|
85
|
+
'NO re-ejecuta la IA ni re-transporta el PDF. Escribe la CADENA (orden 1..N) en la NT, crea los ' +
|
|
86
|
+
'LER que falten en los centros de la cadena, y deriva la terna del último eslabón, bajo tu ' +
|
|
87
|
+
'elevación de operador, por el framework (hooks + auditoría), con regla complete-what\'s-missing ' +
|
|
88
|
+
'(si la NT ya tiene un gestor final distinto → conflicto, no se sobrescribe). Sólo aplica ' +
|
|
89
|
+
'propuestas que hayas verificado.', {
|
|
83
90
|
proposalId: z.string().describe('El proposalId devuelto por preview_nt_final_gestor'),
|
|
84
91
|
}, async ({ proposalId }) => {
|
|
85
92
|
const elev = await resolveElevation(client);
|
|
@@ -91,8 +98,10 @@ export function registerNtFinalGestor(server, client) {
|
|
|
91
98
|
: `# Sin cambios · NT ${r.ntNumero}`;
|
|
92
99
|
const text = `${titulo}\n\n` +
|
|
93
100
|
`- **Resultado:** ${r.outcome}\n` +
|
|
94
|
-
`-
|
|
95
|
-
|
|
101
|
+
`- **Último eslabón (terna del libro):** ${fmtTriple(r.gestorFinal)}\n\n` +
|
|
102
|
+
`**Cadena escrita:**\n${formatChain(r.cadena ?? [])}` +
|
|
103
|
+
formatWarnings(r.avisos ?? []) +
|
|
104
|
+
`\n\n_Recuerda des-elevarte desde el portal web con un motivo cuando termines._`;
|
|
96
105
|
return { content: [{ type: 'text', text }] };
|
|
97
106
|
});
|
|
98
107
|
}
|
|
@@ -101,3 +110,19 @@ function fmtTriple(t) {
|
|
|
101
110
|
return '(vacía)';
|
|
102
111
|
return `entidad ${t.clientId ?? '—'} / centro ${t.centerId ?? '—'} · operación ${t.operacion ?? '—'}`;
|
|
103
112
|
}
|
|
113
|
+
/** Una línea por eslabón, en orden documental. Puro y exportado para test. */
|
|
114
|
+
export function formatChain(cadena) {
|
|
115
|
+
if (cadena.length === 0)
|
|
116
|
+
return '_(sin cadena de gestores finales)_';
|
|
117
|
+
return cadena
|
|
118
|
+
.map((l) => `${l.orden}. entidad ${l.clientId} / centro ${l.centerId} · operación **${l.operacion}**`)
|
|
119
|
+
.join('\n');
|
|
120
|
+
}
|
|
121
|
+
/** Los defectos de forma que E3L reportará al emitir. NO bloquean el vínculo. Puro y exportado para test. */
|
|
122
|
+
export function formatWarnings(avisos) {
|
|
123
|
+
if (avisos.length === 0)
|
|
124
|
+
return '';
|
|
125
|
+
return (`\n\n**Avisos E3L (no bloquean el vínculo):**\n` +
|
|
126
|
+
avisos.map((a) => `- ${a}`).join('\n') +
|
|
127
|
+
`\n\n_Se vincula igualmente (best-effort). E3L reportará estos defectos al emitir el DCS._`);
|
|
128
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { resolveElevation } from './elevation.js';
|
|
3
|
+
export function registerUpsertGestor(server, client) {
|
|
4
|
+
server.tool('upsert_entidad', 'Alta o completado (best-effort) de una entidad gestor/transportista por CIF, bajo la elevación del ' +
|
|
5
|
+
'operador. Idempotente: si el CIF ya existe la completa (solo los campos aportados), si no la crea. ' +
|
|
6
|
+
'No valida E3L: rellena lo disponible.', {
|
|
7
|
+
cif: z.string().describe('CIF de la entidad (identidad; requerido).'),
|
|
8
|
+
nombre: z.string().optional(),
|
|
9
|
+
entityType: z.number().int().optional().describe('tipo_entidad (E3L lo exige a nivel entidad).'),
|
|
10
|
+
telefono: z.string().optional(),
|
|
11
|
+
mail: z.string().optional(),
|
|
12
|
+
gestor: z.boolean().optional(),
|
|
13
|
+
transportista: z.boolean().optional(),
|
|
14
|
+
}, async (args) => {
|
|
15
|
+
const elev = await resolveElevation(client);
|
|
16
|
+
if ('content' in elev)
|
|
17
|
+
return elev;
|
|
18
|
+
const res = await client.post('/mwm/retirada/upsert-entidad', { confirm: true, ...args });
|
|
19
|
+
const verbo = res.outcome === 'created' ? 'creada' : 'actualizada';
|
|
20
|
+
return {
|
|
21
|
+
content: [{
|
|
22
|
+
type: 'text',
|
|
23
|
+
text: `Entidad **${verbo}** (CIF \`${args.cif}\`) — clientId \`${res.clientId}\`.`,
|
|
24
|
+
}],
|
|
25
|
+
};
|
|
26
|
+
});
|
|
27
|
+
server.tool('upsert_centro', 'Alta o completado (best-effort) de un centro de un gestor, bajo la elevación del operador. Identidad ' +
|
|
28
|
+
'por centerId o por nima inequívoco (varios con la misma nima → 409, aporta centerId). Valida un id de ' +
|
|
29
|
+
'catálogo solo si se aporta. Si aportas authId+autorizacion, hace también el upsert de la autorización.', {
|
|
30
|
+
clientId: z.number().int().describe('clientId de la entidad padre (requerido; debe existir).'),
|
|
31
|
+
centerId: z.number().int().optional().describe('Identidad explícita del centro (si se conoce).'),
|
|
32
|
+
nima: z.number().int().optional().describe('NIMA para identificar el centro si no hay centerId.'),
|
|
33
|
+
nombre: z.string().optional(),
|
|
34
|
+
direccion: z.string().optional(),
|
|
35
|
+
ciudad: z.string().optional(),
|
|
36
|
+
cp: z.string().optional(),
|
|
37
|
+
regionId: z.number().int().optional(),
|
|
38
|
+
provinceId: z.number().int().optional(),
|
|
39
|
+
localityId: z.number().int().optional(),
|
|
40
|
+
vialId: z.number().int().optional(),
|
|
41
|
+
codigoCnae: z.string().optional().describe('Código CNAE (varchar(4), PK de IneCnae).'),
|
|
42
|
+
telefono: z.string().optional(),
|
|
43
|
+
mail: z.string().optional(),
|
|
44
|
+
authId: z.string().optional().describe('Tipo de autorización (T01/G0x) — con autorizacion, hace el upsert.'),
|
|
45
|
+
autorizacion: z.string().optional().describe('Número de registro de la autorización (lo aporta el humano).'),
|
|
46
|
+
}, async (args) => {
|
|
47
|
+
const elev = await resolveElevation(client);
|
|
48
|
+
if ('content' in elev)
|
|
49
|
+
return elev;
|
|
50
|
+
const res = await client.post('/mwm/retirada/upsert-centro', { confirm: true, ...args });
|
|
51
|
+
const verbo = res.outcome === 'created' ? 'creado' : 'actualizado';
|
|
52
|
+
return {
|
|
53
|
+
content: [{
|
|
54
|
+
type: 'text',
|
|
55
|
+
text: `Centro **${verbo}** — clientId \`${res.clientId}\`, centerId \`${res.centerId}\`.`,
|
|
56
|
+
}],
|
|
57
|
+
};
|
|
58
|
+
});
|
|
59
|
+
}
|
package/package.json
CHANGED