actual-mcp-server 0.19.2 → 0.19.3
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/README.md
CHANGED
|
@@ -778,7 +778,7 @@ The software is provided **as-is**, without warranty of any kind. The author acc
|
|
|
778
778
|
|
|
779
779
|
---
|
|
780
780
|
|
|
781
|
-
**Version:** 0.19.
|
|
781
|
+
**Version:** 0.19.3 | **Tool Count:** 77 (verified LibreChat-compatible)
|
|
782
782
|
|
|
783
783
|
## Sponsor
|
|
784
784
|
|
|
@@ -258,6 +258,13 @@ export function summarizeAccountFlow(snapshot, input) {
|
|
|
258
258
|
const counterpart = transaction.transfer_id ? maps.transactions.get(transaction.transfer_id) : undefined;
|
|
259
259
|
const payee = transaction.payee ? maps.payees.get(transaction.payee) : undefined;
|
|
260
260
|
const counterpartAccountId = counterpart?.account ?? payee?.transfer_acct ?? null;
|
|
261
|
+
// #428: when the counterpart cannot be resolved (no counterpart row and no payee transfer_acct),
|
|
262
|
+
// counterpartSelected is false and the leg is bucketed by SIGN into intoSelection / outOfSelection.
|
|
263
|
+
// This is the deterministic rule: within-selection membership requires POSITIVE proof both legs are
|
|
264
|
+
// selected, which an unresolvable counterpart cannot give. The outcome is not silent: the
|
|
265
|
+
// transfersByAccount row below carries matchedCounterpart=false, so a caller can see the leg was
|
|
266
|
+
// classified without a resolved counterparty. netTransferEffect (line above) already counted the
|
|
267
|
+
// amount, so reconciliation stays exact regardless of the bucket.
|
|
261
268
|
const counterpartSelected = counterpartAccountId ? selected.has(counterpartAccountId) : false;
|
|
262
269
|
if (counterpartSelected) {
|
|
263
270
|
withinSelectionTimingEffect += amount;
|
|
@@ -290,14 +297,25 @@ export function summarizeAccountFlow(snapshot, input) {
|
|
|
290
297
|
});
|
|
291
298
|
}
|
|
292
299
|
}
|
|
293
|
-
// The tool handler always supplies
|
|
294
|
-
//
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
300
|
+
// #428: balances are OPTIONAL on the snapshot. The tool handler always supplies them (via
|
|
301
|
+
// balanceAccountIds), so on the tool path this is true and reconciliation is exact by construction.
|
|
302
|
+
// A caller that omits either map, though, would otherwise reduce both to 0 and read difference as a
|
|
303
|
+
// false "exact" zero. The check is on the MAP being undefined (entirely unpopulated), NOT a per-id
|
|
304
|
+
// lookup: the `?? 0` per-id default is legitimate for a genuine zero-balance account.
|
|
305
|
+
const balancesAvailable = snapshot.openingBalances !== undefined && snapshot.closingBalances !== undefined;
|
|
306
|
+
const openingBalance = balancesAvailable
|
|
307
|
+
? uniqueAccountIds.reduce((sum, id) => sum + integer(snapshot.openingBalances?.[id] ?? 0, `Opening balance for ${id}`), 0)
|
|
308
|
+
: null;
|
|
309
|
+
const closingBalance = balancesAvailable
|
|
310
|
+
? uniqueAccountIds.reduce((sum, id) => sum + integer(snapshot.closingBalances?.[id] ?? 0, `Closing balance for ${id}`), 0)
|
|
311
|
+
: null;
|
|
312
|
+
const balanceChange = openingBalance !== null && closingBalance !== null ? closingBalance - openingBalance : null;
|
|
298
313
|
const netExternalCashFlow = income - incomeReversals + credits + uncategorizedInflows - expenseOutflow;
|
|
299
314
|
const totalAdjustments = startingBalance;
|
|
300
315
|
const calculatedBalanceChange = netExternalCashFlow + netTransferEffect + totalAdjustments;
|
|
316
|
+
// When balances are unavailable, difference must NOT read as a reconciled value. Null it so a
|
|
317
|
+
// caller cannot mistake an unpopulated run for a reconciled one.
|
|
318
|
+
const difference = balanceChange !== null ? balanceChange - calculatedBalanceChange : null;
|
|
301
319
|
return {
|
|
302
320
|
dateRange: { startDate: input.startDate, endDate: input.endDate },
|
|
303
321
|
accounts: uniqueAccountIds.map(id => ({ id, name: maps.accounts.get(id)?.name ?? id })),
|
|
@@ -322,9 +340,12 @@ export function summarizeAccountFlow(snapshot, input) {
|
|
|
322
340
|
transfersByAccount: transfersByAccount.sort((a, b) => `${a.date}:${a.fromAccountId}:${a.toAccountId}`.localeCompare(`${b.date}:${b.fromAccountId}:${b.toAccountId}`)),
|
|
323
341
|
adjustments: { startingBalance, total: totalAdjustments },
|
|
324
342
|
reconciliation: {
|
|
343
|
+
// #428: balancesAvailable is false when the snapshot omitted the balance maps; difference and
|
|
344
|
+
// actualBalanceChange are then null so an unpopulated run cannot read as a reconciled one.
|
|
345
|
+
balancesAvailable,
|
|
325
346
|
calculatedBalanceChange,
|
|
326
347
|
actualBalanceChange: balanceChange,
|
|
327
|
-
difference
|
|
348
|
+
difference,
|
|
328
349
|
},
|
|
329
350
|
};
|
|
330
351
|
}
|
|
@@ -24,7 +24,7 @@ export default createTool({
|
|
|
24
24
|
'Explain the exact balance change across a selected set of accounts over an inclusive date range, in integer cents.',
|
|
25
25
|
'Separates external income, expense outflow, credits, uncategorized inflows, starting-balance adjustments, and transfers into, out of, or within the selection.',
|
|
26
26
|
'Transfers are detected from transfer_id and are never spending or income.',
|
|
27
|
-
'openingBalance is immediately before startDate and closingBalance is at the end of the inclusive endDate; reconciliation.difference is exact and transfersByAccount names the funding counterparties.',
|
|
27
|
+
'openingBalance is immediately before startDate and closingBalance is at the end of the inclusive endDate; reconciliation.difference is exact (reconciliation.balancesAvailable is true) and transfersByAccount names the funding counterparties.',
|
|
28
28
|
'Amounts are in cents: $100.00 = 10000. Dates are YYYY-MM-DD.',
|
|
29
29
|
].join(' '),
|
|
30
30
|
schema: InputSchema,
|