@dizzlkheinz/ynab-mcpb 0.18.3 → 0.19.0
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/CHANGELOG.md +17 -0
- package/CLAUDE.md +87 -8
- package/bin/ynab-mcp-server.cjs +2 -2
- package/bin/ynab-mcp-server.js +3 -3
- package/biome.json +39 -0
- package/dist/bundle/index.cjs +67 -67
- package/dist/index.d.ts +1 -1
- package/dist/index.js +27 -27
- package/dist/server/YNABMCPServer.d.ts +3 -4
- package/dist/server/YNABMCPServer.js +111 -116
- package/dist/server/budgetResolver.d.ts +6 -5
- package/dist/server/budgetResolver.js +46 -36
- package/dist/server/cacheKeys.js +6 -6
- package/dist/server/cacheManager.js +14 -11
- package/dist/server/completions.d.ts +2 -2
- package/dist/server/completions.js +20 -15
- package/dist/server/config.d.ts +10 -5
- package/dist/server/config.js +24 -7
- package/dist/server/deltaCache.d.ts +2 -2
- package/dist/server/deltaCache.js +22 -16
- package/dist/server/deltaCache.merge.d.ts +2 -2
- package/dist/server/diagnostics.d.ts +4 -4
- package/dist/server/diagnostics.js +38 -32
- package/dist/server/errorHandler.d.ts +5 -12
- package/dist/server/errorHandler.js +219 -217
- package/dist/server/prompts.d.ts +2 -2
- package/dist/server/prompts.js +45 -45
- package/dist/server/rateLimiter.js +4 -4
- package/dist/server/requestLogger.d.ts +1 -1
- package/dist/server/requestLogger.js +40 -35
- package/dist/server/resources.d.ts +3 -3
- package/dist/server/resources.js +55 -52
- package/dist/server/responseFormatter.js +6 -6
- package/dist/server/securityMiddleware.d.ts +2 -2
- package/dist/server/securityMiddleware.js +22 -20
- package/dist/server/serverKnowledgeStore.js +1 -1
- package/dist/server/toolRegistry.d.ts +3 -3
- package/dist/server/toolRegistry.js +47 -40
- package/dist/tools/__tests__/deltaTestUtils.d.ts +3 -3
- package/dist/tools/__tests__/deltaTestUtils.js +2 -2
- package/dist/tools/accountTools.d.ts +9 -8
- package/dist/tools/accountTools.js +47 -47
- package/dist/tools/adapters.d.ts +13 -8
- package/dist/tools/adapters.js +21 -11
- package/dist/tools/budgetTools.d.ts +8 -7
- package/dist/tools/budgetTools.js +22 -22
- package/dist/tools/categoryTools.d.ts +9 -8
- package/dist/tools/categoryTools.js +68 -59
- package/dist/tools/compareTransactions/formatter.d.ts +3 -3
- package/dist/tools/compareTransactions/formatter.js +9 -9
- package/dist/tools/compareTransactions/index.d.ts +6 -6
- package/dist/tools/compareTransactions/index.js +58 -43
- package/dist/tools/compareTransactions/matcher.d.ts +1 -1
- package/dist/tools/compareTransactions/matcher.js +28 -15
- package/dist/tools/compareTransactions/parser.d.ts +2 -2
- package/dist/tools/compareTransactions/parser.js +144 -138
- package/dist/tools/compareTransactions/types.d.ts +4 -4
- package/dist/tools/compareTransactions.d.ts +1 -1
- package/dist/tools/compareTransactions.js +1 -1
- package/dist/tools/deltaFetcher.d.ts +2 -2
- package/dist/tools/deltaFetcher.js +16 -15
- package/dist/tools/deltaSupport.d.ts +4 -4
- package/dist/tools/deltaSupport.js +35 -41
- package/dist/tools/exportTransactions.d.ts +5 -4
- package/dist/tools/exportTransactions.js +61 -59
- package/dist/tools/monthTools.d.ts +7 -6
- package/dist/tools/monthTools.js +31 -29
- package/dist/tools/payeeTools.d.ts +7 -6
- package/dist/tools/payeeTools.js +28 -28
- package/dist/tools/reconcileAdapter.d.ts +2 -2
- package/dist/tools/reconcileAdapter.js +21 -11
- package/dist/tools/reconciliation/analyzer.d.ts +4 -4
- package/dist/tools/reconciliation/analyzer.js +136 -57
- package/dist/tools/reconciliation/csvParser.d.ts +3 -3
- package/dist/tools/reconciliation/csvParser.js +128 -104
- package/dist/tools/reconciliation/executor.d.ts +4 -4
- package/dist/tools/reconciliation/executor.js +148 -109
- package/dist/tools/reconciliation/index.d.ts +10 -10
- package/dist/tools/reconciliation/index.js +96 -83
- package/dist/tools/reconciliation/matcher.d.ts +3 -3
- package/dist/tools/reconciliation/matcher.js +17 -16
- package/dist/tools/reconciliation/payeeNormalizer.js +19 -8
- package/dist/tools/reconciliation/recommendationEngine.d.ts +1 -1
- package/dist/tools/reconciliation/recommendationEngine.js +40 -40
- package/dist/tools/reconciliation/reportFormatter.d.ts +2 -2
- package/dist/tools/reconciliation/reportFormatter.js +79 -54
- package/dist/tools/reconciliation/signDetector.d.ts +1 -1
- package/dist/tools/reconciliation/types.d.ts +19 -16
- package/dist/tools/reconciliation/ynabAdapter.d.ts +2 -2
- package/dist/tools/schemas/common.d.ts +1 -1
- package/dist/tools/schemas/common.js +1 -1
- package/dist/tools/schemas/outputs/accountOutputs.d.ts +1 -1
- package/dist/tools/schemas/outputs/accountOutputs.js +24 -18
- package/dist/tools/schemas/outputs/budgetOutputs.d.ts +1 -1
- package/dist/tools/schemas/outputs/budgetOutputs.js +14 -11
- package/dist/tools/schemas/outputs/categoryOutputs.d.ts +1 -1
- package/dist/tools/schemas/outputs/categoryOutputs.js +49 -29
- package/dist/tools/schemas/outputs/comparisonOutputs.d.ts +1 -1
- package/dist/tools/schemas/outputs/comparisonOutputs.js +12 -12
- package/dist/tools/schemas/outputs/index.d.ts +14 -14
- package/dist/tools/schemas/outputs/index.js +14 -14
- package/dist/tools/schemas/outputs/monthOutputs.d.ts +1 -1
- package/dist/tools/schemas/outputs/monthOutputs.js +56 -41
- package/dist/tools/schemas/outputs/payeeOutputs.d.ts +1 -1
- package/dist/tools/schemas/outputs/payeeOutputs.js +10 -10
- package/dist/tools/schemas/outputs/reconciliationOutputs.d.ts +2 -2
- package/dist/tools/schemas/outputs/reconciliationOutputs.js +45 -45
- package/dist/tools/schemas/outputs/transactionMutationOutputs.d.ts +1 -1
- package/dist/tools/schemas/outputs/transactionMutationOutputs.js +28 -22
- package/dist/tools/schemas/outputs/transactionOutputs.d.ts +1 -1
- package/dist/tools/schemas/outputs/transactionOutputs.js +43 -35
- package/dist/tools/schemas/outputs/utilityOutputs.d.ts +1 -1
- package/dist/tools/schemas/outputs/utilityOutputs.js +5 -3
- package/dist/tools/schemas/shared/commonOutputs.d.ts +1 -1
- package/dist/tools/schemas/shared/commonOutputs.js +15 -9
- package/dist/tools/transactionReadTools.d.ts +11 -0
- package/dist/tools/transactionReadTools.js +202 -0
- package/dist/tools/transactionSchemas.d.ts +309 -0
- package/dist/tools/transactionSchemas.js +235 -0
- package/dist/tools/transactionTools.d.ts +6 -302
- package/dist/tools/transactionTools.js +7 -2054
- package/dist/tools/transactionUtils.d.ts +31 -0
- package/dist/tools/transactionUtils.js +364 -0
- package/dist/tools/transactionWriteTools.d.ts +20 -0
- package/dist/tools/transactionWriteTools.js +1342 -0
- package/dist/tools/utilityTools.d.ts +5 -4
- package/dist/tools/utilityTools.js +11 -11
- package/dist/types/index.d.ts +7 -7
- package/dist/types/index.js +6 -6
- package/dist/types/reconciliation.d.ts +1 -1
- package/dist/types/toolRegistration.d.ts +14 -12
- package/dist/utils/amountUtils.js +1 -1
- package/dist/utils/dateUtils.js +4 -4
- package/dist/utils/errors.d.ts +3 -3
- package/dist/utils/errors.js +4 -4
- package/dist/utils/money.d.ts +2 -2
- package/dist/utils/money.js +8 -8
- package/dist/utils/validationError.d.ts +1 -1
- package/dist/utils/validationError.js +1 -1
- package/docs/assets/examples/reconciliation-with-recommendations.json +66 -66
- package/docs/assets/schemas/reconciliation-v2.json +360 -336
- package/docs/plans/2025-12-25-transaction-tools-refactor-design.md +211 -0
- package/docs/plans/2025-12-25-transaction-tools-refactor.md +905 -0
- package/esbuild.config.mjs +53 -50
- package/meta.json +12548 -12548
- package/package.json +98 -109
- package/scripts/analyze-bundle.mjs +33 -30
- package/scripts/create-pr-description.js +169 -120
- package/scripts/run-all-tests.js +205 -0
- package/scripts/run-domain-integration-tests.js +28 -18
- package/scripts/run-generate-mcpb.js +19 -17
- package/scripts/run-throttled-integration-tests.js +92 -83
- package/scripts/test-delta-params.mjs +149 -120
- package/scripts/test-recommendations.ts +36 -32
- package/scripts/tmpTransaction.ts +80 -43
- package/scripts/validate-env.js +98 -91
- package/scripts/verify-build.js +78 -76
- package/src/__tests__/comprehensive.integration.test.ts +1281 -1154
- package/src/__tests__/performance.test.ts +723 -671
- package/src/__tests__/setup.ts +442 -395
- package/src/__tests__/smoke.e2e.test.ts +41 -39
- package/src/__tests__/testRunner.ts +314 -295
- package/src/__tests__/testUtils.ts +456 -364
- package/src/__tests__/tools/reconciliation/csvParser.integration.test.ts +109 -107
- package/src/__tests__/tools/reconciliation/real-world.integration.test.ts +41 -41
- package/src/index.ts +68 -59
- package/src/server/CLAUDE.md +480 -0
- package/src/server/YNABMCPServer.ts +821 -794
- package/src/server/__tests__/YNABMCPServer.integration.test.ts +929 -893
- package/src/server/__tests__/YNABMCPServer.test.ts +903 -899
- package/src/server/__tests__/budgetResolver.test.ts +466 -423
- package/src/server/__tests__/cacheManager.test.ts +891 -874
- package/src/server/__tests__/completions.integration.test.ts +115 -106
- package/src/server/__tests__/completions.test.ts +334 -313
- package/src/server/__tests__/config.test.ts +98 -86
- package/src/server/__tests__/deltaCache.merge.test.ts +774 -703
- package/src/server/__tests__/deltaCache.swr.test.ts +198 -153
- package/src/server/__tests__/deltaCache.test.ts +946 -759
- package/src/server/__tests__/diagnostics.test.ts +825 -792
- package/src/server/__tests__/errorHandler.integration.test.ts +512 -462
- package/src/server/__tests__/errorHandler.test.ts +402 -397
- package/src/server/__tests__/prompts.test.ts +424 -347
- package/src/server/__tests__/rateLimiter.test.ts +313 -309
- package/src/server/__tests__/requestLogger.test.ts +443 -403
- package/src/server/__tests__/resources.template.test.ts +196 -185
- package/src/server/__tests__/resources.test.ts +294 -288
- package/src/server/__tests__/security.integration.test.ts +487 -421
- package/src/server/__tests__/securityMiddleware.test.ts +519 -444
- package/src/server/__tests__/server-startup.integration.test.ts +509 -490
- package/src/server/__tests__/serverKnowledgeStore.test.ts +174 -173
- package/src/server/__tests__/toolRegistration.test.ts +239 -210
- package/src/server/__tests__/toolRegistry.test.ts +907 -845
- package/src/server/budgetResolver.ts +221 -181
- package/src/server/cacheKeys.ts +6 -6
- package/src/server/cacheManager.ts +498 -484
- package/src/server/completions.ts +267 -243
- package/src/server/config.ts +35 -14
- package/src/server/deltaCache.merge.ts +146 -128
- package/src/server/deltaCache.ts +352 -309
- package/src/server/diagnostics.ts +257 -242
- package/src/server/errorHandler.ts +747 -744
- package/src/server/prompts.ts +181 -176
- package/src/server/rateLimiter.ts +131 -129
- package/src/server/requestLogger.ts +350 -322
- package/src/server/resources.ts +442 -374
- package/src/server/responseFormatter.ts +41 -37
- package/src/server/securityMiddleware.ts +223 -205
- package/src/server/serverKnowledgeStore.ts +67 -67
- package/src/server/toolRegistry.ts +508 -474
- package/src/tools/CLAUDE.md +604 -0
- package/src/tools/__tests__/accountTools.delta.integration.test.ts +128 -111
- package/src/tools/__tests__/accountTools.integration.test.ts +129 -111
- package/src/tools/__tests__/accountTools.test.ts +685 -638
- package/src/tools/__tests__/adapters.test.ts +142 -108
- package/src/tools/__tests__/budgetTools.delta.integration.test.ts +73 -73
- package/src/tools/__tests__/budgetTools.integration.test.ts +132 -124
- package/src/tools/__tests__/budgetTools.test.ts +442 -413
- package/src/tools/__tests__/categoryTools.delta.integration.test.ts +76 -68
- package/src/tools/__tests__/categoryTools.integration.test.ts +314 -288
- package/src/tools/__tests__/categoryTools.test.ts +656 -625
- package/src/tools/__tests__/compareTransactions/formatter.test.ts +535 -462
- package/src/tools/__tests__/compareTransactions/index.test.ts +378 -358
- package/src/tools/__tests__/compareTransactions/matcher.test.ts +497 -398
- package/src/tools/__tests__/compareTransactions/parser.test.ts +765 -747
- package/src/tools/__tests__/compareTransactions.test.ts +352 -332
- package/src/tools/__tests__/compareTransactions.window.test.ts +150 -146
- package/src/tools/__tests__/deltaFetcher.scheduled.integration.test.ts +69 -65
- package/src/tools/__tests__/deltaFetcher.test.ts +325 -265
- package/src/tools/__tests__/deltaSupport.test.ts +211 -184
- package/src/tools/__tests__/deltaTestUtils.ts +37 -33
- package/src/tools/__tests__/exportTransactions.test.ts +205 -200
- package/src/tools/__tests__/monthTools.delta.integration.test.ts +68 -68
- package/src/tools/__tests__/monthTools.integration.test.ts +178 -166
- package/src/tools/__tests__/monthTools.test.ts +561 -512
- package/src/tools/__tests__/payeeTools.delta.integration.test.ts +68 -68
- package/src/tools/__tests__/payeeTools.integration.test.ts +158 -142
- package/src/tools/__tests__/payeeTools.test.ts +486 -434
- package/src/tools/__tests__/transactionSchemas.test.ts +1204 -0
- package/src/tools/__tests__/transactionTools.integration.test.ts +875 -825
- package/src/tools/__tests__/transactionTools.test.ts +4923 -4366
- package/src/tools/__tests__/transactionUtils.test.ts +1016 -0
- package/src/tools/__tests__/utilityTools.integration.test.ts +32 -32
- package/src/tools/__tests__/utilityTools.test.ts +68 -58
- package/src/tools/accountTools.ts +293 -271
- package/src/tools/adapters.ts +120 -63
- package/src/tools/budgetTools.ts +121 -116
- package/src/tools/categoryTools.ts +379 -339
- package/src/tools/compareTransactions/formatter.ts +131 -119
- package/src/tools/compareTransactions/index.ts +249 -214
- package/src/tools/compareTransactions/matcher.ts +259 -209
- package/src/tools/compareTransactions/parser.ts +517 -487
- package/src/tools/compareTransactions/types.ts +38 -38
- package/src/tools/compareTransactions.ts +1 -1
- package/src/tools/deltaFetcher.ts +281 -260
- package/src/tools/deltaSupport.ts +264 -259
- package/src/tools/exportTransactions.ts +230 -218
- package/src/tools/monthTools.ts +180 -165
- package/src/tools/payeeTools.ts +152 -140
- package/src/tools/reconcileAdapter.ts +297 -246
- package/src/tools/reconciliation/CLAUDE.md +506 -0
- package/src/tools/reconciliation/__tests__/adapter.causes.test.ts +135 -112
- package/src/tools/reconciliation/__tests__/adapter.test.ts +249 -227
- package/src/tools/reconciliation/__tests__/analyzer.test.ts +408 -335
- package/src/tools/reconciliation/__tests__/csvParser.test.ts +71 -69
- package/src/tools/reconciliation/__tests__/executor.integration.test.ts +348 -323
- package/src/tools/reconciliation/__tests__/executor.progress.test.ts +503 -457
- package/src/tools/reconciliation/__tests__/executor.test.ts +898 -831
- package/src/tools/reconciliation/__tests__/matcher.test.ts +667 -663
- package/src/tools/reconciliation/__tests__/payeeNormalizer.test.ts +296 -276
- package/src/tools/reconciliation/__tests__/recommendationEngine.integration.test.ts +692 -624
- package/src/tools/reconciliation/__tests__/recommendationEngine.test.ts +1008 -986
- package/src/tools/reconciliation/__tests__/reconciliation.delta.integration.test.ts +187 -146
- package/src/tools/reconciliation/__tests__/reportFormatter.test.ts +583 -530
- package/src/tools/reconciliation/__tests__/scenarios/adapterCurrency.scenario.test.ts +75 -71
- package/src/tools/reconciliation/__tests__/scenarios/extremes.scenario.test.ts +70 -58
- package/src/tools/reconciliation/__tests__/scenarios/repeatAmount.scenario.test.ts +102 -88
- package/src/tools/reconciliation/__tests__/schemaUrl.test.ts +58 -43
- package/src/tools/reconciliation/__tests__/signDetector.test.ts +209 -206
- package/src/tools/reconciliation/__tests__/ynabAdapter.test.ts +66 -60
- package/src/tools/reconciliation/analyzer.ts +582 -406
- package/src/tools/reconciliation/csvParser.ts +656 -609
- package/src/tools/reconciliation/executor.ts +1290 -1128
- package/src/tools/reconciliation/index.ts +580 -528
- package/src/tools/reconciliation/matcher.ts +256 -240
- package/src/tools/reconciliation/payeeNormalizer.ts +92 -78
- package/src/tools/reconciliation/recommendationEngine.ts +357 -345
- package/src/tools/reconciliation/reportFormatter.ts +349 -276
- package/src/tools/reconciliation/signDetector.ts +89 -83
- package/src/tools/reconciliation/types.ts +164 -153
- package/src/tools/reconciliation/ynabAdapter.ts +17 -15
- package/src/tools/schemas/CLAUDE.md +546 -0
- package/src/tools/schemas/common.ts +1 -1
- package/src/tools/schemas/outputs/__tests__/accountOutputs.test.ts +410 -409
- package/src/tools/schemas/outputs/__tests__/budgetOutputs.test.ts +305 -299
- package/src/tools/schemas/outputs/__tests__/categoryOutputs.test.ts +431 -430
- package/src/tools/schemas/outputs/__tests__/comparisonOutputs.test.ts +510 -495
- package/src/tools/schemas/outputs/__tests__/dateValidation.test.ts +179 -153
- package/src/tools/schemas/outputs/__tests__/discrepancyDirection.test.ts +293 -254
- package/src/tools/schemas/outputs/__tests__/monthOutputs.test.ts +457 -457
- package/src/tools/schemas/outputs/__tests__/payeeOutputs.test.ts +362 -356
- package/src/tools/schemas/outputs/__tests__/reconciliationOutputs.test.ts +402 -399
- package/src/tools/schemas/outputs/__tests__/transactionMutationSchemas.test.ts +225 -211
- package/src/tools/schemas/outputs/__tests__/transactionOutputs.test.ts +457 -454
- package/src/tools/schemas/outputs/__tests__/utilityOutputs.test.ts +316 -315
- package/src/tools/schemas/outputs/accountOutputs.ts +40 -34
- package/src/tools/schemas/outputs/budgetOutputs.ts +24 -19
- package/src/tools/schemas/outputs/categoryOutputs.ts +76 -56
- package/src/tools/schemas/outputs/comparisonOutputs.ts +192 -169
- package/src/tools/schemas/outputs/index.ts +163 -163
- package/src/tools/schemas/outputs/monthOutputs.ts +95 -80
- package/src/tools/schemas/outputs/payeeOutputs.ts +18 -18
- package/src/tools/schemas/outputs/reconciliationOutputs.ts +386 -373
- package/src/tools/schemas/outputs/transactionMutationOutputs.ts +259 -231
- package/src/tools/schemas/outputs/transactionOutputs.ts +81 -71
- package/src/tools/schemas/outputs/utilityOutputs.ts +90 -84
- package/src/tools/schemas/shared/commonOutputs.ts +27 -19
- package/src/tools/toolCategories.ts +114 -114
- package/src/tools/transactionReadTools.ts +327 -0
- package/src/tools/transactionSchemas.ts +484 -0
- package/src/tools/transactionTools.ts +107 -2990
- package/src/tools/transactionUtils.ts +621 -0
- package/src/tools/transactionWriteTools.ts +2110 -0
- package/src/tools/utilityTools.ts +46 -41
- package/src/types/CLAUDE.md +477 -0
- package/src/types/__tests__/index.test.ts +51 -51
- package/src/types/index.ts +43 -39
- package/src/types/integration-tests.d.ts +26 -26
- package/src/types/reconciliation.ts +29 -29
- package/src/types/toolAnnotations.ts +30 -30
- package/src/types/toolRegistration.ts +43 -32
- package/src/utils/CLAUDE.md +508 -0
- package/src/utils/__tests__/dateUtils.test.ts +174 -168
- package/src/utils/__tests__/money.test.ts +193 -187
- package/src/utils/amountUtils.ts +5 -5
- package/src/utils/baseError.ts +5 -5
- package/src/utils/dateUtils.ts +29 -26
- package/src/utils/errors.ts +14 -14
- package/src/utils/money.ts +66 -52
- package/src/utils/validationError.ts +1 -1
- package/tsconfig.json +29 -29
- package/tsconfig.prod.json +16 -16
- package/vitest-reporters/split-json-reporter.ts +247 -204
- package/vitest.config.ts +99 -95
- package/.prettierignore +0 -10
- package/.prettierrc.json +0 -10
- package/eslint.config.js +0 -49
package/dist/server/prompts.d.ts
CHANGED
package/dist/server/prompts.js
CHANGED
|
@@ -1,88 +1,88 @@
|
|
|
1
1
|
const defaultPromptDefinitions = [
|
|
2
2
|
{
|
|
3
|
-
name:
|
|
4
|
-
description:
|
|
3
|
+
name: "create-transaction",
|
|
4
|
+
description: "Create a new transaction in YNAB",
|
|
5
5
|
arguments: [
|
|
6
6
|
{
|
|
7
|
-
name:
|
|
8
|
-
description:
|
|
7
|
+
name: "budget_name",
|
|
8
|
+
description: "Name of the budget (optional, uses first budget if not specified)",
|
|
9
9
|
required: false,
|
|
10
10
|
},
|
|
11
11
|
{
|
|
12
|
-
name:
|
|
13
|
-
description:
|
|
12
|
+
name: "account_name",
|
|
13
|
+
description: "Name of the account",
|
|
14
14
|
required: true,
|
|
15
15
|
},
|
|
16
16
|
{
|
|
17
|
-
name:
|
|
18
|
-
description:
|
|
17
|
+
name: "amount",
|
|
18
|
+
description: "Transaction amount (negative for expenses, positive for income)",
|
|
19
19
|
required: true,
|
|
20
20
|
},
|
|
21
21
|
{
|
|
22
|
-
name:
|
|
23
|
-
description:
|
|
22
|
+
name: "payee",
|
|
23
|
+
description: "Who you paid or received money from",
|
|
24
24
|
required: true,
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
|
-
name:
|
|
28
|
-
description:
|
|
27
|
+
name: "category",
|
|
28
|
+
description: "Budget category (optional)",
|
|
29
29
|
required: false,
|
|
30
30
|
},
|
|
31
31
|
{
|
|
32
|
-
name:
|
|
33
|
-
description:
|
|
32
|
+
name: "memo",
|
|
33
|
+
description: "Additional notes (optional)",
|
|
34
34
|
required: false,
|
|
35
35
|
},
|
|
36
36
|
],
|
|
37
37
|
},
|
|
38
38
|
{
|
|
39
|
-
name:
|
|
40
|
-
description:
|
|
39
|
+
name: "budget-summary",
|
|
40
|
+
description: "Get a summary of your budget status",
|
|
41
41
|
arguments: [
|
|
42
42
|
{
|
|
43
|
-
name:
|
|
44
|
-
description:
|
|
43
|
+
name: "budget_name",
|
|
44
|
+
description: "Name of the budget (optional, uses first budget if not specified)",
|
|
45
45
|
required: false,
|
|
46
46
|
},
|
|
47
47
|
{
|
|
48
|
-
name:
|
|
49
|
-
description:
|
|
48
|
+
name: "month",
|
|
49
|
+
description: "Month to analyze (YYYY-MM format, optional, uses current month if not specified)",
|
|
50
50
|
required: false,
|
|
51
51
|
},
|
|
52
52
|
],
|
|
53
53
|
},
|
|
54
54
|
{
|
|
55
|
-
name:
|
|
56
|
-
description:
|
|
55
|
+
name: "account-balances",
|
|
56
|
+
description: "Check balances across all accounts",
|
|
57
57
|
arguments: [
|
|
58
58
|
{
|
|
59
|
-
name:
|
|
60
|
-
description:
|
|
59
|
+
name: "budget_name",
|
|
60
|
+
description: "Name of the budget (optional, uses first budget if not specified)",
|
|
61
61
|
required: false,
|
|
62
62
|
},
|
|
63
63
|
{
|
|
64
|
-
name:
|
|
65
|
-
description:
|
|
64
|
+
name: "account_type",
|
|
65
|
+
description: "Filter by account type (checking, savings, creditCard, etc.)",
|
|
66
66
|
required: false,
|
|
67
67
|
},
|
|
68
68
|
],
|
|
69
69
|
},
|
|
70
70
|
];
|
|
71
71
|
const defaultPromptHandlers = {
|
|
72
|
-
|
|
73
|
-
const budgetName = args?.[
|
|
74
|
-
const accountName = args?.[
|
|
75
|
-
const amount = args?.[
|
|
76
|
-
const payee = args?.[
|
|
77
|
-
const category = args?.[
|
|
78
|
-
const memo = args?.[
|
|
72
|
+
"create-transaction": async (_name, args) => {
|
|
73
|
+
const budgetName = args?.["budget_name"] || "first available budget";
|
|
74
|
+
const accountName = args?.["account_name"] || "[ACCOUNT_NAME]";
|
|
75
|
+
const amount = args?.["amount"] || "[AMOUNT]";
|
|
76
|
+
const payee = args?.["payee"] || "[PAYEE]";
|
|
77
|
+
const category = args?.["category"] || "[CATEGORY]";
|
|
78
|
+
const memo = args?.["memo"] || "";
|
|
79
79
|
return {
|
|
80
80
|
description: `Create a transaction for ${payee} in ${accountName}`,
|
|
81
81
|
messages: [
|
|
82
82
|
{
|
|
83
|
-
role:
|
|
83
|
+
role: "user",
|
|
84
84
|
content: {
|
|
85
|
-
type:
|
|
85
|
+
type: "text",
|
|
86
86
|
text: `Please create a transaction with the following details:
|
|
87
87
|
- Budget: ${budgetName}
|
|
88
88
|
- Account: ${accountName}
|
|
@@ -102,16 +102,16 @@ Use the appropriate YNAB MCP tools to:
|
|
|
102
102
|
],
|
|
103
103
|
};
|
|
104
104
|
},
|
|
105
|
-
|
|
106
|
-
const summaryBudget = args?.[
|
|
107
|
-
const month = args?.[
|
|
105
|
+
"budget-summary": async (_name, args) => {
|
|
106
|
+
const summaryBudget = args?.["budget_name"] || "first available budget";
|
|
107
|
+
const month = args?.["month"] || "current month";
|
|
108
108
|
return {
|
|
109
109
|
description: `Get budget summary for ${summaryBudget}`,
|
|
110
110
|
messages: [
|
|
111
111
|
{
|
|
112
|
-
role:
|
|
112
|
+
role: "user",
|
|
113
113
|
content: {
|
|
114
|
-
type:
|
|
114
|
+
type: "text",
|
|
115
115
|
text: `Please provide a comprehensive budget summary for ${summaryBudget} (${month}):
|
|
116
116
|
|
|
117
117
|
IMPORTANT: In YNAB, understand these key fields:
|
|
@@ -148,16 +148,16 @@ Format the response in a clear, easy-to-read summary.`,
|
|
|
148
148
|
],
|
|
149
149
|
};
|
|
150
150
|
},
|
|
151
|
-
|
|
152
|
-
const balanceBudget = args?.[
|
|
153
|
-
const accountType = args?.[
|
|
151
|
+
"account-balances": async (_name, args) => {
|
|
152
|
+
const balanceBudget = args?.["budget_name"] || "first available budget";
|
|
153
|
+
const accountType = args?.["account_type"] || "all accounts";
|
|
154
154
|
return {
|
|
155
155
|
description: `Check account balances for ${accountType}`,
|
|
156
156
|
messages: [
|
|
157
157
|
{
|
|
158
|
-
role:
|
|
158
|
+
role: "user",
|
|
159
159
|
content: {
|
|
160
|
-
type:
|
|
160
|
+
type: "text",
|
|
161
161
|
text: `Please show account balances for ${balanceBudget}:
|
|
162
162
|
|
|
163
163
|
1. List all budgets and select the appropriate one
|
|
@@ -74,11 +74,11 @@ export class RateLimitError extends Error {
|
|
|
74
74
|
super(message);
|
|
75
75
|
this.resetTime = resetTime;
|
|
76
76
|
this.remaining = remaining;
|
|
77
|
-
this.name =
|
|
77
|
+
this.name = "RateLimitError";
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
export const globalRateLimiter = new RateLimiter({
|
|
81
|
-
enableLogging: process.env[
|
|
82
|
-
process.env[
|
|
83
|
-
process.env[
|
|
81
|
+
enableLogging: process.env["RATE_LIMIT_LOGGING"] === "true" ||
|
|
82
|
+
process.env["LOG_LEVEL"] === "debug" ||
|
|
83
|
+
process.env["VERBOSE_TESTS"] === "true",
|
|
84
84
|
});
|
|
@@ -2,8 +2,8 @@ export class RequestLogger {
|
|
|
2
2
|
constructor(config = {}) {
|
|
3
3
|
this.logs = [];
|
|
4
4
|
this.config = {
|
|
5
|
-
enabled: process.env[
|
|
6
|
-
logLevel: process.env[
|
|
5
|
+
enabled: process.env["NODE_ENV"] !== "production",
|
|
6
|
+
logLevel: process.env["LOG_LEVEL"] || "info",
|
|
7
7
|
maxLogEntries: 1000,
|
|
8
8
|
sanitizeParameters: true,
|
|
9
9
|
...config,
|
|
@@ -16,7 +16,9 @@ export class RequestLogger {
|
|
|
16
16
|
timestamp: new Date(),
|
|
17
17
|
toolName,
|
|
18
18
|
operation,
|
|
19
|
-
parameters: this.config.sanitizeParameters
|
|
19
|
+
parameters: this.config.sanitizeParameters
|
|
20
|
+
? this.sanitizeParameters(parameters)
|
|
21
|
+
: parameters,
|
|
20
22
|
success,
|
|
21
23
|
...(duration !== undefined && { duration }),
|
|
22
24
|
...(error && { error: this.sanitizeError(error) }),
|
|
@@ -46,7 +48,8 @@ export class RequestLogger {
|
|
|
46
48
|
filtered = filtered.filter((log) => log.success === filter.success);
|
|
47
49
|
}
|
|
48
50
|
if (filter.since) {
|
|
49
|
-
|
|
51
|
+
const since = filter.since;
|
|
52
|
+
filtered = filtered.filter((log) => log.timestamp >= since);
|
|
50
53
|
}
|
|
51
54
|
if (filter.limit) {
|
|
52
55
|
filtered = filtered.slice(-filter.limit);
|
|
@@ -61,17 +64,17 @@ export class RequestLogger {
|
|
|
61
64
|
const successfulRequests = this.logs.filter((log) => log.success).length;
|
|
62
65
|
const failedRequests = totalRequests - successfulRequests;
|
|
63
66
|
const durationsWithValues = this.logs
|
|
64
|
-
.
|
|
65
|
-
.
|
|
67
|
+
.map((log) => log.duration)
|
|
68
|
+
.filter((duration) => duration !== undefined);
|
|
66
69
|
const averageDuration = durationsWithValues.length > 0
|
|
67
70
|
? durationsWithValues.reduce((sum, duration) => sum + duration, 0) /
|
|
68
71
|
durationsWithValues.length
|
|
69
72
|
: 0;
|
|
70
73
|
const rateLimitedRequests = this.logs.filter((log) => log.rateLimitInfo?.isLimited).length;
|
|
71
74
|
const toolUsage = {};
|
|
72
|
-
this.logs
|
|
75
|
+
for (const log of this.logs) {
|
|
73
76
|
toolUsage[log.toolName] = (toolUsage[log.toolName] || 0) + 1;
|
|
74
|
-
}
|
|
77
|
+
}
|
|
75
78
|
return {
|
|
76
79
|
totalRequests,
|
|
77
80
|
successfulRequests,
|
|
@@ -85,12 +88,12 @@ export class RequestLogger {
|
|
|
85
88
|
const sanitized = {};
|
|
86
89
|
for (const [key, value] of Object.entries(parameters)) {
|
|
87
90
|
if (this.isSensitiveParameter(key)) {
|
|
88
|
-
sanitized[key] =
|
|
91
|
+
sanitized[key] = "***";
|
|
89
92
|
}
|
|
90
|
-
else if (typeof value ===
|
|
93
|
+
else if (typeof value === "string") {
|
|
91
94
|
sanitized[key] = this.sanitizeString(value);
|
|
92
95
|
}
|
|
93
|
-
else if (typeof value ===
|
|
96
|
+
else if (typeof value === "object" && value !== null) {
|
|
94
97
|
sanitized[key] = this.sanitizeObject(value);
|
|
95
98
|
}
|
|
96
99
|
else {
|
|
@@ -101,39 +104,41 @@ export class RequestLogger {
|
|
|
101
104
|
}
|
|
102
105
|
isSensitiveParameter(key) {
|
|
103
106
|
const sensitiveKeys = [
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
107
|
+
"token",
|
|
108
|
+
"access_token",
|
|
109
|
+
"api_key",
|
|
110
|
+
"password",
|
|
111
|
+
"secret",
|
|
112
|
+
"authorization",
|
|
113
|
+
"auth",
|
|
114
|
+
"key",
|
|
115
|
+
"credential",
|
|
113
116
|
];
|
|
114
117
|
return sensitiveKeys.some((sensitiveKey) => key.toLowerCase().includes(sensitiveKey));
|
|
115
118
|
}
|
|
116
119
|
sanitizeString(value) {
|
|
117
120
|
return value
|
|
118
|
-
.replace(/[a-zA-Z0-9]{30,}/g,
|
|
119
|
-
.replace(/Bearer\s+[a-zA-Z0-9_-]+/gi,
|
|
120
|
-
.replace(/token[s]?[:\s=]+[a-zA-Z0-9_-]+/gi,
|
|
121
|
-
.replace(/key[s]?[:\s=]+[a-zA-Z0-9_-]+/gi,
|
|
121
|
+
.replace(/[a-zA-Z0-9]{30,}/g, "***")
|
|
122
|
+
.replace(/Bearer\s+[a-zA-Z0-9_-]+/gi, "Bearer ***")
|
|
123
|
+
.replace(/token[s]?[:\s=]+[a-zA-Z0-9_-]+/gi, "token=***")
|
|
124
|
+
.replace(/key[s]?[:\s=]+[a-zA-Z0-9_-]+/gi, "key=***");
|
|
122
125
|
}
|
|
123
126
|
sanitizeObject(obj) {
|
|
124
127
|
if (Array.isArray(obj)) {
|
|
125
|
-
return obj.map((item) => typeof item ===
|
|
128
|
+
return obj.map((item) => typeof item === "object" && item !== null
|
|
129
|
+
? this.sanitizeObject(item)
|
|
130
|
+
: item);
|
|
126
131
|
}
|
|
127
|
-
if (typeof obj ===
|
|
132
|
+
if (typeof obj === "object" && obj !== null) {
|
|
128
133
|
const sanitized = {};
|
|
129
134
|
for (const [key, value] of Object.entries(obj)) {
|
|
130
135
|
if (this.isSensitiveParameter(key)) {
|
|
131
|
-
sanitized[key] =
|
|
136
|
+
sanitized[key] = "***";
|
|
132
137
|
}
|
|
133
|
-
else if (typeof value ===
|
|
138
|
+
else if (typeof value === "string") {
|
|
134
139
|
sanitized[key] = this.sanitizeString(value);
|
|
135
140
|
}
|
|
136
|
-
else if (typeof value ===
|
|
141
|
+
else if (typeof value === "object" && value !== null) {
|
|
137
142
|
sanitized[key] = this.sanitizeObject(value);
|
|
138
143
|
}
|
|
139
144
|
else {
|
|
@@ -150,21 +155,21 @@ export class RequestLogger {
|
|
|
150
155
|
outputLog(logEntry) {
|
|
151
156
|
const logMessage = this.formatLogMessage(logEntry);
|
|
152
157
|
if (logEntry.success) {
|
|
153
|
-
if (this.shouldLog(
|
|
158
|
+
if (this.shouldLog("info")) {
|
|
154
159
|
console.error(`[INFO] ${logMessage}`);
|
|
155
160
|
}
|
|
156
161
|
}
|
|
157
162
|
else {
|
|
158
|
-
if (this.shouldLog(
|
|
163
|
+
if (this.shouldLog("error")) {
|
|
159
164
|
console.error(`[ERROR] ${logMessage}`);
|
|
160
165
|
}
|
|
161
166
|
}
|
|
162
|
-
if (logEntry.rateLimitInfo?.isLimited && this.shouldLog(
|
|
167
|
+
if (logEntry.rateLimitInfo?.isLimited && this.shouldLog("warn")) {
|
|
163
168
|
console.error(`[WARN] Rate limit exceeded for ${logEntry.toolName}`);
|
|
164
169
|
}
|
|
165
170
|
}
|
|
166
171
|
shouldLog(level) {
|
|
167
|
-
const levels = [
|
|
172
|
+
const levels = ["error", "warn", "info", "debug"];
|
|
168
173
|
const currentLevelIndex = levels.indexOf(this.config.logLevel);
|
|
169
174
|
const requestedLevelIndex = levels.indexOf(level);
|
|
170
175
|
return requestedLevelIndex <= currentLevelIndex;
|
|
@@ -172,7 +177,7 @@ export class RequestLogger {
|
|
|
172
177
|
formatLogMessage(logEntry) {
|
|
173
178
|
const parts = [
|
|
174
179
|
`${logEntry.toolName}:${logEntry.operation}`,
|
|
175
|
-
logEntry.success ?
|
|
180
|
+
logEntry.success ? "SUCCESS" : "FAILED",
|
|
176
181
|
];
|
|
177
182
|
if (logEntry.duration !== undefined) {
|
|
178
183
|
parts.push(`${logEntry.duration}ms`);
|
|
@@ -183,7 +188,7 @@ export class RequestLogger {
|
|
|
183
188
|
if (logEntry.error) {
|
|
184
189
|
parts.push(`error:"${logEntry.error}"`);
|
|
185
190
|
}
|
|
186
|
-
return parts.join(
|
|
191
|
+
return parts.join(" | ");
|
|
187
192
|
}
|
|
188
193
|
}
|
|
189
194
|
export const globalRequestLogger = new RequestLogger();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import
|
|
3
|
-
import { CacheManager } from
|
|
1
|
+
import { type Resource as MCPResource, type ResourceTemplate as MCPResourceTemplate, type ResourceContents } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
import type * as ynab from "ynab";
|
|
3
|
+
import { CacheManager } from "./cacheManager.js";
|
|
4
4
|
interface ResponseFormatter {
|
|
5
5
|
format(data: unknown): string;
|
|
6
6
|
}
|
package/dist/server/resources.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { ErrorCode, McpError, } from
|
|
2
|
-
import {
|
|
1
|
+
import { ErrorCode, McpError, } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
import { CACHE_TTLS, CacheManager } from "./cacheManager.js";
|
|
3
3
|
const RESOURCE_NOT_FOUND_ERROR_CODE = -32002;
|
|
4
4
|
const defaultResourceHandlers = {
|
|
5
|
-
|
|
6
|
-
const cacheKey = CacheManager.generateKey(
|
|
5
|
+
"ynab://budgets": async (uri, { ynabAPI, responseFormatter, cacheManager }) => {
|
|
6
|
+
const cacheKey = CacheManager.generateKey("resources", "budgets", "list");
|
|
7
7
|
return cacheManager.wrap(cacheKey, {
|
|
8
8
|
ttl: CACHE_TTLS.BUDGETS,
|
|
9
9
|
loader: async () => {
|
|
@@ -20,7 +20,7 @@ const defaultResourceHandlers = {
|
|
|
20
20
|
return [
|
|
21
21
|
{
|
|
22
22
|
uri: uri,
|
|
23
|
-
mimeType:
|
|
23
|
+
mimeType: "application/json",
|
|
24
24
|
text: responseFormatter.format({ budgets }),
|
|
25
25
|
},
|
|
26
26
|
];
|
|
@@ -32,8 +32,8 @@ const defaultResourceHandlers = {
|
|
|
32
32
|
},
|
|
33
33
|
});
|
|
34
34
|
},
|
|
35
|
-
|
|
36
|
-
const cacheKey = CacheManager.generateKey(
|
|
35
|
+
"ynab://user": async (uri, { ynabAPI, responseFormatter, cacheManager }) => {
|
|
36
|
+
const cacheKey = CacheManager.generateKey("resources", "user");
|
|
37
37
|
return cacheManager.wrap(cacheKey, {
|
|
38
38
|
ttl: CACHE_TTLS.USER_INFO,
|
|
39
39
|
loader: async () => {
|
|
@@ -46,7 +46,7 @@ const defaultResourceHandlers = {
|
|
|
46
46
|
return [
|
|
47
47
|
{
|
|
48
48
|
uri: uri,
|
|
49
|
-
mimeType:
|
|
49
|
+
mimeType: "application/json",
|
|
50
50
|
text: responseFormatter.format({ user }),
|
|
51
51
|
},
|
|
52
52
|
];
|
|
@@ -61,30 +61,30 @@ const defaultResourceHandlers = {
|
|
|
61
61
|
};
|
|
62
62
|
const defaultResourceDefinitions = [
|
|
63
63
|
{
|
|
64
|
-
uri:
|
|
65
|
-
name:
|
|
66
|
-
description:
|
|
67
|
-
mimeType:
|
|
64
|
+
uri: "ynab://budgets",
|
|
65
|
+
name: "YNAB Budgets",
|
|
66
|
+
description: "List of all available budgets",
|
|
67
|
+
mimeType: "application/json",
|
|
68
68
|
},
|
|
69
69
|
{
|
|
70
|
-
uri:
|
|
71
|
-
name:
|
|
72
|
-
description:
|
|
73
|
-
mimeType:
|
|
70
|
+
uri: "ynab://user",
|
|
71
|
+
name: "YNAB User Info",
|
|
72
|
+
description: "Current user information including ID and email address",
|
|
73
|
+
mimeType: "application/json",
|
|
74
74
|
},
|
|
75
75
|
];
|
|
76
76
|
const defaultResourceTemplates = [
|
|
77
77
|
{
|
|
78
|
-
uriTemplate:
|
|
79
|
-
name:
|
|
80
|
-
description:
|
|
81
|
-
mimeType:
|
|
78
|
+
uriTemplate: "ynab://budgets/{budget_id}",
|
|
79
|
+
name: "Budget Details",
|
|
80
|
+
description: "Detailed information for a specific budget",
|
|
81
|
+
mimeType: "application/json",
|
|
82
82
|
handler: async (uri, params, { ynabAPI, responseFormatter, cacheManager }) => {
|
|
83
|
-
const budget_id = params[
|
|
83
|
+
const budget_id = params["budget_id"];
|
|
84
84
|
if (!budget_id) {
|
|
85
|
-
throw new McpError(ErrorCode.InvalidParams,
|
|
85
|
+
throw new McpError(ErrorCode.InvalidParams, "Missing budget_id parameter");
|
|
86
86
|
}
|
|
87
|
-
const cacheKey = CacheManager.generateKey(
|
|
87
|
+
const cacheKey = CacheManager.generateKey("resources", "budgets", "get", budget_id);
|
|
88
88
|
return cacheManager.wrap(cacheKey, {
|
|
89
89
|
ttl: CACHE_TTLS.BUDGETS,
|
|
90
90
|
loader: async () => {
|
|
@@ -93,7 +93,7 @@ const defaultResourceTemplates = [
|
|
|
93
93
|
return [
|
|
94
94
|
{
|
|
95
95
|
uri,
|
|
96
|
-
mimeType:
|
|
96
|
+
mimeType: "application/json",
|
|
97
97
|
text: responseFormatter.format(response.data.budget),
|
|
98
98
|
},
|
|
99
99
|
];
|
|
@@ -107,16 +107,16 @@ const defaultResourceTemplates = [
|
|
|
107
107
|
},
|
|
108
108
|
},
|
|
109
109
|
{
|
|
110
|
-
uriTemplate:
|
|
111
|
-
name:
|
|
112
|
-
description:
|
|
113
|
-
mimeType:
|
|
110
|
+
uriTemplate: "ynab://budgets/{budget_id}/accounts",
|
|
111
|
+
name: "Budget Accounts",
|
|
112
|
+
description: "List of accounts for a specific budget",
|
|
113
|
+
mimeType: "application/json",
|
|
114
114
|
handler: async (uri, params, { ynabAPI, responseFormatter, cacheManager }) => {
|
|
115
|
-
const budget_id = params[
|
|
115
|
+
const budget_id = params["budget_id"];
|
|
116
116
|
if (!budget_id) {
|
|
117
|
-
throw new McpError(ErrorCode.InvalidParams,
|
|
117
|
+
throw new McpError(ErrorCode.InvalidParams, "Missing budget_id parameter");
|
|
118
118
|
}
|
|
119
|
-
const cacheKey = CacheManager.generateKey(
|
|
119
|
+
const cacheKey = CacheManager.generateKey("resources", "accounts", "list", budget_id);
|
|
120
120
|
return cacheManager.wrap(cacheKey, {
|
|
121
121
|
ttl: CACHE_TTLS.ACCOUNTS,
|
|
122
122
|
loader: async () => {
|
|
@@ -125,7 +125,7 @@ const defaultResourceTemplates = [
|
|
|
125
125
|
return [
|
|
126
126
|
{
|
|
127
127
|
uri,
|
|
128
|
-
mimeType:
|
|
128
|
+
mimeType: "application/json",
|
|
129
129
|
text: responseFormatter.format(response.data.accounts),
|
|
130
130
|
},
|
|
131
131
|
];
|
|
@@ -139,20 +139,20 @@ const defaultResourceTemplates = [
|
|
|
139
139
|
},
|
|
140
140
|
},
|
|
141
141
|
{
|
|
142
|
-
uriTemplate:
|
|
143
|
-
name:
|
|
144
|
-
description:
|
|
145
|
-
mimeType:
|
|
142
|
+
uriTemplate: "ynab://budgets/{budget_id}/accounts/{account_id}",
|
|
143
|
+
name: "Account Details",
|
|
144
|
+
description: "Detailed information for a specific account within a budget",
|
|
145
|
+
mimeType: "application/json",
|
|
146
146
|
handler: async (uri, params, { ynabAPI, responseFormatter, cacheManager }) => {
|
|
147
|
-
const budget_id = params[
|
|
148
|
-
const account_id = params[
|
|
147
|
+
const budget_id = params["budget_id"];
|
|
148
|
+
const account_id = params["account_id"];
|
|
149
149
|
if (!budget_id) {
|
|
150
|
-
throw new McpError(ErrorCode.InvalidParams,
|
|
150
|
+
throw new McpError(ErrorCode.InvalidParams, "Missing budget_id parameter");
|
|
151
151
|
}
|
|
152
152
|
if (!account_id) {
|
|
153
|
-
throw new McpError(ErrorCode.InvalidParams,
|
|
153
|
+
throw new McpError(ErrorCode.InvalidParams, "Missing account_id parameter");
|
|
154
154
|
}
|
|
155
|
-
const cacheKey = CacheManager.generateKey(
|
|
155
|
+
const cacheKey = CacheManager.generateKey("resources", "accounts", "get", budget_id, account_id);
|
|
156
156
|
return cacheManager.wrap(cacheKey, {
|
|
157
157
|
ttl: CACHE_TTLS.ACCOUNTS,
|
|
158
158
|
loader: async () => {
|
|
@@ -161,7 +161,7 @@ const defaultResourceTemplates = [
|
|
|
161
161
|
return [
|
|
162
162
|
{
|
|
163
163
|
uri,
|
|
164
|
-
mimeType:
|
|
164
|
+
mimeType: "application/json",
|
|
165
165
|
text: responseFormatter.format(response.data.account),
|
|
166
166
|
},
|
|
167
167
|
];
|
|
@@ -181,7 +181,9 @@ export class ResourceManager {
|
|
|
181
181
|
this.resourceHandlers = { ...defaultResourceHandlers };
|
|
182
182
|
this.resourceDefinitions = [...defaultResourceDefinitions];
|
|
183
183
|
this.resourceTemplates = [];
|
|
184
|
-
|
|
184
|
+
for (const template of defaultResourceTemplates) {
|
|
185
|
+
this.registerTemplate(template);
|
|
186
|
+
}
|
|
185
187
|
}
|
|
186
188
|
registerResource(definition, handler) {
|
|
187
189
|
this.resourceDefinitions.push(definition);
|
|
@@ -242,28 +244,28 @@ export class ResourceManager {
|
|
|
242
244
|
}
|
|
243
245
|
matchTemplate(template, uri) {
|
|
244
246
|
if (!/^[a-z0-9:/\-_{}]+$/i.test(template)) {
|
|
245
|
-
throw new Error(
|
|
247
|
+
throw new Error("Invalid template format: contains unsafe characters");
|
|
246
248
|
}
|
|
247
249
|
const paramNames = [];
|
|
248
250
|
const regexPattern = template
|
|
249
|
-
.replace(/[.*+?^$()|[\]\\]/g,
|
|
251
|
+
.replace(/[.*+?^$()|[\]\\]/g, "\\$&")
|
|
250
252
|
.replace(/{([a-z_][a-z0-9_]*)}/gi, (_, name) => {
|
|
251
253
|
paramNames.push(name);
|
|
252
|
-
return
|
|
254
|
+
return "([^/]+)";
|
|
253
255
|
});
|
|
254
256
|
const regex = new RegExp(`^${regexPattern}$`);
|
|
255
257
|
const match = uri.match(regex);
|
|
256
258
|
if (match) {
|
|
257
259
|
const result = {};
|
|
258
|
-
paramNames.
|
|
260
|
+
for (const [i, name] of paramNames.entries()) {
|
|
259
261
|
const value = match[i + 1];
|
|
260
262
|
if (value) {
|
|
261
|
-
if (value.includes(
|
|
263
|
+
if (value.includes("..") || value.includes("\\")) {
|
|
262
264
|
throw new Error(`Invalid parameter value: ${name}=${value}`);
|
|
263
265
|
}
|
|
264
266
|
result[name] = value;
|
|
265
267
|
}
|
|
266
|
-
}
|
|
268
|
+
}
|
|
267
269
|
return result;
|
|
268
270
|
}
|
|
269
271
|
return null;
|
|
@@ -275,13 +277,14 @@ export class ResourceManager {
|
|
|
275
277
|
}
|
|
276
278
|
const placeholderPattern = /{([^}]+)}/g;
|
|
277
279
|
const paramNames = [];
|
|
278
|
-
let match;
|
|
279
|
-
while (
|
|
280
|
-
const paramName = match[1] ??
|
|
280
|
+
let match = placeholderPattern.exec(uriTemplate);
|
|
281
|
+
while (match !== null) {
|
|
282
|
+
const paramName = match[1] ?? "";
|
|
281
283
|
if (!/^[a-z_][a-z0-9_]*$/i.test(paramName)) {
|
|
282
284
|
throw new Error(`Invalid template parameter name '${paramName}' in template ${uriTemplate}`);
|
|
283
285
|
}
|
|
284
286
|
paramNames.push(paramName);
|
|
287
|
+
match = placeholderPattern.exec(uriTemplate);
|
|
285
288
|
}
|
|
286
289
|
const uniqueNames = new Set(paramNames);
|
|
287
290
|
if (uniqueNames.size !== paramNames.length) {
|