smwitkowski/ynab-mcp
If you are the rightful owner of ynab-mcp and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to dayong@mcphub.com.
The YNAB MCP Server is a Model Context Protocol server designed to facilitate interaction with You Need A Budget (YNAB) through AI assistants like Claude and Cursor.
YNAB MCP Server
A Model Context Protocol (MCP) server for interacting with You Need A Budget (YNAB) through AI assistants like Claude and Cursor.
Overview
The YNAB MCP Server enables AI assistants to interact with your YNAB budget data, allowing you to:
- Query budget information, account balances, and transactions
- Create and update transactions through natural language
- Perform bulk operations like recategorizing multiple transactions
- Split transactions across multiple categories
- Analyze spending patterns and trends
See the for more details about architecture and use cases.
Quick Start
-
Install dependencies:
npm install -
Build the project:
npm run build -
Configure your YNAB API key (see ):
# Create .env file echo "YNAB_API_KEY=your_key_here" > .env -
Connect to your MCP client (see ):
- Claude Desktop: Add to
claude_desktop_config.json - Cursor: Configure in settings or
mcp.json
- Claude Desktop: Add to
See the for detailed installation instructions.
Documentation
- - Architecture, concepts, and use cases
- - Installation and development setup
- - Getting and configuring API keys
- - Complete API documentation
- - Connecting to Claude Desktop, Cursor, and other MCP clients
- - Security best practices
Features
Tools
- getBudgets - List all YNAB budgets
- getBudgetDetails - Get detailed information about a specific budget
- getAccounts - List all accounts in a budget
- getCategories - List all categories with balances
- getCategoryBalance - Get specific category details
- getTransactions - Get transactions with optional filters
- createTransaction - Create new transactions
- updateTransaction - Update existing transactions
- batchUpdateTransactions - Update multiple transactions in bulk (e.g., bulk recategorization)
- createSplitTransaction - Create split transactions across multiple categories
- splitTransaction - Split existing transactions into multiple categories
- getPayees - List payees
Resources
- Budget Summary - Overview of all budgets with account balances
- Account Balances - Current balances for all accounts in a budget
Prompts
- budget-overview - Quick overview of budget status
- spending-analysis - Analyze spending in specific categories
Response Format
All tools and resources support two response formats:
toon(default): TOON (Token-Oriented Object Notation) format for reduced token usage (30-60% reduction)json: Standard JSON format, human-readable and easy to parse
You can specify the format by including format: "toon" or format: "json" in your tool parameters. When omitted, toon is used by default for optimal token efficiency.
TOON Format: TOON is a compact data format designed specifically for LLM applications. It maintains data fidelity while significantly reducing token counts. Arrays of objects are represented as tables (e.g., items[2]{id,name}:\n 1,Alice\n 2,Bob), making it ideal for large datasets like transaction lists.
Learn more about TOON: toontools.app
Amounts & Units
Understanding how monetary amounts are handled is crucial for using this MCP server correctly.
YNAB API Format (Milliunits)
The YNAB API represents all monetary amounts as integers in milliunits, where 1000 milliunits equals 1.00 in the budget's currency. For example:
1000milliunits =$1.00USD-50000milliunits =-$50.00USD123456milliunits =$123.46USD
This format avoids floating-point precision issues and ensures exact monetary calculations.
This MCP Server's Format (Currency Units)
Tool Inputs: All amount fields in tool parameters (for transactions, splits, etc.) accept numbers in currency units (e.g., -50.00 for -$50.00). The server automatically converts these to YNAB milliunits using toCurrencyAmount() before sending to the API.
Tool Outputs: All amounts and balances returned by tools (e.g., amount, budgeted, balance, clearedBalance) are returned as numbers in currency units (not raw milliunits) for easier use by LLMs and other consumers.
Sign Conventions
- Negative amounts (
-50.00): Outflow (money going out, expenses, payments) - Positive amounts (
50.00): Inflow (money coming in, income, deposits)
Conversion & Rounding
When converting currency-unit inputs to milliunits, values are rounded to the nearest 0.001 in currency units (i.e., the nearest milliunit) before conversion. For example:
50.123→ rounds to50123milliunits50.1234→ rounds to50123milliunits50.1235→ rounds to50124milliunits
Note: All amount fields in the tool documentation below follow these same unit rules.
Error Handling
When a tool encounters an error, it returns a structured error payload instead of throwing an exception. This allows LLMs to inspect error details and handle them appropriately.
Error Response Format
All error responses follow this structure:
{
"success": false,
"errorType": "YNAB_API_ERROR" | "INTERNAL_ERROR",
"message": "Human-readable error message",
"toolName": "nameOfTheTool"
}
YNAB API Errors
When a YNAB API error occurs (e.g., duplicate transaction, invalid account ID), the error payload includes additional fields:
{
"success": false,
"errorType": "YNAB_API_ERROR",
"statusCode": 409,
"errorId": "ynab.error.duplicate_import_id",
"errorName": "Duplicate Import ID",
"message": "a transaction with the same import_id already exists on the specified account",
"toolName": "splitTransaction"
}
Common YNAB API Errors:
409- Duplicate import ID (transaction with sameimport_idalready exists)400- Validation error (invalid parameters, missing required fields)404- Resource not found (budget, account, category, or transaction doesn't exist)
Internal Errors
For non-YNAB errors (e.g., network issues, unexpected exceptions), the error payload uses:
{
"success": false,
"errorType": "INTERNAL_ERROR",
"message": "Description of what went wrong",
"toolName": "createTransaction"
}
Best Practices for LLMs
- Always check
success: Before processing tool results, verify thatsuccess: true(or check forsuccess: false). - Inspect error details: For
YNAB_API_ERROR, checkstatusCode,errorName, andmessageto understand what went wrong. - Handle gracefully: Use error information to provide helpful feedback to users or retry operations with corrected parameters.
Tools Reference
This section documents all available tools, resources, and prompts with their input parameters and expected output structures.
Note: All examples below show both JSON and TOON formats. By default, tools return TOON format for reduced token usage. Specify format: "json" if you need JSON output.
Tools
getBudgets
List all YNAB budgets available to the user.
Parameters:
format(optional): Response format -"toon"(default) or"json"
Example Output (JSON):
{
"budgets": [
{
"id": "12345678-1234-1234-1234-123456789012",
"name": "My Budget",
"lastModified": "2024-01-15T10:30:00Z",
"currency": "USD"
}
],
"defaultBudget": {
"id": "12345678-1234-1234-1234-123456789012",
"name": "My Budget"
}
}
Example Output (TOON):
budgets[1]{id,name,lastModified,currency}:
12345678-1234-1234-1234-123456789012,My Budget,2024-01-15T10:30:00Z,USD
defaultBudget{id,name}:
12345678-1234-1234-1234-123456789012,My Budget
getBudgetDetails
Get detailed information about a specific budget.
Parameters:
budgetId(required): The budget IDformat(optional): Response format -"toon"(default) or"json"
Example Output:
{
"id": "12345678-1234-1234-1234-123456789012",
"name": "My Budget",
"lastModified": "2024-01-15T10:30:00Z",
"currency": "USD",
"accounts": 5,
"categories": 25,
"payees": 50
}
getAccounts
Get all accounts for a budget with their balances.
Parameters:
budgetId(required): The budget IDformat(optional): Response format -"toon"(default) or"json"
Example Output (JSON):
{
"accounts": [
{
"id": "87654321-4321-4321-4321-210987654321",
"name": "Checking Account",
"type": "checking",
"onBudget": true,
"closed": false,
"balance": "$1,234.56",
"clearedBalance": "$1,200.00",
"unclearedBalance": "$34.56"
}
],
"totalBalance": "$5,678.90",
"totalOnBudget": "$4,567.89"
}
Example Output (TOON):
accounts[1]{id,name,type,onBudget,closed,balance,clearedBalance,unclearedBalance}:
87654321-4321-4321-4321-210987654321,Checking Account,checking,true,false,$1,234.56,$1,200.00,$34.56
totalBalance: $5,678.90
totalOnBudget: $4,567.89
getCategories
Get all categories for a budget, organized by category groups.
Parameters:
budgetId(required): The budget IDformat(optional): Response format -"toon"(default) or"json"
Example Output:
{
"categoryGroups": [
{
"groupName": "Immediate Obligations",
"groupId": "group-123",
"hidden": false,
"categories": [
{
"id": "cat-456",
"name": "Rent",
"budgeted": "$1,200.00",
"activity": "-$1,200.00",
"balance": "$0.00",
"hidden": false,
"goalType": "TB",
"goalTarget": "$1,200.00"
}
]
}
],
"totals": {
"budgeted": "$3,000.00",
"activity": "-$2,500.00",
"balance": "$500.00"
}
}
getCategoryBalance
Get balance and details for a specific category.
Parameters:
budgetId(required): The budget IDcategoryId(required): The category IDformat(optional): Response format -"toon"(default) or"json"
Example Output:
{
"id": "cat-456",
"name": "Groceries",
"budgeted": "$500.00",
"activity": "-$450.00",
"balance": "$50.00",
"hidden": false,
"categoryGroup": "Monthly Bills"
}
getTransactions
Get transactions with optional filters. Returns up to 50 transactions.
Parameters:
budgetId(required): The budget IDaccountId(optional): Filter by account IDcategoryId(optional): Filter by category IDpayeeId(optional): Filter by payee IDsinceDate(optional): Only return transactions on or after this date (ISO format)type(optional): Filter by transaction type ("uncategorized"or"unapproved")format(optional): Response format -"toon"(default) or"json"
Example Output (JSON):
{
"count": 25,
"transactions": [
{
"id": "trans-789",
"date": "2024-01-15",
"amount": "-$50.00",
"payee": "Grocery Store",
"category": "Groceries",
"account": "Checking Account",
"memo": "Weekly shopping",
"cleared": "cleared",
"approved": true
}
],
"totalAmount": "-$1,250.00"
}
Example Output (TOON):
count: 25
transactions[1]{id,date,amount,payee,category,account,memo,cleared,approved}:
trans-789,2024-01-15,-$50.00,Grocery Store,Groceries,Checking Account,Weekly shopping,cleared,true
totalAmount: -$1,250.00
createTransaction
Create a new transaction in a budget.
Parameters:
budgetId(required): The budget IDaccountId(required): The account IDdate(required): Transaction date (ISO format, e.g., "2024-01-15")amount(required): Transaction amount in currency units (e.g.,-50.00for-$50.00). The server converts this to YNAB milliunits. Negative for outflow, positive for inflow.payeeName(optional): Payee namecategoryId(optional): Category IDmemo(optional): Transaction memocleared(optional): Cleared status ("cleared","uncleared", or"reconciled")approved(optional): Whether the transaction is approved (default:true)format(optional): Response format -"toon"(default) or"json"
Example Output:
{
"success": true,
"transaction": {
"id": "trans-789",
"date": "2024-01-15",
"amount": "-$50.00",
"payee": "Grocery Store",
"category": "Groceries",
"account": "Checking Account",
"memo": "Weekly shopping",
"cleared": "uncleared",
"approved": true
}
}
updateTransaction
Update an existing transaction.
Parameters:
budgetId(required): The budget IDtransactionId(required): The transaction IDaccountId(optional): The account IDdate(optional): Transaction date (ISO format)amount(optional): Transaction amount in currency units (e.g.,-50.00for-$50.00). The server converts this to YNAB milliunits. Negative for outflow, positive for inflow.payeeName(optional): Payee namecategoryId(optional): Category IDmemo(optional): Transaction memocleared(optional): Cleared status ("cleared","uncleared", or"reconciled")approved(optional): Whether the transaction is approvedformat(optional): Response format -"toon"(default) or"json"
Example Output:
{
"success": true,
"transaction": {
"id": "trans-789",
"date": "2024-01-15",
"amount": "-$55.00",
"payee": "Grocery Store",
"category": "Groceries",
"account": "Checking Account",
"memo": "Weekly shopping - updated",
"cleared": "cleared",
"approved": true
}
}
batchUpdateTransactions
Update multiple transactions in a single call. Ideal for bulk operations like recategorizing many transactions at once. Uses all-or-nothing semantics: if any transaction fails validation, the entire batch fails and no transactions are updated.
Parameters:
budgetId(required): The budget IDupdates(required): Array of transaction updates (minimum 1). Each update must have exactly one of:id(string): The transaction ID, orimportId(string): The transaction import ID
- Each update can also include any of the optional fields from
updateTransaction:accountId,date,amount(in currency units, converted to YNAB milliunits by the server),payeeId,payeeName,categoryId,memo,cleared,approved,flagColor
format(optional): Response format -"toon"(default) or"json"
When to use:
- Use
updateTransactionfor updating a single transaction - Use
batchUpdateTransactionswhen updating 30+ transactions (e.g., bulk recategorization, bulk approval, bulk flagging) - Recommended batch size: up to 100 transactions per call to stay within YNAB rate limits
Example Usage Flow:
- Call
getTransactionswith filters (e.g.,type: 'uncategorized',sinceDate,payeeId) - Identify transactions that need updates
- Call
batchUpdateTransactionsonce with all updates
Example Input:
{
"budgetId": "12345678-1234-1234-1234-123456789012",
"updates": [
{
"id": "trans-1",
"categoryId": "cat-groceries"
},
{
"id": "trans-2",
"categoryId": "cat-groceries"
},
{
"importId": "YNAB:-50000:2024-01-15:1",
"categoryId": "cat-groceries",
"approved": true
}
]
}
Example Output:
{
"success": true,
"updatedCount": 3,
"transactionIds": ["trans-1", "trans-2", "trans-3"],
"serverKnowledge": 1234567890,
"transactions": [
{
"id": "trans-1",
"date": "2024-01-15",
"amount": "-$50.00",
"payee": "Grocery Store",
"category": "Groceries",
"account": "Checking Account",
"memo": null,
"cleared": "uncleared",
"approved": true,
"flagColor": null
}
]
}
Error Handling: If any transaction in the batch fails validation (e.g., invalid category ID, missing required field), the entire batch fails and returns an error. The LLM should fix the problematic transaction(s) and retry.
createSplitTransaction
Create a new split transaction across multiple categories. Split transactions allow you to divide a single transaction amount across multiple categories (e.g., a $100 Target purchase split into $60 Groceries and $40 Household Supplies).
Parameters:
budgetId(required): The budget IDaccountId(required): The account IDdate(required): Transaction date (ISO format, e.g. 2025-11-15)splits(required): Array of split lines (minimum 2). Each split must have:amount(required): Amount for this split in currency units (e.g.,-60.00for-$60.00). The server converts this to YNAB milliunits. Negative for outflow, positive for inflow.categoryId(optional): Category ID for this splitmemo(optional): Memo for this split linepayeeId(optional): Payee ID specific to this splitpayeeName(optional): Payee name specific to this split
payeeId(optional): Payee ID for the parent transactionpayeeName(optional): Payee name for the parent transactionmemo(optional): Memo applied to the parent transactioncleared(optional): Cleared status ("cleared","uncleared", or"reconciled"). Default:"uncleared"approved(optional): Whether the transaction is approved. Default:trueamount(optional): Total transaction amount in currency units. If provided, must match the sum of all splits. If omitted, sum of splits is used. The server converts this to YNAB milliunits.format(optional): Response format -"toon"(default) or"json"
Example:
{
"budgetId": "budget-123",
"accountId": "account-456",
"date": "2024-01-15",
"payeeName": "Target",
"memo": "Weekly shopping",
"splits": [
{
"amount": -60.00,
"categoryId": "cat-groceries",
"memo": "Food items"
},
{
"amount": -40.00,
"categoryId": "cat-household",
"memo": "Cleaning supplies"
}
]
}
Example Output:
{
"success": true,
"transaction": {
"id": "trans-789",
"date": "2024-01-15",
"amount": "-$100.00",
"payee": "Target",
"category": null,
"account": "Checking Account",
"memo": "Weekly shopping",
"cleared": "uncleared",
"approved": true,
"subtransactions": [
{
"id": "sub-1",
"amount": "-$60.00",
"category": "Groceries",
"memo": "Food items",
"payee": null
},
{
"id": "sub-2",
"amount": "-$40.00",
"category": "Household Supplies",
"memo": "Cleaning supplies",
"payee": null
}
]
}
}
splitTransaction
Split an existing transaction into multiple categories. This tool handles the YNAB API limitation that prevents directly updating subtransactions on existing transactions by creating a new split transaction and optionally managing the original.
Parameters:
budgetId(required): The budget IDtransactionId(required): The ID of the existing transaction to splitsplits(required): Array of split lines (minimum 2). Each split must have:amount(required): Amount for this split in currency units (e.g.,-75.00for-$75.00). Must sum to the original transaction's currency-unit amount. The server converts this to YNAB milliunits.categoryId(optional): Category ID for this splitmemo(optional): Memo for this splitpayeeId(optional): Payee ID for this splitpayeeName(optional): Payee name for this split
mode(optional): Operation mode:"prepare_for_matching"(default): Creates a new split transaction and leaves the original in place, marking it for manual replacement. Safe and non-destructive."replace_original": Creates a split transaction and deletes the original. More advanced, use with caution.
format(optional): Response format -"toon"(default) or"json"
Limitations:
- Cannot split transactions that are already split (have subtransactions)
- Cannot split deleted transactions
- Cannot split transfer transactions
- Split amounts must exactly sum to the original transaction amount
Example (prepare_for_matching mode):
{
"budgetId": "budget-123",
"transactionId": "trans-456",
"mode": "prepare_for_matching",
"splits": [
{
"amount": -75.00,
"categoryId": "cat-groceries"
},
{
"amount": -25.00,
"categoryId": "cat-restaurants"
}
]
}
Example Output:
{
"success": true,
"mode": "prepare_for_matching",
"originalTransactionId": "trans-456",
"originalUpdated": true,
"newTransaction": {
"id": "trans-789",
"date": "2024-01-15",
"amount": "-$100.00",
"payee": "Amazon",
"account": "Checking Account",
"cleared": "uncleared",
"approved": true,
"subtransactions": [
{
"id": "sub-1",
"amount": "-$75.00",
"category": "Groceries",
"memo": null,
"payee": null
},
{
"id": "sub-2",
"amount": "-$25.00",
"category": "Restaurants",
"memo": null,
"payee": null
}
]
}
}
Workflow for prepare_for_matching:
- Tool creates a new split transaction with the specified splits
- Tool marks the original transaction with memo
"[replace with split]"and setsapproved: false - User manually matches the split transaction to the bank import in YNAB UI
- User deletes the original transaction
Workflow for replace_original:
- Tool creates a new split transaction with the specified splits
- Tool deletes the original transaction
- User manually matches the split transaction to the bank import in YNAB UI (if needed)
getPayees
Get all payees for a budget, including transfer payees.
Parameters:
budgetId(required): The budget IDformat(optional): Response format -"toon"(default) or"json"
Example Output:
{
"totalPayees": 50,
"regularPayees": 45,
"transferPayees": 5,
"payees": [
{
"id": "payee-123",
"name": "Grocery Store"
},
{
"id": "payee-456",
"name": "Gas Station"
}
],
"transfers": [
{
"id": "transfer-789",
"name": "Transfer : Checking to Savings",
"transferAccountId": "account-savings"
}
]
}
Resources
Budget Summary
Overview of all budgets with account balances.
URI: ynab://budget-summary
Example Output:
{
"budgets": [
{
"id": "12345678-1234-1234-1234-123456789012",
"name": "My Budget",
"currency": "USD",
"lastModified": "2024-01-15T10:30:00Z",
"accounts": {
"onBudget": {
"count": 3,
"totalBalance": "$4,567.89"
},
"offBudget": {
"count": 2,
"totalBalance": "$10,000.00"
}
}
}
],
"defaultBudget": {
"id": "12345678-1234-1234-1234-123456789012",
"name": "My Budget"
}
}
Account Balances
Current balances for all accounts in a budget, grouped by type.
URI: ynab://account-balances/{budgetId}
Example Output:
{
"budgetId": "12345678-1234-1234-1234-123456789012",
"accountsByType": {
"Budget": [
{
"id": "account-123",
"name": "Checking Account",
"balance": "$1,234.56",
"clearedBalance": "$1,200.00",
"unclearedBalance": "$34.56"
}
],
"checking": [
{
"id": "account-456",
"name": "Savings Account",
"balance": "$10,000.00",
"clearedBalance": "$10,000.00",
"unclearedBalance": "$0.00"
}
]
},
"totals": {
"allAccounts": "$14,567.89",
"budgetAccounts": "$4,567.89",
"trackingAccounts": "$10,000.00"
},
"lastUpdated": "2024-01-15T10:30:00Z"
}
Prompts
budget-overview
Get a quick overview of your budget status.
Arguments:
budgetId(optional): The budget ID (uses default if not provided)
Usage: This prompt generates a user message that requests an overview of the budget, including total available balance, overspent categories, and upcoming bills.
spending-analysis
Analyze spending in a specific category over time.
Arguments:
budgetId(required): The budget IDcategoryId(required): The category IDmonths(optional): Number of months to analyze (default: 3)
Usage: This prompt generates a user message that requests an analysis of spending in the specified category, including average spending, trends, and any unusual activity.
Installation
See the for detailed installation instructions.
# Clone the repository
git clone https://github.com/yourusername/ynab-mcp.git
cd ynab-mcp
# Install dependencies
npm install
# or use Makefile
make install
# Build the project
npm run build
# or use Makefile
make build
Configuration
See the for detailed API key setup.
- Create a
.envfile:
echo "YNAB_API_KEY=your_ynab_api_key_here" > .env
-
Get your YNAB API key from: https://app.ynab.com/settings/developer
-
(Optional) Set a default budget ID:
echo "DEFAULT_BUDGET_ID=your_budget_id" >> .env
Usage with MCP Clients
See the for detailed setup instructions for Claude Desktop, Cursor, and other MCP clients.
Claude Desktop
- Build the project:
npm run build - Add to Claude Desktop configuration (see )
- Restart Claude Desktop
Cursor
- Build the project:
npm run build - Configure in Cursor settings (see )
- Restart Cursor
Development
See the for development workflow details.
# Run in development mode with hot reload
npm run dev
# or use Makefile
make dev
# Build for production
npm run build
# or use Makefile
make build
# Start production server
npm start
# or use Makefile
make start
Using the Makefile
The project includes a Makefile with common development tasks:
make install- Install dependenciesmake build- Build the TypeScript projectmake dev- Run in development modemake start- Start production servermake clean- Clean build artifacts
Example Usage
Once connected to Claude, you can:
- "Show me all my YNAB budgets"
- "What's the balance in my checking account?"
- "How much have I spent on groceries this month?"
- "Create a transaction for $50 at the grocery store"
- "Show me all transactions from the last week"
Batch Operations
The server supports batch operations for efficient bulk updates:
When to Use Batch vs Single Updates
- Single updates (
updateTransaction): Use for updating 1-2 transactions or when you need fine-grained control - Batch updates (
batchUpdateTransactions): Use for updating 30+ transactions, especially for:- Bulk recategorization (e.g., categorizing all "Amazon" transactions as "Shopping")
- Bulk approval of transactions
- Bulk flagging or clearing transactions
- Applying the same change to many transactions
Recommended Batch Sizes
- Optimal: 30-100 transactions per batch
- Maximum: Stay within YNAB API rate limits (typically 200 requests per hour)
- Too large: Batches over 100 transactions may hit payload size limits or timeout
Batch Update Semantics
The batchUpdateTransactions tool uses all-or-nothing semantics:
- If all transactions are valid, they all get updated successfully
- If any transaction fails validation (invalid ID, invalid category, etc.), the entire batch fails
- No partial updates are performed - either all succeed or all fail
- On failure, fix the problematic transaction(s) and retry the batch
Example: Bulk Recategorization Workflow
// 1. Get uncategorized transactions
getTransactions({
budgetId: "budget-123",
type: "uncategorized",
sinceDate: "2024-01-01"
})
// 2. Identify transactions to recategorize
// (LLM analyzes and decides category for each)
// 3. Batch update all at once
batchUpdateTransactions({
budgetId: "budget-123",
updates: [
{ id: "trans-1", categoryId: "cat-groceries" },
{ id: "trans-2", categoryId: "cat-groceries" },
{ id: "trans-3", categoryId: "cat-restaurants" },
// ... up to 100 transactions
]
})
Split Transactions
The server supports creating and managing split transactions, which allow a single transaction to be divided across multiple categories.
YNAB API Limitations
The YNAB API has important limitations around split transactions:
- Cannot update
subtransactionson existing split transactions: Once a transaction is split, you cannot modify its subtransactions via the API - Cannot directly convert a plain transaction to a split: You cannot use
PUT/PATCHto add splits to an existing transaction
When to Use Split Tools
createSplitTransaction: Use when creating brand-new split transactions from scratch (e.g., parsing Amazon/Target order data and creating splits)splitTransaction: Use when you need to split an existing imported transaction (e.g., a bank import that needs to be categorized across multiple categories)
Split Transaction Modes
The splitTransaction tool provides two modes:
-
prepare_for_matching(default, recommended):- Creates a new split transaction
- Leaves the original transaction in place
- Marks the original for manual replacement
- Safe and non-destructive
- User manually matches the split to the bank import in YNAB UI
-
replace_original(advanced):- Creates a new split transaction
- Deletes the original transaction
- More automated but requires trust in the split logic
- User may still need to manually match to bank import
Example: Amazon Order Split Workflow
// 1. Get imported Amazon transaction
getTransactions({
budgetId: "budget-123",
payeeId: "payee-amazon",
sinceDate: "2024-01-01"
})
// 2. Parse order details (from CSV or order history)
// LLM identifies: $60 Groceries, $30 Electronics, $10 Household
// 3. Split the transaction
splitTransaction({
budgetId: "budget-123",
transactionId: "trans-amazon-123",
mode: "prepare_for_matching",
splits: [
{ amount: -60.00, categoryId: "cat-groceries" },
{ amount: -30.00, categoryId: "cat-electronics" },
{ amount: -10.00, categoryId: "cat-household" }
]
})
// 4. User manually matches split to bank import in YNAB UI
// 5. User deletes the original transaction
Best Practices
- Start with
prepare_for_matching: Use the safe mode until you're confident in your split logic - Verify amounts: Always ensure split amounts sum exactly to the original transaction amount
- Handle errors gracefully: If a split fails, check that the transaction isn't already split, deleted, or a transfer
- Use
createSplitTransactionfor new entries: When creating transactions from scratch (e.g., manual entry), usecreateSplitTransactiondirectly
API Rate Limits
YNAB API has rate limits. This server implements best practices:
- Uses specific endpoints instead of bulk requests where appropriate
- Supports batch operations for efficient bulk updates
- Supports split transactions for complex categorization needs
- Caches responses where appropriate
- Handles rate limit errors gracefully
Contributing
Contributions are welcome! Please see for guidelines.
- Read the
- Check existing Issues
- Follow the
Security
See the for security best practices.
- API keys are stored in environment variables
- Never commit
.envfiles - Use read-only access when possible
- Review YNAB API permissions
To report a security vulnerability, please see .
License
This project is licensed under the MIT License - see the file for details.
Acknowledgments
- Built with the Model Context Protocol TypeScript SDK
- Uses the YNAB API
- Inspired by the MCP community and YNAB users