e-arveldaja-mcp 0.3.1 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -93,7 +93,7 @@ async function main() {
93
93
  const api = createScopedApiContext(connectionState, connectionContexts, invocationStorage);
94
94
  const server = new McpServer({
95
95
  name: "e-arveldaja",
96
- version: "0.3.1",
96
+ version: "0.3.2",
97
97
  description: "EXPERIMENTAL, UNOFFICIAL MCP server for the Estonian e-arveldaja (e-Financials) API. " +
98
98
  "NOT affiliated with or endorsed by RIK. Use entirely at your own risk — " +
99
99
  "this software interacts with live financial data and can create, modify, and delete accounting records. " +
@@ -102,17 +102,22 @@ async function main() {
102
102
  "PDF invoice extraction, supplier resolution with business registry lookup, " +
103
103
  "and smart booking suggestions based on past invoices.",
104
104
  }, {
105
- instructions: `Call get_vat_info first — VAT status affects purchase invoice booking.
106
- PDF workflow: extract_pdf_invoice validate_invoice_data resolve_supplier suggest_booking → create_purchase_invoice_from_pdf → upload_invoice_document → confirm_purchase_invoice.
107
- Pass EXACT vat_price and gross_price from original invoices — never recalculate.
108
- Check detect_duplicate_purchase_invoice before creating purchase invoices.
109
- Use list_purchase_articles to find article IDs for expense accounts.
110
- Foreign suppliers (non-Estonian): check if reversed_vat_id=1 applies (reverse charge VAT).
111
- Bank reconciliation: reconcile_transactions first, then auto_confirm_exact_matches with dry_run before executing.
112
- Financial reports require all journals/invoices confirmed first for accurate results.
113
- Multi-company: list_connections / switch_connection. Caches cleared on switch.
114
- Batch operations (import, auto-confirm, recurring invoices) default to dry_run — preview before executing.
115
- Monetary amounts are EUR unless cl_currencies_id specifies otherwise.`,
105
+ instructions: `Purchase invoices:
106
+ - Before booking, call get_vat_info to check VAT registration status.
107
+ - Before creating, call detect_duplicate_purchase_invoice.
108
+ - Pass original vat_price and gross_price exactly — do not recalculate.
109
+ - Use list_purchase_articles to resolve cl_purchase_articles_id.
110
+ - For non-Estonian suppliers, check if reverse charge applies (reversed_vat_id=1).
111
+ - PDF flow: extract_pdf_invoice validate_invoice_data resolve_supplier suggest_booking → create_purchase_invoice_from_pdf → upload_invoice_document → confirm_purchase_invoice.
112
+
113
+ Bank reconciliation:
114
+ - Run reconcile_transactions first, then auto_confirm_exact_matches with dry_run before executing.
115
+
116
+ Reporting:
117
+ - Confirm all journals/invoices/transactions first for accurate financial reports.
118
+ - list_connections / switch_connection for multi-company; switching clears caches.
119
+ - Batch tools default to dry_run — preview before execute=true.
120
+ - Amounts are EUR unless cl_currencies_id specifies otherwise.`,
116
121
  });
117
122
  // --- Multi-account tools ---
118
123
  server.tool("list_connections", "List all available e-arveldaja connections (API key files). " +
@@ -62,10 +62,7 @@ async function computeAccountBalance(api, accountId, clientId, dateFrom, dateTo,
62
62
  };
63
63
  }
64
64
  export function registerAccountBalanceTools(server, api) {
65
- server.tool("compute_account_balance", "Compute account balance from journal postings (D/C logic). " +
66
- "For liability accounts (C-type): balance = credits - debits. " +
67
- "For asset accounts (D-type): balance = debits - credits. " +
68
- "Can filter by client and date range.", {
65
+ server.tool("compute_account_balance", "Compute an account balance from journal postings, with optional client and date filters. Applies the account's debit/credit direction automatically.", {
69
66
  account_id: z.number().describe("Account number (e.g. 2110 for short-term loans)"),
70
67
  client_id: z.number().optional().describe("Filter by client ID"),
71
68
  date_from: z.string().optional().describe("Start date (YYYY-MM-DD)"),
@@ -88,12 +85,10 @@ export function registerAccountBalanceTools(server, api) {
88
85
  };
89
86
  return { content: [{ type: "text", text: JSON.stringify(summary, null, 2) }] };
90
87
  });
91
- server.tool("compute_client_debt", "Compute how much the company owes to a specific client (or vice versa). " +
92
- "Checks accounts 2110 (short-term loans), 2310 (accounts payable), 1210 (accounts receivable) by default. " +
93
- "Override account_ids for other accounts. Uses journal D/C postings.", {
88
+ server.tool("compute_client_debt", "Compute how much the company owes the client and vice versa across selected accounts (default: 2110, 2310, 1210). Uses journal D/C postings.", {
94
89
  client_id: z.number().describe("Client ID"),
95
90
  account_ids: z.string().optional().describe("Comma-separated account IDs to check (default: 2110,2310,1210)"),
96
- }, { ...readOnly, title: "Compute Client Debt" }, async ({ client_id, account_ids }) => {
91
+ }, { ...readOnly, title: "Compute Client Net Position" }, async ({ client_id, account_ids }) => {
97
92
  const ids = account_ids
98
93
  ? account_ids.split(",").map(s => parseInt(s.trim()))
99
94
  : [2110, 2310, 1210]; // short-term loans, accounts payable, accounts receivable
@@ -136,11 +136,10 @@ export function registerBankReconciliationTools(server, api) {
136
136
  }],
137
137
  };
138
138
  });
139
- server.tool("auto_confirm_exact_matches", "Automatically confirm bank transactions that have a single high-confidence match (>=90). " +
140
- "DRY RUN by default - set execute=true to actually confirm.", {
139
+ server.tool("auto_confirm_exact_matches", "Batch-confirm bank transactions with a single high-confidence match (>=90). DRY RUN by default — set execute=true to confirm.", {
141
140
  execute: z.boolean().optional().describe("Actually confirm transactions (default false = dry run)"),
142
141
  min_confidence: z.number().optional().describe("Minimum confidence (default 90)"),
143
- }, { ...batch, title: "Auto-Confirm Matches" }, async ({ execute, min_confidence }) => {
142
+ }, { ...batch, title: "Auto-Confirm Bank Matches" }, async ({ execute, min_confidence }) => {
144
143
  const threshold = min_confidence ?? 90;
145
144
  const dryRun = execute !== true;
146
145
  // Get all unconfirmed transactions across pages
@@ -126,7 +126,7 @@ export function registerCrudTools(server, api) {
126
126
  const result = await api.clients.deactivate(id);
127
127
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
128
128
  });
129
- server.tool("restore_client", "Reactivate a deleted client", idParam.shape, { ...mutate, title: "Restore Client" }, async ({ id }) => {
129
+ server.tool("restore_client", "Reactivate a deactivated client", idParam.shape, { ...mutate, title: "Restore Client" }, async ({ id }) => {
130
130
  const result = await api.clients.restore(id);
131
131
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
132
132
  });
@@ -136,9 +136,9 @@ export function registerCrudTools(server, api) {
136
136
  const results = await api.clients.findByName(name);
137
137
  return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }] };
138
138
  });
139
- server.tool("find_client_by_code", "Find client by registry code", {
139
+ server.tool("find_client_by_code", "Find a client by business registry code or personal ID", {
140
140
  code: z.string().describe("Business registry code or personal ID"),
141
- }, { ...readOnly, title: "Find Client by Code" }, async ({ code }) => {
141
+ }, { ...readOnly, title: "Find Client by Registry Code" }, async ({ code }) => {
142
142
  const result = await api.clients.findByCode(code);
143
143
  return { content: [{ type: "text", text: result ? JSON.stringify(result, null, 2) : "Not found" }] };
144
144
  });
@@ -175,7 +175,7 @@ export function registerCrudTools(server, api) {
175
175
  const result = await api.products.deactivate(id);
176
176
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
177
177
  });
178
- server.tool("restore_product", "Reactivate a deleted product", idParam.shape, { ...mutate, title: "Restore Product" }, async ({ id }) => {
178
+ server.tool("restore_product", "Reactivate a deactivated product", idParam.shape, { ...mutate, title: "Restore Product" }, async ({ id }) => {
179
179
  const result = await api.products.restore(id);
180
180
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
181
181
  });
@@ -252,7 +252,7 @@ export function registerCrudTools(server, api) {
252
252
  });
253
253
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
254
254
  });
255
- server.tool("confirm_transaction", "Confirm a transaction with distribution rows", {
255
+ server.tool("confirm_transaction", "Confirm a bank transaction by providing distribution rows", {
256
256
  id: z.number().describe("Transaction ID"),
257
257
  distributions: z.string().optional().describe("JSON array of distribution rows: [{related_table, related_id?, amount}]"),
258
258
  }, { ...destructive, title: "Confirm Transaction" }, async ({ id, distributions }) => {
@@ -319,7 +319,7 @@ export function registerCrudTools(server, api) {
319
319
  const result = await api.saleInvoices.confirm(id);
320
320
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
321
321
  });
322
- server.tool("get_sale_invoice_delivery_options", "Get delivery options for a sales invoice", idParam.shape, { ...readOnly, title: "Get Delivery Options" }, async ({ id }) => {
322
+ server.tool("get_sale_invoice_delivery_options", "Get available delivery methods for a sales invoice (e-invoice or email)", idParam.shape, { ...readOnly, title: "Get Sale Invoice Delivery Options" }, async ({ id }) => {
323
323
  const result = await api.saleInvoices.getDeliveryOptions(id);
324
324
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
325
325
  });
@@ -349,10 +349,7 @@ export function registerCrudTools(server, api) {
349
349
  const result = await api.purchaseInvoices.get(id);
350
350
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
351
351
  });
352
- server.tool("create_purchase_invoice", "Create a purchase invoice. Pass the EXACT vat_price and gross_price from the original invoice " +
353
- "to ensure amounts match for payment reconciliation. " +
354
- "Items require cl_purchase_articles_id (use list_purchase_articles). " +
355
- "cl_fringe_benefits_id defaults to 1 (not a fringe benefit).", {
352
+ server.tool("create_purchase_invoice", "Create a draft purchase invoice with line items. Requires cl_purchase_articles_id (use list_purchase_articles). Pass EXACT vat_price and gross_price from the original invoice.", {
356
353
  clients_id: z.number().describe("Supplier client ID"),
357
354
  client_name: z.string().describe("Supplier name"),
358
355
  number: z.string().describe("Invoice number"),
@@ -399,13 +396,12 @@ export function registerCrudTools(server, api) {
399
396
  const result = await api.purchaseInvoices.delete(id);
400
397
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
401
398
  });
402
- server.tool("confirm_purchase_invoice", "Confirm a purchase invoice. IRREVERSIBLE locks the invoice for editing. " +
403
- "Automatically fixes vat_price/gross_price if they are missing or inconsistent with the item totals.", idParam.shape, { ...destructive, title: "Confirm Purchase Invoice" }, async ({ id }) => {
399
+ server.tool("confirm_purchase_invoice", "Confirm and lock a purchase invoice. Automatically fixes vat_price/gross_price if missing or inconsistent with item totals.", idParam.shape, { ...destructive, title: "Confirm Purchase Invoice" }, async ({ id }) => {
404
400
  const isVatReg = await isCompanyVatRegistered(api);
405
401
  const result = await api.purchaseInvoices.confirmWithTotals(id, isVatReg);
406
402
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
407
403
  });
408
- server.tool("invalidate_purchase_invoice", "Invalidate (reverse) a confirmed purchase invoice. Returns it to PROJECT status for editing.", idParam.shape, { ...mutate, title: "Invalidate Purchase Invoice" }, async ({ id }) => {
404
+ server.tool("invalidate_purchase_invoice", "Return a confirmed purchase invoice to draft status for editing.", idParam.shape, { ...mutate, title: "Invalidate Purchase Invoice" }, async ({ id }) => {
409
405
  const result = await api.purchaseInvoices.invalidate(id);
410
406
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
411
407
  });
@@ -82,11 +82,11 @@ export function registerDocumentAuditTools(server, api) {
82
82
  }],
83
83
  };
84
84
  });
85
- server.tool("detect_duplicate_purchase_invoice", "Check for potential duplicate purchase invoices based on supplier, invoice number, amount, and date.", {
85
+ server.tool("detect_duplicate_purchase_invoice", "Check for duplicate purchase invoices by supplier + invoice number, and by supplier + amount + date.", {
86
86
  clients_id: z.number().optional().describe("Filter by supplier ID"),
87
87
  date_from: z.string().optional().describe("Start date"),
88
88
  date_to: z.string().optional().describe("End date"),
89
- }, { ...readOnly, title: "Detect Duplicate Invoices" }, async ({ clients_id, date_from, date_to }) => {
89
+ }, { ...readOnly, title: "Detect Duplicate Purchase Invoices" }, async ({ clients_id, date_from, date_to }) => {
90
90
  const allPurchases = await api.purchaseInvoices.listAll();
91
91
  const filtered = allPurchases.filter((inv) => {
92
92
  if (inv.status === "DELETED" || inv.status === "INVALIDATED")
@@ -48,10 +48,7 @@ async function computeRetainedEarningsBalance(api, accountId, asOfDate) {
48
48
  return roundMoney(credit - debit);
49
49
  }
50
50
  export function registerEstonianTaxTools(server, api) {
51
- server.tool("prepare_dividend_package", "Calculate dividend distribution and create draft journal entries. " +
52
- "Estonian CIT on dividends: 22/78 (from 2025). " +
53
- "Creates a debit to retained earnings and credit to payable + tax liability. " +
54
- "Validates accounts exist and checks retained earnings balance before posting.", {
51
+ server.tool("prepare_dividend_package", "Calculate dividend tax (22/78 CIT) and create draft journal entries for dividend payable and tax liability. Validates retained earnings balance and net assets.", {
55
52
  net_dividend: z.number().describe("Net dividend amount to shareholder (EUR)"),
56
53
  shareholder_client_id: z.number().describe("Shareholder client ID"),
57
54
  effective_date: z.string().describe("Distribution date (YYYY-MM-DD)"),
@@ -60,7 +57,7 @@ export function registerEstonianTaxTools(server, api) {
60
57
  tax_payable_account: z.number().optional().describe("CIT payable account (default 2540)"),
61
58
  share_capital_account: z.number().optional().describe("Share capital account for ÄS §157 net-assets check (default 3000)"),
62
59
  force: z.boolean().optional().describe("Create journal even if retained earnings are insufficient (default false)"),
63
- }, { ...create, title: "Prepare Dividend Package" }, async ({ net_dividend, shareholder_client_id, effective_date, retained_earnings_account, dividend_payable_account, tax_payable_account, share_capital_account, force }) => {
60
+ }, { ...create, title: "Prepare Dividend Distribution" }, async ({ net_dividend, shareholder_client_id, effective_date, retained_earnings_account, dividend_payable_account, tax_payable_account, share_capital_account, force }) => {
64
61
  const retainedAccount = retained_earnings_account ?? 3020;
65
62
  const payableAccount = dividend_payable_account ?? 2370;
66
63
  const taxAccount = tax_payable_account ?? 2540;
@@ -172,9 +169,7 @@ export function registerEstonianTaxTools(server, api) {
172
169
  }],
173
170
  };
174
171
  });
175
- server.tool("create_owner_expense_reimbursement", "Book an owner-paid business expense: expense account + VAT + payable to owner. " +
176
- "Common for micro-OÜs where the owner pays with personal funds. " +
177
- "Books input VAT separately only for VAT-registered companies and validates accounts in chart of accounts.", {
172
+ server.tool("create_owner_expense_reimbursement", "Create a journal for a business expense paid personally by the owner. Splits input VAT for VAT-registered companies.", {
178
173
  owner_client_id: z.number().describe("Owner/shareholder client ID"),
179
174
  effective_date: z.string().describe("Expense date (YYYY-MM-DD)"),
180
175
  description: z.string().describe("Expense description"),
@@ -185,7 +180,7 @@ export function registerEstonianTaxTools(server, api) {
185
180
  vat_account: z.number().optional().describe("Input VAT account (default 1510)"),
186
181
  payable_account: z.number().optional().describe("Payable to owner account (default 2110)"),
187
182
  document_number: z.string().optional().describe("Receipt/document number"),
188
- }, { ...create, title: "Book Owner Expense" }, async ({ owner_client_id, effective_date, description, net_amount, vat_rate, vat_amount, expense_account, vat_account, payable_account, document_number }) => {
183
+ }, { ...create, title: "Book Owner-Paid Expense" }, async ({ owner_client_id, effective_date, description, net_amount, vat_rate, vat_amount, expense_account, vat_account, payable_account, document_number }) => {
189
184
  const vatRegistered = await isCompanyVatRegistered(api);
190
185
  const vatAcc = vat_account ?? 1510;
191
186
  const payAcc = payable_account ?? 2110;
@@ -305,7 +305,7 @@ export function registerLightyearTools(server, api) {
305
305
  "distributions, deposits, withdrawals. Filters out BRICEKSP money market fund trades. " +
306
306
  "Pairs foreign currency trades with their FX conversion entries.", {
307
307
  file_path: z.string().describe("Absolute path to Lightyear AccountStatement CSV file"),
308
- }, { ...readOnly, title: "Parse Lightyear Statement" }, async ({ file_path }) => {
308
+ }, { ...readOnly, title: "Parse Lightyear Account Statement" }, async ({ file_path }) => {
309
309
  const csv = await readCsvFile(file_path);
310
310
  const rows = parseAccountStatement(csv);
311
311
  const { trades, warnings: fxWarnings } = extractTrades(rows);
@@ -378,7 +378,7 @@ export function registerLightyearTools(server, api) {
378
378
  server.tool("parse_lightyear_capital_gains", "Parse a Lightyear Capital Gains Statement CSV (FIFO method). " +
379
379
  "Shows cost basis, proceeds, and realized capital gains per sale.", {
380
380
  file_path: z.string().describe("Absolute path to Lightyear CapitalGainsStatement CSV file"),
381
- }, { ...readOnly, title: "Parse Capital Gains" }, async ({ file_path }) => {
381
+ }, { ...readOnly, title: "Parse Lightyear Capital Gains" }, async ({ file_path }) => {
382
382
  const csv = await readCsvFile(file_path);
383
383
  const gains = parseCapitalGains(csv);
384
384
  const totalGains = gains.reduce((s, g) => s + g.capital_gains_eur, 0);
@@ -661,11 +661,7 @@ export function registerLightyearTools(server, api) {
661
661
  }],
662
662
  };
663
663
  });
664
- server.tool("book_lightyear_distributions", "Create journal entries for Lightyear dividend/interest distributions. " +
665
- "Checks for duplicates using reference IDs. " +
666
- "Books: Debit broker account (net received), Credit income account. " +
667
- "Income = gross (net + tax + fee). " +
668
- "Withheld tax (tax_amount) booked to tax_account. Platform fee booked to fee_account (default 8610).", {
664
+ server.tool("book_lightyear_distributions", "Create journal entries for Lightyear dividend and interest distributions, including withheld tax. DRY RUN by default.", {
669
665
  file_path: z.string().describe("Absolute path to Lightyear AccountStatement CSV file"),
670
666
  broker_account: z.number().describe("Broker cash account (e.g. 1120 Lightyear konto)"),
671
667
  broker_dimension_id: z.number().optional().describe("Dimension ID for broker account (accounts_dimensions_id)"),
@@ -789,9 +785,7 @@ export function registerLightyearTools(server, api) {
789
785
  }],
790
786
  };
791
787
  });
792
- server.tool("lightyear_portfolio_summary", "Compute current portfolio holdings and cost basis from Lightyear account statement. " +
793
- "Uses weighted average cost method. Properly reduces cost basis on sells. " +
794
- "Useful for verifying investment account balance.", {
788
+ server.tool("lightyear_portfolio_summary", "Compute current holdings and cost basis from a Lightyear account statement. Useful for verifying investment account balance.", {
795
789
  file_path: z.string().describe("Absolute path to Lightyear AccountStatement CSV file"),
796
790
  }, { ...readOnly, title: "Lightyear Portfolio Summary" }, async ({ file_path }) => {
797
791
  const csv = await readCsvFile(file_path);
@@ -46,12 +46,9 @@ function extractPdfHints(text) {
46
46
  return result;
47
47
  }
48
48
  export function registerPdfWorkflowTools(server, api) {
49
- server.tool("extract_pdf_invoice", "Extract text and machine-readable identifiers from a PDF invoice. " +
50
- "Returns raw text + detected IBAN, registry code, VAT number, reference number. " +
51
- "YOU must read the raw_text and extract: supplier name, invoice number, dates, " +
52
- "amounts (net, VAT, gross), and line items. Then call validate_invoice_data to verify.", {
49
+ server.tool("extract_pdf_invoice", "Extract text and key identifiers from a supplier invoice PDF. Returns raw text + detected IBAN, registry code, VAT number, reference number. Read raw_text to extract all invoice fields, then call validate_invoice_data.", {
53
50
  file_path: z.string().describe("Absolute path to the PDF file"),
54
- }, { ...readOnly, title: "Extract PDF Invoice" }, async ({ file_path }) => {
51
+ }, { ...readOnly, title: "Extract Supplier Invoice PDF" }, async ({ file_path }) => {
55
52
  const resolved = await validatePdfPath(file_path);
56
53
  const buffer = await readFile(resolved);
57
54
  const pdfData = await pdf(buffer);
@@ -187,9 +184,7 @@ export function registerPdfWorkflowTools(server, api) {
187
184
  }],
188
185
  };
189
186
  });
190
- server.tool("resolve_supplier", "Find or create a supplier in e-arveldaja. First searches by registry code, " +
191
- "then VAT number, then name (fuzzy). If not found, optionally creates a new client. " +
192
- "Also looks up business registry (äriregister) data if available.", {
187
+ server.tool("resolve_supplier", "Match a supplier to an existing client by registry code, VAT number, or name (fuzzy). Optionally creates a new client. Looks up Estonian business registry data.", {
193
188
  name: z.string().optional().describe("Supplier name from invoice"),
194
189
  reg_code: z.string().optional().describe("Registry code (registrikood)"),
195
190
  vat_no: z.string().optional().describe("VAT number (KMKR)"),
@@ -197,7 +192,7 @@ export function registerPdfWorkflowTools(server, api) {
197
192
  auto_create: z.boolean().optional().describe("Create client if not found (default false)"),
198
193
  country: z.string().optional().describe("Country code for auto-create (default EST)"),
199
194
  is_physical_entity: z.boolean().optional().describe("Natural person (default false = legal entity)"),
200
- }, { ...create, title: "Resolve Supplier" }, async ({ name, reg_code, vat_no, iban, auto_create, country, is_physical_entity }) => {
195
+ }, { ...create, title: "Find or Create Supplier" }, async ({ name, reg_code, vat_no, iban, auto_create, country, is_physical_entity }) => {
201
196
  // 1. Search by registry code
202
197
  if (reg_code) {
203
198
  const byCode = await api.clients.findByCode(reg_code);
@@ -308,12 +303,11 @@ export function registerPdfWorkflowTools(server, api) {
308
303
  }],
309
304
  };
310
305
  });
311
- server.tool("suggest_booking", "Find similar past purchase invoices from the same supplier to suggest " +
312
- "how to book a new invoice (which accounts, articles, etc).", {
306
+ server.tool("suggest_booking", "Suggest purchase articles and accounts for a new invoice based on similar confirmed invoices from the same supplier.", {
313
307
  clients_id: z.number().describe("Supplier client ID"),
314
308
  description: z.string().optional().describe("Invoice item description to match"),
315
309
  limit: z.number().optional().describe("Max past invoices to return (default 3)"),
316
- }, { ...readOnly, title: "Suggest Booking" }, async ({ clients_id, description, limit }) => {
310
+ }, { ...readOnly, title: "Suggest Purchase Booking" }, async ({ clients_id, description, limit }) => {
317
311
  const maxResults = limit ?? 3;
318
312
  const allInvoices = await api.purchaseInvoices.listAll();
319
313
  // Filter by supplier
@@ -364,9 +358,7 @@ export function registerPdfWorkflowTools(server, api) {
364
358
  }],
365
359
  };
366
360
  });
367
- server.tool("create_purchase_invoice_from_pdf", "Full workflow: create a purchase invoice from extracted PDF data. " +
368
- "Resolves supplier, suggests booking, creates the invoice as DRAFT. " +
369
- "Pass EXACT vat_price and gross_price from the original invoice for payment matching.", {
361
+ server.tool("create_purchase_invoice_from_pdf", "Create a draft purchase invoice from extracted and validated PDF data. Pass EXACT vat_price and gross_price from the original invoice for payment matching.", {
370
362
  supplier_client_id: z.number().describe("Supplier client ID (from resolve_supplier)"),
371
363
  invoice_number: z.string().describe("Invoice number"),
372
364
  invoice_date: z.string().describe("Invoice date (YYYY-MM-DD)"),
@@ -379,7 +371,7 @@ export function registerPdfWorkflowTools(server, api) {
379
371
  notes: z.string().optional().describe("Notes (e.g. PDF filename)"),
380
372
  ref_number: z.string().optional().describe("Reference number"),
381
373
  bank_account_no: z.string().optional().describe("Supplier bank account"),
382
- }, { ...create, title: "Create Invoice from PDF" }, async (params) => {
374
+ }, { ...create, title: "Create Purchase Invoice from PDF" }, async (params) => {
383
375
  const supplier = await api.clients.get(params.supplier_client_id);
384
376
  const isVatReg = await isCompanyVatRegistered(api);
385
377
  const purchaseArticles = await getPurchaseArticlesWithVat(api);
@@ -413,7 +405,7 @@ export function registerPdfWorkflowTools(server, api) {
413
405
  server.tool("upload_invoice_document", "Upload a PDF document to an existing purchase invoice", {
414
406
  invoice_id: z.number().describe("Purchase invoice ID"),
415
407
  file_path: z.string().describe("Absolute path to the PDF file"),
416
- }, { ...mutate, title: "Upload Invoice Document" }, async ({ invoice_id, file_path }) => {
408
+ }, { ...mutate, title: "Upload Purchase Invoice PDF" }, async ({ invoice_id, file_path }) => {
417
409
  const resolved = await validatePdfPath(file_path);
418
410
  const buffer = await readFile(resolved);
419
411
  const base64 = buffer.toString("base64");
@@ -9,7 +9,7 @@ export function registerRecurringInvoiceTools(server, api) {
9
9
  target_journal_date: z.string().describe("New turnover date (YYYY-MM-DD)"),
10
10
  invoice_ids: z.string().optional().describe("Comma-separated source invoice IDs to copy (default: all confirmed from source month)"),
11
11
  auto_confirm: z.boolean().optional().describe("Confirm created invoices (default false)"),
12
- }, { ...batch, title: "Create Recurring Invoices" }, async ({ source_month, target_date, target_journal_date, invoice_ids, auto_confirm }) => {
12
+ }, { ...batch, title: "Create Recurring Sale Invoices" }, async ({ source_month, target_date, target_journal_date, invoice_ids, auto_confirm }) => {
13
13
  // Get source invoices
14
14
  const allSales = await api.saleInvoices.listAll();
15
15
  const sourceFrom = `${source_month}-01`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "e-arveldaja-mcp",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "MCP server for Estonian e-arveldaja (e-Financials) API",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",