@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
@@ -14,7 +14,12 @@ export const MCP_RESOURCE_COUNT = MCP_STATIC_RESOURCE_COUNT + MCP_DYNAMIC_RESOUR
14
14
  /* ------------------------------------------------------------------ */
15
15
  /* Static data */
16
16
  /* ------------------------------------------------------------------ */
17
- const API_SCHEMA_SUMMARY = `Frihet ERP REST API — OpenAPI 3.1
17
+ const API_SCHEMA_SUMMARY = `Frihet ERP REST API — Informational Reference Summary Snapshot
18
+ =============================================================
19
+ Notice: This resource is an informational, non-exhaustive reference snapshot
20
+ of core REST endpoints for LLM context. The canonical, authoritative live
21
+ contract is the OpenAPI 3.1 specification at https://api.frihet.io/v1/openapi.json.
22
+
18
23
  Base URL: https://api.frihet.io/v1
19
24
  Docs: https://docs.frihet.io/desarrolladores/api-reference
20
25
 
@@ -58,13 +63,13 @@ Endpoints:
58
63
  PATCH /quotes/:id — Update quote
59
64
  DELETE /quotes/:id — Delete quote
60
65
 
61
- GET /webhooks — List webhooks (paginated)
66
+ GET /webhooks — List webhooks (unpaginated)
62
67
  GET /webhooks/:id — Get webhook by ID
63
68
  POST /webhooks — Create webhook
64
69
  PATCH /webhooks/:id — Update webhook
65
70
  DELETE /webhooks/:id — Delete webhook
66
71
 
67
- Pagination:
72
+ Pagination (list families that expose limit/offset; webhooks are unpaginated):
68
73
  Query params: limit (1-100, default 50), offset (default 0)
69
74
  Response: { data: [...], total: number, limit: number, offset: number }
70
75
 
@@ -77,10 +82,20 @@ Error responses:
77
82
  500 — Internal server error
78
83
 
79
84
  Content-Type: application/json
80
- All monetary values in EUR (cents not used — decimal euros).
85
+ Monetary values use the currency declared by each record; amounts are decimal major units.
81
86
  Dates in ISO 8601 format (YYYY-MM-DD or full datetime).`;
82
- const TAX_RATES = `Spanish Tax Rates by Fiscal Zone
83
- ================================
87
+ const TAX_RATES = `Spanish Tax Rates & Fiscal Zones — Informational Reference Schedule
88
+ ===================================================================
89
+ Authority:
90
+ - Peninsula & Balearics (IVA): Ley 37/1992 del IVA (AEAT)
91
+ - Canary Islands (IGIC): Ley 20/1991 del IGIC (Agencia Tributaria Canaria)
92
+ - Ceuta & Melilla (IPSI): Ley 8/1991 del IPSI (Servicios Tributarios locales)
93
+ - Withholding (IRPF): Ley 35/2006 del IRPF (AEAT)
94
+ - EU Intra-community: Council Directive 2006/112/EC (Reverse Charge / OSS)
95
+
96
+ Notice: This resource provides reference rate schedules and classification guidelines.
97
+ It is NOT a dynamic tax engine. Transaction rates must be validated against the operation
98
+ date, place of supply, and fiscal status. Server endpoints validate fiscal rules server-side.
84
99
 
85
100
  PENINSULA & BALEARIC ISLANDS — IVA (Impuesto sobre el Valor Añadido)
86
101
  General: 21% — Most goods and services
@@ -121,8 +136,15 @@ SPECIAL REGIMES
121
136
  Applies to: Retail businesses (comercio minorista) buying from wholesalers
122
137
  Simplified regime: Fixed quarterly quotas based on activity modules
123
138
  Agriculture: 12% / 10.5% flat-rate compensation`;
124
- const TAX_CALENDAR = `Spanish Quarterly Tax Calendar
125
- ===============================
139
+ const TAX_CALENDAR = `Spanish Tax Calendar — Statutory Filing Deadlines Reference
140
+ ===========================================================
141
+ Authority: Calendario del Contribuyente (Agencia Tributaria AEAT / ATC)
142
+
143
+ Notice: Informational reference of general quarterly and annual statutory filing
144
+ deadlines in Spain. Deadlines shifting to next business day on weekends/holidays
145
+ or direct-debit (domiciliación bancaria) advance deadlines (typically 5 days earlier)
146
+ are not modeled here. Always verify filing dates with your gestoría or official
147
+ AEAT/ATC electronic office.
126
148
 
127
149
  All deadlines apply to the corresponding fiscal quarter unless noted.
128
150
 
@@ -163,19 +185,21 @@ CANARY ISLANDS (IGIC instead of IVA)
163
185
  Modelo 425 — Annual IGIC summary (same deadline as Modelo 390)
164
186
  Filed with ATC (Administración Tributaria Canaria), NOT AEAT
165
187
 
166
- VERIFACTU (mandatory e-invoicing, phased rollout)
167
- 2026: Voluntary adoption
168
- 2027: Mandatory for large companies
169
- 2028: Mandatory for all businesses
170
-
171
188
  KEY DATES SUMMARY
172
189
  Apr 20 — Q1 filings
173
190
  Jul 20 — Q2 filings
174
191
  Oct 20 — Q3 filings
175
192
  Jan 30 — Q4 filings + annual summaries (390, 180, 190)
176
193
  Apr 1–Jun 30 — Annual income tax (Modelo 100)`;
177
- const EXPENSE_CATEGORIES = `Frihet Expense Categories & Deductibility Rules
178
- =================================================
194
+ const EXPENSE_CATEGORIES = `Frihet Expense Categories & Statutory Deductibility Guidelines
195
+ ==============================================================
196
+ Authority: Statutory deductibility rules derived from Ley 35/2006 (IRPF, art. 30)
197
+ and Ley 37/1992 (IVA, arts. 92-96).
198
+
199
+ Notice: Frihet standard business category mapping with statutory deductibility rules.
200
+ Deductibility requires valid invoices, direct correlation with business activity
201
+ (afectación exclusiva), and accounting registration. Consult your tax advisor
202
+ (gestoría) for specific amortizations or mixed-use deductions.
179
203
 
180
204
  1. OFFICE (oficina)
181
205
  Examples: Rent, utilities, internet, phone, office supplies, cleaning
@@ -232,13 +256,25 @@ const INVOICE_STATUSES = `Frihet Invoice Status Flow
232
256
  Statuses and transitions:
233
257
 
234
258
  DRAFT ──────► SENT ──────► PAID
235
- │ │
236
- │ └──────► OVERDUE ────► PAID
237
- │ │
238
- │ └────────► CANCELLED
239
- │
259
+ │ │ ▲
260
+ │ ├──────► PARTIAL ────┤
261
+ │ │ │ │
262
+ │ └──────► OVERDUE ────┤
263
+ │ │ │
264
+ │ └────────┼───► CANCELLED
265
+ │ │
266
+ │ ▼
267
+ │ REFUND / RECTIFICATIVE
240
268
  └─────────────────────────────────► CANCELLED
241
269
 
270
+ Legal transition paths:
271
+ draft -> sent | cancelled
272
+ sent -> paid | partial | overdue | cancelled
273
+ partial -> paid | overdue | cancelled
274
+ overdue -> paid | partial | cancelled
275
+ paid -> refund (via credit note / rectificative invoice)
276
+ partial -> refund (via credit note / rectificative invoice)
277
+
242
278
  Status definitions:
243
279
 
244
280
  draft — Invoice created but not yet sent to client.
@@ -249,31 +285,40 @@ Status definitions:
249
285
  Payment is expected. The invoice number and date become
250
286
  fiscally relevant — avoid modifications after this point.
251
287
 
252
- paid — Payment received in full. Terminal state.
288
+ partial — Invoice partially paid. A partial payment has been recorded,
289
+ with a remaining positive balance due. Can transition to
290
+ paid upon full payment, overdue if dueDate passes with
291
+ remaining balance, or cancelled/credit note.
292
+
293
+ paid — Payment received in full. Terminal state for settlement.
253
294
  Records the payment date. Invoice is complete.
254
295
 
255
- overdue — Payment deadline (dueDate) has passed without payment.
256
- Triggers follow-up workflows. Can transition to paid
257
- (late payment) or cancelled (write-off / bad debt).
296
+ overdue — Payment deadline (dueDate) has passed without full payment.
297
+ Applies to sent or partial invoices. Triggers follow-up
298
+ workflows. Can transition to paid (late payment), partial,
299
+ or cancelled (write-off / bad debt).
258
300
 
259
301
  cancelled — Invoice voided. Requires a corrective invoice or
260
302
  credit note for fiscal compliance if previously sent.
261
303
  Terminal state. Cannot transition to other statuses.
262
304
 
263
305
  Automation rules (when configured):
264
- - Auto-transition sent → overdue when dueDate passes (daily check)
265
- - Webhook events: invoice.created, invoice.sent, invoice.paid,
266
- invoice.overdue, invoice.cancelled
306
+ - Auto-transition sent/partial → overdue when dueDate passes (daily check)
307
+ - Webhook events: invoice.created, invoice.updated, invoice.generated,
308
+ invoice.one_off_created, invoice.paid, invoice.overdue, invoice.voided,
309
+ invoice.payment_status_updated, invoice.payment_failure
310
+ - A partial payment is reported via invoice.payment_status_updated;
311
+ cancellation is reported via invoice.voided
267
312
  - Overdue reminders can be configured per-client
268
313
 
269
314
  Best practices:
270
315
  - Always set a dueDate when creating invoices (default: 30 days)
271
316
  - Use draft status while iterating with the client
272
317
  - Once sent, create a new corrective invoice rather than editing
273
- - For partial payments, keep status as sent until full payment
318
+ - For partial payments, invoice transitions to partial with remaining balance tracked
274
319
  - cancelled requires a reason (notes field) for audit trail`;
275
320
  const CURRENCIES = JSON.stringify({
276
- EUR: { name: "Euro", symbol: "\u20ac", decimals: 2, format: "1.234,56 \u20ac", countries: ["ES", "DE", "FR", "IT", "NL", "PT", "BE", "AT", "IE", "FI", "GR", "LU", "SK", "SI", "EE", "LV", "LT", "CY", "MT"] },
321
+ EUR: { name: "Euro", symbol: "\u20ac", decimals: 2, format: "1.234,56 \u20ac", countries: ["ES", "DE", "FR", "IT", "NL", "PT", "BE", "AT", "IE", "FI", "GR", "LU", "SK", "SI", "EE", "LV", "LT", "CY", "MT", "BG", "HR"] },
277
322
  USD: { name: "US Dollar", symbol: "$", decimals: 2, format: "$1,234.56", countries: ["US"] },
278
323
  GBP: { name: "British Pound", symbol: "\u00a3", decimals: 2, format: "\u00a31,234.56", countries: ["GB"] },
279
324
  CHF: { name: "Swiss Franc", symbol: "CHF", decimals: 2, format: "CHF 1'234.56", countries: ["CH"] },
@@ -289,8 +334,8 @@ const CURRENCIES = JSON.stringify({
289
334
  CZK: { name: "Czech Koruna", symbol: "K\u010d", decimals: 2, format: "1 234,56 K\u010d", countries: ["CZ"] },
290
335
  HUF: { name: "Hungarian Forint", symbol: "Ft", decimals: 0, format: "1 234 Ft", countries: ["HU"] },
291
336
  RON: { name: "Romanian Leu", symbol: "lei", decimals: 2, format: "1.234,56 lei", countries: ["RO"] },
292
- BGN: { name: "Bulgarian Lev", symbol: "\u043b\u0432", decimals: 2, format: "1 234,56 \u043b\u0432", countries: ["BG"] },
293
- HRK: { name: "Croatian Kuna", symbol: "kn", decimals: 2, format: "1.234,56 kn", countries: ["HR"] },
337
+ BGN: { name: "Bulgarian Lev", symbol: "\u043b\u0432", decimals: 2, format: "1 234,56 \u043b\u0432", countries: [] },
338
+ HRK: { name: "Croatian Kuna", symbol: "kn", decimals: 2, format: "1.234,56 kn", countries: [] },
294
339
  ISK: { name: "Icelandic Kr\u00f3na", symbol: "kr", decimals: 0, format: "1.234 kr", countries: ["IS"] },
295
340
  TRY: { name: "Turkish Lira", symbol: "\u20ba", decimals: 2, format: "\u20ba1.234,56", countries: ["TR"] },
296
341
  ILS: { name: "Israeli Shekel", symbol: "\u20aa", decimals: 2, format: "\u20aa1,234.56", countries: ["IL"] },
@@ -327,12 +372,12 @@ const COUNTRIES = JSON.stringify([
327
372
  { name: "Portugal", code: "PT", fiscalZone: "eu", defaultTaxRate: 23, taxName: "IVA", currency: "EUR", invoicePrefix: "FT" },
328
373
  { name: "Austria", code: "AT", fiscalZone: "eu", defaultTaxRate: 20, taxName: "USt", currency: "EUR", invoicePrefix: "RE" },
329
374
  { name: "Ireland", code: "IE", fiscalZone: "eu", defaultTaxRate: 23, taxName: "VAT", currency: "EUR", invoicePrefix: "INV" },
330
- { name: "Finland", code: "FI", fiscalZone: "eu", defaultTaxRate: 24, taxName: "ALV", currency: "EUR", invoicePrefix: "INV" },
375
+ { name: "Finland", code: "FI", fiscalZone: "eu", defaultTaxRate: 25.5, taxName: "ALV", currency: "EUR", invoicePrefix: "INV" },
331
376
  { name: "Greece", code: "GR", fiscalZone: "eu", defaultTaxRate: 24, taxName: "FPA", currency: "EUR", invoicePrefix: "TIM" },
332
377
  { name: "Luxembourg", code: "LU", fiscalZone: "eu", defaultTaxRate: 17, taxName: "TVA", currency: "EUR", invoicePrefix: "F" },
333
- { name: "Slovakia", code: "SK", fiscalZone: "eu", defaultTaxRate: 20, taxName: "DPH", currency: "EUR", invoicePrefix: "F" },
378
+ { name: "Slovakia", code: "SK", fiscalZone: "eu", defaultTaxRate: 23, taxName: "DPH", currency: "EUR", invoicePrefix: "F" },
334
379
  { name: "Slovenia", code: "SI", fiscalZone: "eu", defaultTaxRate: 22, taxName: "DDV", currency: "EUR", invoicePrefix: "F" },
335
- { name: "Estonia", code: "EE", fiscalZone: "eu", defaultTaxRate: 22, taxName: "KM", currency: "EUR", invoicePrefix: "INV" },
380
+ { name: "Estonia", code: "EE", fiscalZone: "eu", defaultTaxRate: 24, taxName: "KM", currency: "EUR", invoicePrefix: "INV" },
336
381
  { name: "Latvia", code: "LV", fiscalZone: "eu", defaultTaxRate: 21, taxName: "PVN", currency: "EUR", invoicePrefix: "INV" },
337
382
  { name: "Lithuania", code: "LT", fiscalZone: "eu", defaultTaxRate: 21, taxName: "PVM", currency: "EUR", invoicePrefix: "SF" },
338
383
  { name: "Cyprus", code: "CY", fiscalZone: "eu", defaultTaxRate: 19, taxName: "VAT", currency: "EUR", invoicePrefix: "INV" },
@@ -342,8 +387,8 @@ const COUNTRIES = JSON.stringify([
342
387
  { name: "Poland", code: "PL", fiscalZone: "eu", defaultTaxRate: 23, taxName: "VAT", currency: "PLN", invoicePrefix: "FV" },
343
388
  { name: "Czech Republic", code: "CZ", fiscalZone: "eu", defaultTaxRate: 21, taxName: "DPH", currency: "CZK", invoicePrefix: "F" },
344
389
  { name: "Hungary", code: "HU", fiscalZone: "eu", defaultTaxRate: 27, taxName: "\u00c1FA", currency: "HUF", invoicePrefix: "SZ" },
345
- { name: "Romania", code: "RO", fiscalZone: "eu", defaultTaxRate: 19, taxName: "TVA", currency: "RON", invoicePrefix: "F" },
346
- { name: "Bulgaria", code: "BG", fiscalZone: "eu", defaultTaxRate: 20, taxName: "DDS", currency: "BGN", invoicePrefix: "F" },
390
+ { name: "Romania", code: "RO", fiscalZone: "eu", defaultTaxRate: 21, taxName: "TVA", currency: "RON", invoicePrefix: "F" },
391
+ { name: "Bulgaria", code: "BG", fiscalZone: "eu", defaultTaxRate: 20, taxName: "DDS", currency: "EUR", invoicePrefix: "F" },
347
392
  { name: "Croatia", code: "HR", fiscalZone: "eu", defaultTaxRate: 25, taxName: "PDV", currency: "EUR", invoicePrefix: "R" },
348
393
  { name: "United Kingdom", code: "GB", fiscalZone: "international", defaultTaxRate: 20, taxName: "VAT", currency: "GBP", invoicePrefix: "INV" },
349
394
  { name: "United States", code: "US", fiscalZone: "international", defaultTaxRate: 0, taxName: "Sales Tax", currency: "USD", invoicePrefix: "INV" },
@@ -382,8 +427,9 @@ const COUNTRIES = JSON.stringify([
382
427
  /* ------------------------------------------------------------------ */
383
428
  export function registerAllResources(server, client) {
384
429
  server.registerResource("api-schema", "frihet://api/schema", {
385
- description: "OpenAPI schema summary: all endpoints, authentication method, rate limits, pagination, and error codes. " +
386
- "/ Resumen del esquema OpenAPI: endpoints, autenticación, límites, paginación y errores.",
430
+ description: "Informational snapshot summary of selected core REST endpoints, auth, and rate limits. " +
431
+ "Canonical contract: https://api.frihet.io/v1/openapi.json. " +
432
+ "/ Resumen informativo no exhaustivo de endpoints REST principales; el contrato canónico reside en openapi.json.",
387
433
  mimeType: "text/plain",
388
434
  }, async () => ({
389
435
  contents: [
@@ -395,9 +441,9 @@ export function registerAllResources(server, client) {
395
441
  ],
396
442
  }));
397
443
  server.registerResource("tax-rates", "frihet://tax/rates", {
398
- description: "Current tax rates by Spanish fiscal zone: Peninsula IVA (21/10/4%), Canary Islands IGIC (7/3/0%), " +
399
- "Ceuta IPSI, EU reverse charge, international exports, IRPF withholding, and special regimes. " +
400
- "/ Tipos impositivos por zona fiscal: IVA, IGIC, IPSI, intracomunitario, exportaciones, IRPF, regímenes especiales.",
444
+ description: "Spanish statutory tax rate reference (AEAT/ATC/IPSI, IVA/IGIC/IPSI/IRPF). " +
445
+ "Informational reference schedule; server validates tax math per transaction. " +
446
+ "/ Referencia normativa de tipos impositivos por zona fiscal en España.",
401
447
  mimeType: "text/plain",
402
448
  }, async () => ({
403
449
  contents: [
@@ -409,9 +455,9 @@ export function registerAllResources(server, client) {
409
455
  ],
410
456
  }));
411
457
  server.registerResource("tax-calendar", "frihet://tax/calendar", {
412
- description: "Spanish quarterly tax calendar with filing deadlines for Modelo 303, 130, 390, 420 (IGIC), and annual returns. " +
413
- "Includes VeriFactu e-invoicing timeline. " +
414
- "/ Calendario fiscal trimestral español con plazos de presentación de modelos y VeriFactu.",
458
+ description: "Spanish tax filing calendar reference (Calendario del Contribuyente AEAT/ATC). " +
459
+ "Informational reference for statutory filing deadlines. " +
460
+ "/ Calendario fiscal trimestral español con plazos de presentación de modelos.",
415
461
  mimeType: "text/plain",
416
462
  }, async () => ({
417
463
  contents: [
@@ -423,8 +469,8 @@ export function registerAllResources(server, client) {
423
469
  ],
424
470
  }));
425
471
  server.registerResource("expense-categories", "frihet://config/expense-categories", {
426
- description: "The 8 expense categories in Frihet with deductibility rules, IVA treatment, and amortization periods. " +
427
- "Essential for correctly categorizing business expenses. " +
472
+ description: "Frihet standard expense categories and statutory deductibility guidelines (LIRPF/LIVA). " +
473
+ "Informational classification guide. " +
428
474
  "/ Las 8 categorías de gastos con reglas de deducibilidad, IVA y amortización.",
429
475
  mimeType: "text/plain",
430
476
  }, async () => ({
@@ -451,9 +497,9 @@ export function registerAllResources(server, client) {
451
497
  ],
452
498
  }));
453
499
  server.registerResource("currencies", "frihet://config/currencies", {
454
- description: "40 supported currencies with ISO codes, symbols, decimal places, locale formatting examples, and associated countries. " +
455
- "Use to validate currency inputs and format monetary values. " +
456
- "/ 40 divisas soportadas con códigos ISO, símbolos, decimales, formato local y países asociados.",
500
+ description: "Currency codes, symbols and display formatting examples with associated countries, including historical codes. " +
501
+ "Decimals describe these display examples, not authoritative payment precision, conversion rates or API acceptance. " +
502
+ "/ Códigos y ejemplos de formato de divisas; no determinan precisión de pagos ni tipos de cambio.",
457
503
  mimeType: "application/json",
458
504
  }, async () => ({
459
505
  contents: [
@@ -465,9 +511,9 @@ export function registerAllResources(server, client) {
465
511
  ],
466
512
  }));
467
513
  server.registerResource("countries", "frihet://config/countries", {
468
- description: "61 supported countries with ISO codes, fiscal zones (peninsula, canarias, ceuta, melilla, eu, international), " +
469
- "default tax rates, tax names, default currencies, and invoice prefixes. " +
470
- "/ 61 países soportados con zonas fiscales, tipos impositivos, divisas y prefijos de factura.",
514
+ description: "Supported countries with ISO codes, fiscal zones (peninsula, canarias, ceuta, melilla, eu, international), " +
515
+ "default tax rates, tax names, default currencies, and invoice prefixes. Note: default rates are onboarding suggestions only, not legal determination. " +
516
+ "/ Países soportados con zonas fiscales, tipos impositivos por defecto y prefijos de factura.",
471
517
  mimeType: "application/json",
472
518
  }, async () => ({
473
519
  contents: [
@@ -517,33 +563,77 @@ export function registerAllResources(server, client) {
517
563
  };
518
564
  });
519
565
  server.registerResource("overdue-invoices", "frihet://overdue-invoices", {
520
- description: "Live list of all overdue invoices — invoices past their due date that haven't been paid. " +
521
- "Includes client names, amounts, due dates, and days overdue. Critical for cash flow management. " +
522
- "/ Lista en vivo de facturas vencidas — facturas cuya fecha de vencimiento ha pasado sin cobrar.",
566
+ description: "Live invoice records filtered by status overdue (first page up to 100 items). " +
567
+ "Returns stored fields and pagination bounds; does not calculate unpaid balances or days overdue. " +
568
+ "Paginate list_invoices without a status filter and select sent, partial and overdue records client-side for a complete receivables review. " +
569
+ "/ Registros con estado vencida (primera página hasta 100); no calcula saldos pendientes ni días de retraso.",
523
570
  mimeType: "application/json",
524
571
  }, async () => {
525
- const data = await client.listInvoices({ status: "overdue", limit: 100 });
572
+ const response = await client.listInvoices({ status: "overdue", limit: 100 });
573
+ const list = Array.isArray(response)
574
+ ? response
575
+ : Array.isArray(response.data)
576
+ ? (response.data)
577
+ : [];
578
+ const total = typeof response.total === "number"
579
+ ? (response.total)
580
+ : list.length;
581
+ const result = {
582
+ data: list,
583
+ total,
584
+ limit: 100,
585
+ offset: 0,
586
+ hasMore: total > 100 || list.length === 100,
587
+ note: "First 100 records with status overdue; not a complete receivables or unpaid-balance report",
588
+ };
526
589
  return {
527
590
  contents: [
528
591
  {
529
592
  uri: "frihet://overdue-invoices",
530
593
  mimeType: "application/json",
531
- text: JSON.stringify(data, null, 2),
594
+ text: JSON.stringify(result, null, 2),
532
595
  },
533
596
  ],
534
597
  };
535
598
  });
536
599
  server.registerResource("plan-limits", "frihet://status/plan-limits", {
537
- description: "Live plan limits and current usage — plan tier, invoices/month, AI messages/day, team members, " +
538
- "integrations, API requests/minute, and real-time usage counters. " +
539
- "/ Límites del plan y uso actual en vivo — tier, facturas/mes, mensajes IA/día, miembros, integraciones, API req/min.",
600
+ description: "Live plan tier, quotas, and usage counters (invoices, expenses, AI messages) from business context. " +
601
+ "/ Nivel de plan, cuotas y contadores de uso (facturas, gastos, mensajes IA) del contexto del negocio.",
540
602
  mimeType: "application/json",
541
603
  }, async () => {
542
604
  const ctx = await client.getBusinessContext();
543
- const plan = ctx.plan ?? "free";
544
- const limits = ctx.limits ?? {};
545
- const usage = ctx.usage ?? {};
546
- const result = { plan, limits, usage };
605
+ const planRaw = ctx.plan;
606
+ let plan = "free";
607
+ let limits = ctx.limits;
608
+ let usage = ctx.usage;
609
+ if (typeof planRaw === "string") {
610
+ plan = planRaw;
611
+ }
612
+ else if (typeof planRaw === "object" && planRaw !== null) {
613
+ const planObj = planRaw;
614
+ plan = typeof planObj.name === "string" ? planObj.name : "free";
615
+ if ((!limits || Object.keys(limits).length === 0) && (!usage || Object.keys(usage).length === 0)) {
616
+ const extractedLimits = {};
617
+ const extractedUsage = {};
618
+ for (const [key, val] of Object.entries(planObj)) {
619
+ if (key !== "name" && typeof val === "object" && val !== null) {
620
+ const item = val;
621
+ if ("limit" in item)
622
+ extractedLimits[key] = item.limit;
623
+ if ("used" in item)
624
+ extractedUsage[key] = item.used;
625
+ }
626
+ }
627
+ limits = extractedLimits;
628
+ usage = extractedUsage;
629
+ }
630
+ }
631
+ const result = {
632
+ plan,
633
+ limits: limits ?? {},
634
+ usage: usage ?? {},
635
+ ...(typeof planRaw === "object" && planRaw !== null ? { breakdown: planRaw } : {}),
636
+ };
547
637
  return {
548
638
  contents: [
549
639
  {
@@ -0,0 +1,19 @@
1
+ /**
2
+ * `Retry-After` parsing per RFC 9110 §10.2.3: either `delay-seconds`
3
+ * (1*DIGIT) or an HTTP-date. Returns the wait in milliseconds, or `null` when
4
+ * the value is absent or malformed so the caller falls back to its own
5
+ * backoff — never NaN, never negative.
6
+ *
7
+ * Semantics:
8
+ * - delay-seconds: strict non-negative integer digits only (no sign, decimal
9
+ * point, exponent or whitespace inside). Values too large to represent
10
+ * safely are clamped to MAX_SAFE_INTEGER ms, which always exceeds any retry
11
+ * budget and therefore defers instead of sleeping.
12
+ * - HTTP-date: IMF-fixdate, obsolete RFC 850 and ANSI C asctime() forms, all
13
+ * read as GMT. The wait is `date - now`; a date already in the past means
14
+ * the server's embargo has expired, so the wait is 0 (not negative). Clock
15
+ * skew is NOT compensated: shortening the wait would retry earlier than the
16
+ * server asked, lengthening it would hide a valid answer.
17
+ */
18
+ export declare function parseRetryAfter(value: string | null | undefined, nowMs: number): number | null;
19
+ //# sourceMappingURL=retry-after.d.ts.map
@@ -0,0 +1,84 @@
1
+ /**
2
+ * `Retry-After` parsing per RFC 9110 §10.2.3: either `delay-seconds`
3
+ * (1*DIGIT) or an HTTP-date. Returns the wait in milliseconds, or `null` when
4
+ * the value is absent or malformed so the caller falls back to its own
5
+ * backoff — never NaN, never negative.
6
+ *
7
+ * Semantics:
8
+ * - delay-seconds: strict non-negative integer digits only (no sign, decimal
9
+ * point, exponent or whitespace inside). Values too large to represent
10
+ * safely are clamped to MAX_SAFE_INTEGER ms, which always exceeds any retry
11
+ * budget and therefore defers instead of sleeping.
12
+ * - HTTP-date: IMF-fixdate, obsolete RFC 850 and ANSI C asctime() forms, all
13
+ * read as GMT. The wait is `date - now`; a date already in the past means
14
+ * the server's embargo has expired, so the wait is 0 (not negative). Clock
15
+ * skew is NOT compensated: shortening the wait would retry earlier than the
16
+ * server asked, lengthening it would hide a valid answer.
17
+ */
18
+ const MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
19
+ const DAYS_LONG = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
20
+ const DAYS_SHORT = DAYS_LONG.map((d) => d.slice(0, 3));
21
+ const IMF_FIXDATE = /^([A-Z][a-z]{2}), (\d{2}) ([A-Z][a-z]{2}) (\d{4}) (\d{2}):(\d{2}):(\d{2}) GMT$/;
22
+ const RFC850_DATE = /^([A-Z][a-z]+day), (\d{2})-([A-Z][a-z]{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2}) GMT$/;
23
+ const ASCTIME_DATE = /^([A-Z][a-z]{2}) ([A-Z][a-z]{2}) ([ \d]\d) (\d{2}):(\d{2}):(\d{2}) (\d{4})$/;
24
+ function utcMs(year, monthName, day, h, m, s) {
25
+ const month = MONTHS.indexOf(monthName);
26
+ if (month < 0 || h > 23 || m > 59 || s > 60)
27
+ return null;
28
+ const ms = Date.UTC(year, month, day, h, m, s);
29
+ const check = new Date(ms);
30
+ // Reject roll-over such as "31 Feb" that Date.UTC silently normalizes.
31
+ if (check.getUTCFullYear() !== year
32
+ || check.getUTCMonth() !== month
33
+ || check.getUTCDate() !== day) {
34
+ return null;
35
+ }
36
+ return ms;
37
+ }
38
+ function weekdayMatches(ms, name, list) {
39
+ // getUTCDay: 0 = Sunday; the lists start on Monday.
40
+ return list[(new Date(ms).getUTCDay() + 6) % 7] === name;
41
+ }
42
+ function parseHttpDate(value, nowMs) {
43
+ let m = IMF_FIXDATE.exec(value);
44
+ if (m) {
45
+ const ms = utcMs(+m[4], m[3], +m[2], +m[5], +m[6], +m[7]);
46
+ return ms !== null && weekdayMatches(ms, m[1], DAYS_SHORT) ? ms : null;
47
+ }
48
+ m = RFC850_DATE.exec(value);
49
+ if (m) {
50
+ // RFC 9110 §5.6.7: a two-digit year more than 50 years ahead is the past century.
51
+ const yy = +m[4];
52
+ const nowYear = new Date(nowMs).getUTCFullYear();
53
+ let year = Math.floor(nowYear / 100) * 100 + yy;
54
+ if (year > nowYear + 50)
55
+ year -= 100;
56
+ const ms = utcMs(year, m[3], +m[2], +m[5], +m[6], +m[7]);
57
+ return ms !== null && weekdayMatches(ms, m[1], DAYS_LONG) ? ms : null;
58
+ }
59
+ m = ASCTIME_DATE.exec(value);
60
+ if (m) {
61
+ const ms = utcMs(+m[7], m[2], +m[3].trim(), +m[4], +m[5], +m[6]);
62
+ return ms !== null && weekdayMatches(ms, m[1], DAYS_SHORT) ? ms : null;
63
+ }
64
+ return null;
65
+ }
66
+ export function parseRetryAfter(value, nowMs) {
67
+ if (value === null || value === undefined)
68
+ return null;
69
+ const v = value.trim();
70
+ if (v === "")
71
+ return null;
72
+ if (/^\d+$/.test(v)) {
73
+ const seconds = Number(v);
74
+ if (!Number.isFinite(seconds) || seconds * 1000 > Number.MAX_SAFE_INTEGER) {
75
+ return Number.MAX_SAFE_INTEGER;
76
+ }
77
+ return seconds * 1000;
78
+ }
79
+ const dateMs = parseHttpDate(v, nowMs);
80
+ if (dateMs === null)
81
+ return null;
82
+ return Math.max(0, dateMs - nowMs);
83
+ }
84
+ //# sourceMappingURL=retry-after.js.map
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Canonical registration path shared by the stdio server, remote Worker, and
3
+ * generated public-surface contract. Transport/auth setup stays with each host;
4
+ * the exposed MCP operations/resources/prompts are composed here exactly once.
5
+ */
6
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7
+ import type { IFrihetClient } from "./client-interface.js";
8
+ export interface McpSurfaceComposition {
9
+ readonly openaiMode: boolean;
10
+ readonly groupedMode: boolean;
11
+ readonly includeDynamicResources: boolean;
12
+ readonly claudeMode?: boolean;
13
+ readonly googleMode?: boolean;
14
+ readonly museMode?: boolean;
15
+ }
16
+ export declare function localMcpSurfaceComposition(openaiMode: boolean, groupedMode: boolean, claudeMode?: boolean, googleMode?: boolean, museMode?: boolean): McpSurfaceComposition;
17
+ export declare function remoteMcpSurfaceComposition(openaiMode: boolean, groupedMode: boolean, claudeMode?: boolean, googleMode?: boolean, museMode?: boolean): McpSurfaceComposition;
18
+ export declare function registerMcpSurface(server: McpServer, client: IFrihetClient, options: McpSurfaceComposition): void;
19
+ //# sourceMappingURL=server-composition.d.ts.map
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Canonical registration path shared by the stdio server, remote Worker, and
3
+ * generated public-surface contract. Transport/auth setup stays with each host;
4
+ * the exposed MCP operations/resources/prompts are composed here exactly once.
5
+ */
6
+ import { applyPublicCapabilityTruth, buildLocalDiscoveryCapability, CAPABILITY_META_KEY, } from "./capability-truth.js";
7
+ import { applyFiscalAliases } from "./fiscal-aliases.js";
8
+ import { applyClaudeCandidateProfile, CLAUDE_CANDIDATE_TOOLS, CLAUDE_GROUP_METADATA } from "./claude-profile.js";
9
+ import { applyGoogleProfile } from "./google-profile.js";
10
+ import { applyMuseProfile } from "./muse-profile.js";
11
+ import { applyOpenAIProfile, applyOpenAIReviewProfiles, } from "./openai-profile.js";
12
+ import { registerAllPrompts } from "./prompts/register-all.js";
13
+ import { registerAllResources } from "./resources/register-all.js";
14
+ import { applyToolExposureProfile } from "./tool-exposure.js";
15
+ import { registerAllTools } from "./tools/register-all.js";
16
+ export function localMcpSurfaceComposition(openaiMode, groupedMode, claudeMode = false, googleMode = false, museMode = false) {
17
+ return { openaiMode, groupedMode, includeDynamicResources: true, claudeMode, googleMode, museMode };
18
+ }
19
+ export function remoteMcpSurfaceComposition(openaiMode, groupedMode, claudeMode = false, googleMode = false, museMode = false) {
20
+ return { openaiMode, groupedMode, includeDynamicResources: false, claudeMode, googleMode, museMode };
21
+ }
22
+ export function registerMcpSurface(server, client, options) {
23
+ const activeModes = [options.openaiMode, options.claudeMode, options.googleMode, options.museMode].filter(Boolean).length;
24
+ if (activeModes > 1) {
25
+ throw new Error("FRIHET_OPENAI_MODE, FRIHET_CLAUDE_MODE, FRIHET_GOOGLE_MODE, and FRIHET_MUSE_MODE are mutually exclusive");
26
+ }
27
+ if (options.claudeMode) {
28
+ // Directory reads annotations.title. Install before grouped discovery eagerly
29
+ // registers its meta-tools, and clone annotations to preserve shared hints.
30
+ const registerTool = server.registerTool.bind(server);
31
+ server.registerTool = (name, config, handler) => registerTool(name, {
32
+ ...config,
33
+ annotations: { ...config.annotations, title: config.title },
34
+ }, handler);
35
+ }
36
+ if (options.groupedMode) {
37
+ if (options.openaiMode) {
38
+ applyOpenAIReviewProfiles(server);
39
+ }
40
+ else {
41
+ applyToolExposureProfile(server, {
42
+ ...(options.claudeMode ? {
43
+ allowlist: CLAUDE_CANDIDATE_TOOLS,
44
+ groupMetadata: CLAUDE_GROUP_METADATA,
45
+ describeToolExample: "list_invoices",
46
+ } : {}),
47
+ capabilityTruth: {
48
+ metaKey: CAPABILITY_META_KEY,
49
+ localDiscovery: buildLocalDiscoveryCapability,
50
+ },
51
+ });
52
+ }
53
+ }
54
+ else if (options.openaiMode) {
55
+ applyOpenAIProfile(server);
56
+ }
57
+ if (!options.openaiMode && !options.googleMode && !options.museMode) {
58
+ applyPublicCapabilityTruth(server);
59
+ }
60
+ if (options.claudeMode) {
61
+ applyClaudeCandidateProfile(server);
62
+ }
63
+ else if (options.googleMode) {
64
+ applyGoogleProfile(server);
65
+ }
66
+ else if (options.museMode) {
67
+ applyMuseProfile(server);
68
+ }
69
+ registerAllTools(server, client);
70
+ applyFiscalAliases(server);
71
+ registerAllResources(server, options.includeDynamicResources ? client : undefined);
72
+ registerAllPrompts(server);
73
+ }
74
+ //# sourceMappingURL=server-composition.js.map