@frihet/mcp-server 1.16.6 → 2.0.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.
Files changed (108) hide show
  1. package/README.md +168 -82
  2. package/dist/agent-onboarding.d.ts +98 -0
  3. package/dist/agent-onboarding.js +275 -0
  4. package/dist/api-origin.d.ts +9 -0
  5. package/dist/api-origin.js +41 -0
  6. package/dist/canary/mcp-harness.d.ts +77 -0
  7. package/dist/canary/mcp-harness.js +552 -0
  8. package/dist/capability-truth.d.ts +29 -0
  9. package/dist/capability-truth.js +262 -0
  10. package/dist/claude-candidate-contract.d.ts +482 -0
  11. package/dist/claude-candidate-contract.js +87 -0
  12. package/dist/claude-output-privacy.d.ts +17 -0
  13. package/dist/claude-output-privacy.js +240 -0
  14. package/dist/claude-profile.d.ts +18 -0
  15. package/dist/claude-profile.js +260 -0
  16. package/dist/client-interface.d.ts +47 -33
  17. package/dist/client.d.ts +172 -40
  18. package/dist/client.js +708 -165
  19. package/dist/demo-client.d.ts +41 -32
  20. package/dist/demo-client.js +202 -31
  21. package/dist/demo-fixtures.d.ts +3 -3
  22. package/dist/demo-fixtures.js +7 -7
  23. package/dist/deposit-contract.d.ts +29 -0
  24. package/dist/deposit-contract.js +76 -0
  25. package/dist/fetch-no-redirect.d.ts +3 -0
  26. package/dist/fetch-no-redirect.js +16 -0
  27. package/dist/fiscal-aliases.d.ts +44 -0
  28. package/dist/fiscal-aliases.js +62 -0
  29. package/dist/fiscal-period.d.ts +26 -0
  30. package/dist/fiscal-period.js +31 -0
  31. package/dist/google-profile.d.ts +19 -0
  32. package/dist/google-profile.js +122 -0
  33. package/dist/index.js +68 -59
  34. package/dist/logger.d.ts +2 -2
  35. package/dist/logger.js +140 -8
  36. package/dist/muse-profile.d.ts +19 -0
  37. package/dist/muse-profile.js +122 -0
  38. package/dist/observability.d.ts +18 -29
  39. package/dist/observability.js +194 -106
  40. package/dist/openai-profile.d.ts +26 -22
  41. package/dist/openai-profile.js +1193 -157
  42. package/dist/openai-review-contract.d.ts +15 -6
  43. package/dist/openai-review-contract.js +714 -41
  44. package/dist/openai-review-oauth.d.ts +149 -2
  45. package/dist/openai-review-oauth.js +220 -6
  46. package/dist/prompts/register-all.d.ts +2 -0
  47. package/dist/prompts/register-all.js +34 -78
  48. package/dist/public-capability-contract.d.ts +39 -0
  49. package/dist/public-capability-contract.js +186 -0
  50. package/dist/redaction.d.ts +15 -0
  51. package/dist/redaction.js +83 -0
  52. package/dist/resources/register-all.js +154 -64
  53. package/dist/retry-after.d.ts +19 -0
  54. package/dist/retry-after.js +84 -0
  55. package/dist/server-composition.d.ts +19 -0
  56. package/dist/server-composition.js +74 -0
  57. package/dist/tool-exposure.d.ts +31 -27
  58. package/dist/tool-exposure.js +211 -87
  59. package/dist/tools/accountingClose.d.ts +14 -5
  60. package/dist/tools/accountingClose.js +41 -23
  61. package/dist/tools/audit_gl.js +8 -8
  62. package/dist/tools/backend-availability.d.ts +8 -29
  63. package/dist/tools/backend-availability.js +31 -30
  64. package/dist/tools/bank_rules.js +66 -15
  65. package/dist/tools/banking.js +5 -13
  66. package/dist/tools/clients.js +261 -15
  67. package/dist/tools/crm.js +22 -21
  68. package/dist/tools/deposits.js +114 -85
  69. package/dist/tools/einvoice.d.ts +16 -2
  70. package/dist/tools/einvoice.js +59 -12
  71. package/dist/tools/expenses.d.ts +16 -0
  72. package/dist/tools/expenses.js +47 -27
  73. package/dist/tools/fiscal.d.ts +3 -4
  74. package/dist/tools/fiscal.js +93 -60
  75. package/dist/tools/gestoria.js +15 -7
  76. package/dist/tools/hr.d.ts +2 -3
  77. package/dist/tools/hr.js +49 -20
  78. package/dist/tools/igic.d.ts +9 -8
  79. package/dist/tools/igic.js +34 -64
  80. package/dist/tools/impuesto_sociedades.d.ts +4 -5
  81. package/dist/tools/impuesto_sociedades.js +16 -37
  82. package/dist/tools/intelligence.js +166 -12
  83. package/dist/tools/invoices.js +224 -54
  84. package/dist/tools/kitchen.js +19 -9
  85. package/dist/tools/payroll.d.ts +5 -8
  86. package/dist/tools/payroll.js +19 -32
  87. package/dist/tools/permissions.d.ts +4 -7
  88. package/dist/tools/permissions.js +11 -14
  89. package/dist/tools/products.js +6 -6
  90. package/dist/tools/quotes.js +172 -26
  91. package/dist/tools/recurring.js +26 -25
  92. package/dist/tools/register-all.d.ts +2 -2
  93. package/dist/tools/register-all.js +9 -6
  94. package/dist/tools/search.d.ts +19 -0
  95. package/dist/tools/search.js +84 -0
  96. package/dist/tools/shared.d.ts +1445 -109
  97. package/dist/tools/shared.js +478 -102
  98. package/dist/tools/team.js +24 -28
  99. package/dist/tools/time.js +4 -12
  100. package/dist/tools/vendors.js +249 -15
  101. package/dist/tools/webhooks.js +35 -30
  102. package/dist/types.d.ts +31 -5
  103. package/dist/workflows/daily-operational-brief.d.ts +163 -0
  104. package/dist/workflows/daily-operational-brief.js +881 -0
  105. package/docs/agent-onboarding.json +391 -0
  106. package/package.json +49 -12
  107. package/dist/tools/onboarding.d.ts +0 -19
  108. package/dist/tools/onboarding.js +0 -64
@@ -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, type EInvoiceDocument, type EInvoiceExportResult, type McpEInvoiceExportFormat } 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<Rec>;
79
- getInvoiceEInvoice(invoiceId: string): Promise<Rec>;
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,14 +89,31 @@ 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(params?: {
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>;
101
+ deleteWebhook(_id: string): Promise<void>;
102
+ globalSearch(params: {
103
+ q: string;
104
+ types?: ReadonlyArray<"invoices" | "expenses" | "vendors" | "clients" | "products">;
92
105
  limit?: number;
93
106
  offset?: number;
94
- }): Promise<PaginatedResponse<Rec>>;
95
- getWebhook(id: string): Promise<Rec>;
96
- createWebhook(data: Rec): Promise<Rec>;
97
- updateWebhook(id: string, data: Rec): Promise<Rec>;
98
- deleteWebhook(_id: string): Promise<void>;
107
+ }): Promise<{
108
+ data: Record<string, unknown>[];
109
+ total: number;
110
+ limit: number;
111
+ offset: number;
112
+ hasMore: boolean;
113
+ query: string;
114
+ types: ReadonlyArray<"invoices" | "expenses" | "vendors" | "clients" | "products">;
115
+ truncated?: boolean;
116
+ }>;
99
117
  listClientContacts(_clientId: string, params?: {
100
118
  limit?: number;
101
119
  offset?: number;
@@ -106,7 +124,7 @@ export declare class DemoFrihetClient implements IFrihetClient {
106
124
  limit?: number;
107
125
  offset?: number;
108
126
  }): Promise<PaginatedResponse<Rec>>;
109
- logClientActivity(clientId: string, data: Rec): Promise<Rec>;
127
+ logClientActivity(_clientId: string, data: Rec): Promise<Rec>;
110
128
  listClientNotes(_clientId: string, params?: {
111
129
  limit?: number;
112
130
  offset?: number;
@@ -170,14 +188,9 @@ export declare class DemoFrihetClient implements IFrihetClient {
170
188
  }>;
171
189
  exportEInvoice(params: {
172
190
  invoiceId: string;
173
- format: string;
191
+ format: McpEInvoiceExportFormat;
174
192
  signed?: boolean;
175
- }): Promise<{
176
- xmlUrl: string;
177
- filename: string;
178
- format: string;
179
- signed: boolean;
180
- }>;
193
+ }): Promise<EInvoiceExportResult>;
181
194
  faceSubmit(params: {
182
195
  invoiceId: string;
183
196
  mode: "mock" | "sandbox" | "production";
@@ -311,7 +324,7 @@ export declare class DemoFrihetClient implements IFrihetClient {
311
324
  updateTeamMemberRole(memberId: string, role: string): Promise<Rec>;
312
325
  removeTeamMember(_memberId: string): Promise<void>;
313
326
  approveGLEntry(entryId: string, notes?: string): Promise<Rec>;
314
- rejectGLEntry(entryId: string, reason: string): Promise<Rec>;
327
+ rejectGLEntry(entryId: string, note: string): Promise<Rec>;
315
328
  getGLEntryAuditLog(entryId: string): Promise<Rec>;
316
329
  addCustomPortalDomain(data: {
317
330
  domain: string;
@@ -352,15 +365,13 @@ export declare class DemoFrihetClient implements IFrihetClient {
352
365
  }): Promise<PaginatedResponse<Rec>>;
353
366
  createBankRule(data: {
354
367
  name: string;
355
- conditions: Array<{
368
+ bankConditions: Array<{
356
369
  field: string;
357
370
  operator: string;
358
371
  value: string;
359
372
  }>;
360
- actions: Array<{
361
- type: string;
362
- value: string;
363
- }>;
373
+ action: string;
374
+ actionConfig: Record<string, unknown>;
364
375
  isActive?: boolean;
365
376
  }): Promise<Rec>;
366
377
  sendGestoriaMessage(data: {
@@ -403,7 +414,9 @@ export declare class DemoFrihetClient implements IFrihetClient {
403
414
  };
404
415
  }): Promise<Rec>;
405
416
  getGestoriaAgingConsolidated(_params?: {
406
- ownerUid?: string;
417
+ workspaceIds?: string[];
418
+ asOf?: string;
419
+ bustCache?: boolean;
407
420
  }): Promise<Rec>;
408
421
  listLeaves(params?: {
409
422
  limit?: number;
@@ -427,8 +440,8 @@ export declare class DemoFrihetClient implements IFrihetClient {
427
440
  employeeId: string;
428
441
  mood?: string;
429
442
  location?: string;
430
- }): Promise<Rec>;
431
- attendanceClockOut(entryId: string): Promise<Rec>;
443
+ }, _idempotencyKey?: string): Promise<Rec>;
444
+ attendanceClockOut(entryId: string, _idempotencyKey?: string): Promise<Rec>;
432
445
  getOvertimeReport(params: {
433
446
  period: string;
434
447
  employeeId?: string;
@@ -441,20 +454,16 @@ export declare class DemoFrihetClient implements IFrihetClient {
441
454
  eventType?: string;
442
455
  }): Promise<Rec>;
443
456
  exportPayroll(params: {
444
- format: "a3" | "contasol" | "sage" | "holded" | "siltra";
457
+ format: "a3" | "contasol" | "sage" | "siltra";
445
458
  month: string;
446
459
  }): Promise<Rec>;
447
460
  getPayrollChecklist(params: {
448
461
  month: string;
449
462
  }): Promise<Rec>;
450
- getOnboardingStatus(): Promise<Rec>;
451
- setOnboardingPersona(data: {
452
- persona: "autonomo" | "empresa" | "agencia" | "gestoria";
453
- }): Promise<Rec>;
454
463
  getPermissionsMatrix(): Promise<Rec>;
455
464
  getMyPermissions(): Promise<Rec>;
456
465
  getCurrentPeriod(params?: {
457
- periodId?: string;
466
+ fiscalYear?: string;
458
467
  }): Promise<Rec>;
459
468
  closePeriod(data: {
460
469
  type: "monthly" | "quarterly";
@@ -17,7 +17,63 @@
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 { toApiEInvoiceExportFormat, } from "./client.js";
21
+ import { Buffer } from "node:buffer";
20
22
  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";
23
+ const DEMO_RBAC_ROLES = ["owner", "admin", "manager", "sales", "accountant", "employee", "viewer"];
24
+ const DEMO_RBAC_RESOURCES = [
25
+ "workspace", "invoices", "quotes", "expenses", "clients", "products", "accounting",
26
+ "people", "payroll", "integrations", "banking", "settings", "audit_log", "billing",
27
+ ];
28
+ const DEMO_RBAC_ACTIONS = ["read", "create", "update", "delete", "bulk_delete", "export"];
29
+ const DEMO_FULL_ACTIONS = [...DEMO_RBAC_ACTIONS];
30
+ const DEMO_PERMISSION_MATRIX = {
31
+ owner: {
32
+ workspace: ["read", "update", "export"],
33
+ invoices: DEMO_FULL_ACTIONS, quotes: DEMO_FULL_ACTIONS, expenses: DEMO_FULL_ACTIONS,
34
+ clients: DEMO_FULL_ACTIONS, products: DEMO_FULL_ACTIONS, accounting: DEMO_FULL_ACTIONS,
35
+ people: DEMO_FULL_ACTIONS, payroll: DEMO_FULL_ACTIONS, integrations: DEMO_FULL_ACTIONS,
36
+ banking: ["read", "create", "update", "delete", "export"],
37
+ settings: ["read", "update"], audit_log: ["read", "export"],
38
+ billing: ["read", "create", "update", "delete", "export"],
39
+ },
40
+ admin: {
41
+ workspace: ["read", "update", "export"],
42
+ invoices: DEMO_FULL_ACTIONS, quotes: DEMO_FULL_ACTIONS, expenses: DEMO_FULL_ACTIONS,
43
+ clients: DEMO_FULL_ACTIONS, products: DEMO_FULL_ACTIONS, accounting: DEMO_FULL_ACTIONS,
44
+ people: DEMO_FULL_ACTIONS, payroll: DEMO_FULL_ACTIONS, integrations: DEMO_FULL_ACTIONS,
45
+ banking: ["read", "create", "update", "delete", "export"],
46
+ settings: ["read", "update"], audit_log: ["read", "export"],
47
+ },
48
+ manager: {
49
+ workspace: ["read"], invoices: ["read", "export"], quotes: ["read", "export"],
50
+ clients: ["read", "export"], products: ["read", "export"], expenses: ["read", "export"],
51
+ banking: ["read"], people: DEMO_FULL_ACTIONS, payroll: ["read", "export"],
52
+ settings: ["read"], integrations: ["read"], audit_log: ["read"],
53
+ },
54
+ sales: {
55
+ workspace: ["read"], invoices: ["read", "create", "update", "export"],
56
+ quotes: ["read", "create", "update", "export"], clients: ["read", "create", "update", "export"],
57
+ products: ["read", "export"], expenses: ["read", "export"],
58
+ },
59
+ accountant: {
60
+ workspace: ["read"], invoices: ["read", "create", "update", "export"],
61
+ quotes: ["read", "export"], expenses: ["read", "create", "update", "export"],
62
+ clients: ["read", "export"], products: ["read", "export"],
63
+ banking: ["read", "create", "update", "export"], accounting: ["read", "create", "update", "export"],
64
+ people: ["read"], audit_log: ["read", "export"],
65
+ },
66
+ employee: { workspace: ["read"] },
67
+ viewer: {
68
+ workspace: ["read", "export"], invoices: ["read", "export"], quotes: ["read", "export"],
69
+ expenses: ["read", "export"], clients: ["read", "export"], products: ["read", "export"],
70
+ banking: ["read"], accounting: ["read", "export"], people: ["read"], payroll: ["read"],
71
+ settings: ["read"], integrations: ["read"], audit_log: ["read"],
72
+ },
73
+ };
74
+ const DEMO_OWNER_CAPABILITIES = Object.entries(DEMO_PERMISSION_MATRIX.owner)
75
+ .flatMap(([resource, actions]) => actions.map((action) => `${resource}:${action}`))
76
+ .sort();
21
77
  export class DemoFrihetClient {
22
78
  // ---------------------------------------------------------------- Invoices
23
79
  async listInvoices(params) {
@@ -139,13 +195,27 @@ export class DemoFrihetClient {
139
195
  return simulateAction(id, { status: "paid", paidAt: paidDate ?? DEMO_NOW });
140
196
  }
141
197
  async getInvoicePdf(id) {
142
- return { id, url: "https://app.frihet.io/demo/invoice.pdf", contentType: "application/pdf", ...READ_STAMP };
198
+ // #1393: shape mirrors the live BinaryDocument contract (id,
199
+ // contentType, sizeBytes, base64) so demo-mode outputSchema validation
200
+ // runs against the same fields as the live path.
201
+ const demoPdfBytes = Buffer.from("%PDF-1.4\n%%DEMO e-invoice for " + id + "\n%%EOF\n", "utf8");
202
+ return {
203
+ id,
204
+ contentType: "application/pdf",
205
+ sizeBytes: demoPdfBytes.byteLength,
206
+ base64: demoPdfBytes.toString("base64"),
207
+ ...READ_STAMP,
208
+ };
143
209
  }
144
210
  async getInvoiceEInvoice(invoiceId) {
211
+ // #1393: mirrors the live XML arm of the MIME-discriminated contract.
212
+ const xml = `<?xml version="1.0" encoding="UTF-8"?>\n<Invoice><!-- DEMO example e-invoice for ${invoiceId} --></Invoice>`;
145
213
  return {
146
- xml: `<?xml version="1.0" encoding="UTF-8"?>\n<Invoice><!-- DEMO example e-invoice for ${invoiceId} --></Invoice>`,
214
+ id: invoiceId,
215
+ xml,
216
+ contentType: "application/xml; charset=utf-8",
217
+ sizeBytes: Buffer.byteLength(xml, "utf8"),
147
218
  filename: `${invoiceId}.xml`,
148
- format: "facturae",
149
219
  ...FISCAL_STAMP,
150
220
  };
151
221
  }
@@ -190,21 +260,60 @@ export class DemoFrihetClient {
190
260
  return { id, status: "sent", sentAt: DEMO_NOW, ...(to ? { to } : {}), ...FISCAL_STAMP };
191
261
  }
192
262
  // ---------------------------------------------------------------- Webhooks
193
- async listWebhooks(params) {
194
- return demoEmptyPage(params);
263
+ async listWebhooks() {
264
+ const { data, total } = demoEmptyPage();
265
+ return { data: data, total };
195
266
  }
196
267
  async getWebhook(id) {
197
- return { id, ...READ_STAMP };
268
+ return {
269
+ id,
270
+ name: "Demo webhook",
271
+ url: "https://example.com/webhooks/frihet",
272
+ events: ["invoice.paid"],
273
+ status: "active",
274
+ metadata: { demo: true },
275
+ hasSecret: false,
276
+ createdAt: DEMO_NOW,
277
+ updatedAt: DEMO_NOW,
278
+ ...READ_STAMP,
279
+ };
198
280
  }
199
281
  async createWebhook(data) {
200
- return simulateWrite("demo_wh", data, { active: data.active ?? true });
282
+ return simulateWrite("demo_wh", { ...data }, {
283
+ status: data.status ?? "active",
284
+ hasSecret: typeof data.secret === "string" && data.secret.length > 0,
285
+ });
201
286
  }
202
287
  async updateWebhook(id, data) {
203
- return simulateAction(id, { ...data });
288
+ const { secret, ...fields } = data;
289
+ const { success: _success, ...result } = simulateAction(id, {
290
+ name: "Demo webhook",
291
+ url: "https://example.com/webhooks/frihet",
292
+ events: ["invoice.paid"],
293
+ status: "active",
294
+ metadata: { demo: true },
295
+ hasSecret: typeof secret === "string" && secret.length > 0,
296
+ ...fields,
297
+ });
298
+ return result;
204
299
  }
205
300
  async deleteWebhook(_id) {
206
301
  return;
207
302
  }
303
+ // ---------------------------------------------------------------- Cross-resource search
304
+ // ----------------------------------------------------------------
305
+ async globalSearch(params) {
306
+ return {
307
+ data: [],
308
+ total: 0,
309
+ limit: params.limit ?? 25,
310
+ offset: params.offset ?? 0,
311
+ hasMore: false,
312
+ query: params.q,
313
+ types: params.types ?? ["invoices", "expenses", "vendors", "clients", "products"],
314
+ truncated: false,
315
+ };
316
+ }
208
317
  // ---------------------------------------------------------------- CRM: Contacts
209
318
  async listClientContacts(_clientId, params) {
210
319
  return demoEmptyPage(params);
@@ -219,8 +328,13 @@ export class DemoFrihetClient {
219
328
  async listClientActivities(_clientId, params) {
220
329
  return demoEmptyPage(params);
221
330
  }
222
- async logClientActivity(clientId, data) {
223
- return simulateWrite("demo_act", data, { clientId });
331
+ async logClientActivity(_clientId, data) {
332
+ return simulateWrite("demo_act", data, {
333
+ type: data.type === "email" ? "email_sent" : data.type,
334
+ timestamp: DEMO_NOW,
335
+ createdBy: "user",
336
+ metadata: {},
337
+ });
224
338
  }
225
339
  // ---------------------------------------------------------------- CRM: Notes
226
340
  async listClientNotes(_clientId, params) {
@@ -327,7 +441,15 @@ export class DemoFrihetClient {
327
441
  return { fileUrl: "https://app.frihet.io/demo/datev.csv", filename: "demo-datev.csv", rowCount: 24, fiscalPeriod: `${params.periodStart}..${params.periodEnd}`, encoding: "cp1252", ...FISCAL_STAMP };
328
442
  }
329
443
  async exportEInvoice(params) {
330
- return { xmlUrl: "https://app.frihet.io/demo/einvoice.xml", filename: `${params.invoiceId}.xml`, format: params.format, signed: params.signed ?? false, ...FISCAL_STAMP };
444
+ const format = toApiEInvoiceExportFormat(params.format);
445
+ return {
446
+ xml: `<?xml version="1.0" encoding="UTF-8"?><DemoEInvoice format="${format}"/>`,
447
+ contentType: "application/xml",
448
+ filename: `${params.invoiceId}.xml`,
449
+ format,
450
+ signed: params.signed ?? false,
451
+ ...FISCAL_STAMP,
452
+ };
331
453
  }
332
454
  async faceSubmit(params) {
333
455
  return { registroFACe: "DEMO-FACE-0001", status: "submitted", submittedAt: DEMO_NOW, mode: params.mode, ...FISCAL_STAMP };
@@ -404,7 +526,8 @@ export class DemoFrihetClient {
404
526
  }
405
527
  // ---------------------------------------------------------------- Fiscal (simulated / read summaries)
406
528
  async getFiscalModeloSummary(modeloCode, period) {
407
- return { model: modeloCode, period: period ?? "2026-Q2", readonly: true, summary: { totalRevenue: 3960.4, totalExpenses: 405.6 }, ...FISCAL_STAMP };
529
+ const defaultPeriod = modeloCode === "390" || modeloCode === "347" ? "2026" : "2026-Q2";
530
+ return { model: modeloCode, period: period ?? defaultPeriod, readonly: true, summary: { totalRevenue: 3960.4, totalExpenses: 405.6 }, ...FISCAL_STAMP };
408
531
  }
409
532
  async getVerifactuStatus(invoiceId) {
410
533
  return { invoiceId, status: "accepted", accepted: true, submittedAt: DEMO_NOW, ...FISCAL_STAMP };
@@ -476,8 +599,8 @@ export class DemoFrihetClient {
476
599
  async approveGLEntry(entryId, notes) {
477
600
  return simulateAction(entryId, { status: "approved", ...(notes ? { notes } : {}) });
478
601
  }
479
- async rejectGLEntry(entryId, reason) {
480
- return simulateAction(entryId, { status: "rejected", reason });
602
+ async rejectGLEntry(entryId, note) {
603
+ return simulateAction(entryId, { status: "rejected", note });
481
604
  }
482
605
  async getGLEntryAuditLog(entryId) {
483
606
  return { entryId, log: [], ...READ_STAMP };
@@ -515,7 +638,7 @@ export class DemoFrihetClient {
515
638
  return demoEmptyPage(params);
516
639
  }
517
640
  async createBankRule(data) {
518
- return simulateWrite("demo_rule", { name: data.name, conditions: data.conditions, actions: data.actions }, { isActive: data.isActive ?? true });
641
+ return simulateWrite("demo_rule", { name: data.name, bankConditions: data.bankConditions, action: data.action, actionConfig: data.actionConfig }, { isActive: data.isActive ?? true });
519
642
  }
520
643
  // ---------------------------------------------------------------- Gestoria
521
644
  async sendGestoriaMessage(data) {
@@ -549,14 +672,24 @@ export class DemoFrihetClient {
549
672
  async cancelLeave(leaveId) {
550
673
  return simulateAction(leaveId, { status: "cancelled" });
551
674
  }
552
- async attendanceClockIn(data) {
675
+ async attendanceClockIn(data, _idempotencyKey) {
553
676
  return simulateWrite("demo_att", { employeeId: data.employeeId, ...(data.mood ? { mood: data.mood } : {}), ...(data.location ? { location: data.location } : {}) }, { status: "open", clockInAt: DEMO_NOW });
554
677
  }
555
- async attendanceClockOut(entryId) {
678
+ async attendanceClockOut(entryId, _idempotencyKey) {
556
679
  return simulateAction(entryId, { status: "closed", clockOutAt: DEMO_NOW });
557
680
  }
558
681
  async getOvertimeReport(params) {
559
- return { period: params.period, totalRegularHours: 0, totalOvertimeHours: 0, byEmployee: [], generatedAt: DEMO_NOW, ...READ_STAMP };
682
+ return {
683
+ period: params.period,
684
+ employeeId: params.employeeId ?? null,
685
+ recordCount: 0,
686
+ dailyOvertime: [],
687
+ weeklyOvertime: [],
688
+ monthlyTotal: { workedMinutes: 0, overtimeMinutes: 0, regularMinutes: 0 },
689
+ annualOvertimeHours: 0,
690
+ alerts: [],
691
+ ...READ_STAMP,
692
+ };
560
693
  }
561
694
  async listAnomalies(params) {
562
695
  return demoEmptyPage(params);
@@ -565,30 +698,68 @@ export class DemoFrihetClient {
565
698
  async testWebhook(id, data) {
566
699
  return { webhookId: id, delivered: true, statusCode: 200, responseTimeMs: 42, eventType: data?.eventType ?? "ping", attemptedAt: DEMO_NOW, ...FISCAL_STAMP };
567
700
  }
568
- // ---------------------------------------------------------------- Payroll (simulated fiscal)
701
+ // ---------------------------------------------------------------- Payroll (read-only demo)
569
702
  async exportPayroll(params) {
570
- return { format: params.format, month: params.month, fileUrl: "https://app.frihet.io/demo/payroll.txt", filename: "demo-payroll.txt", rowCount: 0, generatedAt: DEMO_NOW, ...FISCAL_STAMP };
703
+ return {
704
+ month: params.month,
705
+ format: params.format,
706
+ employees: [],
707
+ summary: { exportedCount: 0, skippedNotReady: 0, totalGrossAnnual: 0 },
708
+ ...READ_STAMP,
709
+ };
571
710
  }
572
711
  async getPayrollChecklist(params) {
573
- return { month: params.month, totalEmployees: 0, readyEmployees: 0, missingEmployees: 0, employees: [], generatedAt: DEMO_NOW, ...FISCAL_STAMP };
574
- }
575
- // ---------------------------------------------------------------- Onboarding
576
- async getOnboardingStatus() {
577
- return { workspaceId: "demo_ws_001", persona: "autonomo", completedSteps: ["profile"], pendingSteps: ["bank", "invoice"], percentComplete: 33, startedAt: DEMO_NOW, ...READ_STAMP };
578
- }
579
- async setOnboardingPersona(data) {
580
- return { workspaceId: "demo_ws_001", persona: data.persona, updatedAt: DEMO_NOW, ...READ_STAMP };
712
+ return {
713
+ month: params.month,
714
+ employees: [],
715
+ summary: { total: 0, ready: 0, notReady: 0, reviewedThisMonth: 0 },
716
+ ...READ_STAMP,
717
+ };
581
718
  }
582
719
  // ---------------------------------------------------------------- Permissions
583
720
  async getPermissionsMatrix() {
584
- return { roles: [{ role: "owner", permissions: ["*"] }], resources: ["invoices", "expenses", "clients"], generatedAt: DEMO_NOW, ...READ_STAMP };
721
+ return {
722
+ roles: DEMO_RBAC_ROLES,
723
+ resources: DEMO_RBAC_RESOURCES,
724
+ actions: DEMO_RBAC_ACTIONS,
725
+ legacyAliases: { editor: "sales" },
726
+ matrix: DEMO_PERMISSION_MATRIX,
727
+ source: "hand-maintained demo snapshot of the Frihet RBAC model; not derived from firestore.rules and not verified against it",
728
+ ...READ_STAMP,
729
+ };
585
730
  }
586
731
  async getMyPermissions() {
587
- return { userId: "demo_user_001", role: "owner", permissions: ["*"], workspaceId: "demo_ws_001", ...READ_STAMP };
732
+ return {
733
+ role: "owner",
734
+ isOwner: true,
735
+ resources: DEMO_PERMISSION_MATRIX.owner,
736
+ scopes: DEMO_OWNER_CAPABILITIES,
737
+ legacyFieldSemantics: { resources: "rbacResources", scopes: "rbacCapabilities" },
738
+ rbac: {
739
+ role: "owner",
740
+ isOwner: true,
741
+ resources: DEMO_PERMISSION_MATRIX.owner,
742
+ capabilities: DEMO_OWNER_CAPABILITIES,
743
+ },
744
+ apiKeyScopes: [],
745
+ apiKeyUnrestricted: true,
746
+ denied: { einvoice: false },
747
+ deniedSemantics: "Known API-key scope denials only; not an exhaustive effective-authorization report.",
748
+ notIncluded: ["featureFlags", "deployedEndpoints"],
749
+ ...READ_STAMP,
750
+ };
588
751
  }
589
752
  // ---------------------------------------------------------------- Period close
590
753
  async getCurrentPeriod(params) {
591
- return { id: params?.periodId ?? "demo_period_2026_07", type: "monthly", status: "open", startDate: "2026-07-01", endDate: "2026-07-31", ...READ_STAMP };
754
+ const fiscalYear = params?.fiscalYear ?? "2026";
755
+ return {
756
+ fiscalYear,
757
+ fiscalYearStart: "01-01",
758
+ status: "open",
759
+ dateRange: { from: `${fiscalYear}-01-01`, to: `${fiscalYear}-12-31` },
760
+ closing: null,
761
+ ...READ_STAMP,
762
+ };
592
763
  }
593
764
  async closePeriod(data) {
594
765
  return simulateWrite("demo_period", { type: data.type }, { status: "closed", closedAt: DEMO_NOW });
@@ -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>> & DemoStamp;
34
- /** Wrap a fixture array in a stamped PaginatedResponse. */
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 stamped PaginatedResponse for surfaces without dedicated fixtures. */
39
+ /** Empty PaginatedResponse for surfaces without dedicated fixtures. */
40
40
  export declare function demoEmptyPage(params?: {
41
41
  limit?: number;
42
42
  offset?: number;
@@ -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 stamped PaginatedResponse. */
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: params?.limit ?? 50,
45
- offset: params?.offset ?? 0,
46
- ...READ_STAMP,
46
+ limit,
47
+ offset,
47
48
  };
48
49
  }
49
- /** Empty stamped PaginatedResponse for surfaces without dedicated fixtures. */
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,29 @@
1
+ import { z } from "zod/v4";
2
+ /** Locally generated guidance only: never includes input values or provider text. */
3
+ export declare class DepositInputError extends Error {
4
+ readonly statusCode = 400;
5
+ readonly errorCode = "invalid_deposit_input";
6
+ constructor(message: string);
7
+ }
8
+ /** Writable fields accepted by publicApi v132's strict deposits schema. */
9
+ export declare const depositFields: {
10
+ clientId: z.ZodString;
11
+ clientName: z.ZodString;
12
+ amount: z.ZodNumber;
13
+ currency: z.ZodString;
14
+ description: z.ZodString;
15
+ receivedDate: z.ZodString;
16
+ paymentMethod: z.ZodOptional<z.ZodString>;
17
+ paymentReference: z.ZodOptional<z.ZodString>;
18
+ };
19
+ /** Writable fields accepted by publicApi v132's strict /deposits/:id/apply schema. */
20
+ export declare const applyDepositFields: {
21
+ invoiceId: z.ZodString;
22
+ invoiceNumber: z.ZodString;
23
+ amount: z.ZodNumber;
24
+ };
25
+ /** Validate deposit application payload against the strict backend contract before request I/O. */
26
+ export declare function normalizeApplyDepositBody(input?: Record<string, unknown>): Record<string, unknown>;
27
+ /** Preserve the established MCP date/reference names on the canonical API wire. */
28
+ export declare function normalizeDepositBody(input: Record<string, unknown>, partial: boolean): Record<string, unknown>;
29
+ //# sourceMappingURL=deposit-contract.d.ts.map
@@ -0,0 +1,76 @@
1
+ import { z } from "zod/v4";
2
+ /** Locally generated guidance only: never includes input values or provider text. */
3
+ export class DepositInputError extends Error {
4
+ statusCode = 400;
5
+ errorCode = "invalid_deposit_input";
6
+ constructor(message) {
7
+ super(message);
8
+ this.name = "DepositInputError";
9
+ }
10
+ }
11
+ /** Writable fields accepted by publicApi v132's strict deposits schema. */
12
+ export const depositFields = {
13
+ clientId: z.string().min(1).max(128),
14
+ clientName: z.string().min(1).max(500),
15
+ amount: z.number().positive(),
16
+ currency: z.string().length(3),
17
+ description: z.string().min(1).max(2000),
18
+ receivedDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
19
+ paymentMethod: z.string().max(200).optional(),
20
+ paymentReference: z.string().max(200).optional(),
21
+ };
22
+ /** Writable fields accepted by publicApi v132's strict /deposits/:id/apply schema. */
23
+ export const applyDepositFields = {
24
+ invoiceId: z.string().min(1).max(128),
25
+ invoiceNumber: z.string().min(1).max(200),
26
+ amount: z.number().positive(),
27
+ };
28
+ const applyDepositBody = z.object(applyDepositFields).strict();
29
+ /** Validate deposit application payload against the strict backend contract before request I/O. */
30
+ export function normalizeApplyDepositBody(input = {}) {
31
+ if (Object.prototype.hasOwnProperty.call(input, "notes")) {
32
+ throw new DepositInputError("Deposit application field 'notes' is not writable; the API does not accept it. / El campo 'notes' no se puede guardar al aplicar el depósito.");
33
+ }
34
+ const parsed = applyDepositBody.safeParse(input);
35
+ if (!parsed.success) {
36
+ const known = new Set(Object.keys(applyDepositFields));
37
+ const fields = [...new Set(parsed.error.issues.map((issue) => issue.path[0]).filter((field) => typeof field === "string" && known.has(field)))];
38
+ throw new DepositInputError(`Invalid deposit application input${fields.length ? ` (${fields.join(", ")})` : ""}. Provide invoiceId, invoiceNumber and a positive amount. / Indica invoiceId, invoiceNumber y un importe positivo.`);
39
+ }
40
+ return parsed.data;
41
+ }
42
+ const createDepositBody = z.object({
43
+ ...depositFields,
44
+ currency: depositFields.currency.default("EUR"),
45
+ }).strict();
46
+ // PATCH must not inject the create default or turn an empty update into a write.
47
+ const updateDepositBody = z.object(depositFields).partial().strict().refine((fields) => Object.values(fields).some((value) => value !== undefined), { message: "Provide at least one writable deposit field / Indica al menos un campo del depósito" });
48
+ /** Preserve the established MCP date/reference names on the canonical API wire. */
49
+ export function normalizeDepositBody(input, partial) {
50
+ const body = { ...input };
51
+ for (const unsupported of ["status", "notes"]) {
52
+ if (Object.prototype.hasOwnProperty.call(body, unsupported)) {
53
+ throw new DepositInputError(`Deposit field '${unsupported}' is not writable; the API does not accept it. / El campo '${unsupported}' no se puede guardar en el depósito.`);
54
+ }
55
+ }
56
+ for (const [alias, canonical] of [["date", "receivedDate"], ["reference", "paymentReference"]]) {
57
+ if (body[alias] !== undefined) {
58
+ if (body[canonical] !== undefined && body[canonical] !== body[alias]) {
59
+ throw new DepositInputError(`Conflicting deposit fields '${alias}' and '${canonical}'. / Los campos '${alias}' y '${canonical}' no coinciden.`);
60
+ }
61
+ body[canonical] = body[alias];
62
+ }
63
+ delete body[alias];
64
+ }
65
+ const parsed = (partial ? updateDepositBody : createDepositBody).safeParse(body);
66
+ if (!parsed.success) {
67
+ const known = new Set(Object.keys(depositFields));
68
+ const fields = [...new Set(parsed.error.issues.map((issue) => issue.path[0]).filter((field) => typeof field === "string" && known.has(field)))];
69
+ const guidance = partial
70
+ ? "Provide at least one valid writable deposit field; date uses YYYY-MM-DD. / Indica al menos un campo editable válido; date usa YYYY-MM-DD."
71
+ : "Provide clientId, clientName, a positive amount, description and receivedDate (date) in YYYY-MM-DD format. / Indica clientId, clientName, un importe positivo, description y receivedDate (date) con formato YYYY-MM-DD.";
72
+ throw new DepositInputError(`Invalid deposit input${fields.length ? ` (${fields.join(", ")})` : ""}. ${guidance}`);
73
+ }
74
+ return parsed.data;
75
+ }
76
+ //# sourceMappingURL=deposit-contract.js.map
@@ -0,0 +1,3 @@
1
+ /** Validate a manual-mode fetch response without owning any network authority. */
2
+ export declare function rejectRedirectResponse(response: Response): Promise<Response>;
3
+ //# sourceMappingURL=fetch-no-redirect.d.ts.map