From 32cb8a22ced30ccba4afe1fdb1675d7834946b48 Mon Sep 17 00:00:00 2001 From: Mattsson <111893710+mattssonn@users.noreply.github.com> Date: Sun, 30 Aug 2026 16:55:09 +0200 Subject: [PATCH] feat(mcp): Swedish keyword synonyms in tool search index (#2043) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gnubok_search_tools matched only English tool names and descriptions, so Swedish queries like "bankkonto" or "lönekörning" returned zero hits. Adds an optional keywords field on tool definitions, folded into the search matcher (name + description + keywords, case-insensitive, same ranking family as name matches) and seeds Swedish domain terms on 152 tools. Keywords are matcher-side only: they are never serialized into tools/list or search_tools output, since that payload budget has zero headroom. Guarded by new tests in search-tools.test.ts and lazy-auth.test.ts. Claude-Session: https://claude.ai/code/session_018zGah8Yy49esAwpnKGxiGy Co-authored-by: Claude Fable 5 --- .../mcp-server/__tests__/lazy-auth.test.ts | 13 ++ .../mcp-server/__tests__/search-tools.test.ts | 96 ++++++++++ extensions/general/mcp-server/server.ts | 177 +++++++++++++++++- 3 files changed, 280 insertions(+), 6 deletions(-) diff --git a/extensions/general/mcp-server/__tests__/lazy-auth.test.ts b/extensions/general/mcp-server/__tests__/lazy-auth.test.ts index ac79d9ca..0872d86d 100644 --- a/extensions/general/mcp-server/__tests__/lazy-auth.test.ts +++ b/extensions/general/mcp-server/__tests__/lazy-auth.test.ts @@ -102,6 +102,19 @@ describe('MCP lazy authentication', () => { expect(names.length).toBeGreaterThan(20) }) + it('tools/list never serializes matcher-side search keywords', async () => { + // Swedish search synonyms live on the tool definitions for + // gnubok_search_tools matching only; the tools/list payload budget has + // zero headroom, so they must never reach the wire. + const response = await handleMcpRequest(rpc('tools/list')) + expect(response.status).toBe(200) + const body = await response.json() + const raw = JSON.stringify(body.result.tools) + expect(raw).not.toContain('"keywords"') + expect(raw).not.toContain('bankkonto') + expect(raw).not.toContain('lönekörning') + }) + it('runs a public documentation tool without a token', async () => { const response = await handleMcpRequest( rpc('tools/call', { name: 'gnubok_list_skills', arguments: {} }) diff --git a/extensions/general/mcp-server/__tests__/search-tools.test.ts b/extensions/general/mcp-server/__tests__/search-tools.test.ts index 0151f354..e96d38fe 100644 --- a/extensions/general/mcp-server/__tests__/search-tools.test.ts +++ b/extensions/general/mcp-server/__tests__/search-tools.test.ts @@ -156,6 +156,102 @@ describe('gnubok_search_tools', () => { } }) + // Swedish keyword synonyms: matcher-side `keywords` on tool definitions let + // Swedish queries hit the right tools without inflating any payload. + describe('Swedish keyword synonyms', () => { + it('"bankkonto" ranks list_cash_accounts first; "kassakonto" finds it too', async () => { + const bank = await call({ query: 'bankkonto', limit: 10 }) + expect(bank.tools.length).toBeGreaterThan(0) + expect(bank.tools[0].name).toBe('gnubok_list_cash_accounts') + + const kassa = await call({ query: 'kassakonto', limit: 10 }) + expect(kassa.tools.map((t) => t.name)).toContain('gnubok_list_cash_accounts') + }) + + it('"kundfaktura" ranks list_invoices first and includes create_invoice', async () => { + const result = await call({ query: 'kundfaktura', limit: 50 }) + expect(result.tools[0].name).toBe('gnubok_list_invoices') + expect(result.tools.map((t) => t.name)).toContain('gnubok_create_invoice') + }) + + it('"leverantörsfaktura" ranks list_supplier_invoices first', async () => { + const result = await call({ query: 'leverantörsfaktura', limit: 50 }) + expect(result.tools[0].name).toBe('gnubok_list_supplier_invoices') + const names = result.tools.map((t) => t.name) + expect(names).toContain('gnubok_approve_supplier_invoice') + expect(names).toContain('gnubok_get_supplier_ledger') + }) + + it('"lönekörning" finds the salary run tools', async () => { + const result = await call({ query: 'lönekörning', limit: 50 }) + const names = result.tools.map((t) => t.name) + expect(names).toContain('gnubok_create_salary_run') + expect(names).toContain('gnubok_calculate_salary_run') + expect(names).toContain('gnubok_book_salary_run') + }) + + it('"verifikat" finds voucher and journal tools', async () => { + const result = await call({ query: 'verifikat', limit: 50 }) + const names = result.tools.map((t) => t.name) + expect(names).toContain('gnubok_create_voucher') + expect(names).toContain('gnubok_query_journal') + }) + + it('"momsdeklaration" finds the VAT tools', async () => { + const result = await call({ query: 'momsdeklaration', limit: 50 }) + const names = result.tools.map((t) => t.name) + expect(names).toContain('gnubok_get_vat_report') + expect(names).toContain('gnubok_vat_declaration_submit') + }) + + it('"kontoplan" ranks list_accounts first', async () => { + const result = await call({ query: 'kontoplan', limit: 10 }) + expect(result.tools[0].name).toBe('gnubok_list_accounts') + }) + + it('"avstämning" and "skattekonto" rank reconciliation status first', async () => { + const avstamning = await call({ query: 'avstämning', limit: 50 }) + expect(avstamning.tools[0].name).toBe('gnubok_get_reconciliation_status') + expect(avstamning.tools.map((t) => t.name)).toContain('gnubok_reconcile_match') + + const skattekonto = await call({ query: 'skattekonto', limit: 50 }) + expect(skattekonto.tools[0].name).toBe('gnubok_get_reconciliation_status') + }) + + it('"anställd" ranks list_employees first', async () => { + const result = await call({ query: 'anställd', limit: 50 }) + expect(result.tools[0].name).toBe('gnubok_list_employees') + expect(result.tools.map((t) => t.name)).toContain('gnubok_create_employee') + }) + + it('"kvitto" and "underlag" find document and inbox tools', async () => { + const kvitto = await call({ query: 'kvitto', limit: 50 }) + const kvittoNames = kvitto.tools.map((t) => t.name) + expect(kvittoNames).toContain('gnubok_list_inbox_items') + expect(kvittoNames).toContain('gnubok_upload_document') + + const underlag = await call({ query: 'underlag', limit: 50 }) + expect(underlag.tools.map((t) => t.name)).toContain('gnubok_list_unmatched_documents') + }) + + it('"påminnelse" finds list_invoices (chasing overdue invoices)', async () => { + const result = await call({ query: 'påminnelse', limit: 10 }) + expect(result.tools.map((t) => t.name)).toContain('gnubok_list_invoices') + }) + + it('keywords are matcher-side only: never serialized into search results', async () => { + // A keyword-only match proves the keywords were consulted; the payload + // must still not carry them at any detail level. + for (const detail of ['name', 'summary', 'full'] as const) { + const result = await call({ query: 'bankkonto', detail, limit: 5 }) + expect(result.tools.length).toBeGreaterThan(0) + const raw = JSON.stringify(result) + expect(raw).not.toContain('"keywords"') + expect(raw).not.toContain('kassakonto') + } + }) + }) + it('fail-closed: explicitly empty __keyScopes also hides scoped tools', async () => { // The "scopes were checked, granted set is empty" case must behave the same // as "scopes were not injected at all". Both indicate no scoped access. diff --git a/extensions/general/mcp-server/server.ts b/extensions/general/mcp-server/server.ts index 0f87349f..f3d37512 100644 --- a/extensions/general/mcp-server/server.ts +++ b/extensions/general/mcp-server/server.ts @@ -468,6 +468,12 @@ interface McpTool { annotations: McpToolAnnotations /** Wide or specialized tools discoverable through gnubok_search_tools only. */ catalogVisibility?: 'default' | 'search' + // Matcher-side search synonyms (mainly Swedish domain terms like + // "verifikat", "kundfaktura", "avstämning") folded into gnubok_search_tools + // matching only. NEVER serialized: keywords must not appear in tools/list + // or search_tools output; the tools/list payload budget has zero headroom. + // Store lowercase. + keywords?: string[] _meta?: { ui: { resourceUri: string } } // Result-level UI hint: when set, a call passing render_ui=true gets a // _meta.ui.resourceUri on the RESULT, so the host renders the widget only when @@ -3045,24 +3051,31 @@ export const tools: McpTool[] = [ }) if (query) { - // Match: every whitespace-separated term must appear in name or description - // (for a single-word query this is identical to a literal substring match). - // Rank by relevance so the most on-point tool comes first instead of - // whichever happens to be defined earliest: exact-ish name match > full - // query as a name substring > per-term name hits > description hits. Ties + // Match: every whitespace-separated term must appear in name, description + // or keywords (for a single-word query this is identical to a literal + // substring match). Keywords are matcher-side synonyms (mainly Swedish + // domain terms) declared on the tool definition; they are searchable but + // never serialized into any payload. Rank by relevance so the most + // on-point tool comes first instead of whichever happens to be defined + // earliest: exact-ish name match > full query as a name substring or + // exact keyword > per-term name/keyword hits > description hits. Ties // fall back to definition order (stable). const terms = query.split(/\s+/).filter(Boolean) const ranked = candidates .map((t, idx) => { const name = t.name.toLowerCase() const desc = t.description.toLowerCase() - const hay = `${name} ${desc}` + const kw = (t.keywords ?? []).map((k) => k.toLowerCase()) + const kwText = kw.join(' ') + const hay = `${name} ${desc} ${kwText}` if (!terms.every((term) => hay.includes(term))) return null let score = 0 if (name === query || name === `gnubok_${query}` || name.endsWith(`_${query}`)) score += 100 if (name.includes(query)) score += 40 + if (kw.includes(query)) score += 40 for (const term of terms) { if (name.includes(term)) score += 10 + if (kwText.includes(term)) score += 10 if (desc.includes(term)) score += 1 } return { t, score, idx } @@ -3120,6 +3133,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_list_companies', + keywords: ['företag', 'bolag', 'mina företag'], title: 'List Companies', description: 'List every non-archived company this API-key user can access. Use company_id from this result on other tools; omit it there to use the API key default.', inputSchema: { @@ -3223,6 +3237,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_lookup_company', + keywords: ['organisationsnummer', 'företag', 'bolag'], title: 'Look Up Company', description: 'Look up a Swedish company by organisationsnummer in the public registry (name, address, F-skatt, VAT, legal form, fiscal year). Call FIRST in onboarding: the user confirms facts instead of answering questions. Feeds gnubok_create_company; works before any company exists.', @@ -3410,6 +3425,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_create_company', + keywords: ['företag', 'bolag', 'nytt företag'], title: 'Create Company', description: 'Create a NEW company, set up for bookkeeping (chart, settings, first fiscal period, tax deadlines; 30-day trial). Ask ONLY orgnr + moms period: gnubok_lookup_company prefills the rest, accounting_method defaults by form. Preview (no confirm), read back, then confirm=true.', @@ -3552,6 +3568,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_connect_bank', + keywords: ['bankkoppling', 'koppla bank', 'banksynk'], title: 'Connect Bank', description: 'Bank connection status plus the browser connect link (PSD2, BankID; user must be logged in to Accounted). When the user has NAMED their bank, pass it as bank on the FIRST call (a bare call renders a redundant generic card): the link then starts that bank\'s consent directly.', @@ -3620,6 +3637,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_connect_skatteverket', + keywords: ['skatteverket', 'koppla skatteverket', 'deklarationsombud'], title: 'Connect Skatteverket', description: 'Skatteverket connection status plus the browser link where the user authorises Accounted (BankID as firmatecknare; logged in to Accounted there). Enables skattekonto sync and filing of moms/AGI. Use after gnubok_create_company or when a filing tool reports no connection.', @@ -3675,6 +3693,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_connect_migration', + keywords: ['migrering', 'byta system', 'flytta bokföring'], title: 'Connect Previous System', description: 'Connect card into the migration wizard for a NAMED previous system. API systems (fortnox/bjornlunden/briox/wint) fetch all fiscal years plus invoices, customers and documents; visma/bokio complement AFTER a SIE import. Same one-click feel as the bank/Skatteverket cards.', @@ -3741,6 +3760,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_get_company_settings', + keywords: ['inställningar', 'företagsinställningar', 'momsperiod'], title: 'Get Company Settings', description: 'Get invoice payment details, company contact details and the custom invoice email texts. Use before creating invoices or staging a settings update.', inputSchema: { @@ -3848,6 +3868,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_update_company_settings', + keywords: ['inställningar', 'företagsinställningar'], title: 'Update Company Settings', description: 'Stage changes to invoice payment details, company contact details or the custom invoice email texts. Requires approval before company settings are updated.', outputSchema: STAGED_OPERATION_SCHEMA, @@ -4887,6 +4908,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_create_transactions', + keywords: ['banktransaktion', 'kontohändelse', 'kontoutdrag'], title: 'Create Bank Transactions', description: 'Stage bank/cash-account transactions; each becomes a pending operation. For external rows (Airtable, CSV); max 10. A transaction models a cash-account movement: for cashless events (privat utlägg) use gnubok_create_voucher.', outputSchema: { @@ -5008,6 +5030,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_list_uncategorized_transactions', + keywords: ['okategoriserade', 'banktransaktioner', 'kontohändelser', 'att bokföra'], title: 'List Uncategorized Transactions', description: 'List bank transactions with no journal entry yet, newest first. Paginated. cash_account_id narrows to one bank account.', inputSchema: { @@ -5122,6 +5145,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_list_transactions_without_documents', + keywords: ['underlag', 'kvitto', 'saknar underlag'], title: 'List Transactions Missing Receipts', description: 'List booked bank transactions whose verifikat lacks an underlag. Strict subset of gnubok_list_verifikat_without_documents (same document truth, waivers respected): use that tool for full coverage incl. imported/manual verifikat.', inputSchema: { @@ -5202,6 +5226,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_list_verifikat_without_documents', + keywords: ['verifikat', 'verifikation', 'saknar underlag'], title: 'List Verifikat Missing Documents', description: 'List posted verifikat that genuinely lack an underlag: needs-doc source types only, current document versions, user waivers respected. Superset of gnubok_list_transactions_without_documents (covers imported/manual too). Newest first, paginated.', inputSchema: { @@ -5280,6 +5305,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_categorize_transaction', + keywords: ['bokför', 'kontera', 'kategorisera', 'banktransaktion'], title: 'Categorize Bank Transaction', description: 'Categorize a bank transaction. Stages the verifikat: cost line NET of moms, bank line gross (always the tx\'s cash account). account_override books the business side on any active account. Cashless events (privat utlägg): gnubok_create_voucher.', inputSchema: { @@ -5476,6 +5502,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_receipt_matcher', + keywords: ['kvitto', 'underlag', 'matcha kvitton'], title: 'Receipt Matcher Widget', description: 'Open an interactive widget for drag-and-drop receipt-to-transaction matching. Renders inline in compatible clients.', inputSchema: { @@ -5529,6 +5556,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_list_customers', + keywords: ['kund', 'kunder', 'kundregister'], title: 'List Customers', description: 'List active customers. Use to look up customer_id for invoice creation. include_archived=true adds archived rows.', inputSchema: { @@ -5605,6 +5633,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_create_customer', + keywords: ['kund', 'ny kund'], title: 'Create Customer', description: 'Stage a new customer. Stages for user approval: NOT created until approved in the web app. EU VAT numbers trigger VIES validation.', outputSchema: STAGED_OPERATION_SCHEMA, @@ -5759,6 +5788,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_update_customer', + keywords: ['kund', 'ändra kund'], title: 'Update Customer', description: 'Stage a partial update to an existing customer. Find customer_id with gnubok_list_customers. Requires approval before customer data is changed.', outputSchema: STAGED_OPERATION_SCHEMA, @@ -5948,6 +5978,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_list_articles', + keywords: ['artikel', 'artiklar', 'produkter', 'artikelregister'], title: 'List Articles', description: "List the active company's catalog articles (artikelregister). Use to look up an article to add to an invoice line. Active articles only by default.", inputSchema: { @@ -6006,6 +6037,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_create_article', + keywords: ['artikel', 'produkt', 'ny artikel'], title: 'Create Article', description: 'Stage a new catalog article (artikelregister). Stages for approval: not created until approved. Article number auto-assigned. Reuse on invoice lines via gnubok_create_invoice.', outputSchema: STAGED_OPERATION_SCHEMA, @@ -6079,6 +6111,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_update_article', + keywords: ['artikel', 'produkt'], title: 'Update Article', description: 'Stage an edit to a catalog article (price, name, account, etc.) or deactivate it via active:false. Stages for approval. Find article_id with gnubok_list_articles.', outputSchema: STAGED_OPERATION_SCHEMA, @@ -6141,6 +6174,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_list_invoices', + keywords: ['faktura', 'kundfaktura', 'fakturor', 'obetalda', 'förfallna', 'påminnelse'], title: 'List Customer Invoices', description: 'List invoices for the active company, newest first. Optional status filter.', inputSchema: { @@ -6215,6 +6249,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_get_invoice', + keywords: ['faktura', 'kundfaktura'], title: 'Get Invoice', description: 'One invoice: header plus every line with article_id, revenue_account, vat_rate and dimensions. Read it before gnubok_update_invoice (items are a FULL REPLACE) so each line goes back with its article linkage. editable_draft tells whether an edit is possible.', inputSchema: { @@ -6397,6 +6432,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_create_invoice', + keywords: ['faktura', 'kundfaktura', 'fakturera', 'ny faktura'], title: 'Create Customer Invoice', description: 'Stage a new invoice. Validates inputs, calculates VAT preview. Items accept dims bags. Approval creates a draft; the invoice number is assigned on send or mark-as-sent.', outputSchema: STAGED_OPERATION_SCHEMA, @@ -6634,6 +6670,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_get_trial_balance', + keywords: ['råbalans', 'saldobalans'], title: 'Trial Balance (Råbalans)', description: 'Trial balance (huvudbok) for a fiscal period: all account balances with debit/credit totals. Defaults to most recent period. Optional dimensions filter scopes to tagged lines (kostnadsställe/projekt).', inputSchema: { @@ -6749,6 +6786,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_get_vat_report', + keywords: ['moms', 'momsrapport', 'momsdeklaration', 'momsredovisning'], title: 'VAT Declaration (Momsdeklaration)', description: 'VAT declaration (momsdeklaration, SKV 4700) for a period. Returns all rutor; ruta49 = VAT to pay (positive) or refund (negative). Pass render_ui=true to also open the review widget (claude.ai / Desktop).', outputSchema: VAT_REPORT_OUTPUT_SCHEMA, @@ -6787,6 +6825,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_vat_review_widget', + keywords: ['moms', 'momsgranskning'], title: 'VAT Review Widget', description: 'Open the interactive VAT review widget for a period. Equivalent to gnubok_get_vat_report(render_ui=true); kept as an alias for clients pinned to this tool name.', inputSchema: { @@ -6814,6 +6853,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_vat_close_check', + keywords: ['moms', 'momsavstämning', 'momskontroll'], title: 'VAT Close Check (Momsdeklaration)', description: "Answer 'can I close VAT?' in one call. Returns SKV 4700 rutor, bookkeeping blockers (including unavailable deadlines), and declaration_checks from the same momsdeklaration completeness gate used by the web filing UI. ready_to_close covers both.", inputSchema: { @@ -6870,6 +6910,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_get_kpi_report', + keywords: ['nyckeltal'], title: 'Business KPI Report', description: 'Business KPIs for a fiscal period: gross margin, net result, cash position, receivables, expense ratio, payment days, VAT liability, monthly trend.', inputSchema: { @@ -6965,6 +7006,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_get_income_statement', + keywords: ['resultaträkning', 'resultatrapport'], title: 'Income Statement (Resultaträkning)', description: 'Income statement (resultaträkning) for a fiscal period or a from_date/to_date range inside it: revenue, expenses, net result. Optional dimensions filter (kostnadsställe/projekt).', inputSchema: { @@ -7046,6 +7088,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_mark_invoice_as_paid', + keywords: ['faktura betald', 'betalning', 'kundfaktura'], title: 'Mark Invoice as Paid', description: 'Mark an invoice as paid and create the payment journal entry. Stages for approval. Status must be sent or overdue.', inputSchema: { @@ -7137,6 +7180,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_send_invoice', + keywords: ['skicka faktura', 'kundfaktura', 'e-faktura', 'mejla faktura'], title: 'Send Invoice by Email', description: 'Send invoice via email with PDF attachment. Stages for approval. Requires customer email + email service configured.', inputSchema: { @@ -7197,6 +7241,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_get_invoice_deliveries', + keywords: ['fakturaleverans', 'studs', 'kundfaktura'], title: 'Get Invoice Delivery History', description: 'Email delivery attempts for one invoice with the provider outcome (delivered, bounced, complained, delayed, suppressed). Call before chasing an unpaid invoice: a bounce means the customer never received it. Recipients are masked, message content is never returned.', inputSchema: { @@ -7342,6 +7387,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_mark_invoice_as_sent', + keywords: ['faktura skickad', 'kundfaktura'], title: 'Mark Invoice as Sent', description: 'Mark a draft invoice as sent without sending email (when delivered manually). Stages for approval. Status must be draft.', inputSchema: { @@ -7396,6 +7442,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_list_suppliers', + keywords: ['leverantör', 'leverantörer', 'leverantörsregister'], title: 'List Suppliers (Leverantörer)', description: 'List active suppliers (leverantörer) with contact and payment details, sorted by name. include_archived=true adds archived rows.', inputSchema: { @@ -7446,6 +7493,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_create_supplier', + keywords: ['leverantör', 'ny leverantör'], title: 'Create Supplier (Leverantör)', description: 'Stage a new supplier (leverantör). Stages for user approval: NOT created until approved in the web app. Use to add a vendor before booking a supplier invoice or matching expenses.', outputSchema: STAGED_OPERATION_SCHEMA, @@ -7582,6 +7630,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_list_supplier_invoices', + keywords: ['leverantörsfaktura', 'leverantörsfakturor', 'inköpsfaktura'], title: 'List Supplier Invoices', description: 'List supplier invoices (leverantörsfakturor), sorted by due date. Filters: status (to_pay = approved+overdue), supplier_id, supplier_name (substring), date_from/date_to on invoice_date (YYYY-MM-DD).', inputSchema: { @@ -7747,6 +7796,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_get_counterparty_templates', + keywords: ['motpart', 'konteringsmall', 'mallar'], title: 'List Counterparty Templates', description: 'List active counterparty categorization templates: learned patterns from prior categorizations used for auto-matching new transactions.', inputSchema: { @@ -7796,6 +7846,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_suggest_categories', + keywords: ['konteringsförslag', 'kontering', 'kategorisera'], title: 'Suggest Transaction Categories', description: 'Suggest categories for uncategorized transactions using mapping rules, patterns, counterparty history and templates. Up to 20 per call. no_signal_transaction_ids = nothing matched; investigate via gnubok_query_journal instead of guessing.', inputSchema: { @@ -7921,6 +7972,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_list_accounts', + keywords: ['kontoplan', 'konton', 'baskonton'], title: 'List Chart of Accounts (Kontoplan)', description: 'List chart of accounts (kontoplan). account_class: 1=assets, 2=liabilities, 3=revenue, 4-7=expenses, 8=financial.', inputSchema: { @@ -7996,6 +8048,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_create_account', + keywords: ['kontoplan', 'nytt konto', 'baskonto'], title: 'Create Account (Kontoplan)', description: 'Stage a new kontoplan account. BAS 2026 numbers prefill name/type/SRU (overrides win); custom numbers need account_name, account_type, normal_balance. Inactive existing account? Use gnubok_update_account instead.', outputSchema: STAGED_OPERATION_SCHEMA, @@ -8128,6 +8181,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_update_account', + keywords: ['kontoplan', 'ändra konto', 'baskonto'], title: 'Update Account (Kontoplan)', description: 'Stage an edit to a kontoplan account: rename, description, default VAT, SRU code, or activate/deactivate via is_active. Stages for approval. Find accounts with gnubok_list_accounts.', outputSchema: STAGED_OPERATION_SCHEMA, @@ -8223,6 +8277,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_list_dimensions', + keywords: ['dimensioner', 'kostnadsställe', 'projekt', 'resultatenhet'], title: 'List Dimensions (Kostnadsställe/Projekt)', description: 'List the dimension registry with values: 1 = kostnadsställe, 6 = projekt, plus custom dims. Call before tagging voucher lines via the dimensions bag on gnubok_create_voucher. System dims are seeded on first call.', inputSchema: { @@ -8297,6 +8352,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_list_dimension_values', + keywords: ['dimensionsvärden', 'kostnadsställe', 'projekt'], title: 'List Dimension Values', description: 'List values (SIE #OBJEKT codes) for one dimension, optionally fuzzy-matched by query. Use to find the right kostnadsställe/projekt code before tagging lines. sie_dim_no: 1 = kostnadsställe, 6 = projekt.', inputSchema: { @@ -8422,6 +8478,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_create_dimension_value', + keywords: ['kostnadsställe', 'projekt', 'dimensionsvärde'], title: 'Create Dimension Value', description: 'Stage a new dimension value (kostnadsställe/projekt object code, SIE #OBJEKT) for user approval: agents never silently mint reporting values. Use when a dims-bag value has no registry match. sie_dim_no: 1 = kostnadsställe, 6 = projekt.', outputSchema: STAGED_OPERATION_SCHEMA, @@ -8542,6 +8599,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_tag_journal_lines', + keywords: ['kostnadsställe', 'projekt', 'tagga', 'dimension'], title: 'Tag Journal Lines (Bulk Retag)', description: "Bulk-tag POSTED journal lines with dimensions (kostnadsställe/projekt) selected by a filter block, e.g. all 4010 lines with 'Bygg AB' in 2024 → P01. Stages for approval; max 500 lines. Retags internal reporting only: the verifikat stays immutable, every change logged.", outputSchema: STAGED_OPERATION_SCHEMA, @@ -8864,6 +8922,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_get_dimension_pnl', + keywords: ['projektresultat', 'kostnadsställe', 'resultat per projekt'], title: 'P&L per Dimension (Resultat per projekt)', description: 'Resultat per projekt/kostnadsställe: P&L matrix over one SIE dimension: each value with activity becomes a column plus an untagged bucket, and the Totalt column reconciles exactly with the resultatrapport. sie_dim_no: 1 = kostnadsställe, 6 = projekt.', inputSchema: { @@ -8975,6 +9034,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_get_balance_sheet', + keywords: ['balansräkning', 'balansrapport'], title: 'Balance Sheet (Balansräkning)', description: 'Balance sheet (balansräkning) for a fiscal period or as of as_of_date: assets, equity, liabilities with totals + balance check.', inputSchema: { @@ -9035,6 +9095,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_get_general_ledger', + keywords: ['huvudbok', 'huvudboken'], title: 'General Ledger (Huvudbok)', description: 'General ledger (huvudbok) for a fiscal period: per-account opening, entries, closing balances. Optional account range + dimensions filters. For ad-hoc cross-account/amount/free-text queries use gnubok_query_journal.', inputSchema: { @@ -9096,6 +9157,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_query_journal', + keywords: ['verifikat', 'verifikation', 'verifikationer', 'grundbok', 'sök verifikat'], title: 'Query Journal Lines', description: 'Flexible journal-line query for ad-hoc questions. Filters: account, date, amount, voucher, source, status, dimensions, free-text. group_by/group_by_dimension aggregation; include_dimensions returns line bags. Lines + totals over the full match set.', inputSchema: { @@ -9612,6 +9674,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_get_ar_ledger', + keywords: ['kundreskontra', 'kundfordringar'], title: 'AR Ledger (Kundreskontra)', description: 'Accounts receivable ledger (kundreskontra): outstanding customer invoices with aging.', inputSchema: { @@ -9636,6 +9699,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_get_supplier_ledger', + keywords: ['leverantörsreskontra', 'leverantörsskulder', 'leverantörsfaktura'], title: 'AP Ledger (Leverantörsreskontra)', description: 'Accounts payable ledger (leverantörsreskontra): outstanding supplier invoices with aging.', inputSchema: { @@ -9662,6 +9726,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_match_transaction_to_invoice', + keywords: ['matcha betalning', 'kundfaktura', 'inbetalning'], title: 'Match Transaction to Invoice', description: 'Match a bank transaction (income, amount>0) to a customer invoice. Confirm tx date/amount and invoice number/customer before staging. Supports partial payments and auto-storno of prior categorization.', inputSchema: { @@ -9739,6 +9804,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_match_batch_allocate', + keywords: ['klumpbetalning', 'fördela betalning', 'matcha betalningar'], title: 'Batch-Allocate Payment', description: 'Allocate 1 bank tx across N customer OR N supplier invoices (samlingsbetalning, BFL 5 kap 6§). Use when one receipt covers many invoices or one transfer pays many bills. Stages.', inputSchema: { @@ -9928,6 +9994,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_link_transaction_to_journal_entry', + keywords: ['koppla transaktion', 'verifikat'], title: 'Link Transaction to Verifikat', // Search-only since the account-keyed gnubok_reconcile_match covers the // same link (one pair) for bank AND skattekonto; kept callable for clients @@ -10089,6 +10156,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_bulk_book_transactions', + keywords: ['bokför', 'kontera', 'massbokföring', 'banktransaktioner'], title: 'Bulk-Book Transactions', description: 'Bulk-book N bank txs (same date, same direction) into 1 samlingsverifikat (BFL 5 kap 6§). Link txs to an existing posted verifikat, or create one from caller lines. Each tx posts its cash-account line. Stages.', inputSchema: { @@ -10382,6 +10450,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_bulk_book_inbox_items', + keywords: ['bokför', 'inkorg', 'kvitton', 'underlag'], title: 'Bulk-Book Underlag', description: 'Bulk-book N selected Underlag (Dokumentinkorgen) against their matched bank transactions with one shared category + VAT treatment. Set reverse_charge for foreign SaaS. Unmatched/booked items are skipped. Stages one approval.', inputSchema: { @@ -10535,6 +10604,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_find_voucher_candidates_for_invoice', + keywords: ['verifikat', 'kundfaktura', 'koppla faktura'], title: 'Find Voucher Candidates (Invoice)', description: "List posted verifikat that could be this invoice's payment (faktureringsmetoden: credit 1510; kontantmetoden: debit 19xx). Call before gnubok_link_invoice_to_voucher to mark the faktura paid (no new bokföring).", inputSchema: { @@ -10601,6 +10671,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_link_invoice_to_voucher', + keywords: ['koppla faktura', 'verifikat', 'kundfaktura'], title: 'Link Invoice to Voucher', description: 'Markera en faktura som betald via länk till en befintlig verifikation (faktureringsmetoden: krediterar 1510; kontantmetoden: debiterar 19xx). Kör gnubok_find_voucher_candidates_for_invoice först. Stages for approval.', inputSchema: { @@ -10688,6 +10759,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_find_voucher_candidates_for_supplier_invoice', + keywords: ['verifikat', 'koppla'], title: 'Find Voucher Candidates (Supplier Invoice)', description: 'List posted verifikat that debit leverantörsskuld (2440) and could be this supplier invoice\'s payment. Call before gnubok_link_supplier_invoice_to_voucher to mark the leverantörsfaktura paid (no new bokföring).', inputSchema: { @@ -10754,6 +10826,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_link_supplier_invoice_to_voucher', + keywords: ['koppla leverantörsfaktura', 'verifikat'], title: 'Link Supplier Invoice to Voucher', description: 'Markera en leverantörsfaktura som betald via länk till en befintlig verifikation som debiterar leverantörsskuld (2440). Skapar ingen ny verifikation. Kör gnubok_find_voucher_candidates_for_supplier_invoice först. Stages.', inputSchema: { @@ -10841,6 +10914,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_auto_match_period', + keywords: ['automatchning', 'matcha period', 'stäm av månaden'], title: 'Auto-Match Period Income', description: "Bulk reconciliation: scan unmatched income in a date range and propose invoice matches with confidence + reasoning. dry_run=true (default) previews; dry_run=false stages matches above confidence_threshold.", inputSchema: { @@ -11032,6 +11106,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_list_fiscal_periods', + keywords: ['räkenskapsår', 'bokföringsår', 'perioder'], title: 'List Fiscal Periods', description: 'List all fiscal periods (räkenskapsperioder) with status: active (open), locked (no new entries), or closed (year-end completed).', inputSchema: { type: 'object', additionalProperties: false, properties: {} }, @@ -11076,6 +11151,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_get_reconciliation_status', + keywords: ['avstämning', 'skattekonto', 'bankavstämning', 'stäm av'], title: 'Reconciliation Status', description: 'Reconciliation bridge. account_key: "skattekonto", "bank:" or "manual:" (any other balance account, see its manual block). Without it: legacy bank status for account_number. Judge on unexplained_difference.', inputSchema: { @@ -11138,6 +11214,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_list_reconciliation_items', + keywords: ['avstämning', 'skattekonto', 'avstämningsposter'], title: 'Reconciliation Items', description: 'Rows behind one account\'s reconciliation bridge, by bucket: side, qualified id, amount, proposal with confidence + reasons, allowed actions. Link via gnubok_reconcile_match.', inputSchema: { @@ -11190,6 +11267,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_reconcile_match', + keywords: ['avstämning', 'skattekonto', 'matcha'], title: 'Reconcile: Link Pairs', description: 'Link outside rows (bank or skattekonto) to existing verifikat on one account; no new bokföring. Pass pairs, or use_proposals to apply the persisted proposals. Stages. dry_run previews.', // Default catalog since E2E #12: the onboarding efterkontroll instructs @@ -11327,6 +11405,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_reconcile_unmatch', + keywords: ['avstämning', 'ta bort koppling', 'skattekonto'], title: 'Reconcile: Unlink', description: 'Remove the link between one outside row (bank transaction or skattekonto row) and its verifikat on an account. The verifikat is untouched. Stages (low risk).', catalogVisibility: 'search', @@ -11377,6 +11456,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_reconcile_signoff', + keywords: ['avstämning', 'avstämt', 'signera avstämning'], title: 'Reconcile: Sign off', description: 'Mark one account (skattekonto, bank: or manual:) as reconciled through a date ("avstämt t.o.m."). Refused unless unexplained_difference is 0, or force + note. Manual accounts without a specification take external_balance (underlag, ledger sign). Stages; dry_run previews.', catalogVisibility: 'search', @@ -11454,6 +11534,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_reconcile_residual', + keywords: ['avstämning', 'bankavgift', 'ränta', 'öresavrundning', 'restpost'], title: 'Reconcile: Book Residual and Link', description: 'Close a near-match on a bank account in one step: link 1..50 bank tx to one verifikat and book the small difference as bank fee (6570), interest (8410/8310) or rounding (3740). Bank only; refused at 0, above the cap, or wrong direction. Stages; dry_run previews.', catalogVisibility: 'search', @@ -11750,6 +11831,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_list_cash_accounts', + keywords: ['bankkonto', 'kassakonto', 'bankkonton', 'likvidkonton', 'kassa'], title: 'List Cash Accounts', description: 'List the company bank/cash accounts (cash_accounts): BAS ledger, currency, IBAN, primary flag. Use cash_account_id to filter transaction listings and account_number (ledger_account) for gnubok_get_reconciliation_status.', inputSchema: { @@ -11816,6 +11898,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_create_document_upload', + keywords: ['ladda upp', 'underlag', 'kvitto', 'dokument'], title: 'Create Document Upload', description: 'Create a short-lived URL for a model-free document upload. PUT the raw file bytes (max 10 MB) to upload_url, then call gnubok_complete_document_upload with the same upload_id and file_name.', inputSchema: { @@ -11877,6 +11960,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_complete_document_upload', + keywords: ['ladda upp', 'underlag', 'dokument'], title: 'Complete Document Upload', description: 'Validate and archive bytes sent to the URL from gnubok_create_document_upload, run AI extraction and create the inbox item. Idempotent: safe to retry with the same upload_id.', inputSchema: { @@ -11958,6 +12042,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_upload_document', + keywords: ['ladda upp', 'kvitto', 'underlag', 'dokument', 'bifoga'], title: 'Upload Document to Inbox', description: 'Legacy inline-base64 upload for small files (max 10 MB). Prefer gnubok_create_document_upload so raw bytes bypass the model. Runs AI field extraction: requires the AI capability.', inputSchema: { @@ -12017,6 +12102,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_list_inbox_items', + keywords: ['inkorg', 'kvitto', 'kvitton', 'underlag'], title: 'List Inbox Items', description: 'List document inbox items, including each original file_name. `processed` covers all terminal links (transaction, supplier invoice, journal entry); booked receipts count as done. unprocessed_only=true returns docs still needing handling.', inputSchema: { @@ -12187,6 +12273,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_get_inbox_item', + keywords: ['inkorg', 'kvitto', 'underlag'], title: 'Get Inbox Item', description: 'Get a single inbox item with complete extracted data, supplier match, email metadata, and timestamps.', inputSchema: { @@ -12223,6 +12310,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_create_supplier_invoice_from_inbox', + keywords: ['leverantörsfaktura', 'inkorg', 'underlag'], title: 'Create Supplier Invoice from Inbox', description: "Atomic: turn an OCR'd inbox item into a staged supplier invoice. Resolves supplier, builds lines from extracted_data, applies VAT + FX + dimension tags, attaches the document. Stages for human review; honors dry_run. Unresolved supplier → staged:false + candidates + next.", inputSchema: { @@ -12599,6 +12687,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_list_unmatched_documents', + keywords: ['omatchade underlag', 'kvitto', 'kvitton', 'dokument'], title: 'List Unmatched Documents', description: 'List inbox documents not yet attached to any bank transaction, supplier invoice, or journal entry. Returns vendor/amount/currency/date hints. Amount is in the invoice currency; FX-normalise before comparing to transactions.amount.', inputSchema: { @@ -12757,6 +12846,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_get_document_content', + keywords: ['dokument', 'underlag', 'kvitto', 'läs underlag'], title: 'Get Document Content', description: 'Get a 5-minute signed download URL for a document so the agent can read its contents (e.g. with vision). Use after gnubok_list_unmatched_documents to inspect a specific PDF before deciding which transaction it matches.', inputSchema: { @@ -12824,6 +12914,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_attach_document_to_transaction', + keywords: ['bifoga underlag', 'kvitto', 'koppla underlag'], title: 'Attach Document to Transaction', description: 'Stage attaching a document to a bank transaction. Verify tx (date, amount, counterparty) and document (filename, vendor, amount) match first: the reviewer\'s preview mirrors what you pass here. Stages for approval.', inputSchema: { @@ -12953,6 +13044,7 @@ export const tools: McpTool[] = [ }, { name: 'gnubok_link_document_to_voucher', + keywords: ['koppla underlag', 'verifikat', 'kvitto'], title: 'Link Document to Voucher', description: 'Stage linking a document to an already-POSTED verifikation (no bank-tx row). For an unbooked handling prefer gnubok_create_voucher with inbox_item_id (BFL 5 kap 6§). Call gnubok_list_verifikat_without_documents for targets.', inputSchema: { @@ -13042,6 +13134,7 @@ export const tools: McpTool[] = [ }, { name: 'gnubok_link_documents_to_vouchers', + keywords: ['koppla underlag', 'verifikat', 'kvitton'], title: 'Bulk-Link Documents to Vouchers', description: 'Bulk receipt migration: stage up to 300 document-to-verifikat links as ONE approval, addressed by voucher_series/voucher_number/fiscal_year (server resolves the UUID). Returns per-row hit/miss, so a wrong fiscal_year shows before approval. Only resolved rows are staged.', inputSchema: { @@ -13310,6 +13403,7 @@ export const tools: McpTool[] = [ // ── Payroll (Lönehantering) ────────────────────────────────── { name: 'gnubok_list_mileage_trips', + keywords: ['körjournal', 'milersättning', 'tjänsteresor'], title: 'List Mileage Trips (Körjournal)', catalogVisibility: 'search', description: 'List körjournal trips for the active company. Filter by date range, status (draft = not yet booked, booked) or employee. Use before gnubok_book_mileage_period to see what would be booked.', @@ -13374,6 +13468,7 @@ export const tools: McpTool[] = [ }, { name: 'gnubok_log_mileage_trip', + keywords: ['körjournal', 'milersättning', 'tjänsteresa'], title: 'Log Mileage Trip (Körjournal)', catalogVisibility: 'search', description: 'Stage a körjournal trip (date, route, km, purpose per Skatteverket requirements). Approve via gnubok_approve_pending_operation. The trip stays a draft until booked via gnubok_book_mileage_period.', @@ -13467,6 +13562,7 @@ export const tools: McpTool[] = [ }, { name: 'gnubok_book_mileage_period', + keywords: ['körjournal', 'milersättning', 'bokför resor'], title: 'Book Mileage Period (Milersättning)', catalogVisibility: 'search', description: 'Stage booking of all draft körjournal trips in a date range as one milersättning verifikat: debit 7331 at the tax-free schablon rate, credit 2820/2893/1930. Approve via gnubok_approve_pending_operation. Call gnubok_list_mileage_trips first.', @@ -13550,6 +13646,7 @@ export const tools: McpTool[] = [ }, { name: 'gnubok_list_employees', + keywords: ['anställd', 'anställda', 'personal'], title: 'List Employees', description: 'List employees for the active company. Personnummer is returned masked as personnummer_masked (YYYYMMDD-XXXX).', inputSchema: { @@ -13590,6 +13687,7 @@ export const tools: McpTool[] = [ }, { name: 'gnubok_get_salary_run', + keywords: ['lön', 'lönekörning', 'löner'], title: 'Get Salary Run', description: 'Get salary run with status, totals, per-employee breakdown (gross, tax, net, avgifter, vacation accrual) and step-by-step calculation breakdown.', inputSchema: { @@ -13622,6 +13720,7 @@ export const tools: McpTool[] = [ }, { name: 'gnubok_get_salary_journal', + keywords: ['lönejournal', 'lön', 'bokföringsunderlag lön'], title: 'Salary Journal (Lönejournal)', description: 'Salary journal (lönejournal) for a year: per-employee per-month rows + yearly totals.', inputSchema: { @@ -13641,6 +13740,7 @@ export const tools: McpTool[] = [ }, { name: 'gnubok_create_salary_run', + keywords: ['lön', 'lönekörning', 'ny lönekörning', 'löner'], title: 'Create Salary Run', description: 'Stage creation of a draft salary run for a period + base lines for all active employees. Commit via gnubok_approve_pending_operation; then run gnubok_calculate_salary_run and book via gnubok_book_salary_run.', inputSchema: { @@ -13697,6 +13797,7 @@ export const tools: McpTool[] = [ }, { name: 'gnubok_calculate_salary_run', + keywords: ['lön', 'lönekörning', 'beräkna lön'], title: 'Calculate Salary Run', description: 'Calculate a draft salary run: tax, avgifter, vacation accrual, totals. Run must be in draft status.', inputSchema: { @@ -13743,6 +13844,7 @@ export const tools: McpTool[] = [ }, { name: 'gnubok_book_salary_run', + keywords: ['lön', 'lönekörning', 'bokför lön'], title: 'Book Salary Run', description: 'Stage booking of a calculated salary run: advances godkänd/utbetald and posts the immutable lön verifikat. High-risk (BFL 5 kap). Commit via gnubok_approve_pending_operation (confirmed=true); then gnubok_generate_agi.', inputSchema: { @@ -13813,6 +13915,7 @@ export const tools: McpTool[] = [ }, { name: 'gnubok_generate_agi', + keywords: ['arbetsgivardeklaration', 'lön', 'arbetsgivaravgift'], title: 'Generate AGI Declaration', description: 'Stage AGI XML generation (Arbetsgivardeklaration) for a salary run. High-risk: produces statutory Skatteverket underlag (BFL 7-year retention). Commit via gnubok_approve_pending_operation.', inputSchema: { @@ -13867,6 +13970,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_vat_declaration_validate', + keywords: ['momsdeklaration', 'moms', 'granska momsdeklaration'], title: 'Validate VAT Declaration (Momsdeklaration)', description: 'Pre-flight the period momsdeklaration: Skatteverket /kontrollera (read-only, saves nothing) PLUS the local completeness checks. Read arithmetic_ok and completeness_ok separately: Skatteverket only checks that the payload adds up, never that the underlag is complete.', inputSchema: { @@ -13956,6 +14060,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_vat_declaration_submit', + keywords: ['momsdeklaration', 'moms', 'lämna in moms', 'skatteverket'], title: 'Submit VAT Declaration (Momsdeklaration)', description: 'Stage the period momsdeklaration for filing with Skatteverket. High-risk: approval sends it for BankID signing (returns a signing link); it is not filed until you sign. Always staged.', inputSchema: { @@ -14023,6 +14128,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_vat_declaration_status', + keywords: ['momsdeklaration', 'moms'], title: 'VAT Declaration Status (Momsdeklaration)', description: 'Fetch the filing status of a momsdeklaration from Skatteverket: inlämnat (submitted) and/or beslutat (decided). Sections are null when nothing is on file yet.', inputSchema: { @@ -14087,6 +14193,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_agi_submit', + keywords: ['arbetsgivardeklaration', 'lämna in agi'], title: 'Submit AGI Declaration (Arbetsgivardeklaration)', description: "Stage filing of a salary run's arbetsgivardeklaration (AGI) with Skatteverket. High-risk: approval posts the XML underlag and returns a BankID signing link; it is not filed until you sign. Always staged.", inputSchema: { @@ -14148,6 +14255,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_agi_status', + keywords: ['arbetsgivardeklaration'], title: 'AGI Declaration Status (Arbetsgivardeklaration)', description: "Fetch AGI filing status for a salary run: run-scoped filing_state and kvittensnummer (a correction run never inherits the superseded original's receipt), plus live Skatteverket kvittenser.", inputSchema: { @@ -14234,6 +14342,7 @@ export const tools: McpTool[] = [ }, { name: 'gnubok_get_employee', + keywords: ['anställd', 'personal'], title: 'Get Employee', description: 'Get one employee\'s full payroll config: salary, tax table/column, jamkning, F-skatt, vacation rule, vaxa-stod, bank details, dimensions. Personnummer masked. Use after gnubok_list_employees to drill into one employee before payroll work.', inputSchema: { @@ -14330,6 +14439,7 @@ export const tools: McpTool[] = [ }, { name: 'gnubok_get_payslip', + keywords: ['lönebesked', 'lönespecifikation', 'lönespec', 'lön'], title: 'Get Payslip (Lönebesked)', description: 'Get one employee\'s payslip in a salary run: gross, tax, avgifter, net, every line item and the step-by-step calculation breakdown. Personnummer masked. Use after gnubok_get_salary_run to verify how one employee\'s pay was computed.', inputSchema: { @@ -14422,6 +14532,7 @@ export const tools: McpTool[] = [ }, { name: 'gnubok_list_absence', + keywords: ['frånvaro', 'sjukfrånvaro', 'semester', 'vab'], title: 'List Absence (Frånvaro)', description: 'List an employee\'s registered absence days (sick, vab, parental, ...) in a date range, max 92 days. These per-day rows drive karensavdrag and sjuklön at calculation time. Use before gnubok_register_absence to see what is already registered.', inputSchema: { @@ -14469,6 +14580,7 @@ export const tools: McpTool[] = [ }, { name: 'gnubok_update_payslip_line', + keywords: ['lönebesked', 'lönespecifikation', 'lönerad'], title: 'Update Payslip Line', description: 'Stage an edit to one payslip line (amount, description, quantity, unit price) in a DRAFT salary run. Commit via gnubok_approve_pending_operation, then re-run gnubok_calculate_salary_run. NOTE: recalc rebuilds base salary lines: use gnubok_set_run_salary for this month\'s pay.', inputSchema: { @@ -14549,6 +14661,7 @@ export const tools: McpTool[] = [ }, { name: 'gnubok_set_run_salary', + keywords: ['lön', 'lönekörning', 'månadslön', 'ändra lön'], title: 'Set This Month\'s Salary', description: 'Stage this run\'s base salary for one employee in a DRAFT salary run (per-run value; the employee\'s fixed salary is untouched). For variable pay, e.g. owner salary; 0 = nollkörning. Commit via gnubok_approve_pending_operation, then gnubok_calculate_salary_run.', // Default catalog: gnubok_call_tool only bridges READ tools, so a @@ -14634,6 +14747,7 @@ export const tools: McpTool[] = [ }, { name: 'gnubok_register_absence', + keywords: ['frånvaro', 'sjukanmälan', 'semester', 'vab'], title: 'Register Absence (Frånvaro)', description: 'Stage absence registration (sick, vab, parental, ...) for an employee over a date range, max 92 days, weekends skipped unless included. Commit via gnubok_approve_pending_operation; recalculate any open salary run afterwards.', inputSchema: { @@ -14719,6 +14833,7 @@ export const tools: McpTool[] = [ }, { name: 'gnubok_delete_absence', + keywords: ['frånvaro', 'ta bort frånvaro'], title: 'Delete Absence (Frånvaro)', description: 'Stage removal of registered absence days in a date range, optionally one type only. Inverse of gnubok_register_absence. Commit via gnubok_approve_pending_operation; recalculate any draft salary run afterwards.', inputSchema: { @@ -14798,6 +14913,7 @@ export const tools: McpTool[] = [ }, { name: 'gnubok_create_employee', + keywords: ['anställd', 'ny anställd', 'personal'], title: 'Create Employee', description: 'Stage creation of a new employee: salary, tax table, bank details, vacation rule. Personnummer is encrypted at staging and never stored in plaintext. Commit via gnubok_approve_pending_operation; then attach to a salary run.', inputSchema: { @@ -14910,6 +15026,7 @@ export const tools: McpTool[] = [ }, { name: 'gnubok_update_employee', + keywords: ['anställd', 'ändra anställd', 'personal'], title: 'Update Employee', description: 'Stage an update to an employee\'s payroll config: salary, tax, bank details, vacation rule, jamkning, vaxa-stod. Personnummer cannot be changed. Call gnubok_get_employee first to see current values; commit via gnubok_approve_pending_operation.', inputSchema: { @@ -15016,6 +15133,7 @@ export const tools: McpTool[] = [ }, { name: 'gnubok_set_employee_opening_balances', + keywords: ['anställd', 'ingående saldo', 'semesterdagar'], title: 'Set Employee Opening Balances (Cutover)', description: 'Stage payroll cutover state per employee: YTD gross/tax/net, vacation days remaining and taken this year, sparade dagar by origin year, opening semesterlöneskuld SEK, karens adjustment. An omitted field keeps its stored value; send 0 to clear it. Locked after a booked run.', inputSchema: { @@ -15192,6 +15310,7 @@ export const tools: McpTool[] = [ }, { name: 'gnubok_get_vacation_balance', + keywords: ['semester', 'semestersaldo', 'semesterdagar'], title: 'Get Vacation Balance (Semestersaldo)', description: 'Get one employee\'s open vacation balance: entitled/taken/remaining days, sparade dagar per origin year, forced payouts and estimated semesterlöneskuld in SEK. Use before gnubok_close_vacation_year.', inputSchema: { @@ -15267,6 +15386,7 @@ export const tools: McpTool[] = [ }, { name: 'gnubok_close_vacation_year', + keywords: ['semesterår', 'semesterårsskifte', 'semester'], title: 'Close Vacation Year (Semesterårsavslut)', description: 'Stage the vacation year close: rolls balances into the next year (min-20 floor, 5-year expiry to forced payout) and books a 2920/2940 drift adjustment when needed. High risk: review the preview report, then commit via gnubok_approve_pending_operation.', inputSchema: { @@ -15332,6 +15452,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_close_period', + keywords: ['stäng period', 'periodstängning', 'månadsavslut', 'månadsbokslut'], title: 'Close Fiscal Period', description: 'Stage period close (irreversible per BFL). Requires period locked + year-end closing entry posted. High-risk: always staged, never auto-committed.', inputSchema: { @@ -15388,6 +15509,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_lock_period', + keywords: ['lås period', 'låsa bokföringen'], title: 'Lock Fiscal Period', description: 'Stage period lock: blocks new entries. Requires zero untriaged or unbooked business transactions in the period. High-risk, always staged.', inputSchema: { @@ -15488,6 +15610,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_uncategorize_transaction', + keywords: ['ångra bokföring', 'avkategorisera'], title: 'Uncategorize Transaction', description: 'Stage uncategorize: reverses linked journal entry via storno (never deletes) and clears the category. Stages for approval.', inputSchema: { @@ -15554,6 +15677,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_export_sie', + keywords: ['sie', 'sie-fil', 'exportera bokföring'], title: 'Export SIE File', description: 'Generate SIE-4 file for a fiscal period (standard Swedish bookkeeping interchange format). Returns SIE text content.', inputSchema: { @@ -15613,6 +15737,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_generate_rot_rut_file', + keywords: ['rotavdrag', 'rutavdrag', 'husarbete', 'utbetalningsfil'], title: 'Generate Rot/Rut Payout File', description: 'Begäran om utbetalning for rot/rut (Skatteverket husavdrag): XML file from paid deduction invoices, uploaded manually on skatteverket.se (no API exists). Call with list_only=true first to see eligible invoices and blockers. Generating records an active begäran per invoice.', @@ -15742,6 +15867,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_import_rot_rut_beslut', + keywords: ['rotavdrag', 'rutavdrag', 'beslutsfil', 'skatteverket'], title: 'Import Rot/Rut Decision File', description: 'Import Skatteverkets beslutsfil (decision JSON from the rot/rut e-tjänst) and record godkänt belopp on the matching begäran. Exact matching only; per-beslut outcomes in results. Book the payout afterwards via the settle endpoint hint in next.', @@ -15818,6 +15944,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_audit_package', + keywords: ['revision', 'revisor', 'bokslutsunderlag'], title: 'Generate Audit Package', description: "Single-call audit package for a fiscal period: SIE-4 + reports (trial balance, income statement, balance sheet, general ledger, journal, VAT) + receipts + audit log + voucher gaps, zipped.", inputSchema: { @@ -15963,6 +16090,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_year_end_readiness', + keywords: ['bokslut', 'årsbokslut', 'årsavslut', 'redo för bokslut'], title: 'Year-End Readiness Check', // Budget: 280 chars (output-schema.test.ts). Spend it on the blockers an // agent can act on BEFORE calling, in likelihood order. The four @@ -16077,6 +16205,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_post_kontantmetod_cutoff', + keywords: ['kontantmetoden', 'bokslutsmetod', 'brytdag'], title: 'Post Cash-Method Year-End Cut-Off', description: 'Stage the exact year-end receivable/payable cut-off and next-period reversals required for kontantmetoden. Review all proposed lines, then approve with confirmed=true.', inputSchema: { @@ -16271,6 +16400,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_run_year_end', + keywords: ['bokslut', 'årsbokslut', 'årsavslut', 'stäng året'], title: 'Run Year-End Closing (Bokslut)', description: 'Stage year-end closing: zero result accounts (class 3-8) into 2099, lock period, create next period, seed opening balances. High-risk, always staged.', inputSchema: { @@ -16316,6 +16446,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_set_opening_balances', + keywords: ['ingående balans', 'öppningsbalans', 'ingående saldon'], title: 'Set Opening Balances (Ingående Balans)', description: 'Stage opening-balance entry: copy class 1-2 closing balances from a closed period into the next period.', inputSchema: { @@ -16366,6 +16497,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_run_currency_revaluation', + keywords: ['valutaomvärdering', 'valutakurs', 'kursvinst'], title: 'Run Currency Revaluation', description: 'Stage currency revaluation: revalue open FX receivables/payables to closing-date rate (posts 3960/7960). One per period max.', inputSchema: { @@ -16400,6 +16532,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_list_voucher_gaps', + keywords: ['nummerlucka', 'verifikationsnummer', 'verifikat', 'luckor'], title: 'List Voucher Gaps', description: 'List voucher number gaps in a fiscal period (BFNAR 2013:2 audit requirement). Each gap shows whether it has an explanation.', inputSchema: { @@ -16473,6 +16606,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_set_voucher_note', + keywords: ['verifikat', 'anteckning', 'notering'], title: 'Set Voucher Note (Anteckning)', description: 'Stage setting, replacing or clearing the internal note (anteckning) on a verifikat. Notes are annotation metadata, editable even on posted entries: bookkeeping fields stay immutable. Read them via gnubok_query_journal (entry_notes).', outputSchema: STAGED_OPERATION_SCHEMA, @@ -16549,6 +16683,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_explain_voucher_gap', + keywords: ['nummerlucka', 'verifikationsnummer', 'förklara lucka'], title: 'Explain Voucher Gap', description: 'Stage explanation for a voucher gap (BFNAR 2013:2 compliance, every gap needs a documented reason).', inputSchema: { @@ -16596,6 +16731,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_approve_supplier_invoice', + keywords: ['leverantörsfaktura', 'attestera', 'godkänn faktura'], title: 'Approve Supplier Invoice', description: 'Stage approval of a supplier invoice that has not been attested yet (registered or overdue). An invoice that is still past its due date keeps the overdue label after approval. High-risk, always staged.', inputSchema: { @@ -16643,6 +16779,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_credit_supplier_invoice', + keywords: ['leverantörsfaktura', 'kreditfaktura', 'kreditera'], title: 'Credit Supplier Invoice (Kreditfaktura)', description: 'Stage credit-note (kreditfaktura) for a supplier invoice: mirror invoice with negative effect + reverses registration JE (accrual).', inputSchema: { @@ -16685,6 +16822,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_convert_invoice', + keywords: ['proforma', 'kundfaktura', 'omvandla'], title: 'Convert Proforma to Invoice', description: 'Stage conversion of a proforma invoice to a real invoice. Allocates F-series number, copies items, marks proforma cancelled.', inputSchema: { @@ -16728,6 +16866,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_unlock_period', + keywords: ['lås upp period', 'öppna period'], title: 'Unlock Fiscal Period', description: 'Stage period unlock: clears locked_at so entries can be posted again. Cannot unlock a closed period. High-risk, always staged.', inputSchema: { @@ -16777,6 +16916,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_credit_invoice', + keywords: ['kreditfaktura', 'kreditera', 'kundfaktura'], title: 'Credit Customer Invoice (Kreditfaktura)', description: 'Stage credit note (kreditfaktura) for a customer invoice: KR- prefixed mirror invoice + reverses original JE (accrual). Original must be sent/paid/overdue and not already credited.', inputSchema: { @@ -16831,6 +16971,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_update_invoice', + keywords: ['ändra faktura', 'kundfaktura', 'faktura'], title: 'Update Draft Invoice', description: 'Stage an edit to a DRAFT invoice: header fields (incl. default_dimensions) and/or items (FULL REPLACE: read current lines with gnubok_get_invoice first; lines accept article_id). Drafts only, no verifikat, not self-billed, not a credit note; otherwise use gnubok_credit_invoice.', outputSchema: STAGED_OPERATION_SCHEMA, @@ -17197,6 +17338,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_create_sie_upload', + keywords: ['sie', 'sie-fil', 'importera bokföring'], title: 'Create SIE Upload', description: 'The SIE-file intake: on claude.ai/Desktop this renders a DRAG-AND-DROP card that reads exact bytes, preflights and imports: call it as soon as an SIE import is next. Elsewhere: PUT raw bytes (max 50 MB) to upload_url, then pass upload_id + sha256 to preflight/import.', @@ -17260,6 +17402,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_sie_preflight', + keywords: ['sie', 'sie-fil', 'kontrollera sie'], title: 'SIE Preflight Scan', description: 'Scan a SIE file BEFORE import: parse, validate (balances, IB, encoding), duplicates, orgnr match, suggested mappings. Read-only. Call gnubok_create_sie_upload FIRST: its card/URL carries exact bytes; NEVER retype a large file. Mappings feed gnubok_import_sie.', @@ -17427,6 +17570,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_import_sie', + keywords: ['sie', 'sie-fil', 'importera bokföring', 'byta system'], title: 'Import SIE File', description: 'Stage SIE-file import (types 1-4, CP437/UTF-8/Latin-1). On commit creates fiscal period, opening balances, and journal entries. Always staged. Run gnubok_sie_preflight first; large files arrive byte-exact via gnubok_create_sie_upload (card/URL), NEVER retyped inline.', inputSchema: { @@ -17569,6 +17713,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_undo_sie_import', + keywords: ['sie', 'ångra import'], title: 'Undo SIE Import', description: 'Stage undo of a completed SIE import: hard-deletes its entries, detaches docs, resets voucher_sequences, marks the import \'undone\' for re-import. Use after a botched import. Period must be open. HIGH risk.', inputSchema: { @@ -17670,6 +17815,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_create_voucher', + keywords: ['verifikat', 'verifikation', 'manuell bokföring', 'bokföringsorder', 'manuellt verifikat'], title: 'Create Manual Voucher (Verifikation)', description: 'Stage a manual verifikation with balanced lines: capitalization, accruals, FX, rättelser, IB. For a received handling pass inbox_item_id: the document becomes the verifikation (BFL 5 kap 6§); a filename in notes is not underlag. HIGH risk.', inputSchema: { @@ -17976,6 +18122,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_correct_entry', + keywords: ['rättelse', 'rätta verifikat', 'verifikation', 'ändra verifikat'], title: 'Correct Posted Entry (Rättelse)', description: 'Stage a rättelse for a posted verifikation per BFL 5 kap 5§: storno + corrected entry in the original period (never in-place edit). Use for partial fixes like 2641 → 2614/2645; lines accept dimensions bags. Account drives ruta. HIGH risk.', inputSchema: { @@ -18203,6 +18350,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_reverse_journal_entry', + keywords: ['storno', 'återför', 'makulera', 'verifikat'], title: 'Reverse Journal Entry (Storno)', description: 'Stage a storno: inverts debits/credits, original stays visible (BFL 5 kap). Only when it should never have been booked (duplicate, ghost, test). Booked wrong → gnubok_correct_entry; refund → gnubok_credit_invoice. HIGH risk.', inputSchema: { @@ -18374,6 +18522,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_propose_dispositioner', + keywords: ['bokslutsdispositioner', 'periodiseringsfond', 'skatteberäkning'], title: 'Propose Year-End Dispositioner', description: 'Read-only proposal of bokslutsdispositioner for a fiscal period: periodiseringsfond (avsättning + obligatorisk återföring), överavskrivningar, SLP, bolagsskatt. No dedicated MCP poster: stage entries via gnubok_create_voucher (web bokslut UI) before gnubok_run_year_end.', @@ -18400,6 +18549,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_propose_accruals', + keywords: ['periodisering', 'upplupna kostnader', 'förutbetalda intäkter'], title: 'Propose Accruals (Periodiseringar)', description: 'Read-only proposal of periodiseringar (förutbetalda/upplupna kostnader); currently surfaces the vacation-liability change. No dedicated MCP poster: stage accrual entries via gnubok_create_voucher (or the web accruals form).', @@ -18423,6 +18573,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_list_accrual_schedules', + keywords: ['periodisering', 'periodiseringar'], title: 'List Periodiseringar', description: 'Löpande periodiseringar (17xx/29xx): monthly installments, dissolved and remaining amounts.', @@ -18473,6 +18624,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_propose_annual_depreciation', + keywords: ['avskrivning', 'avskrivningar', 'inventarier'], title: 'Propose Annual Depreciation (Avskrivning)', description: 'Read-only per-asset planenlig avskrivning proposal for a fiscal period. Reads the asset register and existing depreciation schedules. Call before staging the post.', @@ -18496,6 +18648,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_post_annual_depreciation', + keywords: ['avskrivning', 'avskrivningar', 'bokför avskrivningar'], title: 'Post Annual Depreciation (Avskrivning)', description: 'Stage planenlig avskrivning posts: one journal entry per asset for independent reversibility. Mid-risk, always staged.', @@ -18567,6 +18720,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_preview_arsredovisning', + keywords: ['årsredovisning', 'förhandsgranska årsredovisning'], title: 'Preview Annual Report (Årsredovisning)', description: 'Read-only annual report preview from the canonical model. Returns report content, eligibility, compliance blockers, and capabilities. PDF and immutable versions are available in the UI.', @@ -18609,6 +18763,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_validate_arsredovisning', + keywords: ['årsredovisning', 'granska årsredovisning'], title: 'Validate Annual Report (Årsredovisning)', description: 'Read-only compliance validation for an annual report. Use draft while editing, signing before locking a version, and filing before a Bolagsverket submission.', @@ -18648,6 +18803,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_list_arsredovisning_versions', + keywords: ['årsredovisning', 'versioner'], title: 'List Annual Report Versions', description: 'Read-only list of immutable annual report versions with content hashes, taxonomy versions, and signing or filing status.', @@ -18674,6 +18830,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_get_arsredovisning_filing_status', + keywords: ['årsredovisning', 'bolagsverket', 'inlämning'], title: 'Get Annual Report Filing Status', description: 'Read-only filing history for a fiscal period, including uncertain upload states that must be reconciled before retrying.', @@ -18726,6 +18883,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_preview_ef_declaration', + keywords: ['ne-bilaga', 'enskild firma', 'inkomstdeklaration'], title: 'Preview EF Declaration (NE-bilaga)', description: 'Read-only EF declaration preview: egenavgifter schablonavdrag, räntefördelning, periodiseringsfond, expansionsfond. All declaration-only, never booked. Pass kapitalunderlag and prior-year amounts as inputs.', @@ -18770,6 +18928,7 @@ export const tools: McpTool[] = [ // Mirrors the /pending web UI for agents that self-review before committing. { name: 'gnubok_list_pending_operations', + keywords: ['väntande åtgärder', 'att godkänna', 'godkännanden'], title: 'List Pending Operations', description: 'List staged pending_operations. Approve via gnubok_approve_pending_operation, reject via gnubok_reject_pending_operation; without pending_operations:approve use /pending. render_ui=true opens the approval widget.', inputSchema: { @@ -18836,6 +18995,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_approve_pending_operation', + keywords: ['godkänn', 'bekräfta'], title: 'Approve Pending Operation', description: "Commit a staged pending_operation the user has explicitly authorised. risk_level=high requires confirmed=true: surface the BFL 5 kap 5§ irreversibility and any preview compliance_warning first. The /pending web UI is an equivalent commit path.", inputSchema: { @@ -18981,6 +19141,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_reject_pending_operation', + keywords: ['avvisa', 'neka'], title: 'Reject Pending Operation', description: 'Reject a staged pending_operation without executing it. Status flips to rejected; no journal entries, invoices, or other side-effects created. Idempotent on already-resolved ops (returns 409).', inputSchema: { @@ -19096,6 +19257,7 @@ export const tools: McpTool[] = [ // ── Bring-your-own-extraction for inbox items ──────────────── { name: 'gnubok_set_inbox_extracted_data', + keywords: ['inkorg', 'underlag', 'tolka kvitto'], title: 'Set Inbox Extracted Data', description: 'Replace extracted_data on an inbox item with agent-supplied fields. Use when your pipeline parses the document better than Accounted\'s OCR. Follow with gnubok_create_supplier_invoice_from_inbox to stage.', inputSchema: { @@ -19208,6 +19370,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_list_recurring_schedules', + keywords: ['återkommande fakturor', 'stående faktura', 'abonnemang'], title: 'List Recurring Invoice Schedules', description: "List the company's recurring invoice schedules: auto-create customer invoices on day_of_month (clamps to the last day in shorter months) every interval_months months (any 1-12; presets 1/3/6/12) at send_hour, Europe/Stockholm. Shows status, auto_send and next_run_date.", inputSchema: { @@ -19349,6 +19512,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_create_recurring_schedule', + keywords: ['återkommande faktura', 'stående faktura', 'abonnemang'], title: 'Create Recurring Invoice Schedule', description: 'Stage a new recurring invoice schedule: creates a customer invoice on day_of_month (clamps to the last day in shorter months) every interval_months months (default 1) at send_hour, Europe/Stockholm. auto_send defaults false; true emails each invoice without new approval.', outputSchema: STAGED_OPERATION_SCHEMA, @@ -19542,6 +19706,7 @@ export const tools: McpTool[] = [ { name: 'gnubok_update_recurring_schedule', + keywords: ['återkommande faktura', 'stående faktura'], title: 'Update Recurring Invoice Schedule', description: 'Stage an update to a recurring invoice schedule (schedule_id from gnubok_list_recurring_schedules). Pause/resume via status. items replace all lines; omit to keep them. day_of_month clamps to the last day in shorter months; send_hour is a whole hour in Europe/Stockholm.', outputSchema: STAGED_OPERATION_SCHEMA,