@kirjapro/mcp-server 0.5.11 → 0.6.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 (67) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +1 -1
  3. package/dist/accounts.d.ts +40 -0
  4. package/dist/accounts.js +45 -0
  5. package/dist/auth.d.ts +17 -0
  6. package/dist/auth.js +245 -0
  7. package/dist/compliance/rates.d.ts +53 -0
  8. package/dist/compliance/rates.js +57 -0
  9. package/dist/database.types.d.ts +6525 -0
  10. package/dist/database.types.js +20 -0
  11. package/dist/index.d.ts +2 -0
  12. package/dist/index.js +58 -52
  13. package/dist/lib/incomeStatementSynthesis.d.ts +47 -0
  14. package/dist/lib/incomeStatementSynthesis.js +140 -0
  15. package/dist/resources/company.d.ts +2 -0
  16. package/dist/resources/company.js +75 -0
  17. package/dist/resources/compliance.d.ts +2 -0
  18. package/dist/resources/compliance.js +338 -0
  19. package/dist/resources/index.d.ts +2 -0
  20. package/dist/resources/index.js +10 -0
  21. package/dist/resources/schema.d.ts +2 -0
  22. package/dist/resources/schema.js +63 -0
  23. package/dist/resources/security.d.ts +2 -0
  24. package/dist/resources/security.js +33 -0
  25. package/dist/schemas.d.ts +12 -0
  26. package/dist/schemas.js +12 -0
  27. package/dist/tools/accounts.d.ts +2 -0
  28. package/dist/tools/accounts.js +48 -0
  29. package/dist/tools/banking.d.ts +2 -0
  30. package/dist/tools/banking.js +196 -0
  31. package/dist/tools/bookings.d.ts +2 -0
  32. package/dist/tools/bookings.js +294 -0
  33. package/dist/tools/customers.d.ts +2 -0
  34. package/dist/tools/customers.js +320 -0
  35. package/dist/tools/financialNotes.d.ts +2 -0
  36. package/dist/tools/financialNotes.js +255 -0
  37. package/dist/tools/fingerprints.d.ts +6 -0
  38. package/dist/tools/fingerprints.js +28 -0
  39. package/dist/tools/fiscal-years.d.ts +2 -0
  40. package/dist/tools/fiscal-years.js +35 -0
  41. package/dist/tools/index.d.ts +9 -0
  42. package/dist/tools/index.js +105 -0
  43. package/dist/tools/invoices.d.ts +2 -0
  44. package/dist/tools/invoices.js +489 -0
  45. package/dist/tools/offers.d.ts +2 -0
  46. package/dist/tools/offers.js +443 -0
  47. package/dist/tools/received-invoices.d.ts +2 -0
  48. package/dist/tools/received-invoices.js +134 -0
  49. package/dist/tools/recurring-invoices.d.ts +2 -0
  50. package/dist/tools/recurring-invoices.js +348 -0
  51. package/dist/tools/reports.d.ts +2 -0
  52. package/dist/tools/reports.js +869 -0
  53. package/dist/tools/templates.d.ts +89 -0
  54. package/dist/tools/templates.js +636 -0
  55. package/dist/tools/transactions.d.ts +2 -0
  56. package/dist/tools/transactions.js +597 -0
  57. package/dist/tools/validation.d.ts +26 -0
  58. package/dist/tools/validation.js +127 -0
  59. package/dist/types.d.ts +388 -0
  60. package/dist/types.js +34 -0
  61. package/dist/utils.d.ts +276 -0
  62. package/dist/utils.js +637 -0
  63. package/dist/version.d.ts +1 -0
  64. package/dist/version.js +3 -0
  65. package/package.json +3 -2
  66. package/server.json +5 -1
  67. package/tool-fingerprints.json +1 -1
@@ -0,0 +1,127 @@
1
+ import { z } from 'zod';
2
+ import { toResult, READ_ONLY_ANNOTATIONS } from '../utils.js';
3
+ import { VALIDATION_OUTPUT_SHAPE } from '../schemas.js';
4
+ const REFERENCE_OUTPUT_SHAPE = {
5
+ input: z.string(),
6
+ reference: z.string(),
7
+ error: z.string().optional(),
8
+ };
9
+ /**
10
+ * Validate Finnish Y-tunnus (business ID).
11
+ * Format: NNNNNNN-C, weights [7, 9, 10, 5, 8, 4, 2].
12
+ */
13
+ function validateFinnishBusinessId(input) {
14
+ const match = input.match(/^(\d{7})-(\d)$/);
15
+ if (!match) {
16
+ return { valid: false, error: 'Invalid format. Expected 7 digits + hyphen + check digit (e.g. 1234567-8).' };
17
+ }
18
+ const [, digits, checkStr] = match;
19
+ const weights = [7, 9, 10, 5, 8, 4, 2];
20
+ let sum = 0;
21
+ for (let i = 0; i < 7; i++) {
22
+ sum += parseInt(digits[i]) * weights[i];
23
+ }
24
+ const remainder = sum % 11;
25
+ if (remainder === 1) {
26
+ return { valid: false, error: 'Checksum calculation produces remainder 1, which is invalid for Y-tunnus.' };
27
+ }
28
+ const expected = remainder === 0 ? 0 : 11 - remainder;
29
+ if (expected !== parseInt(checkStr)) {
30
+ return { valid: false, error: `Check digit should be ${expected}, got ${checkStr}.` };
31
+ }
32
+ return { valid: true };
33
+ }
34
+ /**
35
+ * Validate Finnish IBAN.
36
+ * Format: FI + 16 digits, mod-97 check.
37
+ */
38
+ function validateFinnishIBAN(input) {
39
+ const cleaned = input.replace(/\s/g, '').toUpperCase();
40
+ if (!/^FI\d{16}$/.test(cleaned)) {
41
+ return { valid: false, error: 'Finnish IBAN must be FI + 16 digits (18 characters total).' };
42
+ }
43
+ // Move first 4 chars to end, convert letters to numbers
44
+ const rearranged = cleaned.slice(4) + cleaned.slice(0, 4);
45
+ let numStr = '';
46
+ for (const ch of rearranged) {
47
+ const code = ch.charCodeAt(0);
48
+ if (code >= 65 && code <= 90) {
49
+ numStr += (code - 55).toString();
50
+ }
51
+ else {
52
+ numStr += ch;
53
+ }
54
+ }
55
+ // Mod 97 using chunk processing (handles large numbers)
56
+ let remainder = 0;
57
+ for (let i = 0; i < numStr.length; i += 7) {
58
+ const chunk = remainder.toString() + numStr.slice(i, i + 7);
59
+ remainder = parseInt(chunk) % 97;
60
+ }
61
+ if (remainder !== 1) {
62
+ return { valid: false, error: 'IBAN check digits are invalid (mod-97 check failed).' };
63
+ }
64
+ return { valid: true };
65
+ }
66
+ /**
67
+ * Generate Finnish reference number (viitenumero) with 7-3-1 check digit.
68
+ */
69
+ function generateReferenceNumber(baseNumber) {
70
+ const digits = baseNumber.replace(/\D/g, '');
71
+ if (digits.length < 1 || digits.length > 19) {
72
+ return { reference: '', error: 'Base number must be 1-19 digits.' };
73
+ }
74
+ const weights = [7, 3, 1];
75
+ let sum = 0;
76
+ for (let i = digits.length - 1, w = 0; i >= 0; i--, w++) {
77
+ sum += parseInt(digits[i]) * weights[w % 3];
78
+ }
79
+ const checkDigit = (10 - (sum % 10)) % 10;
80
+ return { reference: digits + checkDigit };
81
+ }
82
+ export function registerValidationTools(server) {
83
+ server.registerTool('kirjapro_validate_business_id', {
84
+ description: 'Validate a Finnish business ID (Y-tunnus). Checks format and checksum. No database access — pure computation.',
85
+ inputSchema: {
86
+ businessId: z.string().describe('Y-tunnus to validate (e.g. "1234567-8")'),
87
+ },
88
+ outputSchema: VALIDATION_OUTPUT_SHAPE,
89
+ annotations: READ_ONLY_ANNOTATIONS,
90
+ }, async (params) => {
91
+ const result = validateFinnishBusinessId(params.businessId);
92
+ return toResult({
93
+ input: params.businessId,
94
+ ...result,
95
+ });
96
+ });
97
+ server.registerTool('kirjapro_validate_iban', {
98
+ description: 'Validate a Finnish IBAN. Checks format (FI + 16 digits) and mod-97 checksum. No database access — pure computation.',
99
+ inputSchema: {
100
+ iban: z.string().describe('Finnish IBAN to validate (e.g. "FI2112345678901234")'),
101
+ },
102
+ outputSchema: VALIDATION_OUTPUT_SHAPE,
103
+ annotations: READ_ONLY_ANNOTATIONS,
104
+ }, async (params) => {
105
+ const result = validateFinnishIBAN(params.iban);
106
+ return toResult({
107
+ input: params.iban,
108
+ ...result,
109
+ });
110
+ });
111
+ server.registerTool('kirjapro_generate_reference', {
112
+ description: 'Generate a Finnish bank reference number (viitenumero) from a base number. Appends the 7-3-1 weighted check digit. No database access — pure computation.',
113
+ inputSchema: {
114
+ baseNumber: z.string().describe('Base number (1-19 digits, e.g. invoice number "00042")'),
115
+ },
116
+ outputSchema: REFERENCE_OUTPUT_SHAPE,
117
+ annotations: READ_ONLY_ANNOTATIONS,
118
+ }, async (params) => {
119
+ const result = generateReferenceNumber(params.baseNumber);
120
+ return toResult({
121
+ input: params.baseNumber,
122
+ ...result,
123
+ });
124
+ });
125
+ }
126
+ // Export for testing
127
+ export { validateFinnishBusinessId, validateFinnishIBAN, generateReferenceNumber };
@@ -0,0 +1,388 @@
1
+ export type AccountType = 'asset' | 'liability' | 'equity' | 'revenue' | 'expense';
2
+ /** VAT rates as strings for Zod enum validation (prevents agent hallucination). */
3
+ export type VatRate = '25.5' | '13.5' | '10' | '0';
4
+ export type InvoiceStatus = 'draft' | 'sent' | 'paid' | 'partial' | 'overdue' | 'cancelled';
5
+ /** VAT rates as strings, matching the VatRate type. */
6
+ export declare const VAT_RATES: readonly ["25.5", "13.5", "10", "0"];
7
+ export interface Account {
8
+ id: string;
9
+ account_number: string;
10
+ name: string;
11
+ account_type: AccountType;
12
+ is_active: boolean;
13
+ }
14
+ export interface FiscalYear {
15
+ id: string;
16
+ company_id: string;
17
+ start_date: string;
18
+ end_date: string;
19
+ is_closed: boolean;
20
+ }
21
+ export interface Transaction {
22
+ id: string;
23
+ fiscal_year_id: string;
24
+ transaction_date: string;
25
+ description: string;
26
+ document_number: string | null;
27
+ created_at: string;
28
+ }
29
+ export interface JournalEntry {
30
+ id: string;
31
+ transaction_id: string;
32
+ account_id: string;
33
+ debit_amount: number;
34
+ credit_amount: number;
35
+ description: string | null;
36
+ }
37
+ export interface Customer {
38
+ id: string;
39
+ customer_number: string;
40
+ name: string;
41
+ business_id: string | null;
42
+ email: string | null;
43
+ phone: string | null;
44
+ address: string | null;
45
+ postal_code: string | null;
46
+ city: string | null;
47
+ country: string;
48
+ payment_term_days: number;
49
+ is_active: boolean;
50
+ }
51
+ export interface Invoice {
52
+ id: string;
53
+ customer_id: string;
54
+ invoice_number: string;
55
+ reference_number: string;
56
+ invoice_date: string;
57
+ due_date: string;
58
+ status: InvoiceStatus;
59
+ subtotal: number;
60
+ vat_total: number;
61
+ total: number;
62
+ notes: string | null;
63
+ }
64
+ export interface InvoiceLine {
65
+ id: string;
66
+ invoice_id: string;
67
+ line_number: number;
68
+ description: string;
69
+ quantity: number;
70
+ unit: string;
71
+ unit_price: number;
72
+ vat_rate: number;
73
+ subtotal: number;
74
+ vat_amount: number;
75
+ total: number;
76
+ }
77
+ export interface InvoicePayment {
78
+ id: string;
79
+ invoice_id: string;
80
+ payment_date: string;
81
+ amount: number;
82
+ payment_method: string;
83
+ reference: string | null;
84
+ }
85
+ export interface BankTransaction {
86
+ id: string;
87
+ bank_account_id: string;
88
+ booking_date: string;
89
+ amount: number;
90
+ credit_debit: 'credit' | 'debit';
91
+ counterparty_name: string | null;
92
+ reference: string | null;
93
+ message: string | null;
94
+ reconciled_at: string | null;
95
+ }
96
+ /** Standard paginated response wrapper */
97
+ export interface PaginatedResult<T> {
98
+ results: T[];
99
+ total_count: number;
100
+ has_more: boolean;
101
+ next_offset: number;
102
+ }
103
+ /** select: id, invoice_number, total, status, invoice_payments(amount) */
104
+ export interface InvoiceWithPayments {
105
+ id: string;
106
+ invoice_number: string;
107
+ total: number;
108
+ status: InvoiceStatus;
109
+ invoice_payments: Array<{
110
+ amount: number;
111
+ }>;
112
+ }
113
+ /** select: id, invoice_number, total, due_date, invoice_date, customers(name) */
114
+ export interface InvoiceWithCustomer {
115
+ id: string;
116
+ invoice_number: string;
117
+ total: number;
118
+ due_date: string;
119
+ invoice_date: string;
120
+ customers: {
121
+ name: string;
122
+ } | null;
123
+ }
124
+ /** select: debit_amount, credit_amount, account:accounts!inner(account_type), transaction:transactions!inner(fiscal_year_id) */
125
+ export interface JournalEntryWithAccountType {
126
+ debit_amount: number;
127
+ credit_amount: number;
128
+ account: {
129
+ account_type: AccountType;
130
+ };
131
+ transaction: {
132
+ fiscal_year_id: string;
133
+ };
134
+ }
135
+ /** select: debit_amount, credit_amount, account:accounts!inner(id, account_number, name, account_type), transaction:transactions!inner(fiscal_year_id, transaction_date) */
136
+ export interface JournalEntryWithAccountDetail {
137
+ debit_amount: number;
138
+ credit_amount: number;
139
+ account: {
140
+ id: string;
141
+ account_number: string;
142
+ name: string;
143
+ account_type: AccountType;
144
+ };
145
+ transaction: {
146
+ fiscal_year_id: string;
147
+ transaction_date: string;
148
+ };
149
+ }
150
+ /** select: account_name, iban, current_balance */
151
+ export interface BankAccountBalance {
152
+ account_name: string | null;
153
+ iban: string;
154
+ current_balance: number | null;
155
+ }
156
+ /** select: id, transaction_date, description, document_number, created_at, journal_entries(...) */
157
+ export interface TransactionWithEntries {
158
+ id: string;
159
+ transaction_date: string;
160
+ description: string;
161
+ document_number: string | null;
162
+ created_at: string;
163
+ journal_entries: Array<{
164
+ id: string;
165
+ account_id: string;
166
+ debit_amount: number;
167
+ credit_amount: number;
168
+ accounts: {
169
+ account_number: string;
170
+ name: string;
171
+ };
172
+ }>;
173
+ }
174
+ /** select: id, account_number (minimal lookup) */
175
+ export interface AccountLookup {
176
+ id: string;
177
+ account_number: string;
178
+ }
179
+ /** Accumulator row for income statement and balance sheet reports */
180
+ export interface AccountBalanceRow {
181
+ account_number: string;
182
+ name: string;
183
+ account_type: string;
184
+ debit: number;
185
+ credit: number;
186
+ balance: number;
187
+ }
188
+ /** Balance sheet account row */
189
+ export interface BalanceSheetRow {
190
+ account_number: string;
191
+ name: string;
192
+ account_type: string;
193
+ balance: number;
194
+ }
195
+ /** AR aging bucket with categorized invoices */
196
+ export interface AgingBucketInvoice {
197
+ invoice_number: string;
198
+ customer: string | null;
199
+ total: number;
200
+ due_date: string;
201
+ days_overdue: number;
202
+ }
203
+ /** AR aging bucket */
204
+ export interface AgingBucket {
205
+ label: string;
206
+ min: number;
207
+ max: number;
208
+ count: number;
209
+ total: number;
210
+ invoices: AgingBucketInvoice[];
211
+ }
212
+ /** Result item from batch transaction creation */
213
+ export interface CreatedTransactionResult {
214
+ id: string;
215
+ description: string;
216
+ date: string;
217
+ }
218
+ /** Bank account row for list_bank_accounts */
219
+ export interface BankAccountRow {
220
+ id: string;
221
+ account_name: string;
222
+ iban: string;
223
+ bic: string | null;
224
+ bank_name: string | null;
225
+ current_balance: number | null;
226
+ available_balance: number | null;
227
+ is_primary: boolean;
228
+ last_sync_at: string | null;
229
+ connection_status: string | null;
230
+ }
231
+ /** Bank transaction row for list_bank_transactions */
232
+ export interface BankTransactionRow {
233
+ id: string;
234
+ bank_account_id: string;
235
+ booking_date: string;
236
+ amount: number;
237
+ credit_debit: string;
238
+ counterparty_name: string | null;
239
+ counterparty_iban: string | null;
240
+ reference: string | null;
241
+ message: string | null;
242
+ reconciled_at: string | null;
243
+ match_method: string | null;
244
+ matched_invoice_id: string | null;
245
+ }
246
+ /** VAT breakdown row for invoice detail */
247
+ export interface VatBreakdownRow {
248
+ vat_rate: number;
249
+ subtotal: number;
250
+ vat_amount: number;
251
+ total: number;
252
+ }
253
+ /** General ledger entry with running balance */
254
+ export interface GeneralLedgerEntry {
255
+ date: string;
256
+ description: string;
257
+ document_number: string | null;
258
+ transaction_id: string;
259
+ debit: number;
260
+ credit: number;
261
+ running_balance: number;
262
+ }
263
+ /** A single journal entry line produced by template expansion */
264
+ export interface TemplateEntry {
265
+ accountNumber: string;
266
+ debitAmount: number;
267
+ creditAmount: number;
268
+ }
269
+ /** Static definition of a transaction template */
270
+ export interface TransactionTemplate {
271
+ id: string;
272
+ description: string;
273
+ vatRate: number | null;
274
+ /** Function that expands the template for a given gross amount */
275
+ expand: (amount: number, opts?: {
276
+ withholdingRate?: number;
277
+ tyelRate?: number;
278
+ accountOverrides?: Record<string, string>;
279
+ }) => TemplateEntry[];
280
+ }
281
+ export type OfferType = 'tarjous' | 'sopimus';
282
+ export type OfferStatus = 'draft' | 'sent' | 'viewed' | 'accepted' | 'rejected' | 'expired';
283
+ export interface Offer {
284
+ id: string;
285
+ company_id: string;
286
+ customer_id: string | null;
287
+ type: OfferType;
288
+ title: string;
289
+ offer_number: string;
290
+ content: string | null;
291
+ status: OfferStatus;
292
+ valid_from: string | null;
293
+ valid_until: string | null;
294
+ subtotal: number;
295
+ vat_total: number;
296
+ total: number;
297
+ notes: string | null;
298
+ invoice_id: string | null;
299
+ sent_at: string | null;
300
+ created_at: string;
301
+ }
302
+ export interface OfferLine {
303
+ id: string;
304
+ offer_id: string;
305
+ line_number: number;
306
+ description: string;
307
+ quantity: number;
308
+ unit: string;
309
+ unit_price: number;
310
+ vat_rate: number;
311
+ subtotal: number;
312
+ vat_amount: number;
313
+ total: number;
314
+ }
315
+ /**
316
+ * Offer status transition map (sender-side, via record_offer_status_change RPC).
317
+ *
318
+ * Removals from the naive 6-status graph:
319
+ * - 'sent → viewed' (Round 2 T1-2): only record_offer_view (public /o/:token
320
+ * GET) may flip a sent offer to viewed.
321
+ * - 'draft → sent' (Round 4 R1-M4): only send-offer-email edge fn may do
322
+ * this — it generates the access_token atomically. Direct status flip
323
+ * would orphan the offer in 'sent' with NULL token, 404-ing the public
324
+ * link.
325
+ *
326
+ * Must stay in lockstep with src/lib/offer-types.ts VALID_OFFER_TRANSITIONS.
327
+ */
328
+ export declare const VALID_OFFER_TRANSITIONS: Record<OfferStatus, OfferStatus[]>;
329
+ export type RecurringFrequency = 'weekly' | 'monthly' | 'quarterly' | 'yearly';
330
+ export interface RecurringInvoice {
331
+ id: string;
332
+ company_id: string;
333
+ customer_id: string;
334
+ name: string;
335
+ template_data: Record<string, unknown>;
336
+ frequency: RecurringFrequency;
337
+ next_date: string;
338
+ end_date: string | null;
339
+ is_active: boolean;
340
+ auto_send: boolean;
341
+ last_generated_at: string | null;
342
+ times_generated: number;
343
+ created_at: string;
344
+ updated_at: string;
345
+ }
346
+ export type ReceivedInvoiceStatus = 'new' | 'reviewed' | 'approved' | 'rejected' | 'booked';
347
+ export interface ReceivedInvoice {
348
+ id: string;
349
+ company_id: string;
350
+ sender_business_id: string | null;
351
+ sender_name: string | null;
352
+ invoice_number: string | null;
353
+ invoice_date: string | null;
354
+ due_date: string | null;
355
+ total_amount: number | null;
356
+ vat_amount: number | null;
357
+ currency: string;
358
+ payment_reference: string | null;
359
+ iban: string | null;
360
+ status: ReceivedInvoiceStatus;
361
+ transaction_id: string | null;
362
+ created_at: string;
363
+ }
364
+ export interface AlvSummaryResult {
365
+ period: {
366
+ from: string;
367
+ to: string;
368
+ };
369
+ output_vat: Record<string, number>;
370
+ input_vat: Record<string, number>;
371
+ net_vat_payable: number;
372
+ omavero_fields: Record<string, number>;
373
+ }
374
+ export interface PrhBusinessResult {
375
+ businessId: string;
376
+ name: string;
377
+ registrationDate: string | null;
378
+ companyForm: string | null;
379
+ address: string | null;
380
+ postalCode: string | null;
381
+ city: string | null;
382
+ country: string | null;
383
+ website: string | null;
384
+ phone: string | null;
385
+ liquidations: unknown[];
386
+ }
387
+ /** Invoice status transition map */
388
+ export declare const VALID_TRANSITIONS: Record<InvoiceStatus, InvoiceStatus[]>;
package/dist/types.js ADDED
@@ -0,0 +1,34 @@
1
+ // Standalone types mirroring the Kirjapro database schema.
2
+ // These are independent from the frontend types to keep the MCP server self-contained.
3
+ /** VAT rates as strings, matching the VatRate type. */
4
+ export const VAT_RATES = ['25.5', '13.5', '10', '0'];
5
+ /**
6
+ * Offer status transition map (sender-side, via record_offer_status_change RPC).
7
+ *
8
+ * Removals from the naive 6-status graph:
9
+ * - 'sent → viewed' (Round 2 T1-2): only record_offer_view (public /o/:token
10
+ * GET) may flip a sent offer to viewed.
11
+ * - 'draft → sent' (Round 4 R1-M4): only send-offer-email edge fn may do
12
+ * this — it generates the access_token atomically. Direct status flip
13
+ * would orphan the offer in 'sent' with NULL token, 404-ing the public
14
+ * link.
15
+ *
16
+ * Must stay in lockstep with src/lib/offer-types.ts VALID_OFFER_TRANSITIONS.
17
+ */
18
+ export const VALID_OFFER_TRANSITIONS = {
19
+ draft: ['expired'],
20
+ sent: ['accepted', 'rejected', 'expired', 'draft'],
21
+ viewed: ['accepted', 'rejected', 'expired', 'draft'],
22
+ accepted: [],
23
+ rejected: ['draft'],
24
+ expired: ['draft'],
25
+ };
26
+ /** Invoice status transition map */
27
+ export const VALID_TRANSITIONS = {
28
+ draft: ['sent', 'cancelled'],
29
+ sent: ['paid', 'partial', 'overdue', 'cancelled', 'draft'],
30
+ partial: ['paid', 'overdue', 'cancelled'],
31
+ overdue: ['paid', 'partial', 'cancelled', 'sent'],
32
+ paid: [],
33
+ cancelled: ['draft'],
34
+ };