@frihet/mcp-server 1.14.1 → 1.14.3
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 +1 -1
- package/dist/__tests__/contract.test.d.ts +33 -0
- package/dist/__tests__/contract.test.d.ts.map +1 -0
- package/dist/__tests__/contract.test.js +208 -0
- package/dist/__tests__/contract.test.js.map +1 -0
- package/dist/__tests__/einvoice-day4-tools.test.js +33 -73
- package/dist/__tests__/einvoice-day4-tools.test.js.map +1 -1
- package/dist/__tests__/einvoice-tools.test.js +70 -107
- package/dist/__tests__/einvoice-tools.test.js.map +1 -1
- package/dist/__tests__/tool-exposure.test.js +4 -1
- package/dist/__tests__/tool-exposure.test.js.map +1 -1
- package/dist/client.d.ts +18 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +42 -2
- package/dist/client.js.map +1 -1
- package/dist/index.js +19 -3
- package/dist/index.js.map +1 -1
- package/dist/observability.d.ts +21 -0
- package/dist/observability.d.ts.map +1 -1
- package/dist/observability.js +79 -4
- package/dist/observability.js.map +1 -1
- package/dist/tools/accountingClose.d.ts.map +1 -1
- package/dist/tools/accountingClose.js +17 -12
- package/dist/tools/accountingClose.js.map +1 -1
- package/dist/tools/audit_gl.d.ts.map +1 -1
- package/dist/tools/audit_gl.js +7 -6
- package/dist/tools/audit_gl.js.map +1 -1
- package/dist/tools/backend-availability.d.ts +73 -0
- package/dist/tools/backend-availability.d.ts.map +1 -0
- package/dist/tools/backend-availability.js +94 -0
- package/dist/tools/backend-availability.js.map +1 -0
- package/dist/tools/bank_rules.d.ts.map +1 -1
- package/dist/tools/bank_rules.js +5 -4
- package/dist/tools/bank_rules.js.map +1 -1
- package/dist/tools/einvoice.d.ts +25 -41
- package/dist/tools/einvoice.d.ts.map +1 -1
- package/dist/tools/einvoice.js +160 -259
- package/dist/tools/einvoice.js.map +1 -1
- package/dist/tools/fiscal.d.ts.map +1 -1
- package/dist/tools/fiscal.js +20 -17
- package/dist/tools/fiscal.js.map +1 -1
- package/dist/tools/gestoria.d.ts.map +1 -1
- package/dist/tools/gestoria.js +11 -10
- package/dist/tools/gestoria.js.map +1 -1
- package/dist/tools/hr.d.ts.map +1 -1
- package/dist/tools/hr.js +19 -18
- package/dist/tools/hr.js.map +1 -1
- package/dist/tools/igic.d.ts.map +1 -1
- package/dist/tools/igic.js +9 -8
- package/dist/tools/igic.js.map +1 -1
- package/dist/tools/impuesto_sociedades.d.ts.map +1 -1
- package/dist/tools/impuesto_sociedades.js +5 -4
- package/dist/tools/impuesto_sociedades.js.map +1 -1
- package/dist/tools/onboard_vies.d.ts.map +1 -1
- package/dist/tools/onboard_vies.js +5 -4
- package/dist/tools/onboard_vies.js.map +1 -1
- package/dist/tools/onboarding.d.ts.map +1 -1
- package/dist/tools/onboarding.js +5 -4
- package/dist/tools/onboarding.js.map +1 -1
- package/dist/tools/payroll.d.ts.map +1 -1
- package/dist/tools/payroll.js +5 -4
- package/dist/tools/payroll.js.map +1 -1
- package/dist/tools/permissions.d.ts.map +1 -1
- package/dist/tools/permissions.js +5 -4
- package/dist/tools/permissions.js.map +1 -1
- package/dist/tools/portal_domain.d.ts.map +1 -1
- package/dist/tools/portal_domain.js +7 -6
- package/dist/tools/portal_domain.js.map +1 -1
- package/dist/tools/shared.d.ts +53 -1
- package/dist/tools/shared.d.ts.map +1 -1
- package/dist/tools/shared.js +68 -2
- package/dist/tools/shared.js.map +1 -1
- package/package.json +2 -2
package/dist/tools/einvoice.js
CHANGED
|
@@ -30,6 +30,43 @@
|
|
|
30
30
|
import { z } from "zod/v4";
|
|
31
31
|
import { withToolLogging, mutateContent, getContent, READ_ONLY_ANNOTATIONS, CREATE_ANNOTATIONS, } from "./shared.js";
|
|
32
32
|
/* ------------------------------------------------------------------ */
|
|
33
|
+
/* Honest "backend endpoint not yet available" result */
|
|
34
|
+
/* ------------------------------------------------------------------ */
|
|
35
|
+
/**
|
|
36
|
+
* Build an HONEST unavailable result for a tool whose backend endpoint is not
|
|
37
|
+
* yet deployed.
|
|
38
|
+
*
|
|
39
|
+
* NORTH #2: a tool that returns a fake-success stub on a backend 404 is a HOLE —
|
|
40
|
+
* an AI agent receives fabricated data (a queued workflow, a valid XML, a DATEV
|
|
41
|
+
* file URL) and reports success. SOUL §16: removing that drama is the whole
|
|
42
|
+
* point. So instead of fabricating a plausible-looking payload we return an
|
|
43
|
+
* explicit error result:
|
|
44
|
+
* - `isError: true` → the MCP client/agent treats it as a failure
|
|
45
|
+
* - `_unavailable: true` → machine-readable marker (also drives the
|
|
46
|
+
* observability downgrade in observability.ts)
|
|
47
|
+
* - `_plannedEndpoint` → which REST endpoint will back this when live
|
|
48
|
+
*
|
|
49
|
+
* The agent gets the truth ("not available yet"), never fabricated business data.
|
|
50
|
+
*/
|
|
51
|
+
function unavailableResult(opts) {
|
|
52
|
+
const note = `${opts.what} is not yet available: the backend endpoint ` +
|
|
53
|
+
`${opts.plannedEndpoint} is not deployed. No data was produced — this is an ` +
|
|
54
|
+
`honest "unavailable" response, NOT a stub success. ` +
|
|
55
|
+
(opts.workaround ? opts.workaround + " " : "") +
|
|
56
|
+
`/ Esta operación aún no está disponible: el endpoint ${opts.plannedEndpoint} ` +
|
|
57
|
+
`no está desplegado. No se ha generado ningún dato.`;
|
|
58
|
+
return {
|
|
59
|
+
content: [{ type: "text", text: `Unavailable — ${opts.what}.\n${note}` }],
|
|
60
|
+
structuredContent: {
|
|
61
|
+
_unavailable: true,
|
|
62
|
+
_note: note,
|
|
63
|
+
_plannedEndpoint: opts.plannedEndpoint,
|
|
64
|
+
tool: opts.tool,
|
|
65
|
+
},
|
|
66
|
+
isError: true,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/* ------------------------------------------------------------------ */
|
|
33
70
|
/* Shared format union */
|
|
34
71
|
/* ------------------------------------------------------------------ */
|
|
35
72
|
/**
|
|
@@ -96,38 +133,44 @@ export const exportDatevOutput = z.object({
|
|
|
96
133
|
fiscalPeriod: z.string().describe("Fiscal period covered (e.g. '2026-01' or '2026-Q1')"),
|
|
97
134
|
encoding: z.literal("cp1252").describe("File encoding — always CP1252 per DATEV EXTF spec"),
|
|
98
135
|
}).passthrough();
|
|
99
|
-
/* Day 4 Wave output schemas
|
|
136
|
+
/* Day 4 Wave output schemas — fields match the LIVE CF response
|
|
137
|
+
* (Frihet-ERP functions/src/publicApi.ts) exactly. No fabricated fields. */
|
|
100
138
|
export const einvoiceExportOutput = z.object({
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
signed: z.boolean().describe("Whether XAdES signature was applied (Facturae only)"),
|
|
139
|
+
xml: z.string().describe("Raw e-invoice XML content (the document itself, returned inline by the CF)"),
|
|
140
|
+
contentType: z.string().describe("MIME type of the XML payload (application/xml)"),
|
|
141
|
+
filename: z.string().describe("Suggested filename (e.g. INV-2026-001_Facturae.xml)"),
|
|
142
|
+
signed: z.boolean().describe("Whether XAdES signature was applied (Facturae + signed=true only)"),
|
|
143
|
+
format: z.string().describe("E-invoice format used (echoes the requested format)"),
|
|
105
144
|
}).passthrough();
|
|
106
145
|
export const faceSubmitOutput = z.object({
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
146
|
+
status: z.literal("submitted").describe("Submission accepted by FACe (the CF returns an error otherwise)"),
|
|
147
|
+
numeroRegistro: z.string().describe("FACe registration number (número de registro) returned by the portal"),
|
|
148
|
+
codigo: z.string().describe("FACe result code returned by the portal (resultado.codigo)"),
|
|
149
|
+
descripcion: z.string().describe("Human-readable FACe result description (resultado.descripcion)"),
|
|
150
|
+
idempotent: z.boolean().optional().describe("True if this echoes a prior already-registered submission (no resubmission occurred)"),
|
|
111
151
|
}).passthrough();
|
|
112
152
|
export const faceStatusOutput = z.object({
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
153
|
+
numeroRegistro: z.string().describe("FACe registration number for this submission"),
|
|
154
|
+
estadoTramitacion: z.string().nullable().describe("FACe processing-state code (estado de tramitación)"),
|
|
155
|
+
codigo: z.string().nullable().describe("FACe status code (e.g. '1200'=Registrada, '1400'=Contabilizada, '3100'=Rechazada)"),
|
|
156
|
+
descripcion: z.string().nullable().describe("Human-readable FACe status description"),
|
|
157
|
+
motivo: z.string().nullable().describe("Reason/motive associated with the current status (e.g. rejection motive)"),
|
|
117
158
|
}).passthrough();
|
|
118
159
|
export const ticketbaiSubmitOutput = z.object({
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
160
|
+
accepted: z.boolean().describe("Whether the hacienda foral accepted the submission"),
|
|
161
|
+
csv: z.string().describe("CSV (Código Seguro de Verificación) returned by the hacienda foral"),
|
|
162
|
+
tbaiIdentifier: z.string().describe("TicketBAI identifier (TBAI-XXXXX) returned by the hacienda foral"),
|
|
163
|
+
qrUrl: z.string().describe("QR code URL to print on the invoice (TBAI compliance)"),
|
|
164
|
+
idempotent: z.boolean().optional().describe("True if this echoes a prior already-accepted submission (no resubmission occurred)"),
|
|
124
165
|
}).passthrough();
|
|
125
166
|
export const ticketbaiStatusOutput = z.object({
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
167
|
+
accepted: z.boolean().describe("Whether the submission has been accepted by the hacienda foral"),
|
|
168
|
+
csv: z.string().describe("CSV (Código Seguro de Verificación) for the submission"),
|
|
169
|
+
tbaiIdentifier: z.string().describe("TicketBAI identifier"),
|
|
170
|
+
estado: z.string().describe("Current submission status stored server-side (e.g. 'accepted', 'rejected')"),
|
|
171
|
+
qrUrl: z.string().nullable().describe("QR code URL to print on the invoice (null if not yet available)"),
|
|
172
|
+
submittedAt: z.string().nullable().describe("ISO 8601 timestamp the submission was recorded (null if unknown)"),
|
|
173
|
+
errors: z.array(z.string()).nullable().describe("Hacienda error messages if rejected (null if none)"),
|
|
131
174
|
}).passthrough();
|
|
132
175
|
export const kSeFSubmitOutput = z.object({
|
|
133
176
|
_notImplemented: z.boolean().optional().describe("Always true — endpoint pending PR #417"),
|
|
@@ -166,7 +209,7 @@ export function registerEInvoiceTools(server, _client) {
|
|
|
166
209
|
" • peppol — transmit via PEPPOL access point (peppol-bis-3 only)\n" +
|
|
167
210
|
" • download — generate and return a signed download URL only\n" +
|
|
168
211
|
"\n" +
|
|
169
|
-
"
|
|
212
|
+
"If the dispatch backend is not deployed for this workspace, returns an honest 'unavailable' response — never fabricated workflow data. " +
|
|
170
213
|
"/ Envia una factura electronica al destinatario mediante el canal de transporte seleccionado. " +
|
|
171
214
|
"Devuelve de forma asincrona — consultar get_einvoice_status para seguimiento.",
|
|
172
215
|
annotations: CREATE_ANNOTATIONS,
|
|
@@ -199,27 +242,15 @@ export function registerEInvoiceTools(server, _client) {
|
|
|
199
242
|
}
|
|
200
243
|
catch (err) {
|
|
201
244
|
if (isNotFoundError(err)) {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
content: [
|
|
212
|
-
mutateContent(`E-invoice queued (stub — CF pending deploy):\n` +
|
|
213
|
-
` Invoice: ${invoiceId}\n` +
|
|
214
|
-
` Format: ${format}\n` +
|
|
215
|
-
` Dispatch: ${dispatchMode}\n` +
|
|
216
|
-
` WorkflowRunId: ${stubResult.workflowRunId}\n` +
|
|
217
|
-
` Status: queued\n` +
|
|
218
|
-
` Estimated: ~${stubResult.estimatedCompletionSec}s\n\n` +
|
|
219
|
-
`Use get_einvoice_status with the workflowRunId to poll for completion.`),
|
|
220
|
-
],
|
|
221
|
-
structuredContent: stubResult,
|
|
222
|
-
};
|
|
245
|
+
// HONEST unavailable — the /v1/einvoice/send transport endpoint is
|
|
246
|
+
// genuinely absent (not deployed). Do NOT fabricate a queued workflow.
|
|
247
|
+
return unavailableResult({
|
|
248
|
+
tool: "send_einvoice",
|
|
249
|
+
plannedEndpoint,
|
|
250
|
+
what: "E-invoice dispatch",
|
|
251
|
+
workaround: "Use einvoice_export to produce the XML, then face_submit (Spain B2G) " +
|
|
252
|
+
"or ticketbai_submit (Basque Country) — those endpoints are live.",
|
|
253
|
+
});
|
|
223
254
|
}
|
|
224
255
|
throw err;
|
|
225
256
|
}
|
|
@@ -231,7 +262,7 @@ export function registerEInvoiceTools(server, _client) {
|
|
|
231
262
|
"Returns current step, ack ID (network confirmation), and download URLs once complete. " +
|
|
232
263
|
"Poll every 5–10 seconds until status is 'succeeded', 'failed', or 'cancelled'. " +
|
|
233
264
|
"\n\n" +
|
|
234
|
-
"
|
|
265
|
+
"If the status backend is not deployed for this workspace, returns an honest 'unavailable' response — never a fabricated status. " +
|
|
235
266
|
"/ Consulta el estado de un flujo de envio de factura electronica.",
|
|
236
267
|
annotations: READ_ONLY_ANNOTATIONS,
|
|
237
268
|
inputSchema: {
|
|
@@ -257,27 +288,13 @@ export function registerEInvoiceTools(server, _client) {
|
|
|
257
288
|
}
|
|
258
289
|
catch (err) {
|
|
259
290
|
if (isNotFoundError(err)) {
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
_note: "CF endpoint pending deploy",
|
|
268
|
-
_plannedEndpoint: plannedEndpoint,
|
|
269
|
-
};
|
|
270
|
-
return {
|
|
271
|
-
content: [
|
|
272
|
-
getContent(`E-invoice status (stub — CF pending deploy):\n` +
|
|
273
|
-
` WorkflowRunId: ${workflowRunId}\n` +
|
|
274
|
-
` Status: ${stubResult.status}\n` +
|
|
275
|
-
` Step: ${stubResult.step}\n` +
|
|
276
|
-
` AckId: ${stubResult.ackId}\n` +
|
|
277
|
-
` XML URL: ${stubResult.xmlUrl}`),
|
|
278
|
-
],
|
|
279
|
-
structuredContent: stubResult,
|
|
280
|
-
};
|
|
291
|
+
// HONEST unavailable — the /v1/einvoice/status endpoint is genuinely
|
|
292
|
+
// absent. Do NOT fabricate a "succeeded" status + fake XML URL.
|
|
293
|
+
return unavailableResult({
|
|
294
|
+
tool: "get_einvoice_status",
|
|
295
|
+
plannedEndpoint,
|
|
296
|
+
what: "E-invoice dispatch status polling",
|
|
297
|
+
});
|
|
281
298
|
}
|
|
282
299
|
throw err;
|
|
283
300
|
}
|
|
@@ -292,7 +309,7 @@ export function registerEInvoiceTools(server, _client) {
|
|
|
292
309
|
"Use before dispatch to catch errors early without incurring network transmission costs. " +
|
|
293
310
|
"A valid=true response means the document passes all schema + business rule checks. " +
|
|
294
311
|
"\n\n" +
|
|
295
|
-
"
|
|
312
|
+
"If the validation backend is not deployed for this workspace, returns an honest 'unavailable' response — never a fabricated valid=true. " +
|
|
296
313
|
"/ Valida un documento XML de factura electronica contra el esquema y reglas schematron del formato especificado.",
|
|
297
314
|
annotations: READ_ONLY_ANNOTATIONS,
|
|
298
315
|
inputSchema: {
|
|
@@ -303,19 +320,6 @@ export function registerEInvoiceTools(server, _client) {
|
|
|
303
320
|
outputSchema: validateEInvoiceOutput,
|
|
304
321
|
}, async ({ xml, format }) => withToolLogging("validate_einvoice_xml", async () => {
|
|
305
322
|
const plannedEndpoint = "/v1/einvoice/validate";
|
|
306
|
-
const validatorMap = {
|
|
307
|
-
"xrechnung-cii": "kosit",
|
|
308
|
-
"xrechnung-ubl": "kosit",
|
|
309
|
-
"facturx-en16931": "mustang",
|
|
310
|
-
"facturx-extended": "mustang",
|
|
311
|
-
"facturx-basic": "mustang",
|
|
312
|
-
"facturx-minimum": "mustang",
|
|
313
|
-
"fatturapa": "xsd",
|
|
314
|
-
"ubl": "schematron",
|
|
315
|
-
"cii": "schematron",
|
|
316
|
-
"peppol-bis-3": "schematron",
|
|
317
|
-
"facturae": "xsd",
|
|
318
|
-
};
|
|
319
323
|
try {
|
|
320
324
|
const result = await _client.validateEInvoiceXml({ xml, format });
|
|
321
325
|
return {
|
|
@@ -332,26 +336,15 @@ export function registerEInvoiceTools(server, _client) {
|
|
|
332
336
|
}
|
|
333
337
|
catch (err) {
|
|
334
338
|
if (isNotFoundError(err)) {
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
};
|
|
344
|
-
return {
|
|
345
|
-
content: [
|
|
346
|
-
getContent(`E-invoice validation result (stub — CF pending deploy):\n` +
|
|
347
|
-
` Format: ${format}\n` +
|
|
348
|
-
` Valid: ${stubResult.valid}\n` +
|
|
349
|
-
` Errors: ${stubResult.errors.length}\n` +
|
|
350
|
-
` Validator: ${stubResult.validator}\n` +
|
|
351
|
-
` Duration: ${stubResult.durationMs}ms`),
|
|
352
|
-
],
|
|
353
|
-
structuredContent: stubResult,
|
|
354
|
-
};
|
|
339
|
+
// HONEST unavailable — the /v1/einvoice/validate endpoint is
|
|
340
|
+
// genuinely absent. Do NOT fabricate valid=true (a fabricated
|
|
341
|
+
// "passes validation" is the most dangerous stub of all: an agent
|
|
342
|
+
// would dispatch an unvalidated invoice believing it is compliant).
|
|
343
|
+
return unavailableResult({
|
|
344
|
+
tool: "validate_einvoice_xml",
|
|
345
|
+
plannedEndpoint,
|
|
346
|
+
what: "E-invoice XML validation",
|
|
347
|
+
});
|
|
355
348
|
}
|
|
356
349
|
throw err;
|
|
357
350
|
}
|
|
@@ -370,7 +363,7 @@ export function registerEInvoiceTools(server, _client) {
|
|
|
370
363
|
"Output encoding is always CP1252 per DATEV EXTF specification. " +
|
|
371
364
|
"Date range: both periodStart and periodEnd must be ISO 8601 dates (YYYY-MM-DD). " +
|
|
372
365
|
"\n\n" +
|
|
373
|
-
"
|
|
366
|
+
"If the DATEV export backend is not deployed for this workspace, returns an honest 'unavailable' response — never a fabricated file URL. " +
|
|
374
367
|
"/ Exporta datos contables en formato DATEV EXTF para importacion en DATEV o sistemas compatibles.",
|
|
375
368
|
annotations: READ_ONLY_ANNOTATIONS,
|
|
376
369
|
inputSchema: {
|
|
@@ -384,12 +377,6 @@ export function registerEInvoiceTools(server, _client) {
|
|
|
384
377
|
outputSchema: exportDatevOutput,
|
|
385
378
|
}, async ({ periodStart, periodEnd, format }) => withToolLogging("export_datev", async () => {
|
|
386
379
|
const plannedEndpoint = "/v1/einvoice/export-datev";
|
|
387
|
-
const formatFileMap = {
|
|
388
|
-
"extf-buchungsstapel": "EXTF_Buchungsstapel",
|
|
389
|
-
"extf-debitoren": "EXTF_Debitoren",
|
|
390
|
-
"extf-kreditoren": "EXTF_Kreditoren",
|
|
391
|
-
};
|
|
392
|
-
const periodLabel = periodStart.slice(0, 7); // YYYY-MM
|
|
393
380
|
try {
|
|
394
381
|
const result = await _client.exportDatev({ periodStart, periodEnd, format });
|
|
395
382
|
return {
|
|
@@ -407,29 +394,15 @@ export function registerEInvoiceTools(server, _client) {
|
|
|
407
394
|
}
|
|
408
395
|
catch (err) {
|
|
409
396
|
if (isNotFoundError(err)) {
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
_plannedEndpoint: plannedEndpoint,
|
|
420
|
-
};
|
|
421
|
-
return {
|
|
422
|
-
content: [
|
|
423
|
-
getContent(`DATEV export ready (stub — CF pending deploy):\n` +
|
|
424
|
-
` Format: ${format}\n` +
|
|
425
|
-
` Period: ${periodStart} → ${periodEnd}\n` +
|
|
426
|
-
` Filename: ${stubResult.filename}\n` +
|
|
427
|
-
` Rows: ${stubResult.rowCount}\n` +
|
|
428
|
-
` Encoding: ${stubResult.encoding}\n` +
|
|
429
|
-
` Download URL: ${stubResult.fileUrl}`),
|
|
430
|
-
],
|
|
431
|
-
structuredContent: stubResult,
|
|
432
|
-
};
|
|
397
|
+
// HONEST unavailable — the /v1/einvoice/export-datev endpoint is
|
|
398
|
+
// genuinely absent. Do NOT fabricate a fileUrl pointing at a stub
|
|
399
|
+
// path with rowCount=0 (an agent would hand a broken/empty link to
|
|
400
|
+
// an accountant).
|
|
401
|
+
return unavailableResult({
|
|
402
|
+
tool: "export_datev",
|
|
403
|
+
plannedEndpoint,
|
|
404
|
+
what: "DATEV EXTF export",
|
|
405
|
+
});
|
|
433
406
|
}
|
|
434
407
|
throw err;
|
|
435
408
|
}
|
|
@@ -473,26 +446,13 @@ export function registerEInvoiceTools(server, _client) {
|
|
|
473
446
|
}
|
|
474
447
|
catch (err) {
|
|
475
448
|
if (isNotFoundError(err)) {
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
_plannedEndpoint: plannedEndpoint,
|
|
484
|
-
};
|
|
485
|
-
return {
|
|
486
|
-
content: [
|
|
487
|
-
getContent(`E-invoice export ready (stub — CF pending deploy):\n` +
|
|
488
|
-
` Invoice: ${invoiceId}\n` +
|
|
489
|
-
` Format: ${format}\n` +
|
|
490
|
-
` Signed: ${stubResult.signed}\n` +
|
|
491
|
-
` Filename: ${stubResult.filename}\n` +
|
|
492
|
-
` Download URL: ${stubResult.xmlUrl}`),
|
|
493
|
-
],
|
|
494
|
-
structuredContent: stubResult,
|
|
495
|
-
};
|
|
449
|
+
// HONEST unavailable — the export CF is genuinely absent. Do NOT
|
|
450
|
+
// fabricate a download URL pointing at a non-existent stub file.
|
|
451
|
+
return unavailableResult({
|
|
452
|
+
tool: "einvoice_export",
|
|
453
|
+
plannedEndpoint,
|
|
454
|
+
what: "E-invoice XML export",
|
|
455
|
+
});
|
|
496
456
|
}
|
|
497
457
|
throw err;
|
|
498
458
|
}
|
|
@@ -538,26 +498,15 @@ export function registerEInvoiceTools(server, _client) {
|
|
|
538
498
|
}
|
|
539
499
|
catch (err) {
|
|
540
500
|
if (isNotFoundError(err)) {
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
};
|
|
550
|
-
return {
|
|
551
|
-
content: [
|
|
552
|
-
mutateContent(`FACe submission queued (stub — CF pending deploy):\n` +
|
|
553
|
-
` Invoice: ${invoiceId}\n` +
|
|
554
|
-
` Mode: ${resolvedMode}\n` +
|
|
555
|
-
` Registro: ${stubResult.registroFACe}\n` +
|
|
556
|
-
` Status: ${stubResult.status}\n\n` +
|
|
557
|
-
`Use face_status with invoiceId to poll the portal for confirmation.`),
|
|
558
|
-
],
|
|
559
|
-
structuredContent: stubResult,
|
|
560
|
-
};
|
|
501
|
+
// HONEST unavailable — the FACe submit CF is genuinely absent. Do
|
|
502
|
+
// NOT fabricate a registro: a fake "submitted" B2G reference would
|
|
503
|
+
// make an agent report a filing that never happened.
|
|
504
|
+
return unavailableResult({
|
|
505
|
+
tool: "face_submit",
|
|
506
|
+
plannedEndpoint,
|
|
507
|
+
what: "FACe (Spain B2G) submission",
|
|
508
|
+
workaround: "No registro was created — do not treat this as an accepted submission.",
|
|
509
|
+
});
|
|
561
510
|
}
|
|
562
511
|
throw err;
|
|
563
512
|
}
|
|
@@ -599,25 +548,13 @@ export function registerEInvoiceTools(server, _client) {
|
|
|
599
548
|
}
|
|
600
549
|
catch (err) {
|
|
601
550
|
if (isNotFoundError(err)) {
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
_plannedEndpoint: plannedEndpoint,
|
|
610
|
-
};
|
|
611
|
-
return {
|
|
612
|
-
content: [
|
|
613
|
-
getContent(`FACe status (stub — CF pending deploy):\n` +
|
|
614
|
-
` Invoice: ${invoiceId}\n` +
|
|
615
|
-
` Registro: ${stubResult.registroFACe}\n` +
|
|
616
|
-
` Status code: ${stubResult.statusCode}\n` +
|
|
617
|
-
` Description: ${stubResult.statusDescription}`),
|
|
618
|
-
],
|
|
619
|
-
structuredContent: stubResult,
|
|
620
|
-
};
|
|
551
|
+
// HONEST unavailable — do NOT fabricate a "Registrada" (1200) state
|
|
552
|
+
// for a submission that was never made.
|
|
553
|
+
return unavailableResult({
|
|
554
|
+
tool: "face_status",
|
|
555
|
+
plannedEndpoint,
|
|
556
|
+
what: "FACe submission status",
|
|
557
|
+
});
|
|
621
558
|
}
|
|
622
559
|
throw err;
|
|
623
560
|
}
|
|
@@ -662,28 +599,14 @@ export function registerEInvoiceTools(server, _client) {
|
|
|
662
599
|
}
|
|
663
600
|
catch (err) {
|
|
664
601
|
if (isNotFoundError(err)) {
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
_plannedEndpoint: plannedEndpoint,
|
|
674
|
-
};
|
|
675
|
-
return {
|
|
676
|
-
content: [
|
|
677
|
-
mutateContent(`TicketBAI submitted (stub — CF pending deploy):\n` +
|
|
678
|
-
` Invoice: ${invoiceId}\n` +
|
|
679
|
-
` Territory: ${stubResult.territory}\n` +
|
|
680
|
-
` TBAI ID: ${stubResult.tbaiId}\n` +
|
|
681
|
-
` Sandbox: ${isSandbox}\n` +
|
|
682
|
-
` QR URL: ${stubResult.qrUrl}\n\n` +
|
|
683
|
-
`Use ticketbai_status with invoiceId to poll for hacienda acknowledgement.`),
|
|
684
|
-
],
|
|
685
|
-
structuredContent: stubResult,
|
|
686
|
-
};
|
|
602
|
+
// HONEST unavailable — do NOT fabricate a TBAI identifier + QR URL.
|
|
603
|
+
// A fake TicketBAI id printed on an invoice is a fiscal forgery.
|
|
604
|
+
return unavailableResult({
|
|
605
|
+
tool: "ticketbai_submit",
|
|
606
|
+
plannedEndpoint,
|
|
607
|
+
what: "TicketBAI (Basque Country) submission",
|
|
608
|
+
workaround: "No TBAI identifier was issued — do not print a QR or treat this as filed.",
|
|
609
|
+
});
|
|
687
610
|
}
|
|
688
611
|
throw err;
|
|
689
612
|
}
|
|
@@ -726,26 +649,13 @@ export function registerEInvoiceTools(server, _client) {
|
|
|
726
649
|
}
|
|
727
650
|
catch (err) {
|
|
728
651
|
if (isNotFoundError(err)) {
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
_note: "CF endpoint pending deploy",
|
|
737
|
-
_plannedEndpoint: plannedEndpoint,
|
|
738
|
-
};
|
|
739
|
-
return {
|
|
740
|
-
content: [
|
|
741
|
-
getContent(`TicketBAI status (stub — CF pending deploy):\n` +
|
|
742
|
-
` Invoice: ${invoiceId}\n` +
|
|
743
|
-
` TBAI ID: ${stubResult.tbaiId}\n` +
|
|
744
|
-
` Territory: ${stubResult.territory}\n` +
|
|
745
|
-
` Status: ${stubResult.status}`),
|
|
746
|
-
],
|
|
747
|
-
structuredContent: stubResult,
|
|
748
|
-
};
|
|
652
|
+
// HONEST unavailable — do NOT fabricate an "accepted" hacienda
|
|
653
|
+
// acknowledgement for a TicketBAI that was never submitted.
|
|
654
|
+
return unavailableResult({
|
|
655
|
+
tool: "ticketbai_status",
|
|
656
|
+
plannedEndpoint,
|
|
657
|
+
what: "TicketBAI submission status",
|
|
658
|
+
});
|
|
749
659
|
}
|
|
750
660
|
throw err;
|
|
751
661
|
}
|
|
@@ -776,34 +686,25 @@ export function registerEInvoiceTools(server, _client) {
|
|
|
776
686
|
},
|
|
777
687
|
outputSchema: kSeFSubmitOutput,
|
|
778
688
|
}, async ({ invoiceId, mode }) => withToolLogging("ksef_submit", async () => {
|
|
779
|
-
//
|
|
780
|
-
//
|
|
781
|
-
//
|
|
689
|
+
// HONEST unavailable: KSeF CF endpoint not yet deployed — PR #417 required.
|
|
690
|
+
// Returns isError:true + _unavailable so an AI agent treats this as a
|
|
691
|
+
// failure, NOT as a (successful) tool result it can act on. When PR #417
|
|
692
|
+
// merges (api.frihet.io/v1/invoices/:id/ksef/submit live), replace this
|
|
693
|
+
// block with: await (_client as IFrihetClientWithDay4EInvoice).kSeFSubmit({ invoiceId, mode })
|
|
782
694
|
const resolvedMode = mode ?? "production";
|
|
783
|
-
const
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
text: `KSeF submission not yet available.\n` +
|
|
797
|
-
` Invoice: ${invoiceId}\n` +
|
|
798
|
-
` Mode: ${resolvedMode}\n\n` +
|
|
799
|
-
`The KSeF Cloud Function is pending deployment (Frihet-ERP PR #417). ` +
|
|
800
|
-
`This tool will activate automatically once PR #417 merges to main. ` +
|
|
801
|
-
`In the meantime, use einvoice_export with format='ubl' + manual KSeF portal upload ` +
|
|
802
|
-
`at ksef.mf.gov.pl as a workaround.`,
|
|
803
|
-
},
|
|
804
|
-
],
|
|
805
|
-
structuredContent: stubResult,
|
|
806
|
-
};
|
|
695
|
+
const result = unavailableResult({
|
|
696
|
+
tool: "ksef_submit",
|
|
697
|
+
plannedEndpoint: `/v1/invoices/${invoiceId}/ksef/submit`,
|
|
698
|
+
what: "KSeF (Poland) submission",
|
|
699
|
+
workaround: "It will activate automatically once Frihet-ERP PR #417 merges. " +
|
|
700
|
+
"In the meantime, use einvoice_export with format='ubl' + manual KSeF portal " +
|
|
701
|
+
"upload at ksef.mf.gov.pl.",
|
|
702
|
+
});
|
|
703
|
+
// Preserve the forward-compat marker + echo the request for callers/tests.
|
|
704
|
+
result.structuredContent._notImplemented = true;
|
|
705
|
+
result.structuredContent.invoiceId = invoiceId;
|
|
706
|
+
result.structuredContent.mode = resolvedMode;
|
|
707
|
+
return result;
|
|
807
708
|
}));
|
|
808
709
|
}
|
|
809
710
|
/* ------------------------------------------------------------------ */
|