@dizzlkheinz/ynab-mcpb 0.16.1 → 0.17.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/.env.example +33 -33
- package/.github/workflows/ci-tests.yml +45 -45
- package/.github/workflows/claude-code-review.yml +57 -57
- package/.github/workflows/claude.yml +50 -50
- package/.github/workflows/full-integration.yml +22 -22
- package/.github/workflows/publish.yml +11 -2
- package/CLAUDE.md +33 -47
- package/README.md +8 -10
- package/dist/bundle/index.cjs +54 -54
- package/dist/server/YNABMCPServer.d.ts +120 -54
- package/dist/server/YNABMCPServer.js +28 -381
- package/dist/server/config.d.ts +2 -0
- package/dist/server/config.js +1 -0
- package/dist/server/securityMiddleware.d.ts +37 -8
- package/dist/tools/accountTools.d.ts +2 -0
- package/dist/tools/accountTools.js +45 -0
- package/dist/tools/adapters.d.ts +12 -0
- package/dist/tools/adapters.js +25 -0
- package/dist/tools/budgetTools.d.ts +2 -0
- package/dist/tools/budgetTools.js +30 -0
- package/dist/tools/categoryTools.d.ts +2 -0
- package/dist/tools/categoryTools.js +45 -0
- package/dist/tools/monthTools.d.ts +2 -0
- package/dist/tools/monthTools.js +32 -0
- package/dist/tools/payeeTools.d.ts +2 -0
- package/dist/tools/payeeTools.js +32 -0
- package/dist/tools/reconciliation/index.d.ts +2 -0
- package/dist/tools/reconciliation/index.js +33 -0
- package/dist/tools/schemas/common.d.ts +3 -0
- package/dist/tools/schemas/common.js +3 -0
- package/dist/tools/schemas/outputs/comparisonOutputs.d.ts +1 -1
- package/dist/tools/schemas/outputs/index.d.ts +2 -2
- package/dist/tools/schemas/outputs/index.js +2 -2
- package/dist/tools/schemas/outputs/utilityOutputs.d.ts +0 -15
- package/dist/tools/schemas/outputs/utilityOutputs.js +0 -9
- package/dist/tools/transactionTools.d.ts +2 -0
- package/dist/tools/transactionTools.js +124 -0
- package/dist/tools/utilityTools.d.ts +2 -7
- package/dist/tools/utilityTools.js +19 -38
- package/dist/types/index.d.ts +1 -0
- package/dist/types/toolRegistration.d.ts +27 -0
- package/dist/types/toolRegistration.js +1 -0
- package/docs/maintainers/npm-publishing.md +27 -0
- package/docs/reference/API.md +15 -70
- package/docs/technical/reconciliation-system-architecture.md +2251 -2251
- package/package.json +6 -6
- package/scripts/analyze-bundle.mjs +41 -41
- package/scripts/generate-mcpb.ps1 +95 -95
- package/scripts/run-domain-integration-tests.js +4 -1
- package/scripts/watch-and-restart.ps1 +49 -49
- package/src/__tests__/comprehensive.integration.test.ts +0 -28
- package/src/__tests__/performance.test.ts +4 -12
- package/src/__tests__/setup.ts +45 -14
- package/src/__tests__/workflows.e2e.test.ts +1 -51
- package/src/server/YNABMCPServer.ts +33 -519
- package/src/server/__tests__/YNABMCPServer.test.ts +0 -1
- package/src/server/__tests__/toolRegistration.test.ts +236 -0
- package/src/server/config.ts +1 -0
- package/src/tools/__tests__/adapters.test.ts +113 -0
- package/src/tools/__tests__/transactionTools.integration.test.ts +63 -3
- package/src/tools/__tests__/utilityTools.integration.test.ts +1 -85
- package/src/tools/__tests__/utilityTools.test.ts +1 -123
- package/src/tools/accountTools.ts +53 -0
- package/src/tools/adapters.ts +74 -0
- package/src/tools/budgetTools.ts +37 -0
- package/src/tools/categoryTools.ts +53 -0
- package/src/tools/monthTools.ts +39 -0
- package/src/tools/payeeTools.ts +39 -0
- package/src/tools/reconciliation/index.ts +45 -0
- package/src/tools/schemas/common.ts +18 -0
- package/src/tools/schemas/outputs/index.ts +0 -3
- package/src/tools/schemas/outputs/utilityOutputs.ts +2 -43
- package/src/tools/toolCategories.ts +0 -1
- package/src/tools/transactionTools.ts +140 -0
- package/src/tools/utilityTools.ts +24 -55
- package/src/types/index.ts +3 -0
- package/src/types/toolRegistration.ts +88 -0
- package/vitest.config.ts +2 -1
- package/.chunkhound.json +0 -11
- package/.code/agents/01a13ef4-3f23-4f52-b33b-3585b73cfa60/error.txt +0 -3
- package/.code/agents/084fd32f-e298-4728-9103-a78d7dc39613/error.txt +0 -3
- package/.code/agents/0fed51e1-a943-4b97-a2a8-a6f0f27c844d/status.txt +0 -1
- package/.code/agents/1059b6bd-5ccd-4d83-a12c-7c9d89137399/error.txt +0 -5
- package/.code/agents/110/exec-call_F9BDNG7JfxKkq7Vc8ESAvdft.txt +0 -1569
- package/.code/agents/11ebcef3-b13f-4e44-ad80-d94a866804b7/error.txt +0 -3
- package/.code/agents/1398/exec-call_CjItcWMU1G6JoPshX62QvpaR.txt +0 -2832
- package/.code/agents/1398/exec-call_SUVq2ivmONQ5LMCmd7ngmOqr.txt +0 -2709
- package/.code/agents/1398/exec-call_SdNY4NOffdcC5pRYjVXHjPCK.txt +0 -2832
- package/.code/agents/1398/exec-call_qblJo9et1gsFFB63TtLOiji2.txt +0 -2832
- package/.code/agents/1398/exec-call_zaRrzlGz7GJcNzVfkAmML7Zg.txt +0 -2709
- package/.code/agents/171834fd-5905-42fc-bbcc-2c755145b0fc/status.txt +0 -1
- package/.code/agents/1724/exec-call_HvHQe0w5CCG3T7Q3ULT6MO3g.txt +0 -5217
- package/.code/agents/1724/exec-call_QwUNESVzfxxk78K1frh1Vahb.txt +0 -2594
- package/.code/agents/1724/exec-call_aJ1Xwz71XmIpD4SBxSHERzLe.txt +0 -2594
- package/.code/agents/1d7d7ab7-7473-4b69-8b97-6e914f56056a/result.txt +0 -231
- package/.code/agents/210/exec-call_0tQCsKNJ1WTuIchb8wlcFJpW.txt +0 -2590
- package/.code/agents/210/exec-call_8ZlY9cUc8Ft1twi4ch8UJ6IN.txt +0 -5195
- package/.code/agents/2188/exec-call_5HqayBxIteJtoI8oPTiLWgvJ.txt +0 -286
- package/.code/agents/2188/exec-call_XRbBKBq3adZe6dcppAvQtM7G.txt +0 -218
- package/.code/agents/2188/exec-call_ehA0SjpYtrUi6GJXmibLjp4i.txt +0 -180
- package/.code/agents/21902821-ecaf-4759-bb9d-222b90921af5/error.txt +0 -3
- package/.code/agents/232073be-aa0e-46da-b478-5b64dbf03cf5/status.txt +0 -1
- package/.code/agents/234ff534-2336-4771-a8d9-aa04421a63be/result.txt +0 -747
- package/.code/agents/253e2695-dc36-4022-b436-27655e0fc6c7/status.txt +0 -1
- package/.code/agents/2583/exec-call_M59I4eDjpjlBIWBiSxyS0YlJ.txt +0 -2594
- package/.code/agents/2583/exec-call_usLRGh7OhVHtsRBL4iUwRhjq.txt +0 -2594
- package/.code/agents/292aa3ff-dbab-470f-97c9-e7e8fd65e0db/result.txt +0 -144
- package/.code/agents/3134/exec-call_IgCAMGx19lWfuo8zfYIt5FFC.txt +0 -416
- package/.code/agents/3134/exec-call_IxvLR2Oo7kba2QTsI1gHVko8.txt +0 -2590
- package/.code/agents/3134/exec-call_jYvc8hksZChSiysbzKjl2ZbB.txt +0 -2590
- package/.code/agents/329/exec-call_4QdP3SfSO7HGPCwVcqZIth6s.txt +0 -2590
- package/.code/agents/472/exec-call_4AxzEEcWwkKhpqRB3bE8Ha4L.txt +0 -790
- package/.code/agents/472/exec-call_CB3LPYQA8QIZRi8I6kj4J17A.txt +0 -766
- package/.code/agents/472/exec-call_YeoUWvaFoktay2nqVUsa9KKX.txt +0 -790
- package/.code/agents/472/exec-call_jPWgKVquBBXTg0T3Lks5ZfkK.txt +0 -2594
- package/.code/agents/472/exec-call_qBkvunpGBDEHph2jPmTwtcsb.txt +0 -1000
- package/.code/agents/472/exec-call_v0ffRV1p0kTckBmJPzzHAEy0.txt +0 -3489
- package/.code/agents/472/exec-call_xAX5FXqWIlk02d9WubHbHWh8.txt +0 -766
- package/.code/agents/5346/exec-call_9q0muXUuLaucwEqI51Pt7idT.txt +0 -2594
- package/.code/agents/5346/exec-call_B2el3B79rVkq9LhWTI2VYlz7.txt +0 -2456
- package/.code/agents/5346/exec-call_BfX08f02qkZI9uJD5dvCvuoj.txt +0 -2594
- package/.code/agents/543328d0-61d6-4fd1-a723-bb168656e2e2/error.txt +0 -18
- package/.code/agents/5580c02c-1383-4d18-9cbd-cc8a06e3408d/result.txt +0 -48
- package/.code/agents/60ce1a22-5126-44b2-b977-1d5b56142a7b/status.txt +0 -1
- package/.code/agents/6215d9db-7fa9-4429-aeec-3835c3212291/error.txt +0 -1
- package/.code/agents/6743db55-30e5-4b4e-9366-a8214fc7f714/error.txt +0 -1
- package/.code/agents/6bf9591b-b9c9-422c-b0a5-e968c7d8422a/status.txt +0 -1
- package/.code/agents/7/exec-call_eww3GfdEiJZx61sJEQ9wNmt3.txt +0 -1271
- package/.code/agents/70/exec-call_owUtDMYiVgqDf8vsz1i32PFf.txt +0 -1570
- package/.code/agents/8/exec-call_UtrjAcLbhYLatxR4O97fZgnm.txt +0 -2590
- package/.code/agents/82490bc9-f34e-4b1b-8a8e-bccc2e6254f5/error.txt +0 -3
- package/.code/agents/841/exec-call_7nTNhSBCNjTDUIJv7py6CepO.txt +0 -3299
- package/.code/agents/841/exec-call_TLI0yUdUijuUAvI4o3DXEvHO.txt +0 -3299
- package/.code/agents/9/exec-call_XaABQT1hIlRpnKZ2uyBMWsTC.txt +0 -1882
- package/.code/agents/941/exec-call_GuGHRx7NNXWIDAnxUG2NEWPa.txt +0 -2594
- package/.code/agents/95d9fbab-19a2-48af-83f9-c792566a347f/error.txt +0 -1
- package/.code/agents/b0098cb8-cb32-4ada-9bc4-37c587518896/result.txt +0 -170
- package/.code/agents/b4fe59a4-81df-42e2-a112-0153e504faca/error.txt +0 -1
- package/.code/agents/bf4ce152-f623-49d7-aa52-c18631625c3c/error.txt +0 -3
- package/.code/agents/d7d1db75-d7eb-468e-adea-4ef4d916d187/status.txt +0 -1
- package/.code/agents/e2baa9c8-bac3-49e3-a39d-024333e6a990/status.txt +0 -1
- package/.code/agents/e350b8c3-8483-408c-b2bb-94515f492a11/error.txt +0 -3
- package/.code/agents/e63f9919-719f-4ad0-bccf-01b1a596e1e9/status.txt +0 -1
- package/.code/agents/e71695a8-3044-478d-8f12-ed13d02884c7/status.txt +0 -1
- package/.code/agents/f95b7464-3e25-4897-b153-c8dfd63fd605/error.txt +0 -5
- package/.code/agents/fa3c5ddf-cdf7-47a2-930a-b806c6363689/status.txt +0 -1
- package/.github/workflows/pr-description-check.yml +0 -88
- package/AGENTS.md +0 -36
- package/NUL +0 -1
- package/docs/README.md +0 -72
- package/docs/getting-started/CONFIGURATION.md +0 -175
- package/docs/getting-started/INSTALLATION.md +0 -333
- package/docs/getting-started/QUICKSTART.md +0 -282
- package/docs/guides/ARCHITECTURE.md +0 -533
- package/docs/guides/DEPLOYMENT.md +0 -189
- package/docs/guides/INTEGRATION_TESTING.md +0 -730
- package/docs/guides/TESTING.md +0 -591
- package/docs/reconciliation-flow.md +0 -83
- package/docs/reference/EXAMPLES.md +0 -946
- package/docs/reference/TOOLS.md +0 -348
- package/docs/reference/TROUBLESHOOTING.md +0 -481
- package/package.json.tmp +0 -105
- package/temp-recon.ts +0 -126
- package/test-exports/ynab_account_e9ddc2a6_minimal_1items_2025-11-19_09-04-53.json +0 -23
- package/test-exports/ynab_account_e9ddc2a6_minimal_1items_2025-11-19_10-37-42.json +0 -23
- package/test-exports/ynab_account_e9ddc2a6_minimal_4items_2025-11-19_09-02-09.json +0 -44
- package/test-exports/ynab_account_e9ddc2a6_minimal_6items_2025-11-19_10-37-52.json +0 -58
- package/test-exports/ynab_since_2025-10-16_account_53298e13_238items_2025-11-28_13-46-20.json +0 -3662
- package/test-exports/ynab_since_2025-11-01_account_4c18e9f0_minimal_14items_2025-11-16_10-07-10.json +0 -115
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dizzlkheinz/ynab-mcpb",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.1",
|
|
4
4
|
"description": "Model Context Protocol server for YNAB (You Need A Budget) integration",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -23,9 +23,9 @@
|
|
|
23
23
|
"verify-build": "node scripts/verify-build.js",
|
|
24
24
|
"lint": "npm run lint:eslint && npm run format:check",
|
|
25
25
|
"lint:eslint": "eslint src --ext .ts",
|
|
26
|
-
"lint:fix": "eslint src --ext .ts --fix && prettier --write .",
|
|
27
|
-
"format": "prettier --write .",
|
|
28
|
-
"format:check": "prettier --check .",
|
|
26
|
+
"lint:fix": "eslint src --ext .ts --fix && prettier --log-level silent --write .",
|
|
27
|
+
"format": "prettier --log-level silent --write .",
|
|
28
|
+
"format:check": "prettier --log-level silent --check .",
|
|
29
29
|
"type-check": "tsc --noEmit",
|
|
30
30
|
"test": "vitest run --project unit && npm run filter-test-results",
|
|
31
31
|
"test:watch": "vitest",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"author": "",
|
|
67
67
|
"license": "AGPL-3.0",
|
|
68
68
|
"dependencies": {
|
|
69
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
69
|
+
"@modelcontextprotocol/sdk": "^1.24.3",
|
|
70
70
|
"chrono-node": "^2.9.0",
|
|
71
71
|
"csv-parse": "^6.1.0",
|
|
72
72
|
"d3-array": "^3.2.4",
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
"eslint": "^9.35.0",
|
|
91
91
|
"eslint-config-prettier": "^10.1.8",
|
|
92
92
|
"prettier": "^3.3.3",
|
|
93
|
-
"rimraf": "^6.
|
|
93
|
+
"rimraf": "^6.1.2",
|
|
94
94
|
"tsx": "^4.20.6",
|
|
95
95
|
"typescript": "^5.9.2",
|
|
96
96
|
"typescript-eslint": "^8.42.0",
|
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
|
|
3
|
-
let meta;
|
|
4
|
-
try {
|
|
5
|
-
meta = JSON.parse(fs.readFileSync('meta.json', 'utf-8'));
|
|
6
|
-
} catch (error) {
|
|
7
|
-
console.error('❌ Error reading meta.json:', error.message);
|
|
8
|
-
process.exit(1);
|
|
9
|
-
}
|
|
10
|
-
if (!meta.inputs || typeof meta.inputs !== 'object') {
|
|
11
|
-
console.error('❌ Error: meta.inputs is missing or invalid in meta.json');
|
|
12
|
-
process.exit(1);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const inputs = Object.entries(meta.inputs)
|
|
16
|
-
.map(([path, data]) => ({
|
|
17
|
-
path,
|
|
18
|
-
bytes: data.bytes,
|
|
19
|
-
}))
|
|
20
|
-
.sort((a, b) => b.bytes - a.bytes)
|
|
21
|
-
.slice(0, 20);
|
|
22
|
-
|
|
23
|
-
const totalBytes = Object.values(meta.inputs).reduce((sum, item) => sum + item.bytes, 0);
|
|
24
|
-
console.log(`\n📦 Total input size: ${(totalBytes / 1024 / 1024).toFixed(2)} MB`);
|
|
25
|
-
const outputKey = 'dist/bundle/index.cjs';
|
|
26
|
-
if (!meta.outputs || !meta.outputs[outputKey]) {
|
|
27
|
-
console.error(`❌ Error: meta.outputs['${outputKey}'] is missing in meta.json`);
|
|
28
|
-
process.exit(1);
|
|
29
|
-
}
|
|
30
|
-
console.log(`📦 Output size: ${(meta.outputs[outputKey].bytes / 1024 / 1024).toFixed(2)} MB`);
|
|
31
|
-
console.log('\n📊 Top 20 largest inputs in bundle:\n');
|
|
32
|
-
inputs.forEach((item) => {
|
|
33
|
-
const kb = (item.bytes / 1024).toFixed(1).padStart(8);
|
|
34
|
-
console.log(`${kb} KB ${item.path}`);
|
|
35
|
-
}); const kb = (item.bytes / 1024).toFixed(1).padStart(8);
|
|
36
|
-
console.log(`${kb} KB ${item.path}`);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
const totalBytes = Object.values(meta.inputs).reduce((sum, item) => sum + item.bytes, 0);
|
|
40
|
-
console.log(`\n📦 Total input size: ${(totalBytes / 1024 / 1024).toFixed(2)} MB`);
|
|
41
|
-
console.log(`📦 Output size: ${(meta.outputs['dist/bundle/index.cjs'].bytes / 1024 / 1024).toFixed(2)} MB`);
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
|
|
3
|
+
let meta;
|
|
4
|
+
try {
|
|
5
|
+
meta = JSON.parse(fs.readFileSync('meta.json', 'utf-8'));
|
|
6
|
+
} catch (error) {
|
|
7
|
+
console.error('❌ Error reading meta.json:', error.message);
|
|
8
|
+
process.exit(1);
|
|
9
|
+
}
|
|
10
|
+
if (!meta.inputs || typeof meta.inputs !== 'object') {
|
|
11
|
+
console.error('❌ Error: meta.inputs is missing or invalid in meta.json');
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const inputs = Object.entries(meta.inputs)
|
|
16
|
+
.map(([path, data]) => ({
|
|
17
|
+
path,
|
|
18
|
+
bytes: data.bytes,
|
|
19
|
+
}))
|
|
20
|
+
.sort((a, b) => b.bytes - a.bytes)
|
|
21
|
+
.slice(0, 20);
|
|
22
|
+
|
|
23
|
+
const totalBytes = Object.values(meta.inputs).reduce((sum, item) => sum + item.bytes, 0);
|
|
24
|
+
console.log(`\n📦 Total input size: ${(totalBytes / 1024 / 1024).toFixed(2)} MB`);
|
|
25
|
+
const outputKey = 'dist/bundle/index.cjs';
|
|
26
|
+
if (!meta.outputs || !meta.outputs[outputKey]) {
|
|
27
|
+
console.error(`❌ Error: meta.outputs['${outputKey}'] is missing in meta.json`);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
console.log(`📦 Output size: ${(meta.outputs[outputKey].bytes / 1024 / 1024).toFixed(2)} MB`);
|
|
31
|
+
console.log('\n📊 Top 20 largest inputs in bundle:\n');
|
|
32
|
+
inputs.forEach((item) => {
|
|
33
|
+
const kb = (item.bytes / 1024).toFixed(1).padStart(8);
|
|
34
|
+
console.log(`${kb} KB ${item.path}`);
|
|
35
|
+
}); const kb = (item.bytes / 1024).toFixed(1).padStart(8);
|
|
36
|
+
console.log(`${kb} KB ${item.path}`);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const totalBytes = Object.values(meta.inputs).reduce((sum, item) => sum + item.bytes, 0);
|
|
40
|
+
console.log(`\n📦 Total input size: ${(totalBytes / 1024 / 1024).toFixed(2)} MB`);
|
|
41
|
+
console.log(`📦 Output size: ${(meta.outputs['dist/bundle/index.cjs'].bytes / 1024 / 1024).toFixed(2)} MB`);
|
|
@@ -1,96 +1,96 @@
|
|
|
1
|
-
# PowerShell script to generate a .mcpb file using the official Anthropic MCPB CLI
|
|
2
|
-
|
|
3
|
-
param(
|
|
4
|
-
[string]$OutputDir = "dist"
|
|
5
|
-
)
|
|
6
|
-
|
|
7
|
-
Write-Host "Generating YNAB MCP Server .mcpb package using official CLI..." -ForegroundColor Green
|
|
8
|
-
|
|
9
|
-
# Configuration
|
|
10
|
-
$PackageJson = Get-Content "package.json" | ConvertFrom-Json
|
|
11
|
-
$PackageName = $PackageJson.name
|
|
12
|
-
$Version = $PackageJson.version
|
|
13
|
-
|
|
14
|
-
# Ensure we have a built version
|
|
15
|
-
if (-not (Test-Path "dist/index.js")) {
|
|
16
|
-
Write-Host "Build not found. Running build first..." -ForegroundColor Red
|
|
17
|
-
npm run build
|
|
18
|
-
if ($LASTEXITCODE -ne 0) {
|
|
19
|
-
Write-Host "Build failed!" -ForegroundColor Red
|
|
20
|
-
exit 1
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
# Check if official MCPB CLI is installed
|
|
25
|
-
try {
|
|
26
|
-
$mcpbVersion = & mcpb --version
|
|
27
|
-
Write-Host "Using MCPB CLI version: $mcpbVersion" -ForegroundColor Green
|
|
28
|
-
} catch {
|
|
29
|
-
Write-Host "MCPB CLI not found. Installing..." -ForegroundColor Yellow
|
|
30
|
-
npm install -g @anthropic-ai/mcpb
|
|
31
|
-
if ($LASTEXITCODE -ne 0) {
|
|
32
|
-
Write-Host "Failed to install MCPB CLI!" -ForegroundColor Red
|
|
33
|
-
exit 1
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
# Ensure manifest.json exists and is valid
|
|
38
|
-
if (-not (Test-Path "manifest.json")) {
|
|
39
|
-
Write-Host "manifest.json not found. Creating one..." -ForegroundColor Yellow
|
|
40
|
-
& mcpb init -y
|
|
41
|
-
if ($LASTEXITCODE -ne 0) {
|
|
42
|
-
Write-Host "Failed to create manifest!" -ForegroundColor Red
|
|
43
|
-
exit 1
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
# Sync version from package.json to manifest.json
|
|
48
|
-
Write-Host "Syncing version from package.json to manifest.json..." -ForegroundColor Yellow
|
|
49
|
-
$ManifestPath = "manifest.json"
|
|
50
|
-
$ManifestContent = Get-Content $ManifestPath -Raw
|
|
51
|
-
$Manifest = $ManifestContent | ConvertFrom-Json
|
|
52
|
-
|
|
53
|
-
if ($Manifest.version -ne $Version) {
|
|
54
|
-
Write-Host "Updating manifest version from $($Manifest.version) to $Version" -ForegroundColor Cyan
|
|
55
|
-
# Use regex to update version while preserving formatting
|
|
56
|
-
$UpdatedContent = $ManifestContent -replace '("version"\s*:\s*)"[^"]*"', "`$1`"$Version`""
|
|
57
|
-
$UpdatedContent | Set-Content $ManifestPath -NoNewline
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
# Validate the manifest
|
|
61
|
-
Write-Host "Validating manifest..." -ForegroundColor Yellow
|
|
62
|
-
& mcpb validate manifest.json
|
|
63
|
-
if ($LASTEXITCODE -ne 0) {
|
|
64
|
-
Write-Host "Manifest validation failed!" -ForegroundColor Red
|
|
65
|
-
exit 1
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
# Pack the MCPB using official CLI
|
|
69
|
-
Write-Host "Packing MCPB file..." -ForegroundColor Yellow
|
|
70
|
-
$DxtFile = "$PackageName-$Version.mcpb"
|
|
71
|
-
$OutputPath = Join-Path $OutputDir $DxtFile
|
|
72
|
-
|
|
73
|
-
& mcpb pack . $OutputPath
|
|
74
|
-
if ($LASTEXITCODE -ne 0) {
|
|
75
|
-
Write-Host "MCPB packing failed!" -ForegroundColor Red
|
|
76
|
-
exit 1
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
# Get file size
|
|
80
|
-
if (Test-Path $OutputPath) {
|
|
81
|
-
$FileSize = (Get-Item $OutputPath).Length
|
|
82
|
-
$FileSizeKB = [math]::Round($FileSize / 1KB, 1)
|
|
83
|
-
$FileSizeMB = [math]::Round($FileSize / 1MB, 1)
|
|
84
|
-
|
|
85
|
-
Write-Host "Created $OutputPath" -ForegroundColor Green
|
|
86
|
-
Write-Host "Size: $FileSizeKB KB ($FileSizeMB MB)" -ForegroundColor Cyan
|
|
87
|
-
} else {
|
|
88
|
-
Write-Host "MCPB file was not created!" -ForegroundColor Red
|
|
89
|
-
exit 1
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
Write-Host ""
|
|
93
|
-
Write-Host "Installation Instructions:" -ForegroundColor Yellow
|
|
94
|
-
Write-Host "1. Drag and drop the .mcpb file into Claude Desktop" -ForegroundColor White
|
|
95
|
-
Write-Host "2. Set YNAB_ACCESS_TOKEN environment variable" -ForegroundColor White
|
|
1
|
+
# PowerShell script to generate a .mcpb file using the official Anthropic MCPB CLI
|
|
2
|
+
|
|
3
|
+
param(
|
|
4
|
+
[string]$OutputDir = "dist"
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
Write-Host "Generating YNAB MCP Server .mcpb package using official CLI..." -ForegroundColor Green
|
|
8
|
+
|
|
9
|
+
# Configuration
|
|
10
|
+
$PackageJson = Get-Content "package.json" | ConvertFrom-Json
|
|
11
|
+
$PackageName = $PackageJson.name
|
|
12
|
+
$Version = $PackageJson.version
|
|
13
|
+
|
|
14
|
+
# Ensure we have a built version
|
|
15
|
+
if (-not (Test-Path "dist/index.js")) {
|
|
16
|
+
Write-Host "Build not found. Running build first..." -ForegroundColor Red
|
|
17
|
+
npm run build
|
|
18
|
+
if ($LASTEXITCODE -ne 0) {
|
|
19
|
+
Write-Host "Build failed!" -ForegroundColor Red
|
|
20
|
+
exit 1
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
# Check if official MCPB CLI is installed
|
|
25
|
+
try {
|
|
26
|
+
$mcpbVersion = & mcpb --version
|
|
27
|
+
Write-Host "Using MCPB CLI version: $mcpbVersion" -ForegroundColor Green
|
|
28
|
+
} catch {
|
|
29
|
+
Write-Host "MCPB CLI not found. Installing..." -ForegroundColor Yellow
|
|
30
|
+
npm install -g @anthropic-ai/mcpb
|
|
31
|
+
if ($LASTEXITCODE -ne 0) {
|
|
32
|
+
Write-Host "Failed to install MCPB CLI!" -ForegroundColor Red
|
|
33
|
+
exit 1
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
# Ensure manifest.json exists and is valid
|
|
38
|
+
if (-not (Test-Path "manifest.json")) {
|
|
39
|
+
Write-Host "manifest.json not found. Creating one..." -ForegroundColor Yellow
|
|
40
|
+
& mcpb init -y
|
|
41
|
+
if ($LASTEXITCODE -ne 0) {
|
|
42
|
+
Write-Host "Failed to create manifest!" -ForegroundColor Red
|
|
43
|
+
exit 1
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
# Sync version from package.json to manifest.json
|
|
48
|
+
Write-Host "Syncing version from package.json to manifest.json..." -ForegroundColor Yellow
|
|
49
|
+
$ManifestPath = "manifest.json"
|
|
50
|
+
$ManifestContent = Get-Content $ManifestPath -Raw
|
|
51
|
+
$Manifest = $ManifestContent | ConvertFrom-Json
|
|
52
|
+
|
|
53
|
+
if ($Manifest.version -ne $Version) {
|
|
54
|
+
Write-Host "Updating manifest version from $($Manifest.version) to $Version" -ForegroundColor Cyan
|
|
55
|
+
# Use regex to update version while preserving formatting
|
|
56
|
+
$UpdatedContent = $ManifestContent -replace '("version"\s*:\s*)"[^"]*"', "`$1`"$Version`""
|
|
57
|
+
$UpdatedContent | Set-Content $ManifestPath -NoNewline
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
# Validate the manifest
|
|
61
|
+
Write-Host "Validating manifest..." -ForegroundColor Yellow
|
|
62
|
+
& mcpb validate manifest.json
|
|
63
|
+
if ($LASTEXITCODE -ne 0) {
|
|
64
|
+
Write-Host "Manifest validation failed!" -ForegroundColor Red
|
|
65
|
+
exit 1
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
# Pack the MCPB using official CLI
|
|
69
|
+
Write-Host "Packing MCPB file..." -ForegroundColor Yellow
|
|
70
|
+
$DxtFile = "$PackageName-$Version.mcpb"
|
|
71
|
+
$OutputPath = Join-Path $OutputDir $DxtFile
|
|
72
|
+
|
|
73
|
+
& mcpb pack . $OutputPath
|
|
74
|
+
if ($LASTEXITCODE -ne 0) {
|
|
75
|
+
Write-Host "MCPB packing failed!" -ForegroundColor Red
|
|
76
|
+
exit 1
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
# Get file size
|
|
80
|
+
if (Test-Path $OutputPath) {
|
|
81
|
+
$FileSize = (Get-Item $OutputPath).Length
|
|
82
|
+
$FileSizeKB = [math]::Round($FileSize / 1KB, 1)
|
|
83
|
+
$FileSizeMB = [math]::Round($FileSize / 1MB, 1)
|
|
84
|
+
|
|
85
|
+
Write-Host "Created $OutputPath" -ForegroundColor Green
|
|
86
|
+
Write-Host "Size: $FileSizeKB KB ($FileSizeMB MB)" -ForegroundColor Cyan
|
|
87
|
+
} else {
|
|
88
|
+
Write-Host "MCPB file was not created!" -ForegroundColor Red
|
|
89
|
+
exit 1
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
Write-Host ""
|
|
93
|
+
Write-Host "Installation Instructions:" -ForegroundColor Yellow
|
|
94
|
+
Write-Host "1. Drag and drop the .mcpb file into Claude Desktop" -ForegroundColor White
|
|
95
|
+
Write-Host "2. Set YNAB_ACCESS_TOKEN environment variable" -ForegroundColor White
|
|
96
96
|
Write-Host "3. Restart Claude Desktop" -ForegroundColor White
|
|
@@ -22,10 +22,13 @@ const env = {
|
|
|
22
22
|
};
|
|
23
23
|
|
|
24
24
|
const vitestArgs = ['vitest', 'run', '--project', 'integration:domain', ...passthroughArgs];
|
|
25
|
-
const runner =
|
|
25
|
+
const runner = 'npx';
|
|
26
|
+
const useShell = process.platform === 'win32';
|
|
27
|
+
|
|
26
28
|
const child = spawn(runner, vitestArgs, {
|
|
27
29
|
stdio: 'inherit',
|
|
28
30
|
env,
|
|
31
|
+
shell: useShell,
|
|
29
32
|
});
|
|
30
33
|
|
|
31
34
|
child.on('close', (code) => {
|
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
# PowerShell script to watch for file changes and restart MCP
|
|
2
|
-
param(
|
|
3
|
-
[string]$Path = ".\src",
|
|
4
|
-
[string]$Filter = "*.ts",
|
|
5
|
-
[int]$RestartDelay = 3
|
|
6
|
-
)
|
|
7
|
-
|
|
8
|
-
Write-Host "🔍 Watching for changes in: $Path"
|
|
9
|
-
Write-Host "📁 Filter: $Filter"
|
|
10
|
-
|
|
11
|
-
$watcher = New-Object System.IO.FileSystemWatcher
|
|
12
|
-
$watcher.Path = Resolve-Path $Path
|
|
13
|
-
$watcher.Filter = $Filter
|
|
14
|
-
$watcher.EnableRaisingEvents = $true
|
|
15
|
-
$watcher.IncludeSubdirectories = $true
|
|
16
|
-
|
|
17
|
-
$action = {
|
|
18
|
-
$path = $Event.SourceEventArgs.FullPath
|
|
19
|
-
$changeType = $Event.SourceEventArgs.ChangeType
|
|
20
|
-
Write-Host "🔄 File $changeType`: $path"
|
|
21
|
-
|
|
22
|
-
# Build the project
|
|
23
|
-
Write-Host "🏗️ Building project..."
|
|
24
|
-
npm run build
|
|
25
|
-
|
|
26
|
-
if ($LASTEXITCODE -eq 0) {
|
|
27
|
-
Write-Host "✅ Build successful"
|
|
28
|
-
Start-Sleep -Seconds $RestartDelay
|
|
29
|
-
|
|
30
|
-
Write-Host "🔄 Reconnecting to YNAB MCP server..."
|
|
31
|
-
/mcp reconnect ynab-mcp-server
|
|
32
|
-
Start-Sleep -Seconds 1
|
|
33
|
-
/mcp reconnect ynab-mcp-server
|
|
34
|
-
Write-Host "✅ MCP server reconnected"
|
|
35
|
-
} else {
|
|
36
|
-
Write-Host "❌ Build failed, skipping MCP restart"
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
Register-ObjectEvent -InputObject $watcher -EventName "Changed" -Action $action
|
|
41
|
-
Register-ObjectEvent -InputObject $watcher -EventName "Created" -Action $action
|
|
42
|
-
|
|
43
|
-
try {
|
|
44
|
-
Write-Host "✅ File watcher started. Press Ctrl+C to stop."
|
|
45
|
-
while ($true) { Start-Sleep 1 }
|
|
46
|
-
}
|
|
47
|
-
finally {
|
|
48
|
-
$watcher.Dispose()
|
|
49
|
-
Write-Host "👋 File watcher stopped"
|
|
1
|
+
# PowerShell script to watch for file changes and restart MCP
|
|
2
|
+
param(
|
|
3
|
+
[string]$Path = ".\src",
|
|
4
|
+
[string]$Filter = "*.ts",
|
|
5
|
+
[int]$RestartDelay = 3
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
Write-Host "🔍 Watching for changes in: $Path"
|
|
9
|
+
Write-Host "📁 Filter: $Filter"
|
|
10
|
+
|
|
11
|
+
$watcher = New-Object System.IO.FileSystemWatcher
|
|
12
|
+
$watcher.Path = Resolve-Path $Path
|
|
13
|
+
$watcher.Filter = $Filter
|
|
14
|
+
$watcher.EnableRaisingEvents = $true
|
|
15
|
+
$watcher.IncludeSubdirectories = $true
|
|
16
|
+
|
|
17
|
+
$action = {
|
|
18
|
+
$path = $Event.SourceEventArgs.FullPath
|
|
19
|
+
$changeType = $Event.SourceEventArgs.ChangeType
|
|
20
|
+
Write-Host "🔄 File $changeType`: $path"
|
|
21
|
+
|
|
22
|
+
# Build the project
|
|
23
|
+
Write-Host "🏗️ Building project..."
|
|
24
|
+
npm run build
|
|
25
|
+
|
|
26
|
+
if ($LASTEXITCODE -eq 0) {
|
|
27
|
+
Write-Host "✅ Build successful"
|
|
28
|
+
Start-Sleep -Seconds $RestartDelay
|
|
29
|
+
|
|
30
|
+
Write-Host "🔄 Reconnecting to YNAB MCP server..."
|
|
31
|
+
/mcp reconnect ynab-mcp-server
|
|
32
|
+
Start-Sleep -Seconds 1
|
|
33
|
+
/mcp reconnect ynab-mcp-server
|
|
34
|
+
Write-Host "✅ MCP server reconnected"
|
|
35
|
+
} else {
|
|
36
|
+
Write-Host "❌ Build failed, skipping MCP restart"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
Register-ObjectEvent -InputObject $watcher -EventName "Changed" -Action $action
|
|
41
|
+
Register-ObjectEvent -InputObject $watcher -EventName "Created" -Action $action
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
Write-Host "✅ File watcher started. Press Ctrl+C to stop."
|
|
45
|
+
while ($true) { Start-Sleep 1 }
|
|
46
|
+
}
|
|
47
|
+
finally {
|
|
48
|
+
$watcher.Dispose()
|
|
49
|
+
Write-Host "👋 File watcher stopped"
|
|
50
50
|
}
|
|
@@ -748,34 +748,6 @@ describe('YNAB MCP Server - Comprehensive Integration Tests', () => {
|
|
|
748
748
|
expect(mockYnabAPI.user.getUser).toHaveBeenCalledTimes(1);
|
|
749
749
|
},
|
|
750
750
|
);
|
|
751
|
-
|
|
752
|
-
it(
|
|
753
|
-
'should handle amount conversion',
|
|
754
|
-
{ meta: { tier: 'domain', domain: 'workflows' } },
|
|
755
|
-
async () => {
|
|
756
|
-
// Test dollar to milliunits conversion
|
|
757
|
-
const toMilliunitsResult = await executeToolCall(server, 'ynab:convert_amount', {
|
|
758
|
-
amount: 25.75,
|
|
759
|
-
to_milliunits: true,
|
|
760
|
-
});
|
|
761
|
-
validateToolResult(toMilliunitsResult);
|
|
762
|
-
|
|
763
|
-
const toMilli = parseToolResult(toMilliunitsResult);
|
|
764
|
-
expect(toMilli.data.conversion.converted_amount).toBe(25750);
|
|
765
|
-
expect(toMilli.data.conversion.description).toBe('$25.75 = 25750 milliunits');
|
|
766
|
-
|
|
767
|
-
// Test milliunits to dollar conversion
|
|
768
|
-
const toDollarsResult = await executeToolCall(server, 'ynab:convert_amount', {
|
|
769
|
-
amount: 25750,
|
|
770
|
-
to_milliunits: false,
|
|
771
|
-
});
|
|
772
|
-
validateToolResult(toDollarsResult);
|
|
773
|
-
|
|
774
|
-
const dollars = parseToolResult(toDollarsResult);
|
|
775
|
-
expect(dollars.data.conversion.converted_amount).toBe(25.75);
|
|
776
|
-
expect(dollars.data.conversion.description).toBe('25750 milliunits = $25.75');
|
|
777
|
-
},
|
|
778
|
-
);
|
|
779
751
|
});
|
|
780
752
|
|
|
781
753
|
describe('Error Handling Integration', () => {
|
|
@@ -623,12 +623,6 @@ describe('YNAB MCP Server - Performance Tests', () => {
|
|
|
623
623
|
|
|
624
624
|
// Test multiple validation scenarios
|
|
625
625
|
const validationTests = [
|
|
626
|
-
// Valid parameters
|
|
627
|
-
executeToolCall(server, 'ynab:convert_amount', {
|
|
628
|
-
amount: 25.5,
|
|
629
|
-
to_milliunits: true,
|
|
630
|
-
}),
|
|
631
|
-
|
|
632
626
|
// Invalid parameters (should fail quickly)
|
|
633
627
|
executeToolCall(server, 'ynab:get_budget', {
|
|
634
628
|
budget_id: '', // Empty string should fail validation
|
|
@@ -648,10 +642,9 @@ describe('YNAB MCP Server - Performance Tests', () => {
|
|
|
648
642
|
|
|
649
643
|
const totalTime = endTime - startTime;
|
|
650
644
|
|
|
651
|
-
expect(parsed).toHaveLength(
|
|
652
|
-
|
|
653
|
-
const
|
|
654
|
-
const secondError = parsed[2].error ?? parsed[2].data?.error;
|
|
645
|
+
expect(parsed).toHaveLength(2);
|
|
646
|
+
const firstError = parsed[0].error ?? parsed[0].data?.error;
|
|
647
|
+
const secondError = parsed[1].error ?? parsed[1].data?.error;
|
|
655
648
|
expect(firstError?.code).toBe(SecurityErrorCode.VALIDATION_ERROR); // Invalid calls should fail
|
|
656
649
|
expect(secondError?.code).toBe(SecurityErrorCode.VALIDATION_ERROR);
|
|
657
650
|
expect(totalTime).toBeLessThan(1000); // Validation should be fast
|
|
@@ -715,7 +708,6 @@ describe('YNAB MCP Server - Performance Tests', () => {
|
|
|
715
708
|
executeToolCall(server, 'ynab:list_accounts', { budget_id: 'test' }),
|
|
716
709
|
executeToolCall(server, 'ynab:list_transactions', { budget_id: 'test' }),
|
|
717
710
|
executeToolCall(server, 'ynab:list_categories', { budget_id: 'test' }),
|
|
718
|
-
executeToolCall(server, 'ynab:convert_amount', { amount: i * 10, to_milliunits: true }),
|
|
719
711
|
);
|
|
720
712
|
}
|
|
721
713
|
|
|
@@ -724,7 +716,7 @@ describe('YNAB MCP Server - Performance Tests', () => {
|
|
|
724
716
|
|
|
725
717
|
const totalTime = endTime - startTime;
|
|
726
718
|
|
|
727
|
-
expect(results).toHaveLength(
|
|
719
|
+
expect(results).toHaveLength(80); // 20 iterations × 4 tools
|
|
728
720
|
results.forEach((result) => expect(result).toBeDefined());
|
|
729
721
|
expect(totalTime).toBeLessThan(10000); // Should complete within 10 seconds
|
|
730
722
|
});
|
package/src/__tests__/setup.ts
CHANGED
|
@@ -12,6 +12,49 @@ const hasAccessToken = !!process.env['YNAB_ACCESS_TOKEN'];
|
|
|
12
12
|
if (!process.env['SKIP_E2E_TESTS']) {
|
|
13
13
|
process.env['SKIP_E2E_TESTS'] = hasAccessToken ? 'false' : 'true';
|
|
14
14
|
}
|
|
15
|
+
if (!process.env['YNAB_ACCESS_TOKEN']) {
|
|
16
|
+
process.env['YNAB_ACCESS_TOKEN'] = 'test-token-for-mocked-tests';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Set test environment variables immediately
|
|
20
|
+
process.env['NODE_ENV'] = 'test';
|
|
21
|
+
if (!process.env['LOG_LEVEL']) {
|
|
22
|
+
process.env['LOG_LEVEL'] = 'error';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Disable console output for cleaner test output unless VERBOSE_TESTS is set
|
|
26
|
+
if (!process.env['VERBOSE_TESTS']) {
|
|
27
|
+
const originalConsoleError = console.error;
|
|
28
|
+
|
|
29
|
+
console.error = (...args: any[]) => {
|
|
30
|
+
const firstArg = args[0];
|
|
31
|
+
const isString = typeof firstArg === 'string';
|
|
32
|
+
// Only show errors that are part of test assertions, actual errors, or explicitly marked [ERROR]
|
|
33
|
+
if (
|
|
34
|
+
(isString &&
|
|
35
|
+
(firstArg.includes('❌') || firstArg.includes('Test') || firstArg.includes('[ERROR]'))) ||
|
|
36
|
+
firstArg instanceof Error
|
|
37
|
+
) {
|
|
38
|
+
originalConsoleError(...args);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
console.warn = () => {
|
|
43
|
+
// Suppress warnings by default
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
console.log = () => {
|
|
47
|
+
// Suppress logs by default
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
console.info = () => {
|
|
51
|
+
// Suppress info logs by default
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
console.debug = () => {
|
|
55
|
+
// Suppress debug logs by default
|
|
56
|
+
};
|
|
57
|
+
}
|
|
15
58
|
|
|
16
59
|
type TierFilter = 'core' | 'domain' | 'full';
|
|
17
60
|
interface TestMeta {
|
|
@@ -47,26 +90,14 @@ const shouldRunDomain = (domain?: string): boolean => {
|
|
|
47
90
|
* Global test setup
|
|
48
91
|
*/
|
|
49
92
|
beforeAll(async () => {
|
|
50
|
-
// Set test environment variables
|
|
51
|
-
process.env['NODE_ENV'] = 'test';
|
|
52
|
-
|
|
53
93
|
// Set default test token if not provided
|
|
54
94
|
if (!process.env['YNAB_ACCESS_TOKEN']) {
|
|
55
95
|
process.env['YNAB_ACCESS_TOKEN'] = 'test-token-for-mocked-tests';
|
|
56
96
|
}
|
|
57
97
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const originalConsoleError = console.error;
|
|
61
|
-
console.error = (...args: any[]) => {
|
|
62
|
-
// Only show errors that are part of test assertions
|
|
63
|
-
if (args[0]?.includes?.('❌') || args[0]?.includes?.('Test')) {
|
|
64
|
-
originalConsoleError(...args);
|
|
65
|
-
}
|
|
66
|
-
};
|
|
98
|
+
if (process.env['VERBOSE_TESTS']) {
|
|
99
|
+
console.warn('🧪 Test environment initialized');
|
|
67
100
|
}
|
|
68
|
-
|
|
69
|
-
console.warn('🧪 Test environment initialized');
|
|
70
101
|
});
|
|
71
102
|
|
|
72
103
|
/**
|
|
@@ -821,34 +821,6 @@ describeE2E('YNAB MCP Server - End-to-End Workflows', () => {
|
|
|
821
821
|
});
|
|
822
822
|
});
|
|
823
823
|
|
|
824
|
-
describe('Utility Tools Workflow', () => {
|
|
825
|
-
it('should convert amounts between dollars and milliunits', async () => {
|
|
826
|
-
if (testConfig.skipE2ETests) return;
|
|
827
|
-
|
|
828
|
-
// Convert dollars to milliunits
|
|
829
|
-
const toMilliunitsResult = await executeToolCall(server, 'ynab:convert_amount', {
|
|
830
|
-
amount: 25.5,
|
|
831
|
-
to_milliunits: true,
|
|
832
|
-
});
|
|
833
|
-
const milliunits = parseToolResult(toMilliunitsResult);
|
|
834
|
-
|
|
835
|
-
expect(milliunits.data?.conversion?.converted_amount).toBe(25500);
|
|
836
|
-
expect(milliunits.data?.conversion?.description).toContain('25500');
|
|
837
|
-
expect(milliunits.data?.conversion?.to_milliunits).toBe(true);
|
|
838
|
-
|
|
839
|
-
// Convert milliunits to dollars
|
|
840
|
-
const toDollarsResult = await executeToolCall(server, 'ynab:convert_amount', {
|
|
841
|
-
amount: 25500,
|
|
842
|
-
to_milliunits: false,
|
|
843
|
-
});
|
|
844
|
-
const dollars = parseToolResult(toDollarsResult);
|
|
845
|
-
|
|
846
|
-
expect(dollars.data?.conversion?.converted_amount).toBe(25.5);
|
|
847
|
-
expect(dollars.data?.conversion?.description).toContain('$25.50');
|
|
848
|
-
expect(dollars.data?.conversion?.to_milliunits).toBe(false);
|
|
849
|
-
});
|
|
850
|
-
});
|
|
851
|
-
|
|
852
824
|
describe('v0.8.x Architecture Integration Tests', () => {
|
|
853
825
|
describe('Cache System Verification', () => {
|
|
854
826
|
it('should demonstrate cache warming after default budget set', async () => {
|
|
@@ -1065,13 +1037,7 @@ describeE2E('YNAB MCP Server - End-to-End Workflows', () => {
|
|
|
1065
1037
|
expect(tool.description).toBeDefined();
|
|
1066
1038
|
expect(tool.inputSchema).toBeDefined();
|
|
1067
1039
|
|
|
1068
|
-
//
|
|
1069
|
-
// Note: Some utility tools like diagnostic_info or clear_cache may not define structured outputs,
|
|
1070
|
-
// but most data-retrieval and CRUD tools should have output schemas.
|
|
1071
|
-
expect(
|
|
1072
|
-
tool.outputSchema,
|
|
1073
|
-
`Tool '${tool.name}' should define an outputSchema`,
|
|
1074
|
-
).toBeDefined();
|
|
1040
|
+
// Output schemas are optional; tools may omit them.
|
|
1075
1041
|
}
|
|
1076
1042
|
});
|
|
1077
1043
|
});
|
|
@@ -1143,7 +1109,6 @@ describeE2E('YNAB MCP Server - End-to-End Workflows', () => {
|
|
|
1143
1109
|
{ name: 'ynab:list_categories', args: { budget_id: testBudgetId } },
|
|
1144
1110
|
{ name: 'ynab:list_payees', args: { budget_id: testBudgetId } },
|
|
1145
1111
|
{ name: 'ynab:get_user', args: {} },
|
|
1146
|
-
{ name: 'ynab:convert_amount', args: { amount: 100, to_milliunits: true } },
|
|
1147
1112
|
];
|
|
1148
1113
|
|
|
1149
1114
|
for (const tool of v7Tools) {
|
|
@@ -1621,21 +1586,6 @@ describeE2E('YNAB MCP Server - End-to-End Workflows', () => {
|
|
|
1621
1586
|
}
|
|
1622
1587
|
});
|
|
1623
1588
|
|
|
1624
|
-
it('should validate convert_amount output schema', async () => {
|
|
1625
|
-
if (testConfig.skipE2ETests) return;
|
|
1626
|
-
|
|
1627
|
-
const result = await executeToolCall(server, 'ynab:convert_amount', {
|
|
1628
|
-
amount: 100,
|
|
1629
|
-
to_milliunits: true,
|
|
1630
|
-
});
|
|
1631
|
-
const validation = validateOutputSchema(server, 'convert_amount', result);
|
|
1632
|
-
expect(validation.hasSchema).toBe(true);
|
|
1633
|
-
expect(validation.valid).toBe(true);
|
|
1634
|
-
if (!validation.valid) {
|
|
1635
|
-
console.error('Schema validation errors:', validation.errors);
|
|
1636
|
-
}
|
|
1637
|
-
});
|
|
1638
|
-
|
|
1639
1589
|
it('should validate export_transactions output schema', async () => {
|
|
1640
1590
|
if (testConfig.skipE2ETests) return;
|
|
1641
1591
|
|