@frihet/mcp-server 1.16.5 → 1.17.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 +92 -31
- package/dist/agent-onboarding.d.ts +98 -0
- package/dist/agent-onboarding.js +275 -0
- package/dist/api-origin.d.ts +9 -0
- package/dist/api-origin.js +41 -0
- package/dist/canary/mcp-harness.d.ts +77 -0
- package/dist/canary/mcp-harness.js +549 -0
- package/dist/capability-truth.d.ts +29 -0
- package/dist/capability-truth.js +254 -0
- package/dist/client-interface.d.ts +22 -14
- package/dist/client.d.ts +114 -18
- package/dist/client.js +520 -58
- package/dist/demo-client.d.ts +16 -13
- package/dist/demo-client.js +209 -29
- package/dist/demo-fixtures.d.ts +3 -3
- package/dist/demo-fixtures.js +7 -7
- package/dist/fiscal-aliases.d.ts +44 -0
- package/dist/fiscal-aliases.js +62 -0
- package/dist/index.js +41 -56
- package/dist/logger.d.ts +2 -2
- package/dist/logger.js +140 -8
- package/dist/observability.d.ts +18 -29
- package/dist/observability.js +192 -105
- package/dist/openai-profile.d.ts +27 -15
- package/dist/openai-profile.js +1191 -145
- package/dist/openai-review-contract.d.ts +49 -0
- package/dist/openai-review-contract.js +919 -0
- package/dist/openai-review-oauth.d.ts +98 -0
- package/dist/openai-review-oauth.js +136 -0
- package/dist/prompts/register-all.d.ts +2 -0
- package/dist/prompts/register-all.js +2 -0
- package/dist/public-capability-contract.d.ts +39 -0
- package/dist/public-capability-contract.js +186 -0
- package/dist/redaction.d.ts +10 -0
- package/dist/redaction.js +74 -0
- package/dist/resources/register-all.js +6 -12
- package/dist/server-composition.d.ts +16 -0
- package/dist/server-composition.js +44 -0
- package/dist/tool-exposure.d.ts +25 -22
- package/dist/tool-exposure.js +199 -77
- package/dist/tools/accountingClose.d.ts +14 -5
- package/dist/tools/accountingClose.js +41 -23
- package/dist/tools/backend-availability.d.ts +3 -3
- package/dist/tools/backend-availability.js +3 -3
- package/dist/tools/clients.js +9 -9
- package/dist/tools/crm.js +22 -21
- package/dist/tools/deposits.js +30 -11
- package/dist/tools/einvoice.d.ts +1 -1
- package/dist/tools/expenses.d.ts +16 -0
- package/dist/tools/expenses.js +47 -27
- package/dist/tools/hr.d.ts +2 -3
- package/dist/tools/hr.js +8 -9
- package/dist/tools/intelligence.js +95 -4
- package/dist/tools/invoices.js +150 -47
- package/dist/tools/payroll.d.ts +5 -8
- package/dist/tools/payroll.js +19 -32
- package/dist/tools/permissions.d.ts +4 -7
- package/dist/tools/permissions.js +11 -14
- package/dist/tools/products.js +6 -6
- package/dist/tools/quotes.js +109 -24
- package/dist/tools/shared.d.ts +1431 -75
- package/dist/tools/shared.js +386 -76
- package/dist/tools/team.js +18 -14
- package/dist/tools/vendors.js +9 -9
- package/dist/tools/webhooks.js +35 -30
- package/dist/types.d.ts +28 -4
- package/docs/agent-onboarding.json +381 -0
- package/package.json +32 -7
package/dist/demo-client.d.ts
CHANGED
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
* stamp; this is the one accepted, documented gap).
|
|
19
19
|
*/
|
|
20
20
|
import type { IFrihetClient } from "./client-interface.js";
|
|
21
|
-
import type { PaginatedResponse } from "./types.js";
|
|
21
|
+
import type { CreateWebhookInput, CreateWebhookResult, PaginatedResponse, UpdateWebhookInput, Webhook } from "./types.js";
|
|
22
|
+
import type { BinaryDocument, EInvoiceDocument } from "./client.js";
|
|
22
23
|
type Rec = Record<string, unknown>;
|
|
23
24
|
export declare class DemoFrihetClient implements IFrihetClient {
|
|
24
25
|
listInvoices(params?: {
|
|
@@ -75,8 +76,8 @@ export declare class DemoFrihetClient implements IFrihetClient {
|
|
|
75
76
|
deleteVendor(_id: string): Promise<void>;
|
|
76
77
|
sendInvoice(id: string, to?: string): Promise<Rec>;
|
|
77
78
|
markInvoicePaid(id: string, paidDate?: string): Promise<Rec>;
|
|
78
|
-
getInvoicePdf(id: string): Promise<
|
|
79
|
-
getInvoiceEInvoice(invoiceId: string): Promise<
|
|
79
|
+
getInvoicePdf(id: string): Promise<BinaryDocument>;
|
|
80
|
+
getInvoiceEInvoice(invoiceId: string): Promise<EInvoiceDocument>;
|
|
80
81
|
createCreditNote(invoiceId: string, data: {
|
|
81
82
|
reason: string;
|
|
82
83
|
reasonDescription?: string;
|
|
@@ -88,13 +89,15 @@ export declare class DemoFrihetClient implements IFrihetClient {
|
|
|
88
89
|
daysOverdue?: number;
|
|
89
90
|
}): Promise<Rec>;
|
|
90
91
|
sendQuote(id: string, to?: string): Promise<Rec>;
|
|
91
|
-
listWebhooks(
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
92
|
+
listWebhooks(): Promise<{
|
|
93
|
+
data: Webhook[];
|
|
94
|
+
total: number;
|
|
95
|
+
_demo?: true;
|
|
96
|
+
_demoNotice?: string;
|
|
97
|
+
}>;
|
|
98
|
+
getWebhook(id: string): Promise<Webhook>;
|
|
99
|
+
createWebhook(data: CreateWebhookInput): Promise<CreateWebhookResult>;
|
|
100
|
+
updateWebhook(id: string, data: UpdateWebhookInput): Promise<Webhook>;
|
|
98
101
|
deleteWebhook(_id: string): Promise<void>;
|
|
99
102
|
listClientContacts(_clientId: string, params?: {
|
|
100
103
|
limit?: number;
|
|
@@ -106,7 +109,7 @@ export declare class DemoFrihetClient implements IFrihetClient {
|
|
|
106
109
|
limit?: number;
|
|
107
110
|
offset?: number;
|
|
108
111
|
}): Promise<PaginatedResponse<Rec>>;
|
|
109
|
-
logClientActivity(
|
|
112
|
+
logClientActivity(_clientId: string, data: Rec): Promise<Rec>;
|
|
110
113
|
listClientNotes(_clientId: string, params?: {
|
|
111
114
|
limit?: number;
|
|
112
115
|
offset?: number;
|
|
@@ -441,7 +444,7 @@ export declare class DemoFrihetClient implements IFrihetClient {
|
|
|
441
444
|
eventType?: string;
|
|
442
445
|
}): Promise<Rec>;
|
|
443
446
|
exportPayroll(params: {
|
|
444
|
-
format: "a3" | "contasol" | "sage" | "
|
|
447
|
+
format: "a3" | "contasol" | "sage" | "siltra";
|
|
445
448
|
month: string;
|
|
446
449
|
}): Promise<Rec>;
|
|
447
450
|
getPayrollChecklist(params: {
|
|
@@ -454,7 +457,7 @@ export declare class DemoFrihetClient implements IFrihetClient {
|
|
|
454
457
|
getPermissionsMatrix(): Promise<Rec>;
|
|
455
458
|
getMyPermissions(): Promise<Rec>;
|
|
456
459
|
getCurrentPeriod(params?: {
|
|
457
|
-
|
|
460
|
+
fiscalYear?: string;
|
|
458
461
|
}): Promise<Rec>;
|
|
459
462
|
closePeriod(data: {
|
|
460
463
|
type: "monthly" | "quarterly";
|
package/dist/demo-client.js
CHANGED
|
@@ -17,7 +17,62 @@
|
|
|
17
17
|
* - delete / remove methods → resolve void (interface return type forbids a
|
|
18
18
|
* stamp; this is the one accepted, documented gap).
|
|
19
19
|
*/
|
|
20
|
+
import { Buffer } from "node:buffer";
|
|
20
21
|
import { READ_STAMP, FISCAL_STAMP, DEMO_NOW, demoPage, demoEmptyPage, findOrStub, demoId, simulateWrite, simulateAction, demoClients, demoProducts, demoInvoices, demoExpenses, demoQuotes, demoVendors, demoBankAccounts, demoTransactions, } from "./demo-fixtures.js";
|
|
22
|
+
const DEMO_RBAC_ROLES = ["owner", "admin", "manager", "sales", "accountant", "employee", "viewer"];
|
|
23
|
+
const DEMO_RBAC_RESOURCES = [
|
|
24
|
+
"workspace", "invoices", "quotes", "expenses", "clients", "products", "accounting",
|
|
25
|
+
"people", "payroll", "integrations", "banking", "settings", "audit_log", "billing",
|
|
26
|
+
];
|
|
27
|
+
const DEMO_RBAC_ACTIONS = ["read", "create", "update", "delete", "bulk_delete", "export"];
|
|
28
|
+
const DEMO_FULL_ACTIONS = [...DEMO_RBAC_ACTIONS];
|
|
29
|
+
const DEMO_PERMISSION_MATRIX = {
|
|
30
|
+
owner: {
|
|
31
|
+
workspace: ["read", "update", "export"],
|
|
32
|
+
invoices: DEMO_FULL_ACTIONS, quotes: DEMO_FULL_ACTIONS, expenses: DEMO_FULL_ACTIONS,
|
|
33
|
+
clients: DEMO_FULL_ACTIONS, products: DEMO_FULL_ACTIONS, accounting: DEMO_FULL_ACTIONS,
|
|
34
|
+
people: DEMO_FULL_ACTIONS, payroll: DEMO_FULL_ACTIONS, integrations: DEMO_FULL_ACTIONS,
|
|
35
|
+
banking: ["read", "create", "update", "delete", "export"],
|
|
36
|
+
settings: ["read", "update"], audit_log: ["read", "export"],
|
|
37
|
+
billing: ["read", "create", "update", "delete", "export"],
|
|
38
|
+
},
|
|
39
|
+
admin: {
|
|
40
|
+
workspace: ["read", "update", "export"],
|
|
41
|
+
invoices: DEMO_FULL_ACTIONS, quotes: DEMO_FULL_ACTIONS, expenses: DEMO_FULL_ACTIONS,
|
|
42
|
+
clients: DEMO_FULL_ACTIONS, products: DEMO_FULL_ACTIONS, accounting: DEMO_FULL_ACTIONS,
|
|
43
|
+
people: DEMO_FULL_ACTIONS, payroll: DEMO_FULL_ACTIONS, integrations: DEMO_FULL_ACTIONS,
|
|
44
|
+
banking: ["read", "create", "update", "delete", "export"],
|
|
45
|
+
settings: ["read", "update"], audit_log: ["read", "export"],
|
|
46
|
+
},
|
|
47
|
+
manager: {
|
|
48
|
+
workspace: ["read"], invoices: ["read", "export"], quotes: ["read", "export"],
|
|
49
|
+
clients: ["read", "export"], products: ["read", "export"], expenses: ["read", "export"],
|
|
50
|
+
banking: ["read"], people: DEMO_FULL_ACTIONS, payroll: ["read", "export"],
|
|
51
|
+
settings: ["read"], integrations: ["read"], audit_log: ["read"],
|
|
52
|
+
},
|
|
53
|
+
sales: {
|
|
54
|
+
workspace: ["read"], invoices: ["read", "create", "update", "export"],
|
|
55
|
+
quotes: ["read", "create", "update", "export"], clients: ["read", "create", "update", "export"],
|
|
56
|
+
products: ["read", "export"], expenses: ["read", "export"],
|
|
57
|
+
},
|
|
58
|
+
accountant: {
|
|
59
|
+
workspace: ["read"], invoices: ["read", "create", "update", "export"],
|
|
60
|
+
quotes: ["read", "export"], expenses: ["read", "create", "update", "export"],
|
|
61
|
+
clients: ["read", "export"], products: ["read", "export"],
|
|
62
|
+
banking: ["read", "create", "update", "export"], accounting: ["read", "create", "update", "export"],
|
|
63
|
+
people: ["read"], audit_log: ["read", "export"],
|
|
64
|
+
},
|
|
65
|
+
employee: { workspace: ["read"] },
|
|
66
|
+
viewer: {
|
|
67
|
+
workspace: ["read", "export"], invoices: ["read", "export"], quotes: ["read", "export"],
|
|
68
|
+
expenses: ["read", "export"], clients: ["read", "export"], products: ["read", "export"],
|
|
69
|
+
banking: ["read"], accounting: ["read", "export"], people: ["read"], payroll: ["read"],
|
|
70
|
+
settings: ["read"], integrations: ["read"], audit_log: ["read"],
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
const DEMO_OWNER_CAPABILITIES = Object.entries(DEMO_PERMISSION_MATRIX.owner)
|
|
74
|
+
.flatMap(([resource, actions]) => actions.map((action) => `${resource}:${action}`))
|
|
75
|
+
.sort();
|
|
21
76
|
export class DemoFrihetClient {
|
|
22
77
|
// ---------------------------------------------------------------- Invoices
|
|
23
78
|
async listInvoices(params) {
|
|
@@ -139,13 +194,27 @@ export class DemoFrihetClient {
|
|
|
139
194
|
return simulateAction(id, { status: "paid", paidAt: paidDate ?? DEMO_NOW });
|
|
140
195
|
}
|
|
141
196
|
async getInvoicePdf(id) {
|
|
142
|
-
|
|
197
|
+
// #1393: shape mirrors the live BinaryDocument contract (id,
|
|
198
|
+
// contentType, sizeBytes, base64) so demo-mode outputSchema validation
|
|
199
|
+
// runs against the same fields as the live path.
|
|
200
|
+
const demoPdfBytes = Buffer.from("%PDF-1.4\n%%DEMO e-invoice for " + id + "\n%%EOF\n", "utf8");
|
|
201
|
+
return {
|
|
202
|
+
id,
|
|
203
|
+
contentType: "application/pdf",
|
|
204
|
+
sizeBytes: demoPdfBytes.byteLength,
|
|
205
|
+
base64: demoPdfBytes.toString("base64"),
|
|
206
|
+
...READ_STAMP,
|
|
207
|
+
};
|
|
143
208
|
}
|
|
144
209
|
async getInvoiceEInvoice(invoiceId) {
|
|
210
|
+
// #1393: mirrors the live XML arm of the MIME-discriminated contract.
|
|
211
|
+
const xml = `<?xml version="1.0" encoding="UTF-8"?>\n<Invoice><!-- DEMO example e-invoice for ${invoiceId} --></Invoice>`;
|
|
145
212
|
return {
|
|
146
|
-
|
|
213
|
+
id: invoiceId,
|
|
214
|
+
xml,
|
|
215
|
+
contentType: "application/xml; charset=utf-8",
|
|
216
|
+
sizeBytes: Buffer.byteLength(xml, "utf8"),
|
|
147
217
|
filename: `${invoiceId}.xml`,
|
|
148
|
-
format: "facturae",
|
|
149
218
|
...FISCAL_STAMP,
|
|
150
219
|
};
|
|
151
220
|
}
|
|
@@ -190,17 +259,42 @@ export class DemoFrihetClient {
|
|
|
190
259
|
return { id, status: "sent", sentAt: DEMO_NOW, ...(to ? { to } : {}), ...FISCAL_STAMP };
|
|
191
260
|
}
|
|
192
261
|
// ---------------------------------------------------------------- Webhooks
|
|
193
|
-
async listWebhooks(
|
|
194
|
-
|
|
262
|
+
async listWebhooks() {
|
|
263
|
+
const { data, total } = demoEmptyPage();
|
|
264
|
+
return { data: data, total };
|
|
195
265
|
}
|
|
196
266
|
async getWebhook(id) {
|
|
197
|
-
return {
|
|
267
|
+
return {
|
|
268
|
+
id,
|
|
269
|
+
name: "Demo webhook",
|
|
270
|
+
url: "https://example.com/webhooks/frihet",
|
|
271
|
+
events: ["invoice.paid"],
|
|
272
|
+
status: "active",
|
|
273
|
+
metadata: { demo: true },
|
|
274
|
+
hasSecret: false,
|
|
275
|
+
createdAt: DEMO_NOW,
|
|
276
|
+
updatedAt: DEMO_NOW,
|
|
277
|
+
...READ_STAMP,
|
|
278
|
+
};
|
|
198
279
|
}
|
|
199
280
|
async createWebhook(data) {
|
|
200
|
-
return simulateWrite("demo_wh",
|
|
281
|
+
return simulateWrite("demo_wh", { ...data }, {
|
|
282
|
+
status: data.status ?? "active",
|
|
283
|
+
hasSecret: typeof data.secret === "string" && data.secret.length > 0,
|
|
284
|
+
});
|
|
201
285
|
}
|
|
202
286
|
async updateWebhook(id, data) {
|
|
203
|
-
|
|
287
|
+
const { secret, ...fields } = data;
|
|
288
|
+
const { success: _success, ...result } = simulateAction(id, {
|
|
289
|
+
name: "Demo webhook",
|
|
290
|
+
url: "https://example.com/webhooks/frihet",
|
|
291
|
+
events: ["invoice.paid"],
|
|
292
|
+
status: "active",
|
|
293
|
+
metadata: { demo: true },
|
|
294
|
+
hasSecret: typeof secret === "string" && secret.length > 0,
|
|
295
|
+
...fields,
|
|
296
|
+
});
|
|
297
|
+
return result;
|
|
204
298
|
}
|
|
205
299
|
async deleteWebhook(_id) {
|
|
206
300
|
return;
|
|
@@ -219,8 +313,13 @@ export class DemoFrihetClient {
|
|
|
219
313
|
async listClientActivities(_clientId, params) {
|
|
220
314
|
return demoEmptyPage(params);
|
|
221
315
|
}
|
|
222
|
-
async logClientActivity(
|
|
223
|
-
return simulateWrite("demo_act", data, {
|
|
316
|
+
async logClientActivity(_clientId, data) {
|
|
317
|
+
return simulateWrite("demo_act", data, {
|
|
318
|
+
type: data.type === "email" ? "email_sent" : data.type,
|
|
319
|
+
timestamp: DEMO_NOW,
|
|
320
|
+
createdBy: "user",
|
|
321
|
+
metadata: {},
|
|
322
|
+
});
|
|
224
323
|
}
|
|
225
324
|
// ---------------------------------------------------------------- CRM: Notes
|
|
226
325
|
async listClientNotes(_clientId, params) {
|
|
@@ -257,23 +356,49 @@ export class DemoFrihetClient {
|
|
|
257
356
|
// ---------------------------------------------------------------- Intelligence
|
|
258
357
|
async getBusinessContext() {
|
|
259
358
|
return {
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
359
|
+
business: {
|
|
360
|
+
name: "Demo Studio SL",
|
|
361
|
+
fiscalZone: "IVA",
|
|
362
|
+
currency: "EUR",
|
|
363
|
+
language: "es",
|
|
364
|
+
country: "ES",
|
|
365
|
+
},
|
|
366
|
+
defaults: { taxRate: 21, irpfRate: 15, dueDays: 30, currency: "EUR" },
|
|
367
|
+
plan: {
|
|
368
|
+
name: "free",
|
|
369
|
+
invoices: { used: 5, limit: 999 },
|
|
370
|
+
expenses: { used: demoExpenses.length, limit: 5 },
|
|
371
|
+
aiMessages: { used: 2, limit: 30 },
|
|
372
|
+
},
|
|
373
|
+
series: [{ id: "default", prefix: "F", current: 5, year: 2026 }],
|
|
374
|
+
recentActivity: {
|
|
375
|
+
lastInvoice: { number: "F-2026-005", date: "2026-07-18", client: "Acme Studio" },
|
|
376
|
+
lastExpense: { date: "2026-07-17", vendor: "Demo Supplies", amount: 405.6 },
|
|
377
|
+
overdueCount: 1,
|
|
378
|
+
overdueAmount: 640,
|
|
379
|
+
unpaidCount: 2,
|
|
380
|
+
},
|
|
381
|
+
topClients: [{ name: "Acme Studio", totalRevenue: 2200, invoiceCount: 2 }],
|
|
382
|
+
currentMonth: {
|
|
383
|
+
revenue: 3960.4,
|
|
384
|
+
expenses: 405.6,
|
|
385
|
+
profit: 3554.8,
|
|
386
|
+
invoiceCount: 5,
|
|
387
|
+
expenseCount: demoExpenses.length,
|
|
388
|
+
},
|
|
265
389
|
...READ_STAMP,
|
|
266
390
|
};
|
|
267
391
|
}
|
|
268
392
|
async getMonthlySummary(month) {
|
|
269
393
|
return {
|
|
270
|
-
|
|
271
|
-
revenue: 3960.4,
|
|
272
|
-
expenses: 405.6,
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
394
|
+
period: month ?? "2026-07",
|
|
395
|
+
revenue: { total: 3960.4, taxBase: 3273.06, tax: 687.34, irpf: 0 },
|
|
396
|
+
expenses: { total: 405.6, deductible: 405.6, tax: 70.4 },
|
|
397
|
+
profit: { gross: 3554.8, net: 2937.86 },
|
|
398
|
+
invoices: { created: 5, sent: 1, paid: 3, overdue: 1 },
|
|
399
|
+
topClients: [{ name: "Acme Studio", totalRevenue: 2200, invoiceCount: 2 }],
|
|
400
|
+
byCategory: { supplies: 405.6 },
|
|
401
|
+
taxLiability: { vatPayable: 616.94, irpfRetained: 0, estimatedModel303: 616.94 },
|
|
277
402
|
...READ_STAMP,
|
|
278
403
|
};
|
|
279
404
|
}
|
|
@@ -530,7 +655,17 @@ export class DemoFrihetClient {
|
|
|
530
655
|
return simulateAction(entryId, { status: "closed", clockOutAt: DEMO_NOW });
|
|
531
656
|
}
|
|
532
657
|
async getOvertimeReport(params) {
|
|
533
|
-
return {
|
|
658
|
+
return {
|
|
659
|
+
period: params.period,
|
|
660
|
+
employeeId: params.employeeId ?? null,
|
|
661
|
+
recordCount: 0,
|
|
662
|
+
dailyOvertime: [],
|
|
663
|
+
weeklyOvertime: [],
|
|
664
|
+
monthlyTotal: { workedMinutes: 0, overtimeMinutes: 0, regularMinutes: 0 },
|
|
665
|
+
annualOvertimeHours: 0,
|
|
666
|
+
alerts: [],
|
|
667
|
+
...READ_STAMP,
|
|
668
|
+
};
|
|
534
669
|
}
|
|
535
670
|
async listAnomalies(params) {
|
|
536
671
|
return demoEmptyPage(params);
|
|
@@ -539,12 +674,23 @@ export class DemoFrihetClient {
|
|
|
539
674
|
async testWebhook(id, data) {
|
|
540
675
|
return { webhookId: id, delivered: true, statusCode: 200, responseTimeMs: 42, eventType: data?.eventType ?? "ping", attemptedAt: DEMO_NOW, ...FISCAL_STAMP };
|
|
541
676
|
}
|
|
542
|
-
// ---------------------------------------------------------------- Payroll (
|
|
677
|
+
// ---------------------------------------------------------------- Payroll (read-only demo)
|
|
543
678
|
async exportPayroll(params) {
|
|
544
|
-
return {
|
|
679
|
+
return {
|
|
680
|
+
month: params.month,
|
|
681
|
+
format: params.format,
|
|
682
|
+
employees: [],
|
|
683
|
+
summary: { exportedCount: 0, skippedNotReady: 0, totalGrossAnnual: 0 },
|
|
684
|
+
...READ_STAMP,
|
|
685
|
+
};
|
|
545
686
|
}
|
|
546
687
|
async getPayrollChecklist(params) {
|
|
547
|
-
return {
|
|
688
|
+
return {
|
|
689
|
+
month: params.month,
|
|
690
|
+
employees: [],
|
|
691
|
+
summary: { total: 0, ready: 0, notReady: 0, reviewedThisMonth: 0 },
|
|
692
|
+
...READ_STAMP,
|
|
693
|
+
};
|
|
548
694
|
}
|
|
549
695
|
// ---------------------------------------------------------------- Onboarding
|
|
550
696
|
async getOnboardingStatus() {
|
|
@@ -555,14 +701,48 @@ export class DemoFrihetClient {
|
|
|
555
701
|
}
|
|
556
702
|
// ---------------------------------------------------------------- Permissions
|
|
557
703
|
async getPermissionsMatrix() {
|
|
558
|
-
return {
|
|
704
|
+
return {
|
|
705
|
+
roles: DEMO_RBAC_ROLES,
|
|
706
|
+
resources: DEMO_RBAC_RESOURCES,
|
|
707
|
+
actions: DEMO_RBAC_ACTIONS,
|
|
708
|
+
legacyAliases: { editor: "sales" },
|
|
709
|
+
matrix: DEMO_PERMISSION_MATRIX,
|
|
710
|
+
source: "hand-maintained demo snapshot of the Frihet RBAC model; not derived from firestore.rules and not verified against it",
|
|
711
|
+
...READ_STAMP,
|
|
712
|
+
};
|
|
559
713
|
}
|
|
560
714
|
async getMyPermissions() {
|
|
561
|
-
return {
|
|
715
|
+
return {
|
|
716
|
+
role: "owner",
|
|
717
|
+
isOwner: true,
|
|
718
|
+
resources: DEMO_PERMISSION_MATRIX.owner,
|
|
719
|
+
scopes: DEMO_OWNER_CAPABILITIES,
|
|
720
|
+
legacyFieldSemantics: { resources: "rbacResources", scopes: "rbacCapabilities" },
|
|
721
|
+
rbac: {
|
|
722
|
+
role: "owner",
|
|
723
|
+
isOwner: true,
|
|
724
|
+
resources: DEMO_PERMISSION_MATRIX.owner,
|
|
725
|
+
capabilities: DEMO_OWNER_CAPABILITIES,
|
|
726
|
+
},
|
|
727
|
+
apiKeyScopes: [],
|
|
728
|
+
apiKeyUnrestricted: true,
|
|
729
|
+
denied: { einvoice: false },
|
|
730
|
+
deniedSemantics: "Known API-key scope denials only; not an exhaustive effective-authorization report.",
|
|
731
|
+
notIncluded: ["featureFlags", "deployedEndpoints"],
|
|
732
|
+
...READ_STAMP,
|
|
733
|
+
};
|
|
562
734
|
}
|
|
563
735
|
// ---------------------------------------------------------------- Period close
|
|
564
736
|
async getCurrentPeriod(params) {
|
|
565
|
-
|
|
737
|
+
const fiscalYear = params?.fiscalYear ?? "2026";
|
|
738
|
+
return {
|
|
739
|
+
fiscalYear,
|
|
740
|
+
fiscalYearStart: "01-01",
|
|
741
|
+
status: "open",
|
|
742
|
+
dateRange: { from: `${fiscalYear}-01-01`, to: `${fiscalYear}-12-31` },
|
|
743
|
+
closing: null,
|
|
744
|
+
...READ_STAMP,
|
|
745
|
+
};
|
|
566
746
|
}
|
|
567
747
|
async closePeriod(data) {
|
|
568
748
|
return simulateWrite("demo_period", { type: data.type }, { status: "closed", closedAt: DEMO_NOW });
|
package/dist/demo-fixtures.d.ts
CHANGED
|
@@ -30,13 +30,13 @@ export declare const EMPTY_STAMP: DemoStamp;
|
|
|
30
30
|
export declare const DEMO_NOW = "2026-07-15T10:00:00.000Z";
|
|
31
31
|
/** Test IBAN (ECBS/AEAT example — never a real account). */
|
|
32
32
|
export declare const DEMO_TEST_IBAN = "ES9121000418450200051332";
|
|
33
|
-
export type DemoPaginated = PaginatedResponse<Record<string, unknown
|
|
34
|
-
/** Wrap a fixture array in a
|
|
33
|
+
export type DemoPaginated = PaginatedResponse<Record<string, unknown>>;
|
|
34
|
+
/** Wrap a fixture array in a PaginatedResponse. */
|
|
35
35
|
export declare function demoPage(data: Record<string, unknown>[], params?: {
|
|
36
36
|
limit?: number;
|
|
37
37
|
offset?: number;
|
|
38
38
|
}): DemoPaginated;
|
|
39
|
-
/** Empty
|
|
39
|
+
/** Empty PaginatedResponse for surfaces without dedicated fixtures. */
|
|
40
40
|
export declare function demoEmptyPage(params?: {
|
|
41
41
|
limit?: number;
|
|
42
42
|
offset?: number;
|
package/dist/demo-fixtures.js
CHANGED
|
@@ -36,24 +36,24 @@ export const EMPTY_STAMP = {
|
|
|
36
36
|
export const DEMO_NOW = "2026-07-15T10:00:00.000Z";
|
|
37
37
|
/** Test IBAN (ECBS/AEAT example — never a real account). */
|
|
38
38
|
export const DEMO_TEST_IBAN = "ES9121000418450200051332";
|
|
39
|
-
/** Wrap a fixture array in a
|
|
39
|
+
/** Wrap a fixture array in a PaginatedResponse. */
|
|
40
40
|
export function demoPage(data, params) {
|
|
41
|
+
const limit = params?.limit ?? 50;
|
|
42
|
+
const offset = params?.offset ?? 0;
|
|
41
43
|
return {
|
|
42
|
-
data,
|
|
44
|
+
data: data.slice(offset, offset + limit),
|
|
43
45
|
total: data.length,
|
|
44
|
-
limit
|
|
45
|
-
offset
|
|
46
|
-
...READ_STAMP,
|
|
46
|
+
limit,
|
|
47
|
+
offset,
|
|
47
48
|
};
|
|
48
49
|
}
|
|
49
|
-
/** Empty
|
|
50
|
+
/** Empty PaginatedResponse for surfaces without dedicated fixtures. */
|
|
50
51
|
export function demoEmptyPage(params) {
|
|
51
52
|
return {
|
|
52
53
|
data: [],
|
|
53
54
|
total: 0,
|
|
54
55
|
limit: params?.limit ?? 50,
|
|
55
56
|
offset: params?.offset ?? 0,
|
|
56
|
-
...EMPTY_STAMP,
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
59
|
/* ------------------------------------------------------------------ */
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fiscal `modelo` tool naming aliases — prefix-drift fix (issue #50).
|
|
3
|
+
*
|
|
4
|
+
* ── Problem ──────────────────────────────────────────────────────────────
|
|
5
|
+
* The `modelo` fiscal tools split 5/5 on naming prefix: the mainland AEAT
|
|
6
|
+
* models (`fiscal.ts`) use `get_modelo_*`, while the Canary IGIC + Impuesto
|
|
7
|
+
* Sociedades models (`igic.ts`, `impuesto_sociedades.ts`) use the house
|
|
8
|
+
* `frihet_*` convention. An agent that learns the `frihet_modelo_NNN_summary`
|
|
9
|
+
* pattern from IGIC/IS will guess `frihet_modelo_303_summary` for VAT and get
|
|
10
|
+
* tool-not-found — 303/130/347 are the most-used models.
|
|
11
|
+
*
|
|
12
|
+
* ── Fix ──────────────────────────────────────────────────────────────────
|
|
13
|
+
* Register thin NAME ALIASES that resolve to the exact same registered tool
|
|
14
|
+
* (same handler, same schema, same annotations — zero semantic divergence),
|
|
15
|
+
* WITHOUT calling `registerTool()` a second time. The MCP SDK's `McpServer`
|
|
16
|
+
* resolves `tools/call` by looking up `this._registeredTools[name]`
|
|
17
|
+
* (mcp.js: `setToolRequestHandlers` -> `CallToolRequestSchema` handler), so
|
|
18
|
+
* pointing an alias key at the SAME registered-tool object makes it callable
|
|
19
|
+
* with zero duplication and zero drift risk.
|
|
20
|
+
*
|
|
21
|
+
* This intentionally does NOT touch `src/tools/*.ts` (where the 157-tool
|
|
22
|
+
* audit / pin tests count literal `registerTool(` call sites) and is applied
|
|
23
|
+
* to the REAL `McpServer` instance only, AFTER `registerAllTools()` — the
|
|
24
|
+
* `tool-exposure.test.ts` / `openai-*.test.ts` suites exercise
|
|
25
|
+
* `registerAllTools()` against a bare `StubMcpServer` and never call
|
|
26
|
+
* `applyFiscalAliases`, so the 157-tool pins are untouched by design.
|
|
27
|
+
*
|
|
28
|
+
* KNOWN LIMITATION: this relies on the SDK's internal (unprefixed-by-type,
|
|
29
|
+
* but underscore-private) `_registeredTools` field. It is FAIL-OPEN — if a
|
|
30
|
+
* future SDK release renames/removes that field, `applyFiscalAliases` is a
|
|
31
|
+
* silent no-op (the canonical `get_modelo_*` names keep working; only the
|
|
32
|
+
* alias convenience is lost). Pinned by `fiscal-aliases.test.ts`.
|
|
33
|
+
*/
|
|
34
|
+
/** alias name -> canonical registered tool name. */
|
|
35
|
+
export declare const FISCAL_MODELO_ALIASES: Readonly<Record<string, string>>;
|
|
36
|
+
/**
|
|
37
|
+
* Apply the fiscal `modelo` aliases to a live `McpServer` instance. Call this
|
|
38
|
+
* AFTER `registerAllTools()` so every canonical tool already exists.
|
|
39
|
+
*
|
|
40
|
+
* Fail-open: if the SDK's internal registry is missing or reshaped, this is a
|
|
41
|
+
* silent no-op — it never throws, and never blocks server startup.
|
|
42
|
+
*/
|
|
43
|
+
export declare function applyFiscalAliases(server: unknown): void;
|
|
44
|
+
//# sourceMappingURL=fiscal-aliases.d.ts.map
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fiscal `modelo` tool naming aliases — prefix-drift fix (issue #50).
|
|
3
|
+
*
|
|
4
|
+
* ── Problem ──────────────────────────────────────────────────────────────
|
|
5
|
+
* The `modelo` fiscal tools split 5/5 on naming prefix: the mainland AEAT
|
|
6
|
+
* models (`fiscal.ts`) use `get_modelo_*`, while the Canary IGIC + Impuesto
|
|
7
|
+
* Sociedades models (`igic.ts`, `impuesto_sociedades.ts`) use the house
|
|
8
|
+
* `frihet_*` convention. An agent that learns the `frihet_modelo_NNN_summary`
|
|
9
|
+
* pattern from IGIC/IS will guess `frihet_modelo_303_summary` for VAT and get
|
|
10
|
+
* tool-not-found — 303/130/347 are the most-used models.
|
|
11
|
+
*
|
|
12
|
+
* ── Fix ──────────────────────────────────────────────────────────────────
|
|
13
|
+
* Register thin NAME ALIASES that resolve to the exact same registered tool
|
|
14
|
+
* (same handler, same schema, same annotations — zero semantic divergence),
|
|
15
|
+
* WITHOUT calling `registerTool()` a second time. The MCP SDK's `McpServer`
|
|
16
|
+
* resolves `tools/call` by looking up `this._registeredTools[name]`
|
|
17
|
+
* (mcp.js: `setToolRequestHandlers` -> `CallToolRequestSchema` handler), so
|
|
18
|
+
* pointing an alias key at the SAME registered-tool object makes it callable
|
|
19
|
+
* with zero duplication and zero drift risk.
|
|
20
|
+
*
|
|
21
|
+
* This intentionally does NOT touch `src/tools/*.ts` (where the 157-tool
|
|
22
|
+
* audit / pin tests count literal `registerTool(` call sites) and is applied
|
|
23
|
+
* to the REAL `McpServer` instance only, AFTER `registerAllTools()` — the
|
|
24
|
+
* `tool-exposure.test.ts` / `openai-*.test.ts` suites exercise
|
|
25
|
+
* `registerAllTools()` against a bare `StubMcpServer` and never call
|
|
26
|
+
* `applyFiscalAliases`, so the 157-tool pins are untouched by design.
|
|
27
|
+
*
|
|
28
|
+
* KNOWN LIMITATION: this relies on the SDK's internal (unprefixed-by-type,
|
|
29
|
+
* but underscore-private) `_registeredTools` field. It is FAIL-OPEN — if a
|
|
30
|
+
* future SDK release renames/removes that field, `applyFiscalAliases` is a
|
|
31
|
+
* silent no-op (the canonical `get_modelo_*` names keep working; only the
|
|
32
|
+
* alias convenience is lost). Pinned by `fiscal-aliases.test.ts`.
|
|
33
|
+
*/
|
|
34
|
+
/** alias name -> canonical registered tool name. */
|
|
35
|
+
export const FISCAL_MODELO_ALIASES = {
|
|
36
|
+
frihet_modelo_303_summary: "get_modelo_303_summary",
|
|
37
|
+
frihet_modelo_130_summary: "get_modelo_130_summary",
|
|
38
|
+
frihet_modelo_390_summary: "get_modelo_390_summary",
|
|
39
|
+
frihet_modelo_180_summary: "get_modelo_180_summary",
|
|
40
|
+
frihet_modelo_347_summary: "get_modelo_347_summary",
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Apply the fiscal `modelo` aliases to a live `McpServer` instance. Call this
|
|
44
|
+
* AFTER `registerAllTools()` so every canonical tool already exists.
|
|
45
|
+
*
|
|
46
|
+
* Fail-open: if the SDK's internal registry is missing or reshaped, this is a
|
|
47
|
+
* silent no-op — it never throws, and never blocks server startup.
|
|
48
|
+
*/
|
|
49
|
+
export function applyFiscalAliases(server) {
|
|
50
|
+
const registry = server._registeredTools;
|
|
51
|
+
if (!registry || typeof registry !== "object")
|
|
52
|
+
return;
|
|
53
|
+
for (const [alias, canonical] of Object.entries(FISCAL_MODELO_ALIASES)) {
|
|
54
|
+
const target = registry[canonical];
|
|
55
|
+
if (!target)
|
|
56
|
+
continue; // Canonical tool not registered in this profile (e.g. an allow-listed mode) — skip, no divergence.
|
|
57
|
+
if (registry[alias])
|
|
58
|
+
continue; // Never overwrite an existing registration.
|
|
59
|
+
registry[alias] = target; // Same object reference as the canonical tool — zero semantic divergence.
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=fiscal-aliases.js.map
|