* feat(reconciliation): close the bank-feed loop on voucher links and re-tag mis-typed opening balances
Two related fixes to bank reconciliation correctness:
1. Auto-reconcile on voucher link. Linking an invoice or supplier invoice to
an existing voucher previously advanced only the invoice — the bank
transaction that paid it kept sitting in the Transactions inbox with a null
journal_entry_id. linkInvoiceToVoucher / linkSupplierInvoiceToVoucher now
call autoReconcileTransactionForLinkedVoucher (lib/reconciliation), which
links the bank transaction to the same verifikat when exactly one unbooked
line matches it. Best-effort and post-commit: a failure here never fails the
link. The result surfaces reconciledTransactionId; the inbox row leaves the
list and the UI shows link_success_tx_reconciled.
2. Re-tag mis-typed opening balances. getReconciliationStatus and the GL-line
matching RPCs identify a cash account's ingående balans solely by
journal_entries.source_type='opening_balance'. Companies migrated from other
systems often booked the bank IB as an ordinary voucher (source_type
'import' or 'manual'), so it was never excluded and surfaced as a phantom
reconciliation difference equal to the opening balance. Adds:
- migration mark_entry_as_opening_balance: a GUC-gated carve-out in the
immutability trigger plus a SECURITY DEFINER RPC that validates the entry
(balance-sheet lines only, dated on a fiscal-period boundary), flips the
source_type, and writes an audit row — no blanket data sweep.
- POST /api/reconciliation/bank/mark-opening-balance + MarkOpeningBalanceSchema.
- BankReconciliationView action to trigger it from the IB diff.
The gnubok_create_voucher executor now accepts a typed is_opening_balance flag
and derives source_type='opening_balance' only after validating class 1/2 lines
on the period start, so new IBs land correctly typed.
Covered by lib/reconciliation auto-reconcile tests, voucher-executors tests,
and a mark-entry-as-opening-balance pg-real test.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore: rebrand gnubok → Accounted and prune swarm agent skills
Product rebrand and skills housekeeping. No runtime behaviour change.
Rebrand: replace user-visible "gnubok" with "Accounted" across docs, READMEs,
in-code comments, doc-site content, MCP skill/resource prose, and the
gnubok-mcp package description. The MCP resource URI scheme is moved gnubok://
→ Accounted:// consistently across resource registrations, the event-type
comment, and the resource/skill tests. Deliberately preserved as stable
identifiers (NOT rebranded): the gnubok-company-id cookie, gnubok_sk_ / gnubok_inv_
token prefixes, the gnubok-mcp npm bridge name, and the AGI <gem:Programnamn>
value (kept 'gnubok' per its source comment — it is the software identifier sent
to Skatteverket and must not churn across visual rebrands).
Skills: remove the 27 swarm-* agent SKILL.md atoms (no longer used; already
absent from the agent_atom_registry in prod), refresh the remaining skill docs,
add the .claude/rules/ path-scoped rule set, and regenerate the
seed_agent_atom_bodies migration + .skill-body-manifest.json via
`npm run skills:generate` so the DB-backed skill bodies match the trimmed set.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
35 lines
3.7 KiB
Markdown
35 lines
3.7 KiB
Markdown
# Accounted MCP server
|
|
|
|
JSON-RPC 2.0 server exposing the Accounted bookkeeping engine to MCP clients (Claude Desktop, Claude Code, etc.). Endpoint: `/api/extensions/ext/mcp-server/mcp`. OAuth and stdio bridge live alongside the API surface — see `app/api/mcp-oauth/` and `packages/gnubok-mcp/`.
|
|
|
|
## Tool authoring contract
|
|
|
|
Enforced by tests in `__tests__/` — these are not style preferences, they're guard rails.
|
|
|
|
1. **`additionalProperties: false`** on every `inputSchema`. Guarded by `strict-schemas.test.ts`. Forces clear rejections on hallucinated fields instead of silent ignores.
|
|
2. **Descriptions ≤ 280 chars.** Guarded by `output-schema.test.ts`. No `Args:` / `Returns:` / `Examples:` prose — those belong in JSON Schema. Use agent-native hints ("Use to…", "Call X first", "HIGH risk").
|
|
3. **Staged-operation envelope** for write tools — `outputSchema: STAGED_OPERATION_SCHEMA` (`server.ts`). Fields: `staged, risk_level, actor, message, preview, period_status?, next?`. The `staged: true` boolean is the explicit completion signal; agents must not infer completion from prose. Do NOT introduce a parallel `{ success, shouldContinue, output }` envelope.
|
|
4. **`period_status` threading** — any tool that ties to a fiscal-period-bound date (categorize, mark paid, create voucher, correct/reverse entry, approve supplier invoice) passes `dateForPeriodCheck` to `stagePendingOperation`. Response then includes `period_status: { period_id, status: open|locked|closed, lock_date }` so widgets and agents disable writes without round-trips.
|
|
5. **Scope mapping** — every new tool needs an entry in `lib/auth/api-keys.ts` `TOOL_SCOPE_MAP`. Missing entries default to deny.
|
|
6. **Tests for new write tools** — add staging-gate coverage to `__tests__/voucher-tools.test.ts` (or a sibling) plus executor coverage to `lib/pending-operations/__tests__/voucher-executors.test.ts` if the tool stages a new `operation_type`.
|
|
|
|
## Determinism / cache stability
|
|
|
|
Tool definitions (name, description, inputSchema, outputSchema, annotations) are declared as static object literals at module load — no timestamps, no UUIDs, no Date/Math.random in the definition layer. This makes the `tools/list` JSON payload byte-stable across requests, which lets agent-side prompt caches stay warm. **Do not introduce per-request non-determinism into the definitions block.** Anything time-bound or random belongs inside `execute()`.
|
|
|
|
For internal Anthropic API usage (today only `extensions/general/invoice-inbox/lib/extract-invoice-fields.ts`): annotate stable prefixes with `cache_control: { type: 'ephemeral' }` and log `usage.cache_read_input_tokens` for hit-ratio observability. The 1h TTL from the agent-native API plan (item 10) requires the direct Anthropic API; Accounted's Bedrock path defaults to a shorter TTL.
|
|
|
|
## Payload-size watchdog
|
|
|
|
`payload-size.bench.test.ts` enforces a `tools/list` JSON payload ceiling (currently 25,000 tokens). If the test fires, the right answer is rarely "raise the ceiling" — instead, trim descriptions or leverage `gnubok_search_tools` (already deployed; tool definitions can defer to it for discovery rather than enumerating in `tools/list`).
|
|
|
|
## Where things live
|
|
|
|
- `server.ts` — the tools array + JSON-RPC dispatcher
|
|
- `tool-result.ts` — `withNext()`, `toToolError()` response helpers
|
|
- `resources/` — read-only `Accounted://` URIs (active company, period, recent activity, capabilities, attention items, voucher gaps, chart of accounts, VAT treatments)
|
|
- `widgets/` — inline HTML widgets (receipt-matcher, vat-review)
|
|
- `prompts/` — slash-command-style prompts
|
|
- `skills/` — domain-knowledge skill bodies served via `gnubok_load_skill`
|
|
- `__tests__/` — strictness guards + per-tool coverage
|