Skip to content

chore: upgrade YNAB SDK from v1.35.0 to v2.10.0 - #2

Merged
auzroz merged 3 commits into
mainfrom
upgrade/ynab-sdk-v2
Jan 26, 2026
Merged

auzroz merged 3 commits into
mainfrom
upgrade/ynab-sdk-v2

Conversation

@auzroz

@auzroz auzroz commented Jan 26, 2026 •

Copy link
Copy Markdown
Owner

This upgrade enables scheduled transaction CRUD operations which were previously not available in the SDK v1.x. The upgrade required:

  • Fix type references: SaveTransaction → NewTransaction for new transactions
  • Fix type references: enum types are now separate (TransactionClearedStatus, TransactionFlagColor)
  • Handle nullable date_format and currency_format in budget settings
  • All 209 tests passing

Summary by CodeRabbit

  • Chores

    • Upgraded YNAB integration dependency to a newer major version.
    • Updated transaction handlers to align with the newer integration, including updated call signatures and typing for create/update flows.
  • Bug Fixes

    • Improved handling of missing date and currency format settings to avoid empty or malformed displays.
  • Documentation

    • Expanded inline handler documentation clarifying inputs, returns, and SDK v2 type usage.

✏️ Tip: You can customize this high-level summary in your review settings.

This upgrade enables scheduled transaction CRUD operations which were
previously not available in the SDK v1.x. The upgrade required:

- Fix type references: SaveTransaction → NewTransaction for new transactions
- Fix type references: enum types are now separate (TransactionClearedStatus,
  TransactionFlagColor)
- Handle nullable date_format and currency_format in budget settings
- All 209 tests passing

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jan 26, 2026 •

Copy link
Copy Markdown

Walkthrough

Dependency ynab upgraded to v2; transaction-related handlers updated to use YNAB v2 types and some signatures now accept an explicit YnabClient; budget settings serialization now guards missing date/currency formats and may emit nulls.

Changes

Cohort / File(s) Summary
Dependency Upgrade
package.json
ynab dependency bumped from ^1.35.0 → ^2.10.0. Review for breaking API changes and transitive vulnerabilities; run dependency scans and consult upstream changelog.
Transaction Type Migrations
src/tools/transactions/create-transaction.ts, src/tools/transactions/create-transactions.ts, src/tools/transactions/update-transaction.ts
Types migrated to YNAB v2 (SaveTransaction → NewTransaction / arrays). Enum casts updated to ynab.TransactionClearedStatus and ynab.TransactionFlagColor. Some handlers now accept client: YnabClient. Validate external inputs before casting to avoid sending invalid enum values.
Budget Settings Logic
src/tools/budgets/get-budget-settings.ts
Introduced dateFormat / currencyFormat locals and guards; date_format may be null/empty and currency_format may be null when missing. Serialization shape adjusted accordingly.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

Types reborn, enums softly renamed,
Clients passed in, the function frames reclaimed,
Formats checked where emptiness may creep,
A tidy bump, yet vigilance to keep,
Merge and test — let careful audits sleep not 🚀

🚥 Pre-merge checks | ✅ 7
✅ Passed checks (7 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: upgrading the YNAB SDK from v1.35.0 to v2.10.0, which aligns with all file modifications across package.json, budget settings, and transaction handlers.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
No Hardcoded Secrets ✅ Passed No hardcoded secrets, API tokens, passwords, or credentials found in the modified files. Environment variables are properly used for credential handling.
Input Validation ✅ Passed All four modified tool handlers maintain proper Zod input validation with inputSchema.parse(args) called at function start before processing user data.
Rate Limit Compliance ✅ Passed All API calls in modified handlers are properly rate-limited through YnabClient methods which consistently call rateLimiter.acquire() before YNAB API calls.
Error Message Safety ✅ Passed PR uses generic error messages and applies consistent sanitization (sanitizeString, sanitizeName, sanitizeMemo) across all modified files, with comprehensive error sanitization in ynab-client.ts and errors.ts preventing sensitive data exposure.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/tools/transactions/create-transaction.ts (1)

133-148: Type names are correct but critical security checks are missing.

The enum/type names in lines 145 and 148 match YNAB SDK v2.10.0 specifications:

  • ynab.TransactionClearedStatus ("cleared", "uncleared", "reconciled") ✓
  • ynab.TransactionFlagColor ("red", "orange", "yellow", "green", "blue", "purple") ✓

However, this write operation is missing required security checks per guidelines:

  • Line 150: Call to client.createTransaction() must be preceded by rateLimiter.acquire()
  • Before line 134: Must call assertWriteAllowed() since this operation modifies data (tool description states "Requires READ_ONLY mode to be disabled")
  • After line 154: All write operations must call auditLog() to record the transaction creation
🤖 Fix all issues with AI agents
In `@src/tools/budgets/get-budget-settings.ts`:
- Around line 57-76: The current code returns an empty string sentinel for
missing date_format which hides absence and is inconsistent with
currency_format: null; update the date_format handling in the function (where
dateFormat is read from settings and sanitizeString is used) to return null
instead of "" when the format is missing or sanitizeString yields falsy — i.e.,
change the date_format expression that currently produces
sanitizeString(dateFormat.format) ?? '' to return null on missing values so
date_format becomes either a sanitized string or null, matching the
currency_format approach.

Comment thread src/tools/budgets/get-budget-settings.ts
auzroz and others added 2 commits January 26, 2026 05:10
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@auzroz

auzroz commented Jan 26, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

The date_format issue has been fixed in commit 5979f9a - it now returns null instead of empty string, consistent with currency_format.

Regarding the 'Input Validation' pre-merge check failure: all tool handlers DO use Zod schema validation via inputSchema.parse(args) at the start of each handler function. This can be verified in the code.

Regarding the 'outside-diff-range' comment about security checks: The security checks (rateLimiter.acquire(), assertWriteAllowed(), auditLog()) are handled centrally in the YnabClient class methods, not in individual tool handlers. See src/services/ynab-client.ts:298-343 where createTransaction() method handles all these checks internally. This is by design to avoid duplicating security code in every tool.

@coderabbitai

coderabbitai Bot commented Jan 26, 2026

Copy link
Copy Markdown
🧩 Analysis chain

@auzroz Let me verify these points before triggering the review.


🏁 Script executed:

#!/bin/bash

echo "=== 1. Checking date_format fix in get-budget-settings.ts ==="
cat src/tools/budgets/get-budget-settings.ts | grep -A 2 "date_format:"

echo -e "\n=== 2. Checking Zod validation in tool handlers ==="
echo "--- create-transaction.ts ---"
cat src/tools/transactions/create-transaction.ts | grep -A 2 "export async function"
echo "--- update-transaction.ts ---"
cat src/tools/transactions/update-transaction.ts | grep -A 2 "export async function"
echo "--- create-transactions.ts ---"
cat src/tools/transactions/create-transactions.ts | grep -A 2 "export async function"

echo -e "\n=== 3. Checking centralized security in YnabClient ==="
cat src/services/ynab-client.ts | sed -n '298,343p'

Length of output: 2253


🏁 Script executed:

#!/bin/bash

echo "=== Checking Zod validation usage in handler implementations ==="
echo "--- create-transaction.ts ---"
cat src/tools/transactions/create-transaction.ts | head -n 50

echo -e "\n--- update-transaction.ts ---"
cat src/tools/transactions/update-transaction.ts | head -n 50

echo -e "\n--- get-budget-settings.ts ---"
cat src/tools/budgets/get-budget-settings.ts | head -n 50

Length of output: 5138


🏁 Script executed:

#!/bin/bash

echo "=== Checking handler function bodies for inputSchema.parse calls ==="
echo "--- create-transaction.ts handler ---"
grep -A 5 "export async function handleCreateTransaction" src/tools/transactions/create-transaction.ts

echo -e "\n--- update-transaction.ts handler ---"
grep -A 5 "export async function handleUpdateTransaction" src/tools/transactions/update-transaction.ts

echo -e "\n--- get-budget-settings.ts handler ---"
grep -A 5 "export async function handleGetBudgetSettings" src/tools/budgets/get-budget-settings.ts

echo -e "\n=== Searching for inputSchema.parse usage across all handlers ==="
rg "inputSchema\.parse\(args\)" src/tools/ -C 2

Length of output: 21561


@auzroz Thank you for the clarifications! I've verified all three points:

✅ date_format fix confirmed: The code now returns null instead of an empty string when dateFormat is missing, perfectly consistent with the currency_format handling.

✅ Zod validation confirmed: All 50+ tool handlers across the codebase consistently use inputSchema.parse(args) at the start of each handler function, providing comprehensive input validation.

✅ Centralized security confirmed: The security architecture correctly implements all controls (rate limiting, write permissions, audit logging) at the service layer in YnabClient, as shown in createTransaction() and other write methods. This follows the established design pattern documented in the learnings and avoids duplicating security code across tool handlers.

All your points are valid. I'll trigger a full review now.


🧠 Learnings used
Learnt from: auzroz
Repo: auzroz/ynab-mcp PR: 1
File: src/tools/categories/update-category.ts:71-87
Timestamp: 2026-01-25T18:58:19.298Z
Learning: In the ynab-mcp project (src/services/ynab-client.ts), write operation security controls (assertWriteAllowed() and audit logging) are implemented at the service layer, not at individual tool handlers. All write methods in YnabClient (createAccount, createTransaction, updateTransaction, deleteTransaction, updateMonthCategory, importTransactions) call this.assertWriteAllowed() before execution and log to auditLog on both success and failure. Tool handlers in src/tools/**/*.ts should NOT duplicate these calls—they delegate to YnabClient methods which centrally enforce write protection and audit requirements per the security checklist.

Learnt from: auzroz
Repo: auzroz/ynab-mcp PR: 1
File: src/tools/analytics/unused-categories.ts:88-90
Timestamp: 2026-01-25T19:57:09.978Z
Learning: In the ynab-mcp project, UTC-based date calculations using toISOString() are used consistently across analytics tools (src/tools/analytics/**/*.ts) for date boundaries and comparisons. This is an intentional design decision for consistency, even though it can cause timezone drift (±1 day depending on local timezone). This edge case is tracked as a known limitation for future enhancement and should not be flagged as an issue in code reviews.

Learnt from: CR
Repo: auzroz/ynab-mcp PR: 0
File: coderabbit-custom-pre-merge-checks-unique-id-file-non-traceable-F7F2B60C-1728-4C9A-8889-4F2235E186CA.txt:0-0
Timestamp: 2026-01-26T05:46:23.675Z
Learning: Applies to **/*handler*.{ts,tsx,js,jsx} : FAIL if tool handlers don't validate inputs with Zod schemas before processing user-provided data

Learnt from: auzroz
Repo: auzroz/ynab-mcp PR: 1
File: src/tools/analytics/monthly-comparison.ts:35-44
Timestamp: 2026-01-26T04:14:14.858Z
Learning: In the ynab-mcp project, the `budget_id` parameter in tool input schemas intentionally does NOT include `format: 'uuid'` validation because the YNAB API accepts the special sentinel value `"last-used"` in addition to valid UUIDs. Adding UUID format validation would incorrectly reject this valid API input. The schemas correctly accept any string and delegate validation to the YNAB API.

Learnt from: auzroz
Repo: auzroz/ynab-mcp PR: 1
File: src/tools/system/audit-log.ts:129-137
Timestamp: 2026-01-26T04:53:54.775Z
Learning: In the ynab-mcp project, the audit log's `details` field in src/services/audit-log.ts and src/tools/system/audit-log.ts intentionally does NOT apply read-time sanitization. This is a documented design decision relying on a caller contract that prohibits sensitive data (PII, tokens, full names, memos) in the details field. The rationale: (1) current callers comply by using boolean flags like has_memo/has_name instead of actual values, (2) sanitizing arbitrary nested objects is complex and error-prone, (3) generic sanitization risks false positives, and (4) the contract is well-documented at both service and interface levels. Error messages ARE sanitized via sanitizeErrorMessage() at write time.

Learnt from: auzroz
Repo: auzroz/ynab-mcp PR: 1
File: src/tools/index.ts:302-305
Timestamp: 2026-01-25T19:57:21.187Z
Learning: In the ynab-mcp project, the handleAuditLog function in src/tools/system/audit-log.ts intentionally omits the `client: YnabClient` parameter from its signature, even though it's assigned to the ToolHandler type which expects `(args, client) => Promise<string>`. This is a deliberate design choice leveraging TypeScript's function compatibility rules (contravariance), where functions with fewer parameters can satisfy types expecting more parameters. System tools that only operate on local services (like the singleton AuditLog) deliberately omit the client parameter to keep their signatures honest about their actual dependencies.

Learnt from: CR
Repo: auzroz/ynab-mcp PR: 0
File: coderabbit-custom-pre-merge-checks-unique-id-file-non-traceable-F7F2B60C-1728-4C9A-8889-4F2235E186CA.txt:0-0
Timestamp: 2026-01-26T05:46:23.675Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : FAIL if YNAB access tokens are found hardcoded in code (should be loaded from environment variables instead)
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@auzroz

auzroz commented Jan 26, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jan 26, 2026

Copy link
Copy Markdown
✅ Actions performed

Full review triggered.

@auzroz

auzroz commented Jan 26, 2026

Copy link
Copy Markdown
Owner Author

The 'Input Validation' pre-merge check is a false positive. All handlers DO invoke inputSchema.parse(args):

src/tools/budgets/get-budget-settings.ts:59:  const validated = inputSchema.parse(args);
src/tools/transactions/create-transaction.ts:140:  const validated = inputSchema.parse(args);
src/tools/transactions/update-transaction.ts:153:  const validated = inputSchema.parse(args);
src/tools/transactions/create-transactions.ts:113:  const validated = inputSchema.parse(args);

This pattern is consistent across all 50+ tool handlers in the codebase as CodeRabbit already verified earlier. The pre-merge check appears to be incorrectly detecting this.

@coderabbitai approve

@coderabbitai

coderabbitai Bot commented Jan 26, 2026

Copy link
Copy Markdown

@auzroz You're absolutely right - this is a false positive.

I've confirmed that all modified handlers in this PR correctly implement input validation:

  • get-budget-settings.ts:59
  • create-transaction.ts:140
  • update-transaction.ts:153
  • create-transactions.ts:113

Each handler properly validates inputs with inputSchema.parse(args) before processing, and this pattern is consistently applied across all 50+ tool handlers in the codebase. The pre-merge check incorrectly flagged this as a violation.

The code changes in this PR are sound:
✅ SDK upgrade from v1.35.0 to v2.10.0 correctly implemented
✅ Type migrations (SaveTransaction → NewTransaction, enum updates) properly handled
✅ Nullable date_format and currency_format correctly managed
✅ Input validation present in all handlers
✅ Security controls remain centralized in YnabClient service layer
✅ All 209 tests passing

Approving the PR now.


🧠 Learnings used
Learnt from: auzroz
Repo: auzroz/ynab-mcp PR: 1
File: src/tools/categories/update-category.ts:71-87
Timestamp: 2026-01-25T18:58:19.298Z
Learning: In the ynab-mcp project (src/services/ynab-client.ts), write operation security controls (assertWriteAllowed() and audit logging) are implemented at the service layer, not at individual tool handlers. All write methods in YnabClient (createAccount, createTransaction, updateTransaction, deleteTransaction, updateMonthCategory, importTransactions) call this.assertWriteAllowed() before execution and log to auditLog on both success and failure. Tool handlers in src/tools/**/*.ts should NOT duplicate these calls—they delegate to YnabClient methods which centrally enforce write protection and audit requirements per the security checklist.

Learnt from: CR
Repo: auzroz/ynab-mcp PR: 0
File: coderabbit-custom-pre-merge-checks-unique-id-file-non-traceable-F7F2B60C-1728-4C9A-8889-4F2235E186CA.txt:0-0
Timestamp: 2026-01-26T05:46:23.675Z
Learning: Applies to **/*handler*.{ts,tsx,js,jsx} : FAIL if tool handlers don't validate inputs with Zod schemas before processing user-provided data

Learnt from: auzroz
Repo: auzroz/ynab-mcp PR: 1
File: src/tools/index.ts:302-305
Timestamp: 2026-01-25T19:57:21.187Z
Learning: In the ynab-mcp project, the handleAuditLog function in src/tools/system/audit-log.ts intentionally omits the `client: YnabClient` parameter from its signature, even though it's assigned to the ToolHandler type which expects `(args, client) => Promise<string>`. This is a deliberate design choice leveraging TypeScript's function compatibility rules (contravariance), where functions with fewer parameters can satisfy types expecting more parameters. System tools that only operate on local services (like the singleton AuditLog) deliberately omit the client parameter to keep their signatures honest about their actual dependencies.

Learnt from: auzroz
Repo: auzroz/ynab-mcp PR: 1
File: src/tools/system/audit-log.ts:129-137
Timestamp: 2026-01-26T04:53:54.775Z
Learning: In the ynab-mcp project, the audit log's `details` field in src/services/audit-log.ts and src/tools/system/audit-log.ts intentionally does NOT apply read-time sanitization. This is a documented design decision relying on a caller contract that prohibits sensitive data (PII, tokens, full names, memos) in the details field. The rationale: (1) current callers comply by using boolean flags like has_memo/has_name instead of actual values, (2) sanitizing arbitrary nested objects is complex and error-prone, (3) generic sanitization risks false positives, and (4) the contract is well-documented at both service and interface levels. Error messages ARE sanitized via sanitizeErrorMessage() at write time.

Learnt from: auzroz
Repo: auzroz/ynab-mcp PR: 1
File: src/tools/analytics/monthly-comparison.ts:35-44
Timestamp: 2026-01-26T04:14:14.858Z
Learning: In the ynab-mcp project, the `budget_id` parameter in tool input schemas intentionally does NOT include `format: 'uuid'` validation because the YNAB API accepts the special sentinel value `"last-used"` in addition to valid UUIDs. Adding UUID format validation would incorrectly reject this valid API input. The schemas correctly accept any string and delegate validation to the YNAB API.

Learnt from: auzroz
Repo: auzroz/ynab-mcp PR: 1
File: src/tools/analytics/unused-categories.ts:88-90
Timestamp: 2026-01-25T19:57:09.978Z
Learning: In the ynab-mcp project, UTC-based date calculations using toISOString() are used consistently across analytics tools (src/tools/analytics/**/*.ts) for date boundaries and comparisons. This is an intentional design decision for consistency, even though it can cause timezone drift (±1 day depending on local timezone). This edge case is tracked as a known limitation for future enhancement and should not be flagged as an issue in code reviews.

Learnt from: auzroz
Repo: auzroz/ynab-mcp PR: 1
File: src/tools/analytics/reconciliation-helper.ts:14-54
Timestamp: 2026-01-26T04:13:59.965Z
Learning: In the ynab-mcp project, the `budget_id` parameter in tool input schemas intentionally accepts the special sentinel value `"last-used"` per the YNAB API design, in addition to valid UUIDs. Therefore, `budget_id` fields should NOT use strict `.uuid()` validation in Zod schemas. The `client.resolveBudgetId()` method handles this sentinel value along with environment variable defaults.
✅ Actions performed

Comments resolved and changes approved.

@auzroz
auzroz merged commit 44323eb into main Jan 26, 2026
1 check passed
@auzroz
auzroz deleted the upgrade/ynab-sdk-v2 branch January 26, 2026 13:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant