MyTruv MCP exposes ten read tools and three write tools. Read tools return data from your connected accounts. Write tools update how transactions are labeled and categorized - your AI assistant calls them automatically when you ask it to fix a category or rename a merchant, and they are also available to any authorized MCP client.
| Tool | What it does |
|---|
| accountBalances | Current balances across all connected accounts |
| balanceHistory | Assets, liabilities, and net worth over time |
| connectedAccountsLookup | List all connected links with status |
| transactionHistory | Transactions from connected bank accounts |
| searchTransactions | Search and filter by text, amount, category, date |
| recurringTransactions | Subscriptions, bills, and recurring income |
| spendingAnalysis | Spending by category, merchant, or time period |
| incomeReport | All income sources from payroll and bank data |
| getPayrollIncome | Pay stub history for a specific employer |
| getBankIncome | Bank-derived income analysis for a specific account |
| recategorizeTransaction | Change a transaction’s category, merchant name, or visibility |
| bulkRecategorizeTransactions | Apply the same change to up to 200 transactions at once |
| createCategorizationRule | Create a standing rule for a merchant that applies going forward |
accountBalances
Returns a snapshot of every connected account with individual balances and aggregated totals grouped by account type.
Parameters: none.
Response fields:
| Field | Type | Description |
|---|
| total_accounts | integer | Number of connected accounts |
| accounts[].id | string | Account ID - pass to account_ids in other tools |
| accounts[].type | string | CHECKING, SAVINGS, CREDIT_CARD, INVESTMENT, LOAN, etc. |
| accounts[].subtype | string | null | e.g. BROKERAGE, ROTH_IRA for investment accounts |
| accounts[].mask | string | Masked account number |
| accounts[].nickname | string | Account display name |
| accounts[].balances.balance | string | Current balance |
| accounts[].balances.available_balance | string | null | Available balance |
| accounts[].balances.credit_limit | string | null | Credit limit (credit cards only) |
| accounts[].provider | object | Institution {id, name, logo_url} |
| aggregated_balances[] | array | Totals grouped by type + currency_code, with account_count |
Example prompt: “What’s my current balance across all accounts?”
Related: REST - Balances
balanceHistory
Returns historical balances across all connected accounts. Aggregates by default - filter with account_ids for specific accounts.
Parameters:
| Parameter | Type | Required | Default | Description |
|---|
| date_range | string | No | 3M | 1M, 3M, 6M, 1Y, or ALL |
| account_ids | string | No | All | Comma-separated account IDs |
Response fields:
| Field | Type | Description |
|---|
| time_series[].date | string | YYYY-MM-DD |
| time_series[].assets | string | Total asset value |
| time_series[].liabilities | string | Total liability value |
| time_series[].net_worth | string | Assets minus liabilities |
| start_date / end_date | string | Range of the series |
Example prompt: “Show me my net worth trend over the last 6 months.”
Related: REST - Balance History
connectedAccountsLookup
Lists every account link (connection) with provider and status.
This returns id for each link. To get the link_id value used by getPayrollIncome and getBankIncome, call incomeReport and use employments[].link_id from there - that field is curated for the income flow.
Parameters: none.
Response fields:
| Field | Type | Description |
|---|
| results[].id | string | Unique link identifier |
| results[].provider | object | Provider {id, name, logo_url} |
| results[].data_source | string | payroll or financial_accounts |
| results[].status | string | done, session_expired, etc. |
| results[].initial_product_type | string | Product the link was originally created for |
| results[].created_at / updated_at / deleted_at | string | Timestamps |
Example prompt: “What accounts do I have connected?”
Related: REST - Account Links
transactionHistory
Fetches transactions from all connected bank accounts and combines them into a single report. Descriptions are anonymized for privacy.
Parameters:
| Parameter | Type | Required | Default | Description |
|---|
| days | integer | No | 30 | Number of days to retrieve (max 365) |
| categories | string | No | All | Comma-separated category names |
Response fields:
| Field | Type | Description |
|---|
| count | integer | Total number of transactions |
| accounts[] | array | Bank accounts with balances (same shape as accountBalances.accounts[]) |
| transactions[].id | string | Transaction ID |
| transactions[].account_id | string | Source account ID |
| transactions[].amount | string | Transaction amount |
| transactions[].currency_code | string | e.g. USD |
| transactions[].categories | array | Display category names (e.g. ["Food & Dining", "Restaurants"]) |
| transactions[].category_slugs | array | Snake-case slugs for the same categories |
| transactions[].description | string | Description (personal names anonymized, e.g. R***a K*****a) |
| transactions[].merchant_name | string | null | Cleaned-up merchant name |
| transactions[].memo | string | null | Memo line if present |
| transactions[].status | string | POSTED or PENDING |
| transactions[].type | string | CREDIT (money in) or DEBIT (money out) |
| transactions[].posted_at | string | null | When the transaction posted (null while pending) |
| transactions[].transacted_at | string | When the purchase actually happened |
Example prompt: “Show me all my grocery transactions from the last 2 weeks.”
Related: REST - Transactions
searchTransactions
More powerful than transactionHistory for targeted lookups. Supports text search, amount ranges, type filtering, and pagination.
Parameters:
| Parameter | Type | Required | Default | Description |
|---|
| query | string | No | - | Text in descriptions and memos (case-insensitive) |
| min_amount | number | No | - | Minimum absolute amount |
| max_amount | number | No | - | Maximum absolute amount |
| transaction_type | string | No | All | DEBIT or CREDIT |
| categories | string | No | All | Comma-separated category names |
| days | integer | No | 90 | Days to look back (max 365) |
| sort_by | string | No | date | date or amount |
| sort_order | string | No | desc | desc or asc |
| limit | integer | No | 20 | Results per page (max 50) |
| offset | integer | No | 0 | Pagination offset |
Response fields:
| Field | Type | Description |
|---|
| total_matches | integer | Total matching transactions before pagination |
| transactions[] | array | Same shape as transactionHistory.transactions[] |
Example prompts:
- “Find all transactions over $500 in the last 90 days.”
- “How much did I spend at coffee shops?“
recurringTransactions
Detects recurring patterns from bank data and splits them into inflows (income) and outflows (expenses).
Parameters:
| Parameter | Type | Required | Default | Description |
|---|
| status | string | No | active | active, inactive, irregular, or all |
Top-level shape: {recurring_transactions: {outflows: [...], inflows: [...]}} .
Outflow fields:
| Field | Type | Description |
|---|
| source_id / source_name | string | Stable ID and human label for the recurring source |
| account_id | string | Account the recurring transaction posts to |
| categories | array | Display category names |
| description | string | Representative description |
| frequency | string | W weekly, BW biweekly, SM semi-monthly, M monthly, Q quarterly, Y yearly |
| average_amount / median_amount / last_amount | string | Stats across detected occurrences |
| status | string | active, inactive, or irregular |
| first_detected / last_transaction_date / next_expected_date | string | Detection window and forecast |
| logo_url | string | null | Brand logo when MyTruv has one |
| historical_transactions[] | array | The individual transactions used to detect this stream |
Inflow fields: same shape as outflow plus income_type (e.g. PAYCHECK).
Example prompt: “Show me all my active subscriptions.”
spendingAnalysis
Spending breakdowns grouped by category, merchant, or time period, with trend comparisons.
Parameters:
| Parameter | Type | Required | Default | Description |
|---|
| group_by | string | Yes | - | category, merchant, or time_period |
| time_period | string | Yes | - | day, week, month, quarter, or year |
| start_date | string | No | 3 periods back | Start date (YYYY-MM-DD) |
| end_date | string | No | Today | End date (YYYY-MM-DD) |
| account_ids | string | No | All | Comma-separated account IDs |
| categories | string | No | All | Comma-separated category names |
| link_id | string | No | All links | Filter to a specific link |
| min_total_amount | number | No | 50.00 | Group amounts below this threshold as Other |
| secondary_group_by | string | No | - | Only when group_by=time_period: nest a top-10 category or merchant breakdown inside each time bucket |
INVESTMENT-type accounts (brokerage, crypto, IRA) are always excluded - stock buys, reinvestments, and margin interest are not personal spending.
Response fields (only the field matching group_by is populated; the other two stay null):
| Field | Type | Description |
|---|
| spending.by_category[].category | string | Category name |
| spending.by_category[].total_amount | string | Total spent in the category |
| spending.by_category[].transaction_count | integer | Number of transactions |
| spending.by_category[].percentage_of_total | string | Share of total spending |
| spending.by_category[].trend | string | null | % change vs prior period |
| spending.by_category[].subcategories[] | array | Nested {name, amount, count} (>= min_total_amount) |
| spending.by_category[].time_series[] | array | Per-period {start_date, end_date, amount, count} |
| spending.by_category[].largest_transaction | object | The biggest single transaction in this group |
| spending.by_merchant[] | array | Same shape but keyed on merchant_name |
| spending.by_time_period[] | array | Same shape but keyed on period_start / period_end |
| summary.total_spending | string | Total across range |
| summary.average_daily_spending | string | Average per day |
| summary.average_monthly_spending | string | Average per month |
| summary.total_transactions | integer | Transactions analyzed |
| summary.unique_merchants | integer | Distinct merchants in range |
| summary.top_category / top_merchant | string | Highest-spending category and merchant |
| request_id | string | Request identifier (useful for debugging) |
| created_at | string | ISO timestamp the analysis was run |
Example prompts:
- “Break down my spending by category this month.”
- “Compare my spending month over month for the last quarter.”
Related: REST - Spending
incomeReport
Aggregates income from all connected sources and deduplicates entries that appear in both payroll and bank data. Returns one record per employer or income source.
Parameters: none.
Response fields:
| Field | Type | Description |
|---|
| employments[].link_id | string | Use with getPayrollIncome or getBankIncome |
| employments[].data_source | string | payroll or financial_accounts |
| employments[].provider | string | Provider slug (e.g. rippling, chase_bank) |
| employments[].company | object | {name, address, phone, ein} for payroll; {name} for bank-derived |
| employments[].income | string | Income amount |
| employments[].income_unit | string | YEARLY, MONTHLY, etc. |
| employments[].pay_rate | string | Per-pay-period amount |
| employments[].pay_frequency | string | W weekly, BW biweekly, SM semi-monthly, M monthly |
| employments[].is_active | boolean | Currently active |
| employments[].job_title / job_type | string | Title and F (full-time) / P (part-time) etc. |
| employments[].start_date / end_date | string | null | Employment range |
| employments[].bank_income_category | string | null | Set when data_source=financial_accounts |
| employments[].statement_count | integer | Pay statements available (payroll only) |
| summary | object | {total_employments, payroll_sources, bank_sources, deduplicated_bank_sources, connected_bank_links} |
Example prompt: “What are my income sources?”
Related: REST - Income
getPayrollIncome
Full payroll income report with pay stub history for a single connected payroll account - earnings, deductions, and year-to-date totals for the last 90 days.
Use the link_id from incomeReport - do not guess this value.
Parameters:
| Parameter | Type | Required | Default | Description |
|---|
| link_id | string | Yes | - | link_id from incomeReport |
Response fields:
| Field | Type | Description |
|---|
| status | string | done when the report is ready |
| provider / data_source | string | Provider slug, always payroll |
| employments[].company | object | {name, address, phone, ein} |
| employments[].is_active / job_title / job_type | various | Role status and metadata |
| employments[].start_date / end_date / employed_in_role | string | Employment timeline |
| employments[].statements[].pay_date | string | Payment date |
| employments[].statements[].period_start / period_end | string | Pay period covered |
| employments[].statements[].gross_pay / net_pay | string | null | Per-statement totals (older statements may be null) |
| employments[].statements[].gross_pay_ytd / net_pay_ytd | string | null | Year-to-date totals |
| employments[].statements[].regular / overtime / bonus / commission / other_pay | string | null | Earnings breakdown |
| employments[].statements[].earnings[] | array | {name, amount, category, rate, units} line items |
| employments[].statements[].deductions[] | array | {name, amount, category} (taxes, retirement, benefits) |
| employments[].annual_income_summary[] | array | Per-year {year, regular, bonus, commission, overtime, other_pay, net_pay, gross_pay} |
Example prompt: “Show me my recent pay stubs from Acme Corp.”
getBankIncome
Income analysis derived from bank transactions for a single connected account. Identifies income streams with historical averages, pay frequency, and the transactions used for detection.
Use the link_id from incomeReport - do not guess this value.
Parameters:
| Parameter | Type | Required | Default | Description |
|---|
| link_id | string | Yes | - | link_id from incomeReport |
Response fields:
| Field | Type | Description |
|---|
| income_sources[].name | string | Source name (e.g. employer) |
| income_sources[].category | string | Income category |
| income_sources[].average_amount | string | Historical average per payment |
| income_sources[].frequency | string | Detected frequency |
| income_sources[].transaction_count | integer | Transactions analyzed |
| income_sources[].transactions[] | array | Individual transactions used for detection |
Example prompt: “Analyze the income deposits in my Chase checking account.”
recategorizeTransaction
Applies an override to a single transaction. Changes the category, renames the merchant as it appears in MyTruv, hides it from spending totals, or flags it as recurring or an internal transfer. Only the fields you pass are changed.
After writing, the response surfaces similar transactions from the same merchant so you can optionally extend the change.
Pass confirmed=false (or omit it) on your first call when the user names only a merchant without pointing to a specific transaction. The tool returns candidates without writing. Once the user confirms which transaction they mean, call again with confirmed=true.
Parameters:
| Parameter | Type | Required | Default | Description |
|---|
| transaction_id | string | Yes | - | ID from searchTransactions or transactionHistory |
| category | string | null | No | - | Display name (e.g. "Groceries") or slug (e.g. "groceries") |
| merchant_name | string | null | No | - | Rename the merchant on this transaction |
| hide | bool | null | No | - | true hides from spending; false un-hides |
| mark_recurring | bool | null | No | - | true flags as a recurring bill or subscription |
| mark_internal_transfer | bool | null | No | - | true flags as a transfer between your own accounts |
| confirmed | bool | No | false | Set true only after the user has confirmed which transaction they mean |
| allow_custom | bool | No | false | Set true only if the user insists on a category name that does not exist in the catalog |
Response (write succeeded):
| Field | Type | Description |
|---|
| override | object | The merged override state applied to the transaction |
| merchant_name | string | The merchant matched |
| similar_transactions[] | array | Up to 20 other transactions from the same merchant (last 90 days) |
| total_similar | integer | Total count of other matching transactions |
| preview_truncated | bool | true if there are more than 20 similar transactions |
| rule_exists | bool | Whether a standing rule already exists for this merchant |
| next_actions[] | array | Suggested follow-up calls (e.g. bulkRecategorizeTransactions, createCategorizationRule) |
Response (confirmation required):
| Field | Type | Description |
|---|
| status | string | "confirmation_required" |
| merchant_name | string | The merchant matched |
| similar_transactions[] | array | Candidate transactions to show the user |
| total_similar | integer | Total count |
| message | string | Instructions to present candidates and ask the user to confirm |
Example prompts:
- “Change the category on that Costco charge to Groceries.”
- “Rename that AMZN MKTPL transaction to Amazon.”
- “Hide this transfer from my spending.”
bulkRecategorizeTransactions
Applies the same override to multiple transactions at once. Intended as a follow-up to recategorizeTransaction when the user agrees to extend the change to similar past transactions. Accepts up to 200 transaction IDs. For applying a change to all future transactions from a merchant, use createCategorizationRule instead.
All IDs are verified as belonging to the authenticated user before any writes are made. If any ID is not found, the entire call fails with an error.
Parameters:
| Parameter | Type | Required | Default | Description |
|---|
| transaction_ids | string | Yes | - | Comma-separated transaction IDs (max 200) |
| category | string | null | No | - | Display name or slug (same semantics as recategorizeTransaction) |
| merchant_name | string | null | No | - | Rename the merchant on all listed transactions |
| hide | bool | null | No | - | Hide or un-hide the transactions |
| mark_recurring | bool | null | No | - | Flag or un-flag as recurring |
| mark_internal_transfer | bool | null | No | - | Flag or un-flag as an internal transfer |
| allow_custom | bool | No | false | Same semantics as recategorizeTransaction |
At least one of category, merchant_name, hide, mark_recurring, or mark_internal_transfer must be provided.
Response:
| Field | Type | Description |
|---|
| applied_count | integer | Number of transactions successfully updated |
| transaction_ids[] | array | The input IDs echoed back |
Example prompt: “Apply that same Groceries category to the other Costco transactions you found.”
createCategorizationRule
Creates a standing rule so that future transactions from a merchant are automatically categorized. By default the rule also applies retroactively to all past transactions from that merchant. Use this as a follow-up after recategorizeTransaction when the user wants the change to stick permanently.
If a rule already exists for the merchant, the tool returns an error with the existing rule ID. Rules can be managed in MyTruv settings; editing or deleting a rule via the MCP is not yet supported.
Parameters:
| Parameter | Type | Required | Default | Description |
|---|
| merchant_name | string | Yes | - | The merchant to match (case-insensitive, max 500 characters) |
| category | string | null | No | - | Display name or slug to apply to matching transactions |
| custom_merchant_name | string | null | No | - | Rename the merchant across all matching transactions |
| hide | bool | null | No | - | Hide matching transactions from spending views |
| mark_recurring | bool | null | No | - | Mark matching transactions as recurring |
| mark_internal_transfer | bool | null | No | - | Mark matching transactions as internal transfers |
| apply_to_past | bool | No | true | true applies retroactively to all existing transactions; false applies only to transactions dated after the rule is created |
| allow_custom | bool | No | false | Same semantics as recategorizeTransaction |
At least one of category, custom_merchant_name, hide, mark_recurring, or mark_internal_transfer must be provided.
Response:
| Field | Type | Description |
|---|
| rule.rule_id | string | UUID of the new rule |
| rule.match_merchant_name | string | The merchant name being matched |
| rule.custom_category | string | null | Category applied by the rule |
| rule.custom_merchant_name | string | null | Merchant rename applied by the rule |
| rule.is_hidden | bool | null | Whether matching transactions are hidden |
| rule.is_recurring | bool | null | Whether matching transactions are flagged recurring |
| rule.is_internal_transfer | bool | null | Whether matching transactions are flagged as transfers |
| rule.scope | string | ALL (retroactive) or FORWARD_ONLY |
| rule.created_at / updated_at | string | ISO timestamps |
Example prompts:
- “Always put Costco in Groceries going forward.”
- “Set a rule so all Venmo transactions are marked as internal transfers.”
Next steps