@dizzlkheinz/ynab-mcpb 0.12.2 → 0.13.1
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/.github/workflows/ci-tests.yml +6 -2
- package/CHANGELOG.md +14 -1
- package/NUL +0 -1
- package/README.md +36 -10
- package/dist/bundle/index.cjs +30 -30
- package/dist/index.js +9 -20
- package/dist/server/YNABMCPServer.d.ts +2 -1
- package/dist/server/YNABMCPServer.js +61 -27
- package/dist/server/cacheKeys.d.ts +8 -0
- package/dist/server/cacheKeys.js +8 -0
- package/dist/server/config.d.ts +22 -3
- package/dist/server/config.js +16 -17
- package/dist/server/securityMiddleware.js +3 -6
- package/dist/server/toolRegistry.js +8 -10
- package/dist/tools/accountTools.js +4 -3
- package/dist/tools/categoryTools.js +8 -7
- package/dist/tools/monthTools.js +2 -1
- package/dist/tools/payeeTools.js +2 -1
- package/dist/tools/reconciliation/executor.js +85 -4
- package/dist/tools/transactionTools.d.ts +3 -17
- package/dist/tools/transactionTools.js +5 -17
- package/dist/utils/baseError.d.ts +3 -0
- package/dist/utils/baseError.js +7 -0
- package/dist/utils/errors.d.ts +13 -0
- package/dist/utils/errors.js +15 -0
- package/dist/utils/validationError.d.ts +3 -0
- package/dist/utils/validationError.js +3 -0
- package/docs/plans/2025-11-20-reloadable-config-token-validation.md +93 -0
- package/docs/plans/2025-11-21-fix-transaction-cached-property.md +362 -0
- package/docs/plans/2025-11-21-reconciliation-error-handling.md +90 -0
- package/package.json +3 -2
- package/scripts/run-throttled-integration-tests.js +9 -3
- package/src/__tests__/performance.test.ts +12 -5
- package/src/__tests__/testUtils.ts +62 -5
- package/src/__tests__/workflows.e2e.test.ts +33 -0
- package/src/index.ts +8 -31
- package/src/server/YNABMCPServer.ts +81 -42
- package/src/server/__tests__/YNABMCPServer.integration.test.ts +10 -12
- package/src/server/__tests__/YNABMCPServer.test.ts +27 -15
- package/src/server/__tests__/config.test.ts +76 -152
- package/src/server/__tests__/server-startup.integration.test.ts +42 -14
- package/src/server/__tests__/toolRegistry.test.ts +1 -1
- package/src/server/cacheKeys.ts +8 -0
- package/src/server/config.ts +20 -38
- package/src/server/securityMiddleware.ts +3 -7
- package/src/server/toolRegistry.ts +14 -10
- package/src/tools/__tests__/categoryTools.test.ts +37 -19
- package/src/tools/__tests__/transactionTools.test.ts +58 -2
- package/src/tools/accountTools.ts +8 -3
- package/src/tools/categoryTools.ts +12 -7
- package/src/tools/monthTools.ts +7 -1
- package/src/tools/payeeTools.ts +7 -1
- package/src/tools/reconciliation/__tests__/executor.integration.test.ts +25 -5
- package/src/tools/reconciliation/__tests__/executor.test.ts +46 -0
- package/src/tools/reconciliation/executor.ts +109 -6
- package/src/tools/schemas/outputs/utilityOutputs.ts +1 -1
- package/src/tools/transactionTools.ts +7 -18
- package/src/utils/baseError.ts +7 -0
- package/src/utils/errors.ts +21 -0
- package/src/utils/validationError.ts +3 -0
- package/temp-recon.ts +126 -0
- package/test_mcp_tools.mjs +75 -0
- package/ADOS-2-Module-1-Complete-Manual.md +0 -757
package/temp-recon.ts
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import 'dotenv/config';
|
|
2
|
+
import * as ynab from 'ynab';
|
|
3
|
+
import { executeReconciliation } from './src/tools/reconciliation/executor.js';
|
|
4
|
+
|
|
5
|
+
async function main() {
|
|
6
|
+
const token = process.env['YNAB_ACCESS_TOKEN'];
|
|
7
|
+
if (!token) throw new Error('No token');
|
|
8
|
+
const api = new ynab.API(token);
|
|
9
|
+
const budgets = await api.budgets.getBudgets();
|
|
10
|
+
const budgetId = budgets.data.budgets[0]?.id;
|
|
11
|
+
if (!budgetId) throw new Error('no budget');
|
|
12
|
+
const accounts = await api.accounts.getAccounts(budgetId);
|
|
13
|
+
const account = accounts.data.accounts.find((a) => !a.closed);
|
|
14
|
+
if (!account) throw new Error('no account');
|
|
15
|
+
const snapshot = {
|
|
16
|
+
balance: account.balance,
|
|
17
|
+
cleared_balance: account.cleared_balance ?? account.balance,
|
|
18
|
+
uncleared_balance: account.uncleared_balance ?? 0,
|
|
19
|
+
};
|
|
20
|
+
const count = 2;
|
|
21
|
+
const transactionAmount = 7;
|
|
22
|
+
const clearedDollars = snapshot.cleared_balance / 1000;
|
|
23
|
+
const totalDelta = transactionAmount * count;
|
|
24
|
+
const statementBalance = clearedDollars + totalDelta;
|
|
25
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
26
|
+
const unmatchedBank = Array.from({ length: count }, (_, i) => {
|
|
27
|
+
const date = new Date(Date.now() + i * 24 * 60 * 60 * 1000).toISOString().slice(0, 10);
|
|
28
|
+
return {
|
|
29
|
+
id: `integration-bank-${i}`,
|
|
30
|
+
date,
|
|
31
|
+
amount: transactionAmount,
|
|
32
|
+
payee: `Integration Payee ${i}`,
|
|
33
|
+
memo: `Integration memo ${i}`,
|
|
34
|
+
original_csv_row: i + 1,
|
|
35
|
+
};
|
|
36
|
+
});
|
|
37
|
+
const analysis = {
|
|
38
|
+
success: true,
|
|
39
|
+
phase: 'analysis',
|
|
40
|
+
summary: {
|
|
41
|
+
statement_date_range: 'Integration test',
|
|
42
|
+
bank_transactions_count: count,
|
|
43
|
+
ynab_transactions_count: 0,
|
|
44
|
+
auto_matched: 0,
|
|
45
|
+
suggested_matches: 0,
|
|
46
|
+
unmatched_bank: count,
|
|
47
|
+
unmatched_ynab: 0,
|
|
48
|
+
current_cleared_balance: clearedDollars,
|
|
49
|
+
target_statement_balance: statementBalance,
|
|
50
|
+
discrepancy: totalDelta,
|
|
51
|
+
discrepancy_explanation: 'Synthetic integration delta',
|
|
52
|
+
},
|
|
53
|
+
auto_matches: [],
|
|
54
|
+
suggested_matches: [],
|
|
55
|
+
unmatched_bank: unmatchedBank,
|
|
56
|
+
unmatched_ynab: [],
|
|
57
|
+
balance_info: {
|
|
58
|
+
current_cleared: clearedDollars,
|
|
59
|
+
current_uncleared: snapshot.uncleared_balance / 1000,
|
|
60
|
+
current_total: snapshot.balance / 1000,
|
|
61
|
+
target_statement: statementBalance,
|
|
62
|
+
discrepancy: totalDelta,
|
|
63
|
+
on_track: false,
|
|
64
|
+
},
|
|
65
|
+
next_steps: [],
|
|
66
|
+
insights: [],
|
|
67
|
+
} as const;
|
|
68
|
+
|
|
69
|
+
const params = {
|
|
70
|
+
budget_id: budgetId,
|
|
71
|
+
account_id: account.id,
|
|
72
|
+
csv_data: 'Date,Description,Amount',
|
|
73
|
+
statement_balance: statementBalance,
|
|
74
|
+
statement_date: today,
|
|
75
|
+
date_tolerance_days: 1,
|
|
76
|
+
amount_tolerance_cents: 1,
|
|
77
|
+
auto_match_threshold: 90,
|
|
78
|
+
suggestion_threshold: 60,
|
|
79
|
+
auto_create_transactions: true,
|
|
80
|
+
auto_update_cleared_status: false,
|
|
81
|
+
auto_unclear_missing: false,
|
|
82
|
+
auto_adjust_dates: false,
|
|
83
|
+
dry_run: false,
|
|
84
|
+
require_exact_match: true,
|
|
85
|
+
confidence_threshold: 0.8,
|
|
86
|
+
max_resolution_attempts: 3,
|
|
87
|
+
include_structured_data: false,
|
|
88
|
+
} as const;
|
|
89
|
+
|
|
90
|
+
const result = await executeReconciliation({
|
|
91
|
+
ynabAPI: api,
|
|
92
|
+
analysis: analysis as any,
|
|
93
|
+
params: params as any,
|
|
94
|
+
budgetId,
|
|
95
|
+
accountId: account.id,
|
|
96
|
+
initialAccount: snapshot,
|
|
97
|
+
currencyCode: 'USD',
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
console.log(
|
|
101
|
+
JSON.stringify(
|
|
102
|
+
{
|
|
103
|
+
summary: result.summary,
|
|
104
|
+
bulk: result.bulk_operation_details,
|
|
105
|
+
actions: result.actions_taken.slice(0, 5),
|
|
106
|
+
},
|
|
107
|
+
null,
|
|
108
|
+
2,
|
|
109
|
+
),
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
const ids: string[] = [];
|
|
113
|
+
for (const action of result.actions_taken) {
|
|
114
|
+
if (action.type === 'create_transaction' && (action.transaction as any)?.id) {
|
|
115
|
+
ids.push((action.transaction as any).id);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
for (const id of ids) {
|
|
119
|
+
await api.transactions.deleteTransaction(budgetId, id);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
main().catch((err) => {
|
|
124
|
+
console.error(err);
|
|
125
|
+
process.exit(1);
|
|
126
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
|
|
3
|
+
const server = spawn('node', ['dist/index.js'], {
|
|
4
|
+
stdio: ['pipe', 'pipe', 'inherit'],
|
|
5
|
+
env: process.env,
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
// Send initialize request
|
|
9
|
+
const initRequest = {
|
|
10
|
+
jsonrpc: '2.0',
|
|
11
|
+
id: 1,
|
|
12
|
+
method: 'initialize',
|
|
13
|
+
params: {
|
|
14
|
+
protocolVersion: '2024-11-05',
|
|
15
|
+
capabilities: {},
|
|
16
|
+
clientInfo: {
|
|
17
|
+
name: 'test-client',
|
|
18
|
+
version: '1.0.0',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// Send list tools request
|
|
24
|
+
const listToolsRequest = {
|
|
25
|
+
jsonrpc: '2.0',
|
|
26
|
+
id: 2,
|
|
27
|
+
method: 'tools/list',
|
|
28
|
+
params: {},
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
let buffer = '';
|
|
32
|
+
server.stdout.on('data', (data) => {
|
|
33
|
+
buffer += data.toString();
|
|
34
|
+
const lines = buffer.split('\n');
|
|
35
|
+
buffer = lines.pop() || '';
|
|
36
|
+
|
|
37
|
+
for (const line of lines) {
|
|
38
|
+
if (line.trim()) {
|
|
39
|
+
try {
|
|
40
|
+
const response = JSON.parse(line);
|
|
41
|
+
console.log('Response:', JSON.stringify(response, null, 2));
|
|
42
|
+
|
|
43
|
+
if (response.id === 1) {
|
|
44
|
+
// After init, send list tools
|
|
45
|
+
server.stdin.write(JSON.stringify(listToolsRequest) + '\n');
|
|
46
|
+
} else if (response.id === 2) {
|
|
47
|
+
// Got tools list
|
|
48
|
+
if (response.result && response.result.tools) {
|
|
49
|
+
console.log(`\n✅ Found ${response.result.tools.length} tools`);
|
|
50
|
+
response.result.tools.forEach((tool) => {
|
|
51
|
+
const desc = tool.description.substring(0, 60);
|
|
52
|
+
console.log(` - ${tool.name}: ${desc}...`);
|
|
53
|
+
});
|
|
54
|
+
} else {
|
|
55
|
+
console.log('\n❌ No tools found in response');
|
|
56
|
+
}
|
|
57
|
+
server.kill();
|
|
58
|
+
}
|
|
59
|
+
} catch (e) {
|
|
60
|
+
// Ignore parse errors for non-JSON output
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
setTimeout(() => {
|
|
67
|
+
console.log('\n⏱️ Timeout - sending requests');
|
|
68
|
+
server.stdin.write(JSON.stringify(initRequest) + '\n');
|
|
69
|
+
}, 1000);
|
|
70
|
+
|
|
71
|
+
setTimeout(() => {
|
|
72
|
+
console.log('❌ Test timed out');
|
|
73
|
+
server.kill();
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}, 10000);
|