@frihet/mcp-server 1.17.0 → 2.0.1
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 +106 -348
- package/dist/agent-onboarding.d.ts +1 -1
- package/dist/agent-onboarding.js +3 -3
- package/dist/canary/mcp-harness.d.ts +1 -1
- package/dist/canary/mcp-harness.js +4 -1
- package/dist/capability-truth.js +20 -12
- package/dist/claude-candidate-contract.d.ts +482 -0
- package/dist/claude-candidate-contract.js +87 -0
- package/dist/claude-output-privacy.d.ts +17 -0
- package/dist/claude-output-privacy.js +249 -0
- package/dist/claude-profile.d.ts +18 -0
- package/dist/claude-profile.js +260 -0
- package/dist/client-interface.d.ts +26 -20
- package/dist/client.d.ts +65 -29
- package/dist/client.js +278 -167
- package/dist/demo-client.d.ts +28 -21
- package/dist/demo-client.js +32 -14
- package/dist/deposit-contract.d.ts +29 -0
- package/dist/deposit-contract.js +76 -0
- package/dist/fetch-no-redirect.d.ts +3 -0
- package/dist/fetch-no-redirect.js +16 -0
- package/dist/fiscal-aliases.d.ts +2 -2
- package/dist/fiscal-aliases.js +2 -2
- package/dist/fiscal-period.d.ts +26 -0
- package/dist/fiscal-period.js +31 -0
- package/dist/google-profile.d.ts +19 -0
- package/dist/google-profile.js +122 -0
- package/dist/index.js +31 -7
- package/dist/logger.d.ts +1 -1
- package/dist/logger.js +2 -2
- package/dist/muse-profile.d.ts +19 -0
- package/dist/muse-profile.js +122 -0
- package/dist/observability.js +3 -2
- package/dist/openai-profile.d.ts +2 -2
- package/dist/openai-profile.js +37 -11
- package/dist/openai-review-contract.d.ts +1 -1
- package/dist/openai-review-contract.js +4 -5
- package/dist/openai-review-oauth.d.ts +107 -7
- package/dist/openai-review-oauth.js +161 -17
- package/dist/prompts/register-all.js +32 -78
- package/dist/redaction.d.ts +5 -0
- package/dist/redaction.js +9 -0
- package/dist/resources/register-all.js +150 -54
- package/dist/retry-after.d.ts +19 -0
- package/dist/retry-after.js +84 -0
- package/dist/server-composition.d.ts +6 -2
- package/dist/server-composition.js +46 -5
- package/dist/tool-exposure.d.ts +7 -5
- package/dist/tool-exposure.js +21 -13
- package/dist/tools/audit_gl.js +8 -8
- package/dist/tools/backend-availability.d.ts +8 -29
- package/dist/tools/backend-availability.js +31 -30
- package/dist/tools/bank_rules.js +66 -15
- package/dist/tools/banking.js +5 -13
- package/dist/tools/clients.js +254 -6
- package/dist/tools/deposits.js +107 -97
- package/dist/tools/einvoice.d.ts +17 -3
- package/dist/tools/einvoice.js +59 -12
- package/dist/tools/fiscal.d.ts +3 -4
- package/dist/tools/fiscal.js +93 -60
- package/dist/tools/gestoria.js +15 -7
- package/dist/tools/hr.js +41 -11
- package/dist/tools/igic.d.ts +9 -8
- package/dist/tools/igic.js +34 -64
- package/dist/tools/impuesto_sociedades.d.ts +4 -5
- package/dist/tools/impuesto_sociedades.js +16 -37
- package/dist/tools/intelligence.js +164 -10
- package/dist/tools/invoices.js +109 -42
- package/dist/tools/kitchen.js +19 -9
- package/dist/tools/quotes.js +86 -25
- package/dist/tools/recurring.js +26 -25
- package/dist/tools/register-all.d.ts +2 -2
- package/dist/tools/register-all.js +9 -6
- package/dist/tools/search.d.ts +19 -0
- package/dist/tools/search.js +84 -0
- package/dist/tools/shared.d.ts +18 -38
- package/dist/tools/shared.js +96 -30
- package/dist/tools/team.js +8 -16
- package/dist/tools/time.js +4 -12
- package/dist/tools/vendors.js +242 -6
- package/dist/types.d.ts +3 -1
- package/dist/workflows/daily-operational-brief.d.ts +163 -0
- package/dist/workflows/daily-operational-brief.js +881 -0
- package/docs/agent-onboarding.json +15 -5
- package/package.json +26 -12
- package/dist/tools/onboarding.d.ts +0 -19
- package/dist/tools/onboarding.js +0 -64
package/dist/tools/einvoice.js
CHANGED
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
* POST /v1/invoices/:id/ksef/submit — Poland KSeF (stub — endpoint not yet exposed; returns unavailable)
|
|
29
29
|
*/
|
|
30
30
|
import { z } from "zod/v4";
|
|
31
|
-
import { withToolLogging, mutateContent, getContent, READ_ONLY_ANNOTATIONS, CREATE_ANNOTATIONS, } from "./shared.js";
|
|
31
|
+
import { withToolLogging, mutateContent, getContent, READ_ONLY_ANNOTATIONS, CREATE_ANNOTATIONS, UPDATE_ANNOTATIONS, } from "./shared.js";
|
|
32
32
|
/* ------------------------------------------------------------------ */
|
|
33
33
|
/* Honest "backend endpoint not yet available" result */
|
|
34
34
|
/* ------------------------------------------------------------------ */
|
|
@@ -99,6 +99,20 @@ export const eInvoiceFormatSchema = z.enum([
|
|
|
99
99
|
"peppol-bis-3",
|
|
100
100
|
"facturae",
|
|
101
101
|
]);
|
|
102
|
+
/** Export is narrower than dispatch/validation. The ERP public export API
|
|
103
|
+
* exposes Factur-X EN16931 only; accepting Extended/Basic/Minimum here and
|
|
104
|
+
* silently mapping them to EN16931 would produce the wrong fiscal profile. */
|
|
105
|
+
export const eInvoiceExportFormatSchema = z.enum([
|
|
106
|
+
"xrechnung-cii",
|
|
107
|
+
"xrechnung-ubl",
|
|
108
|
+
"facturx-en16931",
|
|
109
|
+
"fatturapa",
|
|
110
|
+
"ubl",
|
|
111
|
+
"cii",
|
|
112
|
+
"peppol-bis-3",
|
|
113
|
+
"fa-2-ksef",
|
|
114
|
+
"facturae",
|
|
115
|
+
]);
|
|
102
116
|
/* ------------------------------------------------------------------ */
|
|
103
117
|
/* Output schemas */
|
|
104
118
|
/* ------------------------------------------------------------------ */
|
|
@@ -410,19 +424,23 @@ export function registerEInvoiceTools(server, _client) {
|
|
|
410
424
|
// -- einvoice_export --
|
|
411
425
|
server.registerTool("einvoice_export", {
|
|
412
426
|
title: "Export E-Invoice XML",
|
|
413
|
-
description: "Export an invoice as e-invoicing XML in a specific format. " +
|
|
427
|
+
description: "Export an invoice as inline e-invoicing XML in a specific format. " +
|
|
414
428
|
"Supports Facturae (ES B2G), XRechnung-CII (DE), XRechnung-UBL (DE), " +
|
|
415
|
-
"Factur-X
|
|
429
|
+
"Factur-X EN16931 (FR), FatturaPA (IT), PEPPOL-BIS-3 (EU network), FA-2-KSeF (PL), UBL and CII (generic). " +
|
|
430
|
+
"Factur-X Extended, Basic and Minimum are not exposed by this endpoint and are refused rather than collapsed to a different profile. " +
|
|
416
431
|
"For Facturae, set signed=true to get XAdES-enveloped XML for FACe/AEAT submission. " +
|
|
417
|
-
"Returns
|
|
432
|
+
"Returns the generated XML inline with its MIME type and filename. " +
|
|
418
433
|
"\n\n" +
|
|
419
434
|
"/ Exporta una factura como XML de facturacion electronica en el formato especificado. " +
|
|
420
435
|
"Para Facturae con signed=true devuelve XML firmado XAdES para envio a FACe/AEAT.",
|
|
421
|
-
|
|
436
|
+
// Conservative static classification: signed=true exercises a workspace
|
|
437
|
+
// signing certificate, so the tool cannot truthfully be advertised as a
|
|
438
|
+
// read-only operation even though unsigned generation is side-effect free.
|
|
439
|
+
annotations: UPDATE_ANNOTATIONS,
|
|
422
440
|
inputSchema: {
|
|
423
441
|
invoiceId: z.string().describe("Frihet invoice ID to export / ID de la factura a exportar"),
|
|
424
|
-
format:
|
|
425
|
-
"ES B2G→facturae, DE→xrechnung-cii/ubl, FR→facturx
|
|
442
|
+
format: eInvoiceExportFormatSchema.describe("E-invoice format. Choose based on recipient country: " +
|
|
443
|
+
"ES B2G→facturae, DE→xrechnung-cii/ubl, FR→facturx-en16931, IT→fatturapa, PL→fa-2-ksef, EU PEPPOL→peppol-bis-3, " +
|
|
426
444
|
"generic→ubl or cii / Formato de factura electronica."),
|
|
427
445
|
signed: z.boolean().optional().describe("If true, returns XAdES-enveloped signed XML (Facturae only). Requires workspace signing certificate configured. " +
|
|
428
446
|
"/ Si true, devuelve XML firmado XAdES (solo Facturae). Requiere certificado de firma configurado."),
|
|
@@ -439,7 +457,8 @@ export function registerEInvoiceTools(server, _client) {
|
|
|
439
457
|
` Format: ${format}\n` +
|
|
440
458
|
` Signed: ${signed ?? false}\n` +
|
|
441
459
|
` Filename: ${result.filename}\n` +
|
|
442
|
-
`
|
|
460
|
+
` Content-Type: ${result.contentType}\n` +
|
|
461
|
+
` XML: returned inline (${result.xml.length} characters)`),
|
|
443
462
|
],
|
|
444
463
|
structuredContent: result,
|
|
445
464
|
};
|
|
@@ -460,7 +479,8 @@ export function registerEInvoiceTools(server, _client) {
|
|
|
460
479
|
// -- face_submit --
|
|
461
480
|
server.registerTool("face_submit", {
|
|
462
481
|
title: "Submit to FACe (Spain B2G)",
|
|
463
|
-
description: "
|
|
482
|
+
description: "Requires confirm=true as a local client/agent interlock before submitting a Facturae invoice to Spain's FACe B2G portal. " +
|
|
483
|
+
"This flag is not server-verifiable human attestation and is never forwarded to the API. " +
|
|
464
484
|
"The invoice must have a recipient with DIR3 administrative unit codes (órgano gestor, unidad tramitadora, oficina contable). " +
|
|
465
485
|
"Returns a submission reference (registro) that can be used with face_status to poll the portal. " +
|
|
466
486
|
"\n\n" +
|
|
@@ -477,9 +497,22 @@ export function registerEInvoiceTools(server, _client) {
|
|
|
477
497
|
invoiceId: z.string().describe("Frihet invoice ID to submit / ID de la factura a enviar a FACe"),
|
|
478
498
|
mode: z.enum(["mock", "sandbox", "production"]).optional().describe("Submission mode: mock (local sim), sandbox (FACe pre-prod), production (live). Default: production. " +
|
|
479
499
|
"/ Modo de envío: mock (simulación local), sandbox (pre-producción FACe), production (real). Por defecto: production."),
|
|
500
|
+
confirm: z.boolean().describe("Must be true to pass the local submission interlock in every mode, including mock and sandbox. " +
|
|
501
|
+
"This is not actor/resource/payload/expiry-bound attestation. / Debe ser true para superar el interlock local en todos los modos."),
|
|
480
502
|
},
|
|
481
503
|
outputSchema: faceSubmitOutput,
|
|
482
|
-
}, async ({ invoiceId, mode }) => withToolLogging("face_submit", async () => {
|
|
504
|
+
}, async ({ invoiceId, mode, confirm }) => withToolLogging("face_submit", async () => {
|
|
505
|
+
if (!confirm) {
|
|
506
|
+
return {
|
|
507
|
+
content: [{
|
|
508
|
+
type: "text",
|
|
509
|
+
text: "Error: face_submit requires confirm=true before any FACe submission or simulation. " +
|
|
510
|
+
"It is a local client/agent interlock, not server-verifiable human attestation. " +
|
|
511
|
+
"/ Se requiere confirm=true antes de cualquier envío o simulación FACe.",
|
|
512
|
+
}],
|
|
513
|
+
isError: true,
|
|
514
|
+
};
|
|
515
|
+
}
|
|
483
516
|
const plannedEndpoint = `/v1/invoices/${invoiceId}/face/submit`;
|
|
484
517
|
const resolvedMode = mode ?? "production";
|
|
485
518
|
try {
|
|
@@ -562,7 +595,8 @@ export function registerEInvoiceTools(server, _client) {
|
|
|
562
595
|
// -- ticketbai_submit --
|
|
563
596
|
server.registerTool("ticketbai_submit", {
|
|
564
597
|
title: "Submit TicketBAI (Basque Country)",
|
|
565
|
-
description: "
|
|
598
|
+
description: "Requires confirm=true as a local client/agent interlock before submitting an invoice to TicketBAI. " +
|
|
599
|
+
"This flag is not server-verifiable human attestation and is never forwarded to the API. " +
|
|
566
600
|
"Territory is auto-detected from the workspace address (Bizkaia → BATUZ/LROE, Gipuzkoa → Gipuzkoa TicketBAI, Araba → Araba TicketBAI). " +
|
|
567
601
|
"Returns the submission TicketBAI ID (TBAI identifier) and QR code URL for printing on the invoice. " +
|
|
568
602
|
"\n\n" +
|
|
@@ -577,9 +611,22 @@ export function registerEInvoiceTools(server, _client) {
|
|
|
577
611
|
invoiceId: z.string().describe("Frihet invoice ID to submit / ID de la factura a enviar"),
|
|
578
612
|
sandbox: z.boolean().optional().describe("If true, submits to the hacienda test endpoint instead of production. Default: false. " +
|
|
579
613
|
"/ Si true, envía al entorno de pruebas de la hacienda. Por defecto: false."),
|
|
614
|
+
confirm: z.boolean().describe("Must be true to pass the local submission interlock in both sandbox and production. " +
|
|
615
|
+
"This is not actor/resource/payload/expiry-bound attestation. / Debe ser true para superar el interlock local."),
|
|
580
616
|
},
|
|
581
617
|
outputSchema: ticketbaiSubmitOutput,
|
|
582
|
-
}, async ({ invoiceId, sandbox }) => withToolLogging("ticketbai_submit", async () => {
|
|
618
|
+
}, async ({ invoiceId, sandbox, confirm }) => withToolLogging("ticketbai_submit", async () => {
|
|
619
|
+
if (!confirm) {
|
|
620
|
+
return {
|
|
621
|
+
content: [{
|
|
622
|
+
type: "text",
|
|
623
|
+
text: "Error: ticketbai_submit requires confirm=true before any TicketBAI submission. " +
|
|
624
|
+
"It is a local client/agent interlock, not server-verifiable human attestation. " +
|
|
625
|
+
"/ Se requiere confirm=true antes de cualquier envío TicketBAI.",
|
|
626
|
+
}],
|
|
627
|
+
isError: true,
|
|
628
|
+
};
|
|
629
|
+
}
|
|
583
630
|
const plannedEndpoint = `/v1/invoices/${invoiceId}/ticketbai/submit`;
|
|
584
631
|
const isSandbox = sandbox ?? false;
|
|
585
632
|
try {
|
package/dist/tools/fiscal.d.ts
CHANGED
|
@@ -12,10 +12,9 @@
|
|
|
12
12
|
*
|
|
13
13
|
* NOTE: ticketbai_status is registered by einvoice.ts (canonical, more complete impl).
|
|
14
14
|
*
|
|
15
|
-
* REST surface: /
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* and will surface 404 errors until the backend ships.
|
|
15
|
+
* REST surface: Frihet-ERP functions/src/publicApi.ts serves READ-ONLY
|
|
16
|
+
* GET /v1/fiscal/modelo/{303,130,390,347}. Modelo 180 has no route; its tool
|
|
17
|
+
* returns NOT_DEPLOYED without calling. Period params: src/fiscal-period.ts.
|
|
19
18
|
*/
|
|
20
19
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
21
20
|
import type { IFrihetClient } from "../client-interface.js";
|
package/dist/tools/fiscal.js
CHANGED
|
@@ -12,14 +12,82 @@
|
|
|
12
12
|
*
|
|
13
13
|
* NOTE: ticketbai_status is registered by einvoice.ts (canonical, more complete impl).
|
|
14
14
|
*
|
|
15
|
-
* REST surface: /
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* and will surface 404 errors until the backend ships.
|
|
15
|
+
* REST surface: Frihet-ERP functions/src/publicApi.ts serves READ-ONLY
|
|
16
|
+
* GET /v1/fiscal/modelo/{303,130,390,347}. Modelo 180 has no route; its tool
|
|
17
|
+
* returns NOT_DEPLOYED without calling. Period params: src/fiscal-period.ts.
|
|
19
18
|
*/
|
|
20
19
|
import { z } from "zod/v4";
|
|
21
|
-
import { withToolLogging, formatRecord, getContent, mutateContent, READ_ONLY_ANNOTATIONS, ERROR_CONTENT_ANNOTATIONS, fiscalModeloSummaryOutput, verifactuStatusOutput, } from "./shared.js";
|
|
22
|
-
import { withBackendGuard, isBackendNotFound } from "./backend-availability.js";
|
|
20
|
+
import { withToolLogging, confirmationRequiredError, formatRecord, getContent, mutateContent, READ_ONLY_ANNOTATIONS, ERROR_CONTENT_ANNOTATIONS, fiscalModeloSummaryOutput, verifactuStatusOutput, } from "./shared.js";
|
|
21
|
+
import { withBackendGuard, isBackendNotFound, notDeployedError } from "./backend-availability.js";
|
|
22
|
+
import { FISCAL_PERIOD_RULES } from "../fiscal-period.js";
|
|
23
|
+
function fiscalPeriodError(toolName, structured, message) {
|
|
24
|
+
return {
|
|
25
|
+
content: [{ type: "text", text: `Error: ${message}`, annotations: ERROR_CONTENT_ANNOTATIONS }],
|
|
26
|
+
structuredContent: { ...structured, tool: toolName, message },
|
|
27
|
+
isError: true,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* READ-ONLY modelo summary with a fail-closed period contract:
|
|
32
|
+
* 1. a supplied period must match the backend's own format, or no call is made;
|
|
33
|
+
* 2. the modelo the backend reports (`modeloCode`/`model`) must be the one
|
|
34
|
+
* requested, or the figures are withheld (MODELO_MISMATCH);
|
|
35
|
+
* 3. the period the backend reports must be well-formed and, when one was
|
|
36
|
+
* requested, identical to it — otherwise the figures belong to another
|
|
37
|
+
* period and are withheld (PERIOD_MISMATCH), never shown under the
|
|
38
|
+
* requested label.
|
|
39
|
+
*/
|
|
40
|
+
function invalidFiscalPeriod(toolName, modeloCode, period) {
|
|
41
|
+
const rule = FISCAL_PERIOD_RULES[modeloCode];
|
|
42
|
+
if (!rule) {
|
|
43
|
+
return notDeployedError(toolName, `/v1/fiscal/modelo/${modeloCode}`);
|
|
44
|
+
}
|
|
45
|
+
if (period === undefined || rule.pattern.test(period))
|
|
46
|
+
return undefined;
|
|
47
|
+
return fiscalPeriodError(toolName, { error: "invalid_period", code: "INVALID_PERIOD", modeloCode, requested: period, expectedFormat: rule.format }, `Invalid period for Modelo ${modeloCode}: use ${rule.format}. No request was sent. ` +
|
|
48
|
+
`/ Periodo no valido para el Modelo ${modeloCode}: usa ${rule.format}. No se ha enviado ninguna peticion.`);
|
|
49
|
+
}
|
|
50
|
+
async function fiscalModeloSummary(client, toolName, modeloCode, period, title) {
|
|
51
|
+
const rule = FISCAL_PERIOD_RULES[modeloCode];
|
|
52
|
+
if (!rule) {
|
|
53
|
+
return notDeployedError(toolName, `/v1/fiscal/modelo/${modeloCode}`);
|
|
54
|
+
}
|
|
55
|
+
const result = await client.getFiscalModeloSummary(modeloCode, period);
|
|
56
|
+
// The backend names the modelo in `modeloCode` and/or legacy `model`
|
|
57
|
+
// (Frihet-ERP publicApi.ts). Every key present must name the requested
|
|
58
|
+
// modelo, and at least one must be present; otherwise another modelo's
|
|
59
|
+
// figures would be shown under this one's label.
|
|
60
|
+
const returnedModelos = [result["modeloCode"], result["model"]].filter((v) => v !== undefined);
|
|
61
|
+
const modeloAgrees = returnedModelos.length > 0 && returnedModelos.every((v) => v === modeloCode);
|
|
62
|
+
if (!modeloAgrees) {
|
|
63
|
+
return fiscalPeriodError(toolName, {
|
|
64
|
+
error: "modelo_mismatch",
|
|
65
|
+
code: "MODELO_MISMATCH",
|
|
66
|
+
requestedModelo: modeloCode,
|
|
67
|
+
returnedModelo: returnedModelos.map((v) => (typeof v === "string" ? v : null)),
|
|
68
|
+
}, `Frihet returned a summary that is not identifiable as Modelo ${modeloCode}, so no figures are shown ` +
|
|
69
|
+
`(they would be mislabelled). Do NOT state any amount for this modelo; retry or contact support. ` +
|
|
70
|
+
`/ Frihet devolvio un resumen que no corresponde al Modelo ${modeloCode}; no se muestran cifras. ` +
|
|
71
|
+
`NO indiques ningun importe para este modelo.`);
|
|
72
|
+
}
|
|
73
|
+
const returned = typeof result["period"] === "string" ? result["period"] : null;
|
|
74
|
+
const year = result["year"];
|
|
75
|
+
const yearAgrees = year === undefined || (returned !== null && String(year) === returned);
|
|
76
|
+
const matches = returned !== null &&
|
|
77
|
+
rule.pattern.test(returned) &&
|
|
78
|
+
yearAgrees &&
|
|
79
|
+
(period === undefined || returned === period);
|
|
80
|
+
if (!matches) {
|
|
81
|
+
return fiscalPeriodError(toolName, { error: "period_mismatch", code: "PERIOD_MISMATCH", modeloCode, requested: period ?? null, returned }, `Frihet returned a Modelo ${modeloCode} summary whose period does not match the request, so no figures are shown ` +
|
|
82
|
+
`(they would be mislabelled). Do NOT state any amount for this period; retry or contact support. ` +
|
|
83
|
+
`/ Frihet devolvio un resumen del Modelo ${modeloCode} de un periodo distinto al solicitado; no se muestran cifras. ` +
|
|
84
|
+
`NO indiques ningun importe para este periodo.`);
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
content: [getContent(formatRecord(title, result))],
|
|
88
|
+
structuredContent: result,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
23
91
|
export function registerFiscalTools(server, client) {
|
|
24
92
|
// -- get_modelo_303_summary --
|
|
25
93
|
server.registerTool("get_modelo_303_summary", {
|
|
@@ -34,16 +102,11 @@ export function registerFiscalTools(server, client) {
|
|
|
34
102
|
period: z
|
|
35
103
|
.string()
|
|
36
104
|
.optional()
|
|
37
|
-
.describe("
|
|
105
|
+
.describe("Quarter in format YYYY-QN (e.g. '2026-Q1'); defaults to the current quarter / Trimestre en formato YYYY-QN; por defecto el trimestre actual"),
|
|
38
106
|
},
|
|
39
107
|
outputSchema: fiscalModeloSummaryOutput,
|
|
40
|
-
}, async ({ period }) => withToolLogging("get_modelo_303_summary", () =>
|
|
41
|
-
|
|
42
|
-
return {
|
|
43
|
-
content: [getContent(formatRecord("Modelo 303 Summary", result))],
|
|
44
|
-
structuredContent: result,
|
|
45
|
-
};
|
|
46
|
-
})));
|
|
108
|
+
}, async ({ period }) => withToolLogging("get_modelo_303_summary", async () => invalidFiscalPeriod("get_modelo_303_summary", "303", period) ??
|
|
109
|
+
withBackendGuard("get_modelo_303_summary", "/v1/fiscal/modelo/303", () => fiscalModeloSummary(client, "get_modelo_303_summary", "303", period, "Modelo 303 Summary"))));
|
|
47
110
|
// -- get_modelo_130_summary --
|
|
48
111
|
server.registerTool("get_modelo_130_summary", {
|
|
49
112
|
title: "Get Modelo 130 Summary (IRPF Estimated Payment)",
|
|
@@ -57,16 +120,11 @@ export function registerFiscalTools(server, client) {
|
|
|
57
120
|
period: z
|
|
58
121
|
.string()
|
|
59
122
|
.optional()
|
|
60
|
-
.describe("
|
|
123
|
+
.describe("Quarter in format YYYY-QN (e.g. '2026-Q1'); defaults to the current quarter / Trimestre en formato YYYY-QN; por defecto el trimestre actual"),
|
|
61
124
|
},
|
|
62
125
|
outputSchema: fiscalModeloSummaryOutput,
|
|
63
|
-
}, async ({ period }) => withToolLogging("get_modelo_130_summary", () =>
|
|
64
|
-
|
|
65
|
-
return {
|
|
66
|
-
content: [getContent(formatRecord("Modelo 130 Summary", result))],
|
|
67
|
-
structuredContent: result,
|
|
68
|
-
};
|
|
69
|
-
})));
|
|
126
|
+
}, async ({ period }) => withToolLogging("get_modelo_130_summary", async () => invalidFiscalPeriod("get_modelo_130_summary", "130", period) ??
|
|
127
|
+
withBackendGuard("get_modelo_130_summary", "/v1/fiscal/modelo/130", () => fiscalModeloSummary(client, "get_modelo_130_summary", "130", period, "Modelo 130 Summary"))));
|
|
70
128
|
// -- get_modelo_390_summary --
|
|
71
129
|
server.registerTool("get_modelo_390_summary", {
|
|
72
130
|
title: "Get Modelo 390 Summary (IVA Annual Recap)",
|
|
@@ -83,21 +141,15 @@ export function registerFiscalTools(server, client) {
|
|
|
83
141
|
.describe("Year in format YYYY (e.g. '2025') / Ejercicio en formato YYYY"),
|
|
84
142
|
},
|
|
85
143
|
outputSchema: fiscalModeloSummaryOutput,
|
|
86
|
-
}, async ({ period }) => withToolLogging("get_modelo_390_summary", () =>
|
|
87
|
-
|
|
88
|
-
return {
|
|
89
|
-
content: [getContent(formatRecord("Modelo 390 Summary", result))],
|
|
90
|
-
structuredContent: result,
|
|
91
|
-
};
|
|
92
|
-
})));
|
|
144
|
+
}, async ({ period }) => withToolLogging("get_modelo_390_summary", async () => invalidFiscalPeriod("get_modelo_390_summary", "390", period) ??
|
|
145
|
+
withBackendGuard("get_modelo_390_summary", "/v1/fiscal/modelo/390", () => fiscalModeloSummary(client, "get_modelo_390_summary", "390", period, "Modelo 390 Summary"))));
|
|
93
146
|
// -- get_modelo_180_summary --
|
|
94
147
|
server.registerTool("get_modelo_180_summary", {
|
|
95
148
|
title: "Get Modelo 180 Summary (IRPF Rentals Annual)",
|
|
96
|
-
description: "
|
|
97
|
-
"
|
|
98
|
-
"
|
|
99
|
-
"
|
|
100
|
-
"Devuelve retenciones totales por inquilino, inmueble y agregado anual.",
|
|
149
|
+
description: "NOT DEPLOYED: Modelo 180 (annual informative return of IRPF withholdings on rental income, Spain) has no Frihet backend yet; " +
|
|
150
|
+
"calling this tool returns a NOT_DEPLOYED error and never data. " +
|
|
151
|
+
"/ NO DESPLEGADO: el Modelo 180 (resumen anual de retenciones sobre alquileres) aun no tiene backend en Frihet; " +
|
|
152
|
+
"esta herramienta devuelve un error NOT_DEPLOYED y nunca datos.",
|
|
101
153
|
annotations: READ_ONLY_ANNOTATIONS,
|
|
102
154
|
inputSchema: {
|
|
103
155
|
period: z
|
|
@@ -106,13 +158,7 @@ export function registerFiscalTools(server, client) {
|
|
|
106
158
|
.describe("Year in format YYYY (e.g. '2025') / Ejercicio en formato YYYY"),
|
|
107
159
|
},
|
|
108
160
|
outputSchema: fiscalModeloSummaryOutput,
|
|
109
|
-
}, async (
|
|
110
|
-
const result = await client.getFiscalModeloSummary("180", period);
|
|
111
|
-
return {
|
|
112
|
-
content: [getContent(formatRecord("Modelo 180 Summary", result))],
|
|
113
|
-
structuredContent: result,
|
|
114
|
-
};
|
|
115
|
-
})));
|
|
161
|
+
}, async () => withToolLogging("get_modelo_180_summary", async () => notDeployedError("get_modelo_180_summary", "/v1/fiscal/modelo/180")));
|
|
116
162
|
// -- get_modelo_347_summary --
|
|
117
163
|
server.registerTool("get_modelo_347_summary", {
|
|
118
164
|
title: "Get Modelo 347 Summary (Operations >€3,005 Annual Recap)",
|
|
@@ -129,13 +175,8 @@ export function registerFiscalTools(server, client) {
|
|
|
129
175
|
.describe("Year in format YYYY (e.g. '2025') / Ejercicio en formato YYYY"),
|
|
130
176
|
},
|
|
131
177
|
outputSchema: fiscalModeloSummaryOutput,
|
|
132
|
-
}, async ({ period }) => withToolLogging("get_modelo_347_summary", () =>
|
|
133
|
-
|
|
134
|
-
return {
|
|
135
|
-
content: [getContent(formatRecord("Modelo 347 Summary", result))],
|
|
136
|
-
structuredContent: result,
|
|
137
|
-
};
|
|
138
|
-
})));
|
|
178
|
+
}, async ({ period }) => withToolLogging("get_modelo_347_summary", async () => invalidFiscalPeriod("get_modelo_347_summary", "347", period) ??
|
|
179
|
+
withBackendGuard("get_modelo_347_summary", "/v1/fiscal/modelo/347", () => fiscalModeloSummary(client, "get_modelo_347_summary", "347", period, "Modelo 347 Summary"))));
|
|
139
180
|
// -- verifactu_status --
|
|
140
181
|
server.registerTool("verifactu_status", {
|
|
141
182
|
title: "Get VeriFactu Submission Status",
|
|
@@ -222,18 +263,10 @@ export function registerFiscalTools(server, client) {
|
|
|
222
263
|
outputSchema: verifactuStatusOutput,
|
|
223
264
|
}, async ({ invoiceId, confirm }) => withToolLogging("verifactu_resubmit", async () => {
|
|
224
265
|
if (!confirm) {
|
|
225
|
-
return
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
text: "Error: confirm=true is required for VeriFactu resubmission. " +
|
|
230
|
-
"This is a COMPLIANCE action that submits fiscal data to AEAT. " +
|
|
231
|
-
"Verify the invoice data is correct before setting confirm=true. / " +
|
|
232
|
-
"Se requiere confirm=true para el reenvio VeriFactu. Accion de COMPLIANCE que envia datos fiscales a AEAT.",
|
|
233
|
-
},
|
|
234
|
-
],
|
|
235
|
-
isError: true,
|
|
236
|
-
};
|
|
266
|
+
return confirmationRequiredError("Error: confirm=true is required for VeriFactu resubmission. " +
|
|
267
|
+
"This is a COMPLIANCE action that submits fiscal data to AEAT. " +
|
|
268
|
+
"Verify the invoice data is correct before setting confirm=true. / " +
|
|
269
|
+
"Se requiere confirm=true para el reenvio VeriFactu. Accion de COMPLIANCE que envia datos fiscales a AEAT.");
|
|
237
270
|
}
|
|
238
271
|
return withBackendGuard("verifactu_resubmit", "/v1/fiscal/verifactu/resubmit", async () => {
|
|
239
272
|
const result = await client.resubmitVerifactu(invoiceId);
|
package/dist/tools/gestoria.js
CHANGED
|
@@ -224,22 +224,30 @@ export function registerGestoriaTools(server, client) {
|
|
|
224
224
|
title: "Consolidated AR Aging (Gestoria)",
|
|
225
225
|
description: "Get a cross-client AR aging report for a gestor — totals bucketed by current / 30-60 / " +
|
|
226
226
|
"60-90 / 90+ days overdue, a per-workspace breakdown, and the top overdue invoices. " +
|
|
227
|
-
"
|
|
228
|
-
"(
|
|
227
|
+
"The ERP requires POST /v1/gestoria/aging with a body listing the workspaces to include " +
|
|
228
|
+
"(defaults to the authenticated gestor when `workspaceIds` is omitted). " +
|
|
229
229
|
"Useful for dunning prioritisation and end-of-month chase lists. " +
|
|
230
|
-
"/ Obtiene un informe de antiguedad de saldos cruzando
|
|
230
|
+
"/ Obtiene un informe de antiguedad de saldos cruzando los clientes del gestor — " +
|
|
231
231
|
"totales por tramo (al dia / 30-60 / 60-90 / 90+ dias), desglose por espacio y top vencidas. " +
|
|
232
232
|
"Util para priorizar reclamaciones y listas de cobro de fin de mes.",
|
|
233
233
|
annotations: READ_ONLY_ANNOTATIONS,
|
|
234
234
|
inputSchema: {
|
|
235
|
-
|
|
235
|
+
workspaceIds: z
|
|
236
|
+
.array(z.string().min(1))
|
|
237
|
+
.optional()
|
|
238
|
+
.describe("Workspace IDs to include (defaults to the authenticated gestor when omitted) / IDs de espacios a incluir (por defecto los del gestor)"),
|
|
239
|
+
asOf: z
|
|
236
240
|
.string()
|
|
237
241
|
.optional()
|
|
238
|
-
.describe("
|
|
242
|
+
.describe("Reference date in ISO 8601 (YYYY-MM-DD). Defaults to today / Fecha de referencia"),
|
|
243
|
+
bustCache: z
|
|
244
|
+
.boolean()
|
|
245
|
+
.optional()
|
|
246
|
+
.describe("Force-refresh server cache (default false) / Forzar refresco de cache del servidor"),
|
|
239
247
|
},
|
|
240
248
|
outputSchema: gestoriaAgingConsolidatedOutput,
|
|
241
|
-
}, async ({
|
|
242
|
-
const result = await client.getGestoriaAgingConsolidated({
|
|
249
|
+
}, async ({ workspaceIds, asOf, bustCache }) => withToolLogging("gestoria_aging_consolidated", () => withBackendGuard("gestoria_aging_consolidated", "/v1/gestoria/aging", async () => {
|
|
250
|
+
const result = await client.getGestoriaAgingConsolidated({ workspaceIds, asOf, bustCache });
|
|
243
251
|
return {
|
|
244
252
|
content: [getContent(formatRecord("Aging consolidated", result))],
|
|
245
253
|
structuredContent: result,
|
package/dist/tools/hr.js
CHANGED
|
@@ -25,13 +25,15 @@ export function registerHrTools(server, client) {
|
|
|
25
25
|
server.registerTool("leave_request_create", {
|
|
26
26
|
title: "Create Leave Request",
|
|
27
27
|
description: "Create a new leave/PTO request for an employee. " +
|
|
28
|
-
"Types: 'vacation', 'sick', 'personal', 'parental', '
|
|
28
|
+
"Types: 'vacation', 'sick', 'personal', 'parental', 'bereavement', 'other'. " +
|
|
29
29
|
"Dates must be ISO 8601 (YYYY-MM-DD). Status starts as 'pending' awaiting manager approval. " +
|
|
30
30
|
"/ Crea una nueva solicitud de vacaciones/permiso. Estado inicial 'pending' pendiente aprobacion.",
|
|
31
31
|
annotations: CREATE_ANNOTATIONS,
|
|
32
32
|
inputSchema: {
|
|
33
33
|
employeeId: z.string().describe("Employee ID / ID del empleado"),
|
|
34
|
-
type: z
|
|
34
|
+
type: z
|
|
35
|
+
.enum(["vacation", "sick", "personal", "parental", "bereavement", "other"])
|
|
36
|
+
.describe("Leave type slug (vacation, sick, personal, parental, bereavement, other) / Tipo de permiso"),
|
|
35
37
|
startDate: z.string().describe("Start date ISO 8601 (YYYY-MM-DD) / Fecha inicio"),
|
|
36
38
|
endDate: z.string().describe("End date ISO 8601 (YYYY-MM-DD) / Fecha fin"),
|
|
37
39
|
reason: z.string().optional().describe("Optional reason / Motivo opcional"),
|
|
@@ -140,10 +142,19 @@ export function registerHrTools(server, client) {
|
|
|
140
142
|
employeeId: z.string().describe("Employee ID / ID del empleado"),
|
|
141
143
|
mood: z.string().optional().describe("Optional mood slug (e.g. 'great','ok','tired') / Estado de animo"),
|
|
142
144
|
location: z.string().optional().describe("Optional location ('remote','office','site') / Ubicacion"),
|
|
145
|
+
idempotencyKey: z
|
|
146
|
+
.string()
|
|
147
|
+
.max(64)
|
|
148
|
+
.optional()
|
|
149
|
+
.describe("Optional idempotency key, max 64 chars. One is generated per call when omitted or blank; " +
|
|
150
|
+
"pass your own to make a retry replay the stored open entry instead of creating a second clock-in. " +
|
|
151
|
+
"Reusing a key with a different body returns 409 IDEMPOTENCY_KEY_REUSED — reconcile, do not " +
|
|
152
|
+
"retry with a new key. / Clave de idempotencia opcional (max 64). Se genera una por llamada si se omite."),
|
|
143
153
|
},
|
|
144
154
|
outputSchema: attendanceEntryItemOutput,
|
|
145
155
|
}, async (input) => withToolLogging("attendance_clock_in", () => withBackendGuard("attendance_clock_in", "/v1/time-entries/clock-in", async () => {
|
|
146
|
-
const
|
|
156
|
+
const { idempotencyKey, ...data } = input;
|
|
157
|
+
const result = await client.attendanceClockIn(data, idempotencyKey);
|
|
147
158
|
return {
|
|
148
159
|
content: [mutateContent(formatRecord("Clocked in", result))],
|
|
149
160
|
structuredContent: result,
|
|
@@ -158,10 +169,18 @@ export function registerHrTools(server, client) {
|
|
|
158
169
|
annotations: UPDATE_ANNOTATIONS,
|
|
159
170
|
inputSchema: {
|
|
160
171
|
entryId: z.string().describe("Open attendance entry ID / ID de la entrada abierta"),
|
|
172
|
+
idempotencyKey: z
|
|
173
|
+
.string()
|
|
174
|
+
.max(64)
|
|
175
|
+
.optional()
|
|
176
|
+
.describe("Optional idempotency key, max 64 chars. One is generated per call when omitted or blank; " +
|
|
177
|
+
"pass your own to make a retry replay the stored close instead of erroring on a closed entry. " +
|
|
178
|
+
"/ Clave de idempotencia opcional (max 64). Se genera una por llamada si se omite."),
|
|
161
179
|
},
|
|
162
180
|
outputSchema: attendanceEntryItemOutput,
|
|
163
|
-
}, async (
|
|
164
|
-
const
|
|
181
|
+
}, async (input) => withToolLogging("attendance_clock_out", () => withBackendGuard("attendance_clock_out", "/v1/time-entries/clock-out", async () => {
|
|
182
|
+
const { entryId, idempotencyKey } = input;
|
|
183
|
+
const result = await client.attendanceClockOut(entryId, idempotencyKey);
|
|
165
184
|
return {
|
|
166
185
|
content: [mutateContent(formatRecord("Clocked out", result))],
|
|
167
186
|
structuredContent: result,
|
|
@@ -191,19 +210,30 @@ export function registerHrTools(server, client) {
|
|
|
191
210
|
server.registerTool("anomaly_list", {
|
|
192
211
|
title: "List Anomalies",
|
|
193
212
|
description: "List HR / operational / financial anomalies detected by the system. " +
|
|
194
|
-
"Filter by type (
|
|
195
|
-
"severity (
|
|
213
|
+
"Filter by type (daily_exceeded, weekly_exceeded, annual_approaching, " +
|
|
214
|
+
"annual_exceeded, missing_break), severity (warning/critical), or period. " +
|
|
215
|
+
"Anomalies are COMPUTE-ON-READ over the live attendance records (Art.34/35 ET " +
|
|
216
|
+
"overtime engine) — there is no stored anomalies collection. " +
|
|
196
217
|
"Useful for daily HR review and compliance audits. " +
|
|
197
218
|
"/ Lista anomalias detectadas (RRHH/operativas/financieras) con filtros opcionales.",
|
|
198
219
|
annotations: READ_ONLY_ANNOTATIONS,
|
|
199
220
|
inputSchema: {
|
|
200
|
-
type: z
|
|
221
|
+
type: z
|
|
222
|
+
.enum([
|
|
223
|
+
"daily_exceeded",
|
|
224
|
+
"weekly_exceeded",
|
|
225
|
+
"annual_approaching",
|
|
226
|
+
"annual_exceeded",
|
|
227
|
+
"missing_break",
|
|
228
|
+
])
|
|
229
|
+
.optional()
|
|
230
|
+
.describe("Filter by anomaly type slug / Tipo"),
|
|
201
231
|
severity: z
|
|
202
|
-
.enum(["
|
|
232
|
+
.enum(["warning", "critical"])
|
|
203
233
|
.optional()
|
|
204
234
|
.describe("Filter by severity / Severidad"),
|
|
205
|
-
from: z.string().optional().describe("Period start ISO 8601 / Inicio"),
|
|
206
|
-
to: z.string().optional().describe("Period end ISO 8601 / Fin"),
|
|
235
|
+
from: z.string().optional().describe("Period start ISO 8601 (YYYY-MM-DD) / Inicio"),
|
|
236
|
+
to: z.string().optional().describe("Period end ISO 8601 (YYYY-MM-DD) / Fin"),
|
|
207
237
|
limit: z.number().int().min(1).max(100).optional().describe("Max results / Maximos"),
|
|
208
238
|
offset: z.number().int().min(0).optional().describe("Offset / Desplazamiento"),
|
|
209
239
|
},
|
package/dist/tools/igic.d.ts
CHANGED
|
@@ -2,18 +2,19 @@
|
|
|
2
2
|
* IGIC (Canary Islands indirect tax) tools for the Frihet MCP server — Day 1 Megasprint (4 tools).
|
|
3
3
|
*
|
|
4
4
|
* Tools:
|
|
5
|
-
* 1. frihet_modelo_415_summary — M415 operations >€3,005 annual (Canarias)
|
|
6
|
-
* 2. frihet_modelo_425_summary — M425 resumen anual IGIC (Canarias)
|
|
7
|
-
* 3. frihet_modelo_418_summary — M418 mensual
|
|
8
|
-
*
|
|
5
|
+
* 1. frihet_modelo_415_summary — M415 operations >€3,005 annual (Canarias) — NOT DEPLOYED
|
|
6
|
+
* 2. frihet_modelo_425_summary — M425 resumen anual IGIC (Canarias) — NOT DEPLOYED
|
|
7
|
+
* 3. frihet_modelo_418_summary — M418 autoliquidacion mensual individual, regimen especial
|
|
8
|
+
* del grupo de entidades (ATC) — NOT DEPLOYED
|
|
9
|
+
* 4. frihet_aiem_calculate — AIEM (Arbitrio sobre Importaciones y Entrega de Mercancías) — NOT DEPLOYED
|
|
9
10
|
*
|
|
10
11
|
* NOTE: ATC SOAP integration skipped — internal infrastructure, not exposed via MCP.
|
|
11
12
|
*
|
|
12
|
-
* REST surface: /v1/igic/*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
13
|
+
* REST surface: Frihet-ERP functions/src/publicApi.ts has NO /v1/igic/* route
|
|
14
|
+
* (neither modelo summaries nor AIEM), so all four tools return NOT_DEPLOYED
|
|
15
|
+
* without calling (capability truth: `unavailable`, src/capability-truth.ts).
|
|
15
16
|
*/
|
|
16
17
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
17
18
|
import type { IFrihetClient } from "../client-interface.js";
|
|
18
|
-
export declare function registerIgicTools(server: McpServer,
|
|
19
|
+
export declare function registerIgicTools(server: McpServer, _client: IFrihetClient): void;
|
|
19
20
|
//# sourceMappingURL=igic.d.ts.map
|