Skip to main content
Glama

Senaro Personal Finance

Server Details

Credit card payoff, mortgages, refinancing, 401(k), rent vs buy. Every rule and default is cited.

Ownership verified
Status
Healthy
Uptime
100.0% over 22 days
Last Tested
Transport
Streamable HTTP · MCP 2025-11-25
URL

TDQS

A4.4/5.0

Scored across 18 tools

Disambiguation5/5

Each tool has a distinct, well-scoped purpose, and the descriptions explicitly cross-reference siblings (e.g. calculate_cc_payoff vs compare_payoff_strategies vs compare_debt_consolidation, calculate_emergency_fund vs calculate_runway, calculate_loan_payoff vs calculate_cc_payoff) with 'pick this over X' guidance. Even the seemingly overlapping debt/credit-card tools are clearly separated by task shape (single timeline, strategy comparison, consolidation).

Naming Consistency4/5

Nearly every tool follows a consistent verb_noun pattern (analyze_*, calculate_*, compare_*, list_defaults, optimize_401k_match). The only clear deviation is server_info, a noun-style metadata tool, which is a single minor break in an otherwise predictable scheme.

Tool Count4/5

18 tools span a genuinely broad personal-finance scope (credit cards, mortgages, loans, retirement, budgeting, refi), so the count is largely justified rather than padded. It sits at the upper end of comfortable, with a few closely-related calculators that could arguably be folded together.

Completeness4/5

Coverage across the domain is strong: payoff calculations, strategy and consolidation comparisons, mortgage/refi/PMI analysis, rent-vs-buy, DTI, compounding, emergency fund, runway, and 401(k) match, plus list_defaults and server_info for discoverability. Minor gaps remain (e.g. broader investment/retirement projection and tax-planning surfaces), but the core lifecycle is well covered with few dead ends.

Available Tools

18 tools
analyze_cash_advanceCash Advance AnalyzerA
Read-onlyIdempotent
Inspect

Calculation, not advice. Verify with a professional before acting. Deep analysis of a single card's cash advance showing payment allocation, CA interest cost, CA payoff months, and whether the CA is growing. Pick this over calculate_cc_payoff when one card's cash advance is the question: it shows how the CARD Act above-minimum allocation rule, 15 U.S.C. §1666c(b)(1), sends payment above the minimum to the highest-APR segment first, which is the cash advance whenever its rate is the higher one. Pick calculate_cc_payoff for a payoff timeline across several cards or a windfall schedule. The response includes chart_hints with rendering directives any client can use.

ParametersJSON Schema
NameRequiredDescriptionDefault
chart_titleNoOverride for the chart title. Must not contain em-dashes or en-dashes. Max 120 characters. Optional.
minimum_paymentNoIssuer-stated minimum payment. Decimal, at least 0, at most 2 decimal places. Optional; defaults to auto-calculate when omitted. When supplied, this DOES bind: the per-month mandatory payment is min(max(the issuer minimum recomputed from the balance, minimum_payment), the remaining balance, total_monthly_payment). This differs from calculate_cc_payoff's default dynamic path, where the identically-named minimum_payment is parsed and validated but never applied unless fixed_payments = true: the two tools do not share behavior for this parameter, only its name.
purchase_apr_pctYesPurchase APR as a percentage, e.g. 24.99 not 0.2499. Decimal from 0 to 100. REQUIRED, no default.
purchase_balanceYesCurrent balance carrying the purchase APR. Decimal, at least 0. REQUIRED, no default.
cash_advance_apr_pctYesCash advance APR as a percentage. Decimal, greater than 0, at most 100. A cash advance always accrues interest immediately, so 0 is not a valid rate. REQUIRED, no default.
cash_advance_balanceYesCurrent balance carrying the cash advance APR. Decimal, greater than 0. REQUIRED, no default.
cash_advance_fee_minNoMinimum dollar cash advance fee, e.g. 10 for $10. Decimal, at least 0. Optional; defaults to 0 when omitted.
cash_advance_fee_pctNoCash advance fee as a percentage of the advance, e.g. 5 for 5%. Decimal, at least 0 and at most 100. Optional; defaults to 0 when omitted.
total_monthly_paymentYesTotal payment applied across both balances this month. Decimal, greater than 0, at most 2 decimal places. REQUIRED, no default.

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already carry the safety profile (readOnlyHint=true, idempotentHint=true, destructiveHint=false), so the description correctly does not repeat it. It adds genuine behavioral context beyond the annotations: the CARD Act §1666c(b)(1) above-minimum allocation rule and its consequence for the cash advance segment. The 'Calculation, not advice' disclaimer is mild filler, but the CARD Act explanation earns credit for disclosing the tool's underlying behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with purpose before differentiation, and every sentence earns its place — the purpose, the sibling routing, and the chart_hints note all carry information. The leading 'Calculation, not advice. Verify with a professional before acting.' is slightly redundant filler, and the legal citation is detailed but justifiable. Minor trimming would make it ideal.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 9-parameter tool with rich annotations and 100% schema coverage, the description covers purpose, sibling differentiation, and hints at the response shape via chart_hints (partially compensating for the absent output schema). The close sibling calculate_cc_payoff is fully addressed, and other siblings are clearly unrelated by domain. Slightly more on output/return behavior would round it out, but nothing critical is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% — all 9 parameters carry their own rich descriptions, including an extensive note on minimum_payment clarifying how it binds versus calculate_cc_payoff's identically-named parameter. The description itself adds no parameter detail, which is appropriate since the schema does the heavy lifting. Baseline 3 is correct.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb+resource combination ('Deep analysis of a single card's cash advance') and enumerates concrete outputs: payment allocation, CA interest cost, CA payoff months, and whether the CA is growing. It explicitly distinguishes itself from calculate_cc_payoff, making sibling differentiation immediate and unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage guidance is explicit and action-oriented: 'Pick this over calculate_cc_payoff when one card's cash advance is the question' and 'Pick calculate_cc_payoff for a payoff timeline across several cards or a windfall schedule.' It states both when to use this tool and when to use the alternative, leaving nothing to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

analyze_pmi_removalPMI Removal AnalysisA
Read-onlyIdempotent
Inspect

Calculation, not advice. Verify with a professional before acting. Compute every standard PMI-removal pathway:

  • HPA automatic at 78% LTV

  • HPA borrower-requested at 80% LTV

  • optional re-appraisal at a simplified 75% of current market value; the actual Fannie Mae ceiling is seasoning- and property-type-dependent, 75% for a one-unit home seasoned two to five years, 80% for five-plus, and 70% for investment and two- to four-unit properties

Also returns current monthly PMI cost, total PMI dollars between now and automatic removal, and the effective annual return of paying the gap-to-80% (of the original value) as a lump sum today.

Supplying original_loan_term_months and loan_age_months also applies the 12 U.S.C. 4902(c) statutory final-termination midpoint, which bounds automatic removal at the earlier of the 78% schedule and that midpoint where HPA applies and the borrower is current.

Pairs with calculate_refi_breakeven for a refinance's rate-and-term break-even, and with compare_mortgage_terms when choosing between purchase mortgages. Scope: conventional mortgages only; FHA loans use MIP (Mortgage Insurance Premium) with different rules. This tool does not model MIP.

ParametersJSON Schema
NameRequiredDescriptionDefault
chart_titleNoOverride for the chart title. Must not contain em-dashes or en-dashes. Max 120 characters. Optional.
annual_rate_pctYesMortgage APR as a percentage. Decimal from 0 to 20. REQUIRED, no default.
current_balanceYesToday's loan balance. Decimal, greater than 0. REQUIRED, no default.
loan_age_monthsNoMonths elapsed since origination. Integer from 0 to 480. Optional. Cannot exceed original_loan_term_months when both are supplied. Supplying either one without the other is rejected; both are required together. See original_loan_term_months.
monthly_paymentYesCurrent P&I monthly payment, excluding tax, insurance, and PMI. Decimal, greater than 0. REQUIRED, no default.
current_home_valueNoCurrent market value of the home. Decimal, greater than 0. Optional. When supplied AND greater than the HPA original value (the lesser of purchase price and any closing appraisal), the response also computes the re-appraisal pathway (some lenders allow PMI removal based on current market value with a fresh appraisal).
annual_pmi_rate_pctNoAnnual PMI as a percentage of the current loan balance. Decimal from 0 to 5. Optional; omitting it uses the cited 0.5 default. Typical conventional-loan PMI ranges from 0.3% to 1.5%.
extra_monthly_paymentNoExtra principal paid each month beyond the regular payment. Decimal, at least 0. Optional; defaults to 0 when omitted.
original_purchase_priceYesWhat you paid for the home (the purchase-price side of the HPA basis; when a closing appraisal is lower, see original_appraised_value). Decimal, greater than 0. REQUIRED, no default.
original_appraised_valueNoThe home's appraised value at closing. Decimal, greater than 0. Optional. HPA sets the PMI trigger basis to the LESSER of purchase price and this appraisal (12 U.S.C. 4901); supply it when your closing appraisal came in below the purchase price.
original_loan_term_monthsNoThe loan's original term in months. Integer from 1 to 480. Optional. Supplied together with loan_age_months, this computes the 12 U.S.C. 4902(c) statutory final-termination midpoint (12 U.S.C. 4901(7)) and bounds automatic_removal at the earlier of it and the 78% schedule date, where HPA applies and the borrower is current. Supplying either one without the other is rejected; both are required together.

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint, and destructiveHint, and the description adds substantial context: it is a calculation, not advice; it computes statutory midpoint behavior; the re-appraisal pathway depends on supplying current_home_value; and it does not model MIP. There is no contradiction with the annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the caveat 'Calculation, not advice,' then uses a bulleted list for pathways, followed by outputs, boundary conditions, statutory notes, and sibling routing. Every sentence adds distinct information, and there is no filler despite the length.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For an 11-parameter tool with no output schema, the description covers core outputs, exclusions, related tools, conditional parameters, and statutory boundaries. The only gap is that it does not specify the exact response object shape, but the schema's parameter descriptions and the listed outputs are sufficient for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3. The description adds real domain meaning beyond the schema: the HPA basis uses the lesser of purchase price and appraisal, current_home_value triggers the re-appraisal pathway, and original_loan_term_months plus loan_age_months activates the 12 U.S.C. 4902(c) midpoint. This earns an above-baseline score.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb ('Compute') and resource ('every standard PMI-removal pathway'), then enumerates HPA automatic, borrower-requested, and re-appraisal pathways. It also lists concrete outputs and distinguishes itself from sibling mortgage tools via its conventional-only scope.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly names related tools—calculate_refi_breakeven and compare_mortgage_terms—and states when they pair with this tool. It also draws a clear boundary: conventional mortgages only, FHA/MIP excluded. This gives an agent enough information to select the correct tool without opening schemas.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

calculate_cc_payoffCredit Card Payoff CalculatorA
Read-onlyIdempotent
Inspect

Calculation, not advice. Verify with a professional before acting. Calculate credit card payoff timeline with month-by-month amortization. Pick this for a payoff timeline across credit cards under one strategy, including windfalls or fixed payments; pick compare_payoff_strategies to compare avalanche against snowball, calculate_loan_payoff for a single installment loan, and analyze_cash_advance when one card's cash advance is the question. Supports dual APR segments (purchase + cash advance), two strategies (avalanche, snowball), fixed/dynamic payment modes, and an optional schedule of one-time windfalls (tax refund, bonus, inheritance) applied at specific months.

Returns:

  • debt-free date, total interest, per-card payoff order

  • first_month_interest_ratio_frac (the first month's interest/payment fraction) and lifetime_interest_ratio_frac (whole-projection total_interest/total_paid fraction)

  • domain warnings emitted as warnings[].type (DEATH_SPIRAL, CA_TRAP, MIN_PAYMENT_TRAP, DAY_ZERO_COST, DEBT_GROWING, PROMO_CLIFF, PREDATORY_RATE, SPEND_EXCEEDS_PAYDOWN, PLAN_TERM_ELAPSED_WITH_BALANCE, NO_PAYOFF_WITHIN_PROJECTION), and, separately, validation disclosures emitted as warnings[].code (MINIMUM_PAYMENT_NOT_BINDING on the default dynamic-budget path, MINIMUM_PAYMENT_NOT_BINDING_IN_BASELINE when fixed_payments = true; these two shapes coexist in the same warnings[] array but are distinguishable by which keys each entry carries)

  • when a plan exists (extra_monthly_payment > 0, windfalls present, or fixed_payments = true), a min_payments_only baseline block with interest_saved_vs_min_payments and months_saved_vs_min_payments quantifying the interest and time difference versus paying minimums only; when windfalls are also present the response additionally gains a baseline vs with_windfalls savings block

The response echoes strategy (canonical lowercase 'avalanche' | 'snowball', the strategy the plan was actually simulated under) at the top level.

The response includes chart_hints with rendering directives any client can use.

Top-level entered_starting_balance echoes the caller's supplied balances by segment type (total, purchase, cash_advance, balance_transfer, promotional, installment_plan), before any fee, recurring spend, or interest posts; total is the sum of the other five. Top-level total_purchase_segment_fees_paid reports the portion of card fees (plan_fees_monthly plus annual_fee accruals) charged to the purchase segment across the projection, also present on every monthly_totals[] and analysis.buckets[] row.

These month counts are each a {status, value, explanation} object rather than a bare month count: months_to_payoff, card_payoff_order[].payoff_month, min_payments_only.months, minimums_only_baseline.months, and, when windfalls are supplied, baseline.months and with_windfalls.months. status is "defined" with an integer value when that plan reaches a zero balance, and "none" with value null when it does not, in which case explanation says why.

ParametersJSON Schema
NameRequiredDescriptionDefault
cardsYesCredit cards to pay off, 1 to 20. Required: omitting the field answers contract_error MISSING_REQUIRED_FIELD, while an explicit JSON null answers parse_error ("cards is required and must be an array."); the two are not the same rejection. Every card may carry segments[]. Max 100 segments total across all cards in the request. Per-card, per-type caps: 1 purchase, 1 cash_advance, 5 balance_transfer, 1 promotional, 10 installment_plan.
outputNoValid values: 'summary' (default), 'inline'. summary returns a compact response with a data_preview block; inline returns the full payload.
strategyNoPayoff order: 'avalanche' pays the highest APR first, 'snowball' the smallest balance first. Optional; defaults to 'avalanche' when omitted. Matched case-insensitively, and the response echoes the canonical lowercase form actually simulated.
windfallsNoOne-time principal payments, at most 12, e.g. a tax refund or a year-end bonus. Optional; a JSON null is treated as omitted. When non-empty the response gains baseline, with_windfalls, months_saved_vs_baseline, interest_saved_vs_baseline, windfalls_applied[] and windfalls_unused[]; an empty array returns the same shape as omitting it.
chart_titleNoOverride for the chart title. Optional; must not contain an em dash or en dash. Max 120 characters.
per_segmentNoWhether each schedule row carries its per-segment breakdown. Optional; defaults to true when omitted.
full_scheduleNoWhether to return the full per-card month-by-month schedule. Optional; defaults to false when omitted. The per-card monthly_schedule is capped at 1000 rows total across cards and months, so later months are omitted on a long multi-card payoff; the portfolio-level monthly_totals series is always complete and is the one to read for the whole timeline.
apply_rate_capNoWhether to cap each card's APR at the regulatory ceiling before simulating. Optional; defaults to false when omitted.
fixed_paymentsNoWhether to hold each card's payment constant for the whole payoff. Optional; defaults to false when omitted. When true, each card's payment is locked at max(cards[].minimum_payment, that card's issuer minimum computed at month 1), and the total monthly budget (those locked amounts plus extra_monthly_payment) is held constant for the whole payoff, so once a card is paid off its freed-up payment rolls forward onto the remaining cards. This is the ONLY mode in which cards[].minimum_payment affects calculate_cc_payoff's result; compare_payoff_strategies has no fixed_payments parameter and binds minimum_payment on both of its strategy arms regardless.
extra_monthly_paymentNoExtra monthly payment in dollars, $0 to $1,000,000,000, applied on top of the required minimums. Optional; defaults to 0 when omitted.
include_card_timelineNoWhether to add the card_timeline block, a per-card view of when each card clears. Optional; defaults to false when omitted.

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly/idempotent/non-destructive, and the description adds substantial context beyond them: the 'Calculation, not advice' disclaimer, the domain warnings[] types, the validation disclosures MINIMUM_PAYMENT_NOT_BINDING vs MINIMUM_PAYMENT_NOT_BINDING_IN_BASELINE, the conditional baseline blocks, and the {status, value, explanation} month-count contract. This is rich behavioral disclosure an agent could not infer from the schema.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Well front-loaded: disclaimer, purpose, sibling routing, then capabilities, then a Returns section. The Returns section is long and enumerates many internal field names and warning codes, but since there is no output schema it is largely justified. Some prose could be trimmed without loss.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, and the description compensates by documenting the return contract in detail (debt-free date, interest ratios, warnings shapes, baseline savings blocks, echo fields, chart_hints, the status-object month counts). For an 11-parameter tool with deep nested inputs, the description is complete enough to call and interpret correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3; the description adds orientation over the 11 params by tying strategy, payment mode, and windfalls together ('fixed/dynamic payment modes... windfalls applied at specific months') and by noting the strategy echo behavior. It does not add syntax or format detail beyond the exhaustive schema, so it stays short of 5.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('Calculate credit card payoff timeline with month-by-month amortization') and immediately distinguishes itself from siblings by name (compare_payoff_strategies, calculate_loan_payoff, analyze_cash_advance). An agent can select this tool without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit routing language: 'Pick this for a payoff timeline across credit cards under one strategy... pick compare_payoff_strategies to compare avalanche against snowball, calculate_loan_payoff for a single installment loan, and analyze_cash_advance when one card's cash advance is the question.' When-to-use and when-not-to-use are both stated with named alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

calculate_compound_interestCompound Interest CalculatorA
Read-onlyIdempotent
Inspect

Calculation, not advice. Verify with a professional before acting. Standard compound interest calculator. Returns future value, total contributions, interest earned, effective annual rate, and milestones. Pick this for a balance that grows with contributions. Pick calculate_opportunity_cost when the money is recurring spending being priced against investing, and calculate_loan_payoff when the balance amortizes down under a fixed payment. The response includes chart_hints with rendering directives any client can use.

ParametersJSON Schema
NameRequiredDescriptionDefault
yearsYesTime horizon in years. Integer, at least 1. REQUIRED, no default.
principalYesStarting balance. Decimal, at least 0. REQUIRED, no default.
chart_titleNoOverride for the chart title. Must not contain em-dashes or en-dashes. Max 120 characters. Optional.
annual_rate_pctYesAnnual interest rate as a percentage, e.g. 7 not 0.07. Decimal from 0 to 100. REQUIRED, no default.
rate_conventionNo'nominal' or 'real'. 'real' unconditionally suppresses the inflation overlay; use when annual_rate_pct is already inflation-adjusted. Optional; defaults to 'nominal' when omitted.
compounds_per_yearNoCompounding periods per year. Integer, greater than 0. Optional; defaults to 12 when omitted.
inflation_rate_pctNoInflation rate as a percentage. Decimal: -1 (deprecated suppress, same as 0), 0 (no real-value overlay), or greater than 0 and less than 100 (explicit percentage, e.g. 3.5). Optional; defaults to 0 when omitted.
monthly_contributionNoAdditional contribution added each month. Decimal, at least 0. Optional; defaults to 0 when omitted.
apply_default_inflationNoWhen true and inflation_rate_pct is 0, auto-selects inflation via MacroeconomicDefaults.ForHorizon. Ignored when inflation_rate_pct is greater than 0 or rate_convention is 'real'. Optional; defaults to false when omitted.

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already establish the read-only, idempotent, non-destructive profile. The description adds useful behavior beyond that: it discloses the full response shape (future value, total contributions, interest earned, effective annual rate, milestones) and the presence of chart_hints rendering directives.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact and front-loaded: calculation disclaimer, return values, use-case guidance, and chart_hints are each given one purposeful sentence. There is no filler or repetition of structured schema information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The output list and chart_hints mention compensate for the absence of an output schema. Optional-parameter behaviors like inflation overlay and rate_convention are not described in prose, but the highly detailed schema covers them, so enough context exists for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3. The description's mention of 'balance that grows with contributions' minimally reinforces principal and monthly_contribution, but the schema already provides complete meaning for all nine parameters, including units, defaults, and constraints.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource ('standard compound interest calculator') and enumerates the return metrics. It also distinguishes itself from sibling tools by naming what it is not, so an agent can pick it apart from calculate_opportunity_cost and calculate_loan_payoff without inspecting schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explicitly says to use this tool when a balance grows with contributions and names the conditions under which calculate_opportunity_cost or calculate_loan_payoff should be selected instead. The professional-verification caveat also frames when to treat results as advisory.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

calculate_debt_to_incomeDebt-to-Income CalculatorA
Read-onlyIdempotent
Inspect

Calculation, not advice. Verify with a professional before acting. Calculate your debt-to-income ratio and check qualification for conventional, FHA, VA, and USDA mortgage programs. Accepts existing debts and an optional proposed new debt or home price. Pick this to measure DTI ratios and mortgage-program qualification against existing debts; pick compare_debt_consolidation when the question is whether a consolidation loan costs less than keeping the current credit cards, and pick compare_rent_vs_buy when the question is whether to buy a home at all rather than what DTI a given price implies.

Returns current DTI, front-end and back-end ratios with proposed housing, maximum affordable home price, and what-if scenarios showing the resulting DTI and which programs would then qualify if a given debt were paid off, plus income-increase and home-price-reduction variants. Includes the 10-month rule (Fannie Mae) for debts near payoff.

qualification.<program>.qualification_status is a tri-state verdict ('qualifies' | 'underwriting_dependent' | 'ineligible'):

  • 'underwriting_dependent' means the manual-underwriting baseline is exceeded but further underwriting can still approve it, an automated-underwriting system for conventional and FHA, either automated or manual underwriting for USDA, or a supervisory underwriter's written justification under 38 CFR 36.4340(c)(2) for VA (never an automated decision), so qualification.<program>.qualifies=false does NOT by itself mean the borrower is blocked

  • Read qualification_status, not the bare qualifies boolean, for the real answer; qualification.<program>.note explains the specific underwriting or hard-cap context, naming which mechanism applies

  • VA's back-end overage alone never returns 'ineligible' either (38 CFR 36.4340(c)(2)/(c)(3) both contemplate approval above 41%), and conventional is now the only program that ever returns 'ineligible', since FHA, VA and USDA are each disclosure-only above their baselines

  • what_if.scenarios[].qualifies_va and changes_qualification_va are decided on the scenario's unrounded VA ratio rounded to a whole percent under 38 CFR 36.4340(d), not on new_back_end_dti_va (two decimals), so a scenario showing 41.44 can qualify; qualification.va.your_back_end_compared states the rounded figure VA's row compares with max_back_end

ParametersJSON Schema
NameRequiredDescriptionDefault
family_sizeNoHousehold size for the VA residual income guideline. Supply family_size and property_state together, or neither. Optional; must be between 1 and 20.
hoa_monthlyNoMonthly homeowners association dues in dollars. Optional; defaults to 0 when omitted. Must be zero or more.
pmi_monthlyNoMonthly PMI (private mortgage insurance) in dollars. Optional; if omitted, auto-estimated at 0.5% of the loan annually when loan-to-value exceeds 80%. Feeds the conventional-basis PITI, so it moves with_proposed.front_end_dti, with_proposed.back_end_dti, with_proposed.front_end_breakdown, the conventional qualification row, and what_if.scenarios[].new_front_end_dti and new_back_end_dti. VA carries no PMI, and FHA and USDA always compute their own upfront-plus-annual mortgage insurance instead, at every loan-to-value, never this override. Must be zero or more.
annual_incomeNoAnnual gross income in dollars, divided by 12 to get monthly income. Exactly one of gross_monthly_income or annual_income is required. Must be at least $0.01, one cent, the smallest amount of money.
proposed_debtNoA proposed new debt, as an alternative to proposed_home_price. Provide at most one of proposed_debt or proposed_home_price; providing neither computes the current DTI only. Optional; a JSON null is treated as omitted, the same as leaving the field out.
existing_debtsNoExisting debts to include in the DTI calculation. Optional; omit it or send an empty array for no existing debts. A JSON null is rejected; omit the field instead. At most 50 debts are allowed.
property_stateNoTwo-letter USPS state code, or 'DC'/'PR'/'GU'/'VI'/'AS'/'MP'. Supply family_size and property_state together, or neither. Together these compute the VA residual income guideline (38 CFR 36.4340(e)) in va_residual_income_guideline: the dollar amount VA's tables require for this family size, region, and loan amount (derived from proposed_home_price; not computable without it), plus the 38 CFR 36.4340(c)(3) review-waiver figure. Computed only for family_size 1-7 and a property_state among the 50 states, DC, or PR (not GU, VI, AS, or MP; 38 CFR 36.4340(e) assigns no region to those four); outside those bounds, or without proposed_home_price, va_residual_income_guideline.status reads 'not_computable' with the reason instead. This block alone is a LOOKUP, not a verdict: it never compares against your actual residual income by itself. Optional.
include_what_ifNoWhether to generate what-if scenarios showing how paying off a debt, increasing income, or reducing the home price would improve DTI. Optional; defaults to true when omitted.
additional_incomeNoAdditional monthly income: side income, rental income, or bonuses. Optional; defaults to 0 when omitted. Must be zero or more.
proposed_rate_pctNoProposed mortgage interest rate as a percent, e.g. 7.0 for 7.0%. Optional; the 7.0% default applies whenever this field is omitted, whether the proposal is proposed_debt or proposed_home_price. The default-rate warning fires only when proposed_home_price is used. Must be between 0 and 20.
property_tax_annualNoAnnual property tax in dollars. Optional; if omitted, estimated at 0.88% of the proposed home price. Must be zero or more.
proposed_home_priceNoProposed home purchase price in dollars. Provide at most one of proposed_debt or proposed_home_price; providing neither computes the current DTI only. Auto-calculates full PITI (principal, interest, taxes, insurance). Optional; must be at least $0.01, one cent, the smallest amount of money.
proposed_term_yearsNoProposed mortgage term in years. Optional; defaults to 30 when omitted. Must be between 1 and 40.
transaction_purposeNoMortgage transaction purpose: 'purchase', 'refinance', or 'streamlined_assist'. Optional, defaults to 'purchase' when omitted. Affects USDA only, and only what is disclosed. USDA's 32% PITI and 44% Total Debt figures are purchase-transaction waiver conditions (HB-1-3555 11.3.A.2), disclosed rather than applied as ceilings: Senaro cannot observe how the file is underwritten, so a USDA ratio overage is never 'ineligible' on any transaction purpose. For a refinance, 11.3.B states debt ratios 'are not limited to the maximum purchase debt ratio thresholds', so where the note fires it names both figures and states that neither applies. Streamlined-assist refinances require no debt ratio calculation at all. Conventional, FHA and VA are unaffected.
gross_monthly_incomeNoGross monthly income in dollars. Exactly one of gross_monthly_income or annual_income is required. Must be at least $0.01, one cent, the smallest amount of money.
home_insurance_annualNoAnnual home insurance in dollars. Optional; if omitted, estimated at 0.65% of the proposed home price. Must be zero or more.
proposed_down_payment_pctNoDown payment as a percent of the proposed home price, e.g. 20 for 20%. Optional; defaults to 20 when omitted. Must be between 0 and 99.9 (100% cash purchases are not supported).
va_funding_fee_financed_monthlyNoThe additional monthly payment from financing a VA funding fee into the loan balance, if any. 38 CFR 36.4313(e)'s applicable percentage depends on down payment, prior VA-loan use, and service category, none of which Senaro collects, so there is no default. If omitted while any VA figure that depends on it is produced, meaning qualification.va.your_back_end and its verdict, any what_if VA ratio, what_if.max_affordable_home.va, or qualification.va.residual_income_comparison, a VA_FUNDING_FEE_NOT_MODELED warning discloses that no fee is assumed. The home-price-reduction what_if scenario's hypothetical price is fixed before the fee is considered, so a supplied fee is scaled to that EXACT hypothetical loan size, since 38 CFR 36.4313(e)'s fee is a percentage of loan principal. what_if.max_affordable_home.va is a two-pass approximation instead, so its supplied fee is scaled to an ESTIMATE of the hypothetical loan size, not the exact figure reported; the published price itself passes an exact forward VA check under 38 CFR 36.4340(d) with that reserved fee, and one dollar more fails it, so only this fee-scaling step is approximate. Without proposed_home_price there is no reference loan size to scale from either way, so the raw fee is reserved unscaled instead, and a VA_FUNDING_FEE_NOT_SCALED warning discloses it. This is mutually exclusive with VA_FUNDING_FEE_NOT_MODELED by construction, since one requires the fee omitted and the other requires it supplied. Optional; must be zero or more.
monthly_maintenance_and_utilitiesNoEstimated monthly maintenance and utilities for the proposed property. 38 CFR 36.4340 calls for a realistic estimate of this figure for the property and local utility rates and sets no numeric multiplier itself, but VA underwriting guidance (the Lender's Handbook, Pamphlet 26-7) publishes a per-square-foot multiplier for this same estimate. Applying it needs the property's square footage, which this tool does not currently collect, so Senaro has no default to offer here and you supply the aggregate monthly amount instead. Supplying BOTH this field and monthly_taxes_and_retirement_withholding, together with proposed_home_price and a computable family_size/property_state, computes qualification.va.residual_income_comparison: your ACTUAL monthly residual income, its ratio to the va_residual_income_guideline figure, and whether residual_income_meets_review_waiver_margin (residual income at or above 120% of the guideline) is met. The shelter expense used here excludes any PMI (VA loans carry no monthly PMI; 38 CFR 36.4313(e) sets a funding fee instead, commonly financed into the loan; see va_funding_fee_financed_monthly for the financed-fee field). 38 CFR 36.4340(c)(3)'s review-waiver condition is CONJUNCTIVE: it also requires the back-end debt-to-income ratio, rounded to a whole percent under 38 CFR 36.4340(d) (qualification.va.your_back_end_compared), to exceed 41%, which this field does not by itself confirm. Check both fields together. Even when both hold, (c)(3) only WAIVES a second-level review requirement; it is not itself an approval. Whether this file is actually approved is an underwriting determination Senaro does not make and no input combination here determines. Missing any one of the needed inputs reads qualification.va.residual_income_comparison.status 'not_computable' with every reason named. Optional; must be zero or more.
monthly_taxes_and_retirement_withholdingNoYour federal, state, and FICA tax withholding, plus any amount paid or withheld for retirement, monthly. 38 CFR 36.4340(f)(13) treats these as one class of deduction from gross income. Optional; must be zero or more.

TDQS

A4.4/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly/idempotent/non-destructive/closed-world, and the description goes well beyond them: it discloses the tri-state `qualification_status` semantics, the meaning of the bare `qualifies` boolean, the CF A rounding rule for VA scenarios, and the conditional WARNING behavior for unmodeled VA fees. This is unusually rich behavioral context for a read-only calculator.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The purpose and routing are correctly front-loaded, but the body is very long and duplicates blocks that the input schema already spells out verbatim (VA funding-fee scaling, PMI/tax/insurance defaults). For a domain this complex some length is earned, but the repetition with structured data costs it.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema and 20 parameters, the description compensates well by describing return fields (current DTI, front/back-end ratios, max affordable price, what-if scenarios) and the tri-state verdict semantics. A few mechanics (e.g., what `changes_qualification_va` contains) are named but not fully unpacked, keeping it short of a 5.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% and the parameter descriptions are themselves extremely detailed, so the schema does the heavy lifting. The description adds framing ('Accepts existing debts and an optional proposed new debt or home price') but no parameter-level syntax or constraints beyond what the schema already provides, so the baseline of 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Calculate your debt-to-income ratio and check qualification for conventional, FHA, VA, and USDA mortgage programs') and explicitly names the sibling tools it is not (`compare_debt_consolidation`, `compare_rent_vs_buy`). An agent can route to it without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states when to pick this tool ('to measure DTI ratios and mortgage-program qualification against existing debts') and gives two named alternatives with the exact question each answers. This is textbook when/when-not/alternative guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

calculate_emergency_fundEmergency Fund CalculatorA
Read-onlyIdempotent
Inspect

Calculation, not advice. Verify with a professional before acting. Size an emergency fund target based on the borrower's monthly essential expenses, job stability, dependents, and income redundancy. Returns a tier breakdown (minimum, target, conservative; the target row is omitted when it equals the 3-month minimum, and the conservative row is omitted when it equals the 12-month ceiling), the gap between current savings and the target, and time-to-target at the supplied monthly savings cadence. Pick this to size an emergency-fund target; pick calculate_runway when savings are already fixed and the question is how many months they will last, not what the target is. Pairs with debt-payoff content for the recurring 'save vs pay debt' question. Target months are clamped to [3, 12]: never below the 3-month personal-finance minimum, never above 12.

ParametersJSON Schema
NameRequiredDescriptionDefault
dependentsNoNumber of dependents. Integer from 0 to 20. +1 target month per dependent, capped at +3. Optional; defaults to 0 when omitted.
chart_titleNoOverride for the chart title. Must not contain em-dashes or en-dashes. Max 120 characters. Optional.
job_stabilityNo'stable_w2' | 'variable_income' | 'self_employed' | 'between_jobs'. Drives the target-months multiplier: stable_w2 salaried W-2 with consistent paycheck (+0 months); variable_income W-2 with commission/bonus/shift-based pay (+1 month); self_employed 1099 contractor/freelancer/sole proprietor (+3 months); between_jobs actively job hunting, no current paycheck (+5 months). Optional; defaults to 'stable_w2' when omitted.
current_savingsNoWhat you have in liquid emergency-accessible savings today. Decimal from 0 to $1,000,000,000. Optional; defaults to 0 when omitted.
has_dual_incomeNoWhen true, partner income reduces the buffer by 1 month. Optional; defaults to false when omitted.
monthly_savings_capacityNoWhat you can contribute toward the gap each month. Drives months_to_target. Decimal, either 0 or from $0.01 to $1,000,000,000. Optional; defaults to 0 when omitted.
monthly_essential_expensesYesRent/mortgage + utilities + food + insurance + minimum debt payments. NOT discretionary spending. Decimal from $0.01 to $1,000,000,000. REQUIRED, no default.

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint, and non-destructive. The description adds behavioral nuances beyond that: tier omission rules, clamping to [3,12], and the 'Calculation, not advice' caveat. It does not contradict annotations and adds algorithmic context. Slightly more depth (e.g., on error handling) could push to 5, but it is strong.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is informative and well-organized, starting with the core purpose and proceeding to outputs, usage guidance, and constraints. Every sentence earns its place, though it is somewhat long. It could be tightened without losing critical context, but it is not bloated.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description fully explains return values (tier breakdown, gap, time-to-target) and covers edge cases like tier omissions and clamping. It also includes the professional-verification caveat. For a calculator tool, nothing needed for correct invocation is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, with each parameter fully documented (types, ranges, defaults). The description summarizes the key inputs but does not add semantic detail beyond what the schema already states. Per the rubric, this is the baseline 3; it does not compensate or extend schema information.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a clear, specific verb and resource: 'Size an emergency fund target based on the borrower's monthly essential expenses, job stability, dependents, and income redundancy.' It also names the output components (tier breakdown, gap, time-to-target), making the tool's function unmistakable. It directly differentiates from the sibling calculate_runway by contrasting the questions each answers.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit selection guidance: 'Pick this to size an emergency-fund target; pick `calculate_runway` when savings are already fixed and the question is how many months they will last, not what the target is.' Also adds context for the 'save vs pay debt' question, showing situational awareness. This is exemplary usage guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

calculate_loan_payoffLoan Payoff CalculatorA
Read-onlyIdempotent
Inspect

Calculation, not advice. Verify with a professional before acting. Standard amortizing loan calculator. Pick this for a single fixed-rate installment loan (mortgage, auto, student, personal) with one amortization schedule; pick calculate_cc_payoff for a credit card, which carries revolving balances, multiple APR segments, and a snowball or avalanche strategy this tool does not model.

Returns:

  • monthly payment, total interest, and a payoff date (null when the loan does not clear within its term)

  • a bucketed Analysis suitable for chart rendering. The Analysis includes KPIs, annotations (e.g. crossover month), and summary strings

When extra_monthly_payment is supplied, every computed figure in the response outside with_extra describes this loan without the extra payment, including monthly_payment, payoff_months, total_paid, total_interest, remaining_balance_at_term, payoff_date, months_to_halfway_principal, months_to_interest_flip, and every value under analysis; a warnings[] entry names this.

The response includes chart_hints with rendering directives any client can use. This tool does not support output: 'inline' on any transport.

ParametersJSON Schema
NameRequiredDescriptionDefault
outputNoValid values: 'summary' (default). 'summary' returns the full payload including analysis.buckets[] inline.
loan_typeNo'personal', 'auto', 'student', or 'mortgage'. Optional; defaults to 'personal' when omitted.
principalYesLoan principal to amortize. Decimal, greater than 0. REQUIRED, no default.
chart_titleNoOverride for the chart title. Must not contain em-dashes or en-dashes. Max 120 characters. Optional.
term_monthsYesLoan term. Integer, greater than 0. REQUIRED, no default.
chart_bucketNoTime-axis granularity of analysis.buckets[]: 'auto', 'monthly', 'quarterly', 'yearly', or 'biennial'. Optional; defaults to 'auto', which selects by term length (<=24 months: monthly; <=60: quarterly; <=360: yearly; else biennial).
annual_rate_pctYesLoan annual percentage rate, e.g. 6.5 not 0.065. Decimal from 0 to 100. REQUIRED, no default.
extra_monthly_paymentNoExtra principal paid each month beyond the regular payment. Decimal, at least 0. Optional; defaults to 0 when omitted.

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Even though annotations already declare readOnly, idempotent, and non-destructive, the description adds meaningful response behavior: payoff date can be null, the analysis is bucketed and chart-ready, chart_hints are included, and extra_monthly_payment causes all non-with_extra figures to describe the loan without the extra payment. No contradiction exists.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is organized with a lead statement, a uses paragraph, and bullet-like return details. It is somewhat lengthy, but each section covers a distinct need, and the extra-payment behavior paragraph is justified because it prevents misinterpretation of the response.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a read-only calculation tool with fully described parameters and rich annotations, the description covers returns, edge cases, sibling differentiation, and a critical response-semantics caveat. The lack of an output schema is compensated by the explicit return list and chart_hints note.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3UTE and most parameters are fully documented in the schema. The description adds extra semantic value by explaining how extra_monthly_payment changes the entire response shape and by flagging the unsupported output mode 'inline', which is not evident from the enum alone.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names a specific operation and resource: 'Standard amortizing loan calculator' for a single fixed-rate installment loan. It also explicitly differentiates from calculate_cc_payoff by contrasting amortization schedules with revolving credit card balances, so an agent can identify this tool among siblings.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives a clear selection rule: use this for mortgage, auto, student, or personal loans with a single amortization schedule, and use calculate_cc_payoff for credit cards. This is an explicit when-to-use/when-not-to-use contrast with a named alternative.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

calculate_opportunity_costOpportunity Cost CalculatorA
Read-onlyIdempotent
Inspect

Calculation, not advice. Verify with a professional before acting. Calculate the true cost of recurring spending by showing what that money would be worth if invested. Pick this when the input is recurring spending. Pick calculate_compound_interest when the input is a balance and its contributions with no spending to price, and compare_payoff_vs_invest when the alternative to investing is paying down debt. The response includes chart_hints with rendering directives any client can use.

ParametersJSON Schema
NameRequiredDescriptionDefault
labelNoWhat the spending is, e.g. 'coffee' or 'streaming subscriptions'. Optional. Max 120 characters.
yearsYesTime horizon in years. Integer, greater than 0. REQUIRED, no default.
chart_titleNoOverride for the chart title. Must not contain em-dashes or en-dashes. Max 120 characters. Optional.
tax_bracket_pctNoTax bracket as a percentage, applied as a flat haircut to the investment gain. Decimal from 0 to 100. Optional.
monthly_spendingYesMonthly spending to price against investing. Decimal, at least $0.01. The opportunity cost of spending $0 is degenerate and is rejected. REQUIRED, no default.
annual_return_pctYesEffective annual investment return, e.g. 8 not 0.08; the monthly compounding step is (1+pct/100)^(1/12)-1. Decimal from 0 to 100. REQUIRED, no default.

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description adds important behavioral context beyond the annotations. It explicitly states 'Calculation, not advice. Verify with a professional before acting,' which is a critical caveat for a financial tool. It also mentions that the response includes `chart_hints` with rendering directives, which is not indicated by the annotations or schema. Since the annotations already declare readOnlyHint=true and destructiveHint=false, this additional disclosure is exemplary.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise and well-structured. It front-loads the critical caveat and purpose, then immediately provides usage routing. Every sentence adds value—no fluff. The inclusion of the output note about `chart_hints` is efficient. It is not overly long given the tool's complexity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description is complete for this tool's complexity. It covers the purpose, usage guidelines, a critical caveat, and even the output's special feature. The schema covers parameters, and the absence of an output schema is mitigated by the mention of `chart_hints`. For a financial calculator, this is exceptionally thorough.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Although schema description coverage is 100%, the description goes beyond the schema by explaining the core concept of opportunity cost and the context of the parameters (recurring spending, investment return). It also clarifies the tax_bracket_pct's role (flat haircut) and the annual_return_pct's compounding formula, which is not in the schema. This enriches the parameter semantics beyond the field descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: to calculate the true cost of recurring spending by showing what that money would be worth if invested. It uses specific verbs (calculate, show) and identifies the resource (recurring spending). It also distinguishes itself from sibling tools by naming them explicitly.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides explicit guidance on when to use this tool vs. alternatives: 'Pick this when the input is recurring spending.' It also tells the agent to use `calculate_compound_interest` for balance/contributions and `compare_payoff_vs_invest` when the alternative is paying off debt. This is clear routing with no ambiguity.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

calculate_refi_breakevenRefinance Break-Even AnalysisA
Read-onlyIdempotent
Inspect

Calculation, not advice. Verify with a professional before acting. Deterministic mortgage refinance break-even analysis. Given your current loan (balance, rate, remaining term) and a refinance offer (new rate, new term, closing costs, optional points), computes:

  • monthly P&I savings;

  • the cash-flow break-even month (total refinance cost divided by monthly savings, CFPB convention);

  • the lifetime interest delta over your remaining-term horizon;

  • a term-matched scenario that isolates the rate cut from a term reset;

  • a term-reset-trap flag (lower payment but higher lifetime interest from extending the term); and

  • the economic break-even (net-worth crossover) month using an equal-outflow invest-the-savings model.

Rate-and-term refis only (cash-out and tax effects are out of scope).

Pick this when refinancing your existing mortgage into a new rate and term is the question; pick compare_mortgage_terms when comparing two mortgage structures on a purchase you have not yet taken out.

All defaults cite primary sources (LodeStar/ALTA closing-cost data, CFPB break-even convention). Scalar output, no chart series.

ParametersJSON Schema
NameRequiredDescriptionDefault
pointsNoDiscount points paid at closing, where 1.0 means 1% of the loan. Decimal from 0 to 4. Optional; defaults to 0 when omitted.
chart_titleNoReserved for the chart pipeline; validated but not yet used. Must not contain em-dashes or en-dashes. Max 120 characters. Optional.
closing_costsNoExplicit closing costs in dollars, excluding points; total upfront cost is closing_costs plus the points cost. Decimal from 0 to 1,000,000,000. Optional; omitting it uses the cited default of 0.67% of the loan (LodeStar 2026). An IMMEDIATE break-even requires total upfront cost to be zero (or non-positive) AND monthly_savings to be non-negative, i.e. closing_costs AND points both 0, not closing_costs alone; a zero-total-cost refi into a worse deal (negative monthly_savings) reports NEAR_ZERO_OR_NEGATIVE_SAVINGS instead.
current_balanceYesOutstanding principal you would refinance. Decimal, greater than 0 and at most 1,000,000,000. REQUIRED, no default.
new_term_monthsYesThe new loan term in months. Integer from 1 to 480. REQUIRED, no default.
new_annual_rate_pctYesThe offered refinance rate as a percentage. Decimal from 0 to 20. REQUIRED, no default.
roll_costs_into_loanNoWhether closing costs and points are added to the new principal instead of paid upfront; cash-flow break-even then reports COSTS_ROLLED_INTO_LOAN. Optional; defaults to false when omitted.
investment_return_pctNoAnnual return used for the economic (invest-the-savings) break-even, an EFFECTIVE ANNUAL rate; the monthly compounding step is (1+pct/100)^(1/12)-1. Decimal from 0 to 30. Optional; omitting it uses the cited long-run S&P 500 nominal total-return default, about 10%; call list_defaults for the exact current value.
remaining_term_monthsYesMonths left on the current loan. Integer from 1 to 480. REQUIRED, no default.
current_annual_rate_pctYesCurrent loan's annual rate as a percentage, e.g. 6.5 not 0.065. Decimal from 0 to 20. REQUIRED, no default.
current_monthly_paymentNoYour actual statement P&I payment. Decimal, greater than 0 and at most 1,000,000,000. Optional; when supplied it overrides the formula-derived payment, so match your statement.
compute_economic_break_evenNoWhether to compute the economic (net-worth crossover) break-even. When false, only the cash-flow break-even and interest delta are returned, and economic_break_even reports NOT_REQUESTED. Optional; defaults to true when omitted.

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly, idempotent, and non-destructive, so the description builds on that. It adds meaningful behavioral context: deterministic calculation, scalar output with no chart series, 'calculation, not advice', source-cited defaults, and a term-reset-trap flag. This exceeds a baseline 3, though it does not describe the exact response envelope or error codes, which are not covered by an output schema.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is longer than minimal but every sentence earns its place: it front-loads the disclaimer, uses a concise bulleted output list, and closes with scope/alternatives. It could be tightened slightly by removing redundant phrasing like 'Calculation, not advice' but overall it is structured for scanning.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given a complex tool with 12 parameters, no output schema, and many computed results, the description is complete enough: it lists all six output categories, states the scope limitations, and references defaults sources. It does not enumerate output types or exact return conventions, but the parameter schema already documents the most intricate edge behaviors, so nothing critical prevents correct selection and invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema itself thoroughly explains every parameter, including default values and edge cases. The description only frames inputs at a high level ('closing costs, optional points') without increasing per-parameter meaning. Baseline 3 is appropriate when the schema carries the semantic load.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb ('calculates'), a clear resource ('mortgage refinance break-even analysis'), and enumerates the exact outputs. It also explicitly contrasts with sibling 'compare_mortgage_terms', so an agent can distinguish them without inspecting either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives explicit when-to-use guidance: 'pick this when refinancing your existing mortgage into a new rate and term is the question' and names the alternative for purchases not yet taken out ('compare_mortgage_terms'). It also states scope exclusions ('Rate-and-term refis only (cash-out and tax effects are out of scope)') and includes a professional-verification caution.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

calculate_runwayCash Runway CalculatorA
Read-onlyIdempotent
Inspect

Calculation, not advice. Verify with a professional before acting. Deterministic cash-runway calculator. Given your liquid savings, monthly essential expenses, optional ongoing inflows (partner income, side income, severance paid as a monthly stream, unemployment benefits), and an optional expense-inflation rate, computes how many months the fund lasts before it hits zero, plus a month-by-month drawdown schedule.

Single scenario per call: to compare 'status quo' vs 'cutbacks' vs 'cutbacks + unemployment', call once per scenario with the matching expenses and inflows.

When inflows meet or exceed expenses (and expenses are not inflating), the fund does not draw down and a self-describing does-not-deplete outcome is returned instead of a month.

Yield on the fund is treated as 0% in v1 (conservative). All assumptions cite their source.

Pick this to project how many months current savings will last against expenses and inflows; pick calculate_emergency_fund when the question is the fund's target size, not how long it lasts.

HEAVY tool: use output='summary' (default) for the headline or output='inline' for the full schedule.

ParametersJSON Schema
NameRequiredDescriptionDefault
outputNoValid values: 'summary' (default), 'inline'. 'summary' returns headline scalars (depletion month or does-not-deplete) and citations with the schedule stripped. 'inline' returns the full payload including the month-by-month schedule[] for chart rendering.
chart_titleNoReserved for the chart pipeline; validated but not yet used. Must not contain em-dashes or en-dashes. Max 120 characters. Optional.
current_savingsYesLiquid fund available to draw down. Decimal from 0 to 1,000,000,000; 0 is valid (an already-empty fund). REQUIRED, no default.
monthly_inflowsNoOngoing monthly income that continues during the drawdown (partner income, side income, unemployment benefit, severance paid monthly), modeled as a flat monthly stream; a one-time severance lump and time-limited benefits are not modeled in v1. Decimal from 0 to 1,000,000,000. Optional; defaults to 0 when omitted.
use_essential_expensesNoExpense basis: true means essential-only spending (survival runway), false means total spending (current-pace runway). Labels the reported expense_basis. Optional; defaults to true when omitted.
annual_inflation_rate_pctNoAnnual expense-inflation rate as a percentage. When greater than 0, expenses grow each month by the monthly equivalent of this annual rate; when 0, expenses are constant. Decimal, at least 0 and less than 100. Optional; defaults to 0 when omitted.
monthly_essential_expensesYesMonthly outflow at the chosen expense basis. Decimal, greater than 0 and at most 1,000,000,000. REQUIRED, no default.

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint and idempotentHint, and the description adds meaningful behavioral context: 'Calculation, not advice', 0% yield assumption in v1, the does-not-deplete edge case, and the HEAVY-tool output-mode behavior. These details go well beyond what the structured annotations convey.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is longer than average, but every section earns its place: safety disclaimer, core computation, single-scenario rule, edge-case behavior, assumptions, sibling differentiation, and output-mode warning. Key guidance is front-loaded and the structure is easy to scan.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description compensates by specifying what summary returns ('headline scalars', 'depletion month or does-not-deplete', citations) and what inline returns ('month-by-month schedule[]'). It also covers assumptions, edge cases, and behavior limits sufficiently for an agent to select and invoke the tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3. The description adds interpretative meaning beyond the schema by framing current_savings as 'liquid savings', explaining that inflows include partner income, side income, severance-as-monthly-stream, and unemployment benefits, and clarifying the expense-inflation behavior in context. This is useful but somewhat redundant with already-rich schema descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: a 'deterministic cash-runway calculator' that computes how many months a fund lasts before hitting zero. It also differentiates itself from calculate_emergency_fund by clarifying the question being answered is duration, not target fund size.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit guidance is provided: 'Pick this to project how many months current savings will last' versus 'pick calculate_emergency_fund when the question is the fund's target size.' It also instructs users to call once per scenario for comparisons and to use output='summary' or 'inline' appropriately.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

compare_debt_consolidationDebt Consolidation ComparisonA
Read-onlyIdempotent
Inspect

Calculation, not advice. Verify with a professional before acting. Compares keeping cards vs. consolidation loan vs. optional balance transfer offers (single offer or head-to-head multi-offer comparison via bt_offers[]).

Supports partial balance transfers via bt_transfer_limit: transfers only up to that dollar amount (choosing cards by highest APR, highest balance, or manually), then runs a combined simulation of the BT card + remaining original-card balances together, so freed minimum payments are correctly redistributed.

Shows total cost, interest saved, monthly payment change, origination fee breakeven, hidden risks (reracking), promo-trap detection per offer, and what-if scenarios. Works with multiple cards including cash advance balances.

Pick this to weigh a consolidation loan (required input), plus any balance-transfer offers, against keeping current cards; pick calculate_cc_payoff for a single payoff timeline with no consolidation option, and pick compare_payoff_strategies to compare avalanche against snowball ordering on the cards as they stand.

The response includes chart_hints with rendering directives any client can use.

HEAVY tool: use output='summary' (default) for the headline comparison or output='inline' for the full payload.

ParametersJSON Schema
NameRequiredDescriptionDefault
cardsYesCredit cards to include, 1 to 20. Required: omitting the field answers contract_error MISSING_REQUIRED_FIELD, while an explicit JSON null answers parse_error ("cards must be a JSON array."); the two are not the same rejection. segments and stop_spend_month are not supported by this tool and are rejected; a non-zero monthly_spend, annual_fee, or plan_fees_monthly is rejected too, but zero (including an explicit 0) is accepted and dropped for all three: this tool always reads the flat purchase_balance / cash_advance_balance fields on each card below, never segments[] balances, and card-level fees and ongoing spend are not modeled here. Those five fields are supported by calculate_cc_payoff and compare_payoff_strategies instead.
outputNoValid values: 'summary' (default), 'inline'. summary: compact response with a data_preview block; no heavy array exists on this response today, so summary and inline are currently identical in content. inline: full payload (currently identical to summary; will diverge once a heavy array is added for chart rendering).
bt_offersNoBalance-transfer offers to compare head-to-head, 1 to 10, preferred over the four scalar bt_* fields above when comparing two or more offers: when supplied, bt_offers overrides bt_apr_pct, bt_promo_months, bt_regular_apr_pct, and bt_fee_pct, and their range checks are skipped. The response includes a balance_transfer_offers block with per-offer simulation results, selected_offer_index, selected_offer_label, selected_offer_reason, and all_offers_trap. Optional; a JSON null is rejected, unlike bt_manual_transfers and windfalls below, where a JSON null is treated as omitted.
windfallsNoOne-time principal payments, at most 12, same shape as calculate_cc_payoff. Optional; a JSON null is treated as omitted, the same as leaving the field out. When non-empty, the response adds windfalls_applied[] and windfalls_unused[] on keep_cards, on each balance_transfer scenario (and on each offer in balance_transfer_offers.offers[]), on consolidation_loan (single-row per windfall since the loan is a single-balance instrument), and on optimized_consolidation.keep_subset. All scenarios use with-windfalls totals so cross-option ranking stays apples-to-apples, except what_if.same_budget_accelerated, a windfall-free hypothetical about extra-payment behavior, not your actual lump-sum schedule.
bt_apr_pctNoBalance-transfer APR as a percentage, 0-100, e.g. 0 for a 0% promo. Required when include_balance_transfer is true and bt_offers is omitted. bt_offers, when supplied, overrides this and the other three bt_* scalars below. The 0-100 range applies only when include_balance_transfer is true and bt_offers is omitted; a non-number is rejected in every case.
bt_fee_pctNoBalance-transfer fee as a percentage of the transferred balance, 0-10. Optional; defaults to 3.0 when omitted. bt_offers, when supplied, overrides this. The 0-10 range applies only when include_balance_transfer is true and bt_offers is omitted; a non-number is rejected in every case.
chart_titleNoOverride for the chart title. Optional; must not contain an em dash or en dash. Max 120 characters.
full_scheduleNoWhether to return the full month-by-month amortization schedule instead of the compact default. Optional; defaults to false when omitted.
bt_promo_monthsNoBalance-transfer promo period in months, 1-60. Optional; defaults to 18 when omitted. bt_offers, when supplied, overrides this. The 1-60 range applies only when include_balance_transfer is true and bt_offers is omitted; a non-number is rejected in every case.
current_strategyNoYour current payoff strategy, compared against the consolidation loan / balance-transfer alternative: 'avalanche' or 'snowball'. Optional; defaults to 'avalanche' when omitted. Case-sensitive.
bt_transfer_limitNoCap on the total dollar amount transferred to the balance-transfer card, at least $0.01. When set, only this amount moves to the BT card; remaining balances stay on original cards, and a combined simulation runs both halves together, correctly redistributing freed minimum payments. Optional; when omitted, the entire balance is transferred (legacy behavior). Needs include_balance_transfer or bt_offers.
bt_regular_apr_pctNoAPR that applies after the promo period ends, as a percentage, 0-100. Optional; defaults to 25.20 when omitted. bt_offers, when supplied, overrides this. The 0-100 range applies only when include_balance_transfer is true and bt_offers is omitted; a non-number is rejected in every case.
consolidation_loanYesLoan terms for the consolidation option. Required: omitting the field answers contract_error MISSING_REQUIRED_FIELD, while an explicit JSON null answers parse_error ("consolidation_loan must be an object."); the two are not the same rejection.
bt_manual_transfersNoExact per-card transfer amounts. Required when bt_transfer_strategy is 'manual'; each entry's card_name must match a name in cards[]. Optional otherwise; a JSON null is treated as omitted, the same as leaving the field out.
bt_transfer_strategyNoHow to choose which balances move when bt_transfer_limit is less than your total debt: 'highest_apr_first' (transfer from highest-APR segments first, maximizes interest savings), 'highest_balance_first' (transfer largest balances first), or 'manual' (use bt_manual_transfers to specify exact amounts per card). Optional; defaults to 'highest_apr_first' when omitted. Needs bt_transfer_limit.
extra_monthly_paymentNoExtra monthly payment in dollars, $0 to $1,000,000,000, applied on top of the required minimums for keep-cards and any balance transfer, and on top of the loan's own required payment for consolidation. Optional; defaults to 0 when omitted. fixed_payments is not a parameter of this tool, unlike calculate_cc_payoff: the keep-cards baseline is always simulated under the canonical constant rolled-forward payment (see comparison_basis in the response); supplying fixed_payments returns an unknown_parameter error.
include_balance_transferNoWhether to run a single-offer balance-transfer scenario using the four scalar bt_* fields below. Optional; defaults to false when omitted. Ignored once bt_offers is supplied; use bt_offers when comparing two or more offers.

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare a safe, idempotent, closed-world read, so the safety profile needs no restating. The description adds real behavioral context beyond that: it is a calculation with an explicit non-advice disclaimer, it is flagged HEAVY with a summary/inline output switch, it discloses promo-trap detection and reracking risk analysis, and it notes the response carries chart_hints. It stops short of describing pagination or result size limits.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Six short paragraphs, front-loaded with the comparison and the required-input constraint, then sibling routing, then output modes. Dense but each paragraph carries distinct information; the disclaimer line and the output-mode note are the only near-redundant portions given the depth of the schema.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists and the tool is large (17 parameters, nested objects), so the description must orient the agent on results, which it does by listing the headline metrics, scenario comparisons, and the balance_transfer_offers/selection blocks that the schema details further. Some response structure is only documented in schema descriptions, but the combination is complete enough to call the tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3; the description still earns above it by explaining behavior the schema does not: bt_transfer_limit triggers a combined simulation of the BT card plus remaining original-card balances with freed minimum payments redistributed, and bt_offers supersedes the scalar bt_* fields. These are semantic interactions, not parameter restatements.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening sentence names the exact comparison performed (keeping cards vs. a consolidation loan vs. optional balance-transfer offers) and specifies both the single-offer and head-to-head multi-offer modes. It then explicitly distinguishes itself from two named siblings, so an agent can route correctly without opening schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives an explicit selection rule with alternatives and their conditions: use this tool to weigh a consolidation loan (declared required) plus BT offers against keeping cards, use `calculate_cc_payoff` for a single payoff timeline, and `compare_payoff_strategies` for avalanche-vs-snowball on cards as they stand. Exclusions and the required input are stated outright.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

compare_mortgage_termsMortgage Terms ComparisonA
Read-onlyIdempotent
Inspect

Calculation, not advice. Verify with a professional before acting. Compare two fixed-rate mortgage options side-by-side:

  • 15 vs 30 year,

  • different rates,

  • points vs no points, or

  • any two terms.

Shows total interest, monthly payment breakdown (with tax, insurance, PMI, HOA), equity buildup, and, critically, what happens if you take the cheaper mortgage and invest the monthly savings. Finds the break-even investment return rate.

Includes tax deduction analysis (itemizing vs standard deduction), PMI auto-removal tracking per the Homeowners Protection Act, and after-tax net worth comparison.

Pick this to compare two mortgage structures on a purchase, such as 15 versus 30 year or points versus no points; pick calculate_refi_breakeven when the comparison is against your existing loan and a refinance offer, and pick compare_payoff_strategies when the question is payoff strategy on revolving debt, not a mortgage term.

Note: ARM (adjustable-rate) mortgages are not yet supported.

ParametersJSON Schema
NameRequiredDescriptionDefault
option_aYesThe first fixed-rate mortgage option to compare. Required.
option_bYesThe second fixed-rate mortgage option to compare, same shape as option_a. Required. Must differ from option_a on at least one of term_years, annual_rate_pct, or points.
tax_yearNoTax year, 2025 or 2026, selecting the IRS standard-deduction table for the itemize-vs-standard analysis. Optional; defaults to 2026 when omitted.
home_priceYesHome purchase price in dollars. Required. Must be positive and no more than $1,000,000,000.
hoa_monthlyNoMonthly homeowners association dues in dollars. Optional; defaults to 0 when omitted. Must be zero or more and no more than $1,000,000,000.
pmi_monthlyNoMonthly PMI (private mortgage insurance) in dollars, charged when down payment is below 20%. Optional; defaults to 0 when omitted. Must be zero or more and no more than $1,000,000,000.
filing_statusNoTax filing status: 'single', 'married', or 'head_of_household'. Optional; matched case-insensitively.
full_scheduleNoWhether to return the full month-by-month amortization schedule instead of the compact default. Optional; defaults to false when omitted.
tax_bracket_pctNoMarginal tax bracket as a percentage, 0-50. Optional; enables after-tax investment return and mortgage interest deduction analysis when supplied. The after-tax comparison credits each option's annual deduction savings to its investments at year end.
closing_cost_pctNoClosing costs as a percentage of the loan amount, 0-20, points excluded. Optional; defaults to the Urban Institute loan-size regressive schedule (about 4.6% at a $97K loan down to about 1.4% at a $679K loan) when omitted. Pass 0 to model zero closing costs.
down_payment_pctNoDown payment as a percent of home_price, e.g. 20 for 20%. Optional; defaults to 20 when omitted. Range 0-100 applies only when down_payment_amount is absent; when down_payment_amount is supplied, down_payment_pct is ignored entirely, including in provenance.
standard_deductionNoStandard deduction in dollars, compared against itemized mortgage-interest deductions. Optional; defaults to the IRS basic standard deduction for tax_year and filing_status when omitted (TY2026: 16100 single, 32200 married, 24150 head_of_household; TY2025: 15750 single, 31500 married, 23625 head_of_household; source Rev. Proc. 2025-32). Must be zero or more.
time_horizon_yearsNoNumber of years to project the invest-the-difference comparison, 1-40. Optional; defaults to the maximum of both options' term_years when omitted.
down_payment_amountNoDown payment in dollars. Optional; overrides down_payment_pct entirely, including its provenance row, when supplied. Must be at least $0 and strictly below home_price.
pmi_removal_ltv_pctNoLoan-to-value percentage at which to model borrower-requested PMI removal, 50-100. Optional; when omitted, PMI is modeled as removed at the 78% HPA automatic-termination threshold instead. The rejection guard applies only when pmi_monthly is above 0 and the loan's own initial LTV is above 80 percent, the range where PMI applies: there, a value at or above that initial LTV is rejected, because the requested removal point would already be met at the first payment, so no PMI would be modeled for any month of the loan. When pmi_monthly is omitted or 0, no such check runs, whatever the LTV, and the supplied value is accepted but changes nothing in the response, because no PMI is modeled in that case. Either way PMI also stops at the statutory amortization midpoint of each option's own term (12 U.S.C. §4901(7), §4902(c)), which this value cannot postpone.
property_tax_annualNoAnnual property tax in dollars, for true monthly cost. Optional; defaults to 0 when omitted. Must be zero or more.
extra_monthly_paymentNoExtra principal payment in dollars, applied equally to BOTH options every month. Optional; defaults to 0 when omitted. Must be zero or more and no more than $1,000,000,000.
home_insurance_annualNoAnnual home insurance in dollars. Optional; defaults to 0 when omitted. Must be zero or more.
invest_the_differenceNoWhether both options deploy the same total budget every month: the higher option's P&I plus any extra_monthly_payment plus the month-1 PMI both carry. The cheaper-mortgage holder invests the payment gap each month; an option that stops paying PMI earlier invests the freed cash; the option with the lower upfront points cost invests the difference at month 0; after payoff the full budget goes to investments. See comparison_basis in the response. Optional; defaults to true when omitted.
investment_return_pctNoAssumed investment return on the invested payment gap, as a percentage, an EFFECTIVE ANNUAL rate; the monthly compounding step is (1+pct/100)^(1/12)-1. Optional; defaults to the cited Senaro long-run S&P 500 nominal return, about 10%, when omitted. Range 0-30.

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and destructiveHint=false, so the baseline bar is lower, but the description still adds meaningful behavioral context: it warns 'Calculation, not advice,' states the outputs produced, notes the invest-the-difference and break-even analysis, and explicitly calls out that ARMs are unsupported. No annotation contradiction exists.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is long but every section earns its place: caveat, scenarios, outputs, routing to alternatives, and unsupported case. Bullet formatting and the front-loaded 'Calculation, not advice' warning make it scannable and effective for a tool with 20 parameters.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity, nested parameters, and absence of an output schema, the description is complete: it summarizes what results are returned, which analyses are included, how to choose among sibling tools, and what is not supported. An agent has enough context to invoke it correctly without additional documentation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the schema already documents all 20 parameters. The description adds value by explaining the purpose of key concepts such as invest-the-difference, break-even investment return, PMI auto-removal per the Homeowners Protection Act, and itemize-vs-standard deduction analysis, which are not obvious from parameter names alone.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: 'Compare two fixed-rate mortgage options side-by-side,' and enumerates exact scenarios (15 vs 30 year, different rates, points vs no points, any two terms). It also names sibling tools it is not, making the distinction explicit.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives explicit routing instructions: use this tool for comparing two mortgage structures, use calculate_refi_breakeven for refinance comparisons against an existing loan, and use compare_payoff_strategies for revolving-debt payoff strategy. It also excludes ARM mortgages, which is a clear when-not-to-use signal.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

compare_payoff_strategiesPayoff Strategy ComparisonA
Read-onlyIdempotent
Inspect

Calculation, not advice. Verify with a professional before acting. Compare avalanche and snowball payoff strategies side-by-side. Pick this to compare avalanche against snowball ordering on the same set of cards; pick calculate_cc_payoff once a strategy is chosen and the need is a single payoff timeline with windfalls or fixed payments.

Returns months, total interest, payoff order, savings, total_balance, weighted_average_apr_pct, monthly_interest_now, and cost_breakdown array. When an arm is not projected to reach a zero balance, the savings.* fields for that arm carry status: "none" with value: null and an explanation, not a number.

savings.strategies_convergence separately discloses the avalanche/snowball arms' own convergence:

  • "both_converge" (ordinary case),

  • "worst_never_amortizes" (the losing strategy arm does not reach a zero balance in this projection but the winning arm does, so savings.best_vs_worst_months/interest carry status: "none"), or

  • "neither_converges" (the WINNING strategy arm does not reach a zero balance either, so no dollar or month figure derived from either strategy arm is reportable).

lower_total_interest_strategy names whichever of avalanche or snowball has the lower total interest, or "tie" when the two arms' total interest and months are exactly equal. A "tie" can co-occur with savings.strategies_convergence="neither_converges". It reports only that comparison, never that either arm pays off.

chart_hints.title and chart_hints.annotations[0].label report a dollar figure only when the comparison they describe is Defined, and otherwise state plainly that no figure is reportable, matching the "none" status on the corresponding savings.* field. Unlike chart_hints, summary and savings.savings_summary hedge rather than decline a figure, and the two always carry the same sentence as each other: when the compared arm does not converge (savings.baseline_never_amortizes=true, or savings.strategies_convergence=worst_never_amortizes) but the winning side still does, both report a figure hedged as a floor, "saves at least $X and N months ..., so the true saving could be higher than reported". Only when strategies_convergence=neither_converges do summary and savings.savings_summary also state that no figure is reportable. No savings.* sentinel object carries a hedged floor in its value key; the only place a floor ever appears is the savings.savings_summary sentence.

This tool carries no domain-shaped warnings[].type entries (unlike calculate_cc_payoff); warnings[] here is exclusively validation disclosures emitted as warnings[].code, e.g. MINIMUM_PAYMENT_NOT_BINDING_IN_BASELINE plus the general portfolio findings (MANY_CARDS, HIGH_CARD_BALANCE, etc.) shared with every card-based tool.

The response includes chart_hints with rendering directives any client can use.

This tool takes no strategy, fixed_payments, full_schedule or per_segment parameter: it always compares avalanche against snowball over the full schedule, so all four are rejected as unknown_parameter.

ParametersJSON Schema
NameRequiredDescriptionDefault
cardsYesCredit cards to compare, 1 to 20, the same shape calculate_cc_payoff takes. Required: omitting the field answers contract_error MISSING_REQUIRED_FIELD, while an explicit JSON null answers parse_error ("cards is required and must be an array."); the two are not the same rejection. Every card may carry segments[]. Max 100 segments total across all cards in the request. Per-card, per-type caps: 1 purchase, 1 cash_advance, 5 balance_transfer, 1 promotional, 10 installment_plan.
outputNoValid values: 'summary' (default), 'inline'. summary returns the headline comparison with a data_preview block; inline returns the full payload.
chart_titleNoOverride for the chart title. Optional; must not contain an em dash or en dash. Max 120 characters.
apply_rate_capNoWhether to cap each card's APR at the regulatory ceiling before simulating. Optional; defaults to false when omitted.
extra_monthly_paymentNoExtra monthly payment in dollars, $0 to $1,000,000,000, applied on top of the required minimums. Optional; defaults to 0 when omitted.

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and destructiveHint=false, and the description aligns ('Calculation, not advice'). It goes far beyond annotations by disclosing the convergence sentinel behavior (both_converge, worst_never_amortizes, neither_converges), the hedging rules in summary vs. chart_hints, the warnings[] shape difference from calculate_cc_payoff, and the rejection of strategy/fixed_payments/full_schedule/per_segment as unknown_parameter. Exceptionally rich behavioral disclosure.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The most important content is front-loaded: disclaimers, purpose, sibling routing, then return summary. The description is long, but the extended sections on convergence states, hedging, and warnings are dense technical spec material that earns their place for this complex tool. Minor deducting for length and a few redundancies (e.g., repeated explanation of 'neither_converges').

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, so the description carries the full burden of explaining return values — and it does, listing months, total interest, payoff order, savings, cost_breakdown, chart_hints, and the non-convergence sentinel semantics in detail. Combined with 100% input-schema coverage and safety annotations, an agent has everything needed to invoke it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3. The description adds genuine value beyond the schema by warning that strategy, fixed_payments, full_schedule, and per_segment are rejected as unknown_parameter, and by clarifying that the tool 'always compares avalanche against snowball over the full schedule.' This prevents agents from passing sibling-style parameters.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a precise verb and resource: 'Compare avalanche and snowball payoff strategies side-by-side.' It explicitly names its sibling `calculate_cc_payoff` and states the distinguishing condition (comparing two strategies vs. a single chosen strategy), so an agent can tell them apart without opening schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives direct routing guidance: 'Pick this to compare avalanche against snowball ordering on the same set of cards; pick calculate_cc_payoff once a strategy is chosen and the need is a single payoff timeline with windfalls or fixed payments.' This is explicit when-to-use and when-not-to-use with the named alternative.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

compare_payoff_vs_investDebt Payoff vs. InvestA
Read-onlyIdempotent
Inspect

Calculation, not advice. Verify with a professional before acting. Compares the guaranteed return of debt payoff against expected investment returns over a time horizon.

Accounts for post-payoff investing, tax implications, and finds the break-even investment return rate. Works for credit cards, auto loans, student loans, personal loans, and mortgages.

Pick this when the alternative to investing is paying down an existing debt balance; pick calculate_opportunity_cost when there is no debt in the picture and the alternative is simply forgoing recurring spending to invest it instead.

The response echoes debt_type (canonical lowercase enum the projection was computed for) and debt_type_label (display-ready string, e.g. 'student loan') at the top level.

HEAVY tool: use output='summary' (default) for the headline comparison or output='inline' for the full month-by-month schedule.

When no projection can be computed (very long horizon, very high rate, or very large amounts), the response is { meta, not_representable: { code, explanation } } instead.

ParametersJSON Schema
NameRequiredDescriptionDefault
outputNoValid values: 'summary' (default), 'inline'. 'summary' returns headline comparison scalars, milestones, and citations with monthly_schedule stripped. 'inline' returns the full payload including the month-by-month monthly_schedule[].
debt_typeYesDebt type: 'credit_card', 'auto', 'student', 'personal', or 'mortgage'. REQUIRED, no default.
chart_titleNoOverride for the chart title. Must not contain em-dashes or en-dashes. Max 120 characters. Optional.
debt_apr_pctYesDebt annual percentage rate, e.g. 6.5 not 0.065. Decimal from 0 to 100. REQUIRED, no default.
debt_balanceYesCurrent debt balance. Decimal, greater than 0. REQUIRED, no default.
extra_monthlyYesExtra monthly amount available for debt payoff or investing, the amount in question. Decimal, greater than 0. REQUIRED, no default.
full_scheduleNoWhether to return the full month-by-month schedule. Optional; defaults to false (compact schedule) when omitted.
minimum_paymentNoMinimum monthly payment. Decimal, at least 0. Optional; auto-calculated for amortizing loans when omitted or 0.
tax_bracket_pctNoTax bracket, e.g. 22 not 0.22. Decimal from 0 to 100. Optional; enables an after-tax comparison when supplied.
time_horizon_yearsNoProjection horizon. Integer, greater than 0. Optional; defaults to the greater of the payoff horizon or 10 years when omitted.
investment_return_pctNoExpected investment return, an EFFECTIVE ANNUAL rate; the monthly compounding step is (1+pct/100)^(1/12)-1. Decimal from 0 to 30. Optional; defaults to the cited long-run S&P 500 nominal return, about 10%, when omitted.
term_months_remainingNoMonths remaining on the debt. Integer, greater than 0. Required for non-credit-card debt types; optional for credit_card.
investment_tax_advantagedNoWhether the investment is tax-advantaged, e.g. 401k or IRA. Optional; defaults to false when omitted.
investment_volatility_pctNoInvestment volatility. Decimal. Optional; adds a volatility risk note when supplied.

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly/idempotent/non-destructive, and the description adds meaningful behavioral detail: the tool returns echoed debt_type/debt_type_label fields, has a not_representable error shape for extreme inputs, and is a calculation with a professional-verification caveat. No contradiction with annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is dense but well-structured: purpose first, scope, sibling routing, response shape, output modes, and error case. A few phrases restate schema details (e.g., output='summary' default), but every sentence earns its place and the critical scoping is front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a complex 14-parameter tool with no output schema, the description is notably complete: it covers the comparison's scope, when to use it, output variants, top-level response fields, and the not_representable failure mode. An agent has enough context to invoke it correctly without further inference.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so each of the 14 parameters is already documented. The description adds contextual framing (post-payoff investing, tax implications, break-even) but does not explain any parameter beyond what the schema provides; it only slightly enriches output-parameter behavior (summary vs inline, not_representable).

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names the exact operation: 'Compares the guaranteed return of debt payoff against expected investment returns over a time horizon,' with a clear verb and resource. It also enumerates supported debt types and explicitly contrasts itself with calculate_opportunity_cost, making sibling differentiation immediate.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Contains explicit when-to-use guidance: 'Pick this when the alternative to investing is paying down an existing debt balance; pick calculate_opportunity_cost when there is no debt.' It also gives output-selection guidance ('HEAVY tool: use output='summary' ... or output='inline'') and notes failure conditions with not_representable.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

compare_rent_vs_buyRent vs. Buy CalculatorA
Read-onlyIdempotent
Inspect

Calculation, not advice. Verify with a professional before acting. Deterministic rent-vs-buy decision model. Given a home price, monthly rent, and mortgage rate, computes the breakeven month (when BUY net worth first equals or exceeds RENT net worth), per-horizon net-worth comparison (years 5, 10, 30 + user horizon), and the full year-by-year cost/wealth series for both paths.

Includes carrying costs (property tax, insurance, maintenance, PMI, HOA) on the BUY path and opportunity-cost investing on the RENT path.

Optional federal tax benefit model (Tier-3: apply_tax_benefit=true) with the statute's year-by-year SALT cap schedule (advancing with the horizon, not frozen at tax_year), mortgage interest deduction, PMI deductibility, and marginal-excess formula.

All assumptions cite primary sources (FHFA, BLS, Harvard JCHS, IRS Pub 936, OBBBA Pub. L. 119-21) and can be overridden.

Pick this for the buy-vs-rent tenure decision; pick compare_mortgage_terms when you are already buying and comparing two mortgage structures, compare_payoff_strategies to order credit-card payoff, and compare_payoff_vs_invest to weigh extra debt payments against investing.

HEAVY tool: use output='summary' (default) for scalar headline or output='inline' for chart series.

ParametersJSON Schema
NameRequiredDescriptionDefault
outputNoValid values: 'summary' (default), 'inline'. 'summary' returns headline scalars and per-horizon snapshots with the heavy series stripped. 'inline' returns the full payload including yearly_series[] and monthly_series[] for chart rendering.
pmi_pctNoAnnual PMI rate as a percentage of the loan. Decimal from 0 to 5. Optional; defaults to 0.5% when omitted. The removal threshold is set by pmi_removal_ltv_pct.
tax_yearNoTax year for year 1 of the horizon: 2025 or 2026. Integer. Required when apply_tax_benefit is true; otherwise not used. Horizon year y uses tax year (tax_year + y - 1) on both the tax benefit and the sale-side capital-gains tax; the SALT cap follows the statute's own schedule for that year, and every other indexed amount holds at its last-loaded table value beyond it.
charitableNoCharitable contributions. Decimal from 0 to 1,000,000,000; TY2026 and later apply a 0.5%-of-AGI floor per OBBBA §70425. Only used when apply_tax_benefit is true. Optional; defaults to 0 when omitted.
home_priceYesHome purchase price. Decimal, greater than 0 and at most 1,000,000,000. REQUIRED, no default.
pmi_annualNoAnnual PMI premium in dollars while PMI is active. Decimal from 0 to 1,000,000,000. Only used when apply_tax_benefit is true. Optional; when omitted it is derived from pmi_pct and the original loan amount.
real_termsNoWhether to deflate the output series to a real-terms view. Optional; defaults to false when omitted.
blind_countNoNumber of blind filers, each adding the additional standard deduction. Integer from 0 to 2. Only used when apply_tax_benefit is true. Optional; defaults to 0 when omitted.
chart_titleNoOverride for the chart title. Must not contain em-dashes or en-dashes. Max 120 characters. Optional.
hoa_monthlyNoMonthly HOA dues on the BUY path. Decimal from 0 to 1,000,000,000. Optional; defaults to 0 when omitted.
monthly_rentYesMonthly rent on the RENT path. Decimal from 0 to 1,000,000,000; 0 is valid (a free-housing baseline). REQUIRED, no default.
filing_statusNoTax filing status: 'Single', 'MFJ', 'MFS' or 'HoH'. Required when apply_tax_benefit is true; otherwise not used.
horizon_yearsNoProjection horizon in years. Integer from 1 to 40. Optional; when omitted, the response returns snapshots at years 5, 10 and 30.
inflation_pctNoInflation as a percentage, for the real_terms toggle. Decimal, at least 0 and less than 100. Optional; when omitted, a nominal run uses 2.5%, and a real_terms run uses the horizon-based default: 2.0% for a horizon of 7 years or less, 2.6% for 8 to 19 years, 2.5% for 20 years or more.
sell_side_pctNoSell-side transaction cost as a percentage of the sale price. Decimal from 0 to 25. Optional; defaults to the cited 7.5% (Redfin post-NAR) when omitted; override for your market.
maintenance_pctNoAnnual maintenance as a percentage of home value. Decimal from 0 to 20. Optional; defaults to the cited 1.5% (Harvard JCHS) when omitted; override for your market.
rent_growth_pctNoAnnual rent escalation as a percentage. Decimal from -50 to 50. Optional; defaults to the cited 3.4% (BLS CPI ROPR) when omitted; override for your market.
state_local_taxNoState and local tax paid (state income or sales tax), for the SALT cap. Decimal from 0 to 1,000,000,000. Only used when apply_tax_benefit is true. Optional; defaults to 0 when omitted.
down_payment_pctNoDown payment as a percentage of the home price. Decimal from 0 to 100; 0 is valid (zero-down programs). Optional; defaults to 20% when omitted.
loan_term_monthsNoMortgage term in months. Integer from 1 to 480. Optional; defaults to 360 (a 30-year fixed) when omitted.
other_itemizableNoOther itemizable deductions, such as medical expenses above 7.5% of AGI or casualty losses. Decimal from 0 to 1,000,000,000. Only used when apply_tax_benefit is true. Optional; defaults to 0 when omitted.
property_tax_pctNoAnnual property tax as a percentage of home value. Decimal from 0 to 10. Optional; defaults to 0.88% when omitted; override for your market.
age_65_plus_countNoNumber of filers aged 65 or older, each adding the additional standard deduction. Integer from 0 to 2. Only used when apply_tax_benefit is true. Optional; defaults to 0 when omitted.
apply_tax_benefitNoWhether to apply the federal tax benefit model (SALT cap following the statute's year-by-year schedule, mortgage interest deduction, PMI deductibility). When true, filing_status, tax_year and annual_gross_income are required. Optional; defaults to false when omitted. The §121 home-sale gain exclusion applies only at sale points 24 months or more after purchase (IRC §121(a) 2-of-5-year test); year 1 of yearly_series, months 1-23 in monthly_series, and any horizon under 24 months use no exclusion. Separately, IRC §1222(3) makes a holding period of 12 months or less short-term, taxed at ordinary rates instead of the long-term rates otherwise applied, on the home-sale gain and the renter's portfolio gain alike (24 months vs. 12 months are two different thresholds); this tool applies that split exactly to the home-sale gain, and to the renter's portfolio at annual grain: prior years' contributions are compounded together as one long-term lot rather than aged individually, so months inside a year understate short-term gain.
mortgage_rate_pctYesMortgage annual rate as a percentage, e.g. 6.75 for 6.75%. Decimal, greater than 0 and at most 100. Never cached; provide the current rate. REQUIRED, no default.
home_insurance_pctNoAnnual home insurance as a percentage of home value. Decimal from 0 to 15. Optional; defaults to 0.65% when omitted; override for your market.
annual_gross_incomeNoAnnual gross income, the MAGI proxy for the bracket and the SALT cap. Decimal from 0 to 1,000,000,000. Required when apply_tax_benefit is true; otherwise not used.
pmi_removal_ltv_pctNoLoan-balance trigger for PMI removal, as a percentage loan-to-value. Decimal from 50 to 100. Optional; omitted, it is the 78% HPA automatic-termination threshold; provided (e.g. 80), it is whichever of 78% or your value is reached first. The rejection guard applies only when down_payment_pct is below 20 and the resolved pmi_pct is above 0, the range where PMI applies: there, a value at or above the loan's own initial LTV is rejected, because the requested removal point would already be met at the first payment, so no PMI would be modeled for any month of the loan. When the resolved pmi_pct is 0, or down_payment_pct is 20 or more, no such check runs, whatever the LTV. Either way PMI also stops at the statutory amortization midpoint of the original term (12 U.S.C. §4901(7), §4902(c)), which this value cannot postpone.
purchase_year_pointsNoDiscount points paid at origination, as a percentage of the loan. Decimal from 0 to 4. Counted as cash paid at closing on every call; when apply_tax_benefit is true they are also deducted in year 1. Optional; defaults to 0 when omitted.
home_appreciation_pctNoAnnual home price appreciation as a percentage. Decimal from -50 to 50. Optional; defaults to the cited 4.25% (FHFA HPI) when omitted; override for your market.
investment_return_pctNoRENT path annual return as a percentage, an EFFECTIVE ANNUAL rate; the monthly compounding step is (1+pct/100)^(1/12)-1. Decimal from -50 to 30. Optional; defaults to 10.0% when omitted.
loan_origination_dateNoMortgage origination date, YYYY-MM-DD. Selects the mortgage interest deduction cap: on or before 2017-12-15 the $1M cap, after it the $750k cap (Single and MFJ; MFS caps are half). Only used when apply_tax_benefit is true. Optional; omitting it uses the post-2017 cap.
basis_capitalizable_pct_overrideNoPercentage of the buy-side closing cost that is capitalizable into the §121 adjusted basis (IRS Pub 523 split: abstract fees, title search, recording fees, survey fees, transfer taxes, owner's title insurance). Decimal from 0 to 100; 0 means a HomePrice-only basis, 100 means the full closing cost is in basis. Optional; defaults to 50% (Senaro deterministic midpoint) when omitted. Supply your actual HUD-1 split when available.

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly/idempotent/non-destructive, and the description goes well beyond them: it flags determinism, a 'calculation, not advice' disclaimer, which assumption values are cited defaults that can be overridden, and that apply_tax_benefit switches on a heavier tax model whose SALT cap advances with the horizon. It also discloses the return shape (headline scalars vs full yearly/monthly series) in the absence of an output schema.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The routing rules and the heavy-payload warning are in the final two paragraphs rather than front-loaded, and the tax-model middle section is dense. Still, nearly every sentence carries either a behavioral fact or a disambiguation rule, so there is little waste.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 33-parameter financial model with no output schema, the description covers what is computed, which costs are modeled on each path, the optional tax tier and how to engage it, the sourcing/override convention for defaults, and the sizing tradeoff of the two output modes. An agent has enough to call it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents every parameter, including defaults, ranges and the output enum. The description repeats the output='summary'/'inline' distinction and notes required inputs, adding little beyond what is already in the structured fields.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Deterministic rent-vs-buy decision model') and enumerates what it produces (breakeven month, per-horizon net-worth comparison, year-by-year series). It then explicitly names the sibling tools it should not be confused with, so an agent can disambiguate without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives an explicit routing rule: 'Pick this for the buy-vs-rent tenure decision; pick compare_mortgage_terms when you are already buying... compare_payoff_strategies to order credit-card payoff, and compare_payoff_vs_invest to weigh extra debt payments against investing.' It also states the usage mode for the heavy payload via output='summary' vs 'inline'.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_defaultsDefault Assumptions CatalogA
Read-onlyIdempotent
Inspect

Calculation, not advice. Verify with a professional before acting.

Returns named default values used by Senaro Finance tools with source citations. Use before calling other tools to understand what assumptions apply automatically. This tool does not accept an 'output' parameter; it always returns inline JSON.

JSON parameters (all optional):

  • category: string. Filter entries by category: the canonical MCP tool name that consumes the default, or "shared" for a default consumed by two or more tools (or not yet wired to a specific tool). Case-insensitive. The 14 valid values: calculate_cc_payoff, calculate_compound_interest, calculate_debt_to_income, calculate_emergency_fund, calculate_opportunity_cost, calculate_refi_breakeven, calculate_runway, compare_debt_consolidation, compare_mortgage_terms, compare_payoff_strategies, compare_payoff_vs_invest, compare_rent_vs_buy, optimize_401k_match, shared. An unrecognized value returns validation_error listing the accepted set.

  • name: string. Return one full entry. Requires category. Returns validation_error if not found.

  • verbose: boolean. Return all 10 citation fields per entry instead of just category+name. Ignored when name is also present (single entry always includes all fields). Omit category to get the full verbose catalog.

Default (no args): index mode. Every entry with just category and name fields (~20 KB).

ParametersJSON Schema
NameRequiredDescriptionDefault
nameNoOptional: return one full entry. Requires category. Returns validation_error if not found.
verboseNoOptional: return all 10 citation fields per entry instead of just category+name.
categoryNoOptional: filter entries by category, the canonical MCP tool name (e.g. "compare_rent_vs_buy") or "shared". Case-insensitive.

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint true, idempotentHint true, and destructiveHint false, so the description does not need to restate safety. It adds substantial behavioral detail: the 'calculation, not advice' disclaimer, the always-inline-JSON return, index vs verbose modes, ~20KB size, and validation_error behavior for unrecognized categories. No contradiction with annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with a disclaimer, purpose, usage note, and parameter details, and the key facts are front-loaded. It is somewhat lengthy due to the full category list, but each sentence earns its place. It could be slightly more concise, but it remains clear and organized.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description fully carries the burden of explaining return formats: index mode returns only category+name (~20KB), verbose mode returns all 10 citation fields, and single-entry mode includes all fields. It also covers validation_error behavior and how this tool fits among the calculation siblings. Nothing an agent needs to call it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Though schema coverage is 100%, the description goes beyond the schema by listing all 14 valid category values, explaining that name requires category, and clarifying that verbose is ignored when name is present. It also describes error responses for invalid categories, which is not in the schema. This adds meaningful usage context.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool returns named default values used by Senaro Finance tools with source citations. It distinguishes itself from calculation siblings by explicitly framing itself as a catalog to consult before other tools, and notes it always returns inline JSON without an 'output' parameter. This is a specific verb+resource with clear scope.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly instructs agents to use this tool before calling other tools to understand applicable assumptions. It also clarifies that it does not accept an 'output' parameter and explains parameter interactions (name requires category, verbose ignored when name is present). Default behavior for no arguments is specified as index mode. This leaves no ambiguity about when and how to use it.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

optimize_401k_match401(k) Match OptimizerA
Read-onlyIdempotent
Inspect

Calculation, not advice. Verify with a professional before acting. Deterministic 401(k) employer-match optimizer. Given your annual salary, pay frequency, current contribution percent, and your employer's match formula (as a named preset or a custom tier list), computes:

  • the annual match you receive today;

  • the maximum match available (full match entitlement);

  • the match forfeited at your current rate;

  • the minimum contribution percent to receive the full match;

  • whether front-loading contributions would forfeit match at a no-true-up plan; and

  • a per-period schedule showing level vs. front-load paths side by side.

Pick this to size 401(k) contributions against the employer match formula; pick compare_payoff_vs_invest when the question is putting an extra monthly amount toward a debt versus investing it (that tool takes no employer-match input), not how much of the match to receive.

IRS limits (402(g) elective deferral, 401(a)(17) compensation cap, 415(c) annual additions) are applied and cited in provenance. Catch-up contribution ceilings for ages 50+ and 60-63 (SECURE 2.0) are computed when participant_age is provided; when a catch-up allowance applies, applied_caps[].cap_name reads '402(g) elective deferral plus 414(v) catch-up' and limit_value carries the combined ceiling, not the bare 402(g) amount.

HEAVY tool: use output='summary' (default) for the headline scalars or output='inline' for the full per-period schedule.

ParametersJSON Schema
NameRequiredDescriptionDefault
outputNoValid values: 'summary' (default), 'inline'. Response envelope. 'summary': headline scalars, period_schedule stripped. 'inline': full payload including period_schedule[].
chart_titleNoOverride for the chart title. Reserved for the chart pipeline. Must not contain em-dashes or en-dashes. Max 120 characters. Optional.
has_true_upNoWhether the plan provides an annual true-up. false is the conservative assumption: surfaces front-loading forfeiture risk. Optional; defaults to false when omitted.
match_tiersNoCustom match-formula tier list. Example: [{"match_frac": 1.0, "up_to_deferral_pct": 3}, {"match_frac": 0.5, "up_to_deferral_pct": 2}] = safe_harbor_basic. Exactly one of match_preset or match_tiers is required. Optional; omit when supplying match_preset.
match_presetNoNamed match-formula preset: 'safe_harbor_basic' (100% of first 3% + 50% of next 2%), 'safe_harbor_enhanced_simple' (100% of first 4%), 'qaca' (100% of first 1% + 50% of next 5%), 'fifty_pct_of_first_six_pct' (50% of first 6%). Exactly one of match_preset or match_tiers is required. Optional; omit when supplying match_tiers.
annual_salaryYesAnnual gross salary. Decimal > 0. REQUIRED, no default.
participant_ageNoDetermines which catch-up limit applies (age 50+, or age 60-63 SECURE 2.0 super catch-up). Integer >= 0. Optional; omit when age is unknown or participant is under 50.
contribution_pctYesCurrent employee contribution as a percent of gross pay. Decimal in [0, 100]. REQUIRED, no default.
pay_periods_per_yearYesPay periods per year (12=monthly, 24=semi-monthly, 26=biweekly, 52=weekly). Integer in [1, 365]. REQUIRED, no default.

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already supply readOnlyHint=true, idempotentHint=true, and destructiveHint=false; the description then adds substantial non-redundant behavior: determinism, the 'Calculation, not advice' disclaimer, IRS 402(g)/401(a)(17)/415(c) limits applied and cited in provenance, and precise catch-up semantics ('applied_caps[].cap_name reads \'402(g) elective deferral plus 414(v) catch-up\' and limit_value carries the combined ceiling'). It also flags output weight ('HEAVY tool') so agents can choose summary vs. inline. Nothing contradicts the annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Purpose is front-loaded (brief disclaimer, then the 'Deterministic 401(k) employer-match optimizer' statement), and each subsequent block - computed outputs, sibling routing, IRS/catch-up behavior, output-mode guidance - earns its place. It is long, but the density is justified for a 9-parameter financial tool with complicated limit behavior; the catch-up paragraph is the densest and could be tightened slightly.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool this complex with no output schema, the description is remarkably complete: it enumerates the computed results in prose, names the output envelope modes (summary/inline), specifies period_schedule and applied_caps[] behavior, and covers limits, conservative defaults, and when-not-to-use. Combined with 100% schema coverage and safety annotations, an agent has everything needed to select and invoke this tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the schema already documents all nine parameters thoroughly, putting the baseline at 3. The description adds genuine value by connecting participant_age to concrete output behavior (catch-up ceilings for ages 50+ and 60-63, plus the applied_caps cap_name/limit_value semantics) and by framing the preset-vs-custom-tier choice in prose. It does not restate per-parameter schema details, so 4 rather than 5.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description leads with a specific, differentiated purpose: a 'Deterministic 401(k) employer-match optimizer' that computes match entitlement from salary, pay frequency, contribution percent, and match formula. It lists six concrete computed outputs (current match, max match, forfeited match, minimum percent to full match, front-loading risk, per-period schedule) and explicitly distinguishes itself from sibling compare_payoff_vs_invest. An agent can tell exactly what this tool does and does not do.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit guidance is embedded: 'Pick this to size 401(k) contributions against the employer match formula; pick `compare_payoff_vs_invest` when the question is putting an extra monthly amount toward a debt versus investing it.' It even notes the sibling takes no employer-match input, removing ambiguity. The 'HEAVY tool: use output=\'summary\'...' sentence also tells the agent when to invoke cheaply vs. request the full schedule.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

server_infoSenaro Server InfoA
Read-onlyIdempotent
Inspect

Calculation, not advice. Verify with a professional before acting.

Returns metadata about the running Senaro MCP server: version, contract version, build commit, build timestamp, and the full list of supported tools. Call this once per session before drafting any content to verify the running process is not stale after a deploy. Idempotent, no side effects, < 50 ms. If server_version is below the minimum required by your skill, STOP and ask the user to restart Claude Desktop.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint, destructiveHint=false, and openWorldHint=false. The description reinforces idempotency and adds value beyond the annotations with 'no side effects' and a latency bound ('< 50 ms'), plus the staleness-check workflow. It does not add auth or rate-limit context, but the annotation coverage is strong, so a 4 is warranted.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The return fields and the once-per-session guidance are front-loaded and efficient. The leading 'Calculation, not advice. Verify with a professional before acting.' is boilerplate disclaimer that is relevant but not strictly about this tool, keeping it short of a perfect 5.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, so the description carries the return-value burden, and it lists all returned fields explicitly. For a zero-parameter, read-only diagnostic tool, this is fully complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so per the rubric the baseline is 4. The description correctly adds no parameter guidance because none is needed, and schema coverage is 100%.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Returns metadata about the running Senaro MCP server') and enumerates exactly what is returned: version, contract version, build commit, build timestamp, and supported tools. This clearly distinguishes it from the financial-calculation siblings, none of which are server/diagnostic tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit timing ('Call this once per session before drafting any content') and the reason ('to verify the running process is not stale after a deploy'). It even prescribes a conditional action on the result: if server_version is below the required minimum, STOP and ask the user to restart. Nothing is left to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 3 tool updates
    • Changedcalculate_debt_to_income2 fields changed
      • changedInput schema / properties / monthly_maintenance_and_utilities / description
        Previous value: -"Estimated monthly maintenance and utilities for the proposed property. 38 CFR 36.4340 calls for a realistic estimate of this figure for the property and local utility rates and sets no numeric multiplier itself, but VA underwriting guidance (the Lender's Handbook, Pamphlet 26-7) publishes a per-square-foot multiplier for this same estimate. Applying it needs the property's square footage, which this tool does not currently collect, so Senaro has no default to offer here and you supply the aggregate monthly amount instead. Supplying BOTH this field and monthly_taxes_and_retirement_withholding, together with proposed_home_price and a computable family_size/property_state, computes qualification.va.residual_income_comparison: your ACTUAL monthly residual income, its ratio to the va_residual_income_guideline figure, and whether residual_income_meets_review_waiver_margin (residual income at or above 120% of the guideline) is met. The shelter expense used here excludes any PMI (VA loans carry no monthly PMI; 38 CFR 36.4313(e) sets a funding fee instead, commonly financed into the loan; see va_funding_fee_financed_monthly for the financed-fee field). 38 CFR 36.4340(c)(3)'s review-waiver condition is CONJUNCTIVE: it also requires the back-end debt-to-income ratio (qualification.va.your_back_end) to exceed 41%, which this field does not by itself confirm. Check both fields together. Even when both hold, (c)(3) only WAIVES a second-level review requirement; it is not itself an approval. Whether this file is actually approved is an underwriting determination Senaro does not make and no input combination here determines. Missing any one of the needed inputs reads qualification.va.residual_income_comparison.status 'not_computable' with every reason named. Optional; must be zero or more."New value: +"Estimated monthly maintenance and utilities for the proposed property. 38 CFR 36.4340 calls for a realistic estimate of this figure for the property and local utility rates and sets no numeric multiplier itself, but VA underwriting guidance (the Lender's Handbook, Pamphlet 26-7) publishes a per-square-foot multiplier for this same estimate. Applying it needs the property's square footage, which this tool does not currently collect, so Senaro has no default to offer here and you supply the aggregate monthly amount instead. Supplying BOTH this field and monthly_taxes_and_retirement_withholding, together with proposed_home_price and a computable family_size/property_state, computes qualification.va.residual_income_comparison: your ACTUAL monthly residual income, its ratio to the va_residual_income_guideline figure, and whether residual_income_meets_review_waiver_margin (residual income at or above 120% of the guideline) is met. The shelter expense used here excludes any PMI (VA loans carry no monthly PMI; 38 CFR 36.4313(e) sets a funding fee instead, commonly financed into the loan; see va_funding_fee_financed_monthly for the financed-fee field). 38 CFR 36.4340(c)(3)'s review-waiver condition is CONJUNCTIVE: it also requires the back-end debt-to-income ratio, rounded to a whole percent under 38 CFR 36.4340(d) (qualification.va.your_back_end_compared), to exceed 41%, which this field does not by itself confirm. Check both fields together. Even when both hold, (c)(3) only WAIVES a second-level review requirement; it is not itself an approval. Whether this file is actually approved is an underwriting determination Senaro does not make and no input combination here determines. Missing any one of the needed inputs reads qualification.va.residual_income_comparison.status 'not_computable' with every reason named. Optional; must be zero or more."
      • changedInput schema / properties / va_funding_fee_financed_monthly / description
        Previous value: -"The additional monthly payment from financing a VA funding fee into the loan balance, if any. 38 CFR 36.4313(e)'s applicable percentage depends on down payment, prior VA-loan use, and service category, none of which Senaro collects, so there is no default. If omitted while any VA figure that depends on it is produced, meaning qualification.va.your_back_end and its verdict, any what_if VA ratio, what_if.max_affordable_home.va, or qualification.va.residual_income_comparison, a VA_FUNDING_FEE_NOT_MODELED warning discloses that no fee is assumed. The home-price-reduction what_if scenario's hypothetical price is fixed before the fee is considered, so a supplied fee is scaled to that EXACT hypothetical loan size, since 38 CFR 36.4313(e)'s fee is a percentage of loan principal. what_if.max_affordable_home.va is a two-pass approximation instead, so its supplied fee is scaled to an ESTIMATE of the hypothetical loan size, not the exact figure reported; the PMI, tax, and insurance reservation itself is an EXACT closed-form solve, so only this fee-scaling step is approximate. Without proposed_home_price there is no reference loan size to scale from either way, so the raw fee is reserved unscaled instead, and a VA_FUNDING_FEE_NOT_SCALED warning discloses it. This is mutually exclusive with VA_FUNDING_FEE_NOT_MODELED by construction, since one requires the fee omitted and the other requires it supplied. Optional; must be zero or more."New value: +"The additional monthly payment from financing a VA funding fee into the loan balance, if any. 38 CFR 36.4313(e)'s applicable percentage depends on down payment, prior VA-loan use, and service category, none of which Senaro collects, so there is no default. If omitted while any VA figure that depends on it is produced, meaning qualification.va.your_back_end and its verdict, any what_if VA ratio, what_if.max_affordable_home.va, or qualification.va.residual_income_comparison, a VA_FUNDING_FEE_NOT_MODELED warning discloses that no fee is assumed. The home-price-reduction what_if scenario's hypothetical price is fixed before the fee is considered, so a supplied fee is scaled to that EXACT hypothetical loan size, since 38 CFR 36.4313(e)'s fee is a percentage of loan principal. what_if.max_affordable_home.va is a two-pass approximation instead, so its supplied fee is scaled to an ESTIMATE of the hypothetical loan size, not the exact figure reported; the published price itself passes an exact forward VA check under 38 CFR 36.4340(d) with that reserved fee, and one dollar more fails it, so only this fee-scaling step is approximate. Without proposed_home_price there is no reference loan size to scale from either way, so the raw fee is reserved unscaled instead, and a VA_FUNDING_FEE_NOT_SCALED warning discloses it. This is mutually exclusive with VA_FUNDING_FEE_NOT_MODELED by construction, since one requires the fee omitted and the other requires it supplied. Optional; must be zero or more."
    • Changedcompare_debt_consolidation1 field changed
      • changedInput schema / properties / consolidation_loan / properties / annual_rate_pct / description
        Previous value: -"Annual interest rate for the consolidation loan, as a percentage, 0-36, e.g. 10.99. Required."New value: +"Annual interest rate for the consolidation loan, as a percentage, 0-36, e.g. 10.99. This is the loan's note interest rate, not the disclosed APR. The APR already reflects the origination fee, so entering it here counts the fee twice. Required."
    • Changedcompare_rent_vs_buy2 fields changed
      • changedInput schema / properties / apply_tax_benefit / description
        Previous value: -"Whether to apply the federal tax benefit model (TY2025/TY2026 SALT caps, mortgage interest deduction, PMI deductibility). When true, filing_status, tax_year and annual_gross_income are required. Optional; defaults to false when omitted. The §121 home-sale gain exclusion applies only at sale points 24 months or more after purchase (IRC §121(a) 2-of-5-year test); year 1 of yearly_series, months 1-23 in monthly_series, and any horizon under 24 months use no exclusion. Separately, IRC §1222(3) makes a holding period of 12 months or less short-term, taxed at ordinary rates instead of the long-term rates otherwise applied, on the home-sale gain and the renter's portfolio gain alike (24 months vs. 12 months are two different thresholds); this tool applies that split exactly to the home-sale gain, and to the renter's portfolio at annual grain: prior years' contributions are compounded together as one long-term lot rather than aged individually, so months inside a year understate short-term gain."New value: +"Whether to apply the federal tax benefit model (SALT cap following the statute's year-by-year schedule, mortgage interest deduction, PMI deductibility). When true, filing_status, tax_year and annual_gross_income are required. Optional; defaults to false when omitted. The §121 home-sale gain exclusion applies only at sale points 24 months or more after purchase (IRC §121(a) 2-of-5-year test); year 1 of yearly_series, months 1-23 in monthly_series, and any horizon under 24 months use no exclusion. Separately, IRC §1222(3) makes a holding period of 12 months or less short-term, taxed at ordinary rates instead of the long-term rates otherwise applied, on the home-sale gain and the renter's portfolio gain alike (24 months vs. 12 months are two different thresholds); this tool applies that split exactly to the home-sale gain, and to the renter's portfolio at annual grain: prior years' contributions are compounded together as one long-term lot rather than aged individually, so months inside a year understate short-term gain."
      • changedInput schema / properties / tax_year / description
        Previous value: -"Tax year: 2025 or 2026. Integer. Required when apply_tax_benefit is true; otherwise not used."New value: +"Tax year for year 1 of the horizon: 2025 or 2026. Integer. Required when apply_tax_benefit is true; otherwise not used. Horizon year y uses tax year (tax_year + y - 1) on both the tax benefit and the sale-side capital-gains tax; the SALT cap follows the statute's own schedule for that year, and every other indexed amount holds at its last-loaded table value beyond it."
  2. 22 tool updates
    • Changedanalyze_cash_advance5 fields changed
      • addedInput schema / properties / cash_advance_apr_pct / exclusiveMinimum
        Added value: +0
      • addedInput schema / properties / cash_advance_apr_pct / maximum
        Added value: +100
      • addedInput schema / properties / cash_advance_fee_pct / maximum
        Added value: +100
      • addedInput schema / properties / cash_advance_fee_pct / minimum
        Added value: +0
      • addedInput schema / properties / chart_title / maxLength
        Added value: +120
    • Changedanalyze_pmi_removal6 fields changed
      • addedInput schema / properties / chart_title / maxLength
        Added value: +120
      • addedInput schema / properties / current_balance / minimum
        Added value: +0.01
      • addedInput schema / properties / current_home_value / minimum
        Added value: +0.01
      • addedInput schema / properties / monthly_payment / minimum
        Added value: +0.01
      • addedInput schema / properties / original_appraised_value / minimum
        Added value: +0.01
      • addedInput schema / properties / original_purchase_price / minimum
        Added value: +0.01
    • Changedcalculate_cc_payoff12 fields changed
      • changedInput schema / properties / cards / items / properties / minimum_payment / description
        Previous value: -"Minimum payment in dollars, $0 to $1,000,000,000 (optional, omit or set 0 to auto-calculate). Where it binds, the card is locked at max(minimum_payment, that card's issuer minimum computed at month 1), and that locked amount is paid every month. On calculate_cc_payoff it binds ONLY when fixed_payments = true; on the DEFAULT path (fixed_payments = false) it is NOT used, because the plan recomputes each card's issuer minimum from its current balance every month, and the response discloses that as MINIMUM_PAYMENT_NOT_BINDING. On compare_strategies there is no fixed_payments parameter and supplying one is rejected: the avalanche and snowball arms ALWAYS run locked, so the value DOES bind and moves both arms, while the minimum-payments-only baseline always recomputes and ignores it, disclosed as MINIMUM_PAYMENT_NOT_BINDING_IN_BASELINE."New value: +"Minimum payment in dollars, $0 to $1,000,000,000 (optional, omit or set 0 to auto-calculate). Where it binds, the card is locked at max(minimum_payment, that card's issuer minimum computed at month 1), and that locked amount is paid every month. On calculate_cc_payoff it binds ONLY when fixed_payments = true; on the DEFAULT path (fixed_payments = false) it is NOT used, because the plan recomputes each card's issuer minimum from its current balance every month, and the response discloses that as MINIMUM_PAYMENT_NOT_BINDING. On compare_payoff_strategies there is no fixed_payments parameter and supplying one is rejected: the avalanche and snowball arms ALWAYS run locked, so the value DOES bind and moves both arms, while the minimum-payments-only baseline always recomputes and ignores it, disclosed as MINIMUM_PAYMENT_NOT_BINDING_IN_BASELINE."
      • addedInput schema / properties / cards / items / properties / name / maxLength
        Added value: +120
      • addedInput schema / properties / cards / items / properties / purchase_apr_pct / maximum
        Added value: +100
      • addedInput schema / properties / cards / items / properties / purchase_apr_pct / minimum
        Added value: +0
      • addedInput schema / properties / cards / maxItems
        Added value: +20
      • addedInput schema / properties / chart_title / maxLength
        Added value: +120
      • changedInput schema / properties / fixed_payments / description
        Previous value: -"Whether to hold each card's payment constant for the whole payoff. Optional; defaults to false when omitted. When true, each card's payment is locked at max(cards[].minimum_payment, that card's issuer minimum computed at month 1), and the total monthly budget (those locked amounts plus extra_monthly_payment) is held constant for the whole payoff, so once a card is paid off its freed-up payment rolls forward onto the remaining cards. This is the ONLY mode in which cards[].minimum_payment affects calculate_cc_payoff's result; compare_strategies has no fixed_payments parameter and binds minimum_payment on both of its strategy arms regardless."New value: +"Whether to hold each card's payment constant for the whole payoff. Optional; defaults to false when omitted. When true, each card's payment is locked at max(cards[].minimum_payment, that card's issuer minimum computed at month 1), and the total monthly budget (those locked amounts plus extra_monthly_payment) is held constant for the whole payoff, so once a card is paid off its freed-up payment rolls forward onto the remaining cards. This is the ONLY mode in which cards[].minimum_payment affects calculate_cc_payoff's result; compare_payoff_strategies has no fixed_payments parameter and binds minimum_payment on both of its strategy arms regardless."
      • changedInput schema / properties / output / enum
        Previous value: -[
        -  "summary",
        -  "inline"
        -]New value: +[
        +  "summary",
        +  "inline",
        +  null
        +]
      • changedInput schema / properties / output / type
        Previous value: -"string"New value: +[
        +  "string",
        +  "null"
        +]
      • addedInput schema / properties / strategy / enum
        Added value: +[
        +  "avalanche",
        +  "snowball",
        +  null
        +]
      • addedInput schema / properties / windfalls / items / properties / label / maxLength
        Added value: +120
      • addedInput schema / properties / windfalls / maxItems
        Added value: +12
    • Addedcalculate_compound_interest
    • Changedcalculate_debt_to_income13 fields changed
      • addedInput schema / properties / annual_income / minimum
        Added value: +0.01
      • changedInput schema / properties / existing_debts / items / properties / name / description
        Previous value: -"Optional label for this debt, e.g. 'Car Loan'."New value: +"Optional label for this debt, e.g. 'Car Loan'. At most 120 characters."
      • addedInput schema / properties / existing_debts / items / properties / name / maxLength
        Added value: +120
      • changedInput schema / properties / existing_debts / items / properties / type / description
        Previous value: -"Debt type: 'auto', 'student', 'credit_card', 'personal', 'mortgage', 'heloc', 'child_support', or 'other'. Optional, defaults to 'other' when omitted; an unrecognized value warns rather than rejects."New value: +"Debt type: 'auto', 'student', 'credit_card', 'personal', 'mortgage', 'heloc', 'child_support', or 'other'. Optional, defaults to 'other' when omitted. An unrecognized value of at most 120 characters warns rather than rejects; a longer one is rejected."
      • addedInput schema / properties / existing_debts / items / properties / type / maxLength
        Added value: +120
      • addedInput schema / properties / existing_debts / maxItems
        Added value: +50
      • addedInput schema / properties / family_size / maximum
        Added value: +20
      • addedInput schema / properties / gross_monthly_income / minimum
        Added value: +0.01
      • changedInput schema / properties / proposed_debt / properties / name / description
        Previous value: -"Optional label for this proposed debt."New value: +"Optional label for this proposed debt. At most 120 characters."
      • addedInput schema / properties / proposed_debt / properties / name / maxLength
        Added value: +120
      • addedInput schema / properties / proposed_debt / properties / type / enum
        Added value: +[
        +  "credit_card",
        +  "auto",
        +  "student",
        +  "personal",
        +  "mortgage",
        +  "heloc",
        +  "child_support",
        +  "other",
        +  null
        +]
      • addedInput schema / properties / proposed_home_price / minimum
        Added value: +0.01
      • addedInput schema / properties / transaction_purpose / enum
        Added value: +[
        +  "purchase",
        +  "refinance",
        +  "streamlined_assist",
        +  null
        +]
    • Changedcalculate_emergency_fund3 fields changed
      • addedInput schema / properties / chart_title / maxLength
        Added value: +120
      • addedInput schema / properties / job_stability / enum
        Added value: +[
        +  "stable_w2",
        +  "variable_income",
        +  "self_employed",
        +  "between_jobs",
        +  null
        +]
      • addedInput schema / properties / monthly_essential_expenses / minimum
        Added value: +0.01
    • Changedcalculate_loan_payoff6 fields changed
      • addedInput schema / properties / chart_bucket / enum
        Added value: +[
        +  "auto",
        +  "monthly",
        +  "quarterly",
        +  "yearly",
        +  "biennial",
        +  null
        +]
      • addedInput schema / properties / chart_title / maxLength
        Added value: +120
      • addedInput schema / properties / loan_type / enum
        Added value: +[
        +  "personal",
        +  "auto",
        +  "student",
        +  "mortgage",
        +  null
        +]
      • changedInput schema / properties / output / enum
        Previous value: -[
        -  "summary"
        -]New value: +[
        +  "summary",
        +  null
        +]
      • changedInput schema / properties / output / type
        Previous value: -"string"New value: +[
        +  "string",
        +  "null"
        +]
      • addedInput schema / properties / term_months / maximum
        Added value: +480
    • Changedcalculate_opportunity_cost4 fields changed
      • addedInput schema / properties / chart_title / maxLength
        Added value: +120
      • changedInput schema / properties / label / description
        Previous value: -"What the spending is, e.g. 'coffee' or 'streaming subscriptions'. Optional."New value: +"What the spending is, e.g. 'coffee' or 'streaming subscriptions'. Optional. Max 120 characters."
      • addedInput schema / properties / label / maxLength
        Added value: +120
      • addedInput schema / properties / monthly_spending / minimum
        Added value: +0.01
    • Addedcalculate_refi_breakeven
    • Changedcalculate_runway3 fields changed
      • addedInput schema / properties / chart_title / maxLength
        Added value: +120
      • changedInput schema / properties / output / enum
        Previous value: -[
        -  "summary",
        -  "inline"
        -]New value: +[
        +  "summary",
        +  "inline",
        +  null
        +]
      • changedInput schema / properties / output / type
        Previous value: -"string"New value: +[
        +  "string",
        +  "null"
        +]
    • Changedcompare_debt_consolidation13 fields changed
      • addedInput schema / properties / bt_manual_transfers / items / properties / card_name / maxLength
        Added value: +120
      • addedInput schema / properties / bt_offers / items / properties / apr_pct / maximum
        Added value: +100
      • addedInput schema / properties / bt_offers / items / properties / apr_pct / minimum
        Added value: +0
      • addedInput schema / properties / bt_offers / items / properties / label / maxLength
        Added value: +120
      • addedInput schema / properties / bt_offers / maxItems
        Added value: +10
      • addedInput schema / properties / bt_transfer_limit / minimum
        Added value: +0.01
      • changedInput schema / properties / cards / description
        Previous value: -"Credit cards to include, 1 to 20. Required: omitting the field answers contract_error MISSING_REQUIRED_FIELD, while an explicit JSON null answers parse_error (\"cards must be a JSON array.\"); the two are not the same rejection. segments and stop_spend_month are not supported by this tool and are rejected; a non-zero monthly_spend, annual_fee, or plan_fees_monthly is rejected too, but zero (including an explicit 0) is accepted and dropped for all three: this tool always reads the flat purchase_balance / cash_advance_balance fields on each card below, never segments[] balances, and card-level fees and ongoing spend are not modeled here. Those five fields are supported by calculate_cc_payoff and compare_strategies instead."New value: +"Credit cards to include, 1 to 20. Required: omitting the field answers contract_error MISSING_REQUIRED_FIELD, while an explicit JSON null answers parse_error (\"cards must be a JSON array.\"); the two are not the same rejection. segments and stop_spend_month are not supported by this tool and are rejected; a non-zero monthly_spend, annual_fee, or plan_fees_monthly is rejected too, but zero (including an explicit 0) is accepted and dropped for all three: this tool always reads the flat purchase_balance / cash_advance_balance fields on each card below, never segments[] balances, and card-level fees and ongoing spend are not modeled here. Those five fields are supported by calculate_cc_payoff and compare_payoff_strategies instead."
      • addedInput schema / properties / cards / items / properties / name / maxLength
        Added value: +120
      • addedInput schema / properties / chart_title / maxLength
        Added value: +120
      • addedInput schema / properties / current_strategy / enum
        Added value: +[
        +  "avalanche",
        +  "snowball",
        +  null
        +]
      • changedInput schema / properties / output / enum
        Previous value: -[
        -  "summary",
        -  "inline"
        -]New value: +[
        +  "summary",
        +  "inline",
        +  null
        +]
      • changedInput schema / properties / output / type
        Previous value: -"string"New value: +[
        +  "string",
        +  "null"
        +]
      • addedInput schema / properties / windfalls / items / properties / label / maxLength
        Added value: +120
    • Changedcompare_mortgage_terms7 fields changed
      • addedInput schema / properties / filing_status / enum
        Added value: +[
        +  "single",
        +  "married",
        +  "head_of_household",
        +  null
        +]
      • addedInput schema / properties / option_a / properties / label / maxLength
        Added value: +120
      • addedInput schema / properties / option_b / properties / label / maxLength
        Added value: +120
      • addedInput schema / properties / tax_bracket_pct / maximum
        Added value: +50
      • addedInput schema / properties / tax_bracket_pct / minimum
        Added value: +0
      • addedInput schema / properties / tax_year / maximum
        Added value: +2026
      • addedInput schema / properties / tax_year / minimum
        Added value: +2025
    • Addedcompare_payoff_strategies
    • Addedcompare_payoff_vs_invest
    • Addedcompare_rent_vs_buy
    • Removedcompare_strategies
    • Removedcompound_interest
    • Changedlist_defaults1 field changed
      • changedInput schema / properties / category / description
        Previous value: -"Optional: filter entries by category, the canonical MCP tool name (e.g. \"rent_vs_buy\") or \"shared\". Case-insensitive."New value: +"Optional: filter entries by category, the canonical MCP tool name (e.g. \"compare_rent_vs_buy\") or \"shared\". Case-insensitive."
    • Changedoptimize_401k_match9 fields changed
      • addedInput schema / properties / chart_title / maxLength
        Added value: +120
      • addedInput schema / properties / contribution_pct / maximum
        Added value: +100
      • addedInput schema / properties / contribution_pct / minimum
        Added value: +0
      • addedInput schema / properties / match_preset / enum
        Added value: +[
        +  "safe_harbor_basic",
        +  "safe_harbor_enhanced_simple",
        +  "qaca",
        +  "fifty_pct_of_first_six_pct",
        +  null
        +]
      • addedInput schema / properties / match_tiers / maxItems
        Added value: +10
      • changedInput schema / properties / output / enum
        Previous value: -[
        -  "summary",
        -  "inline"
        -]New value: +[
        +  "summary",
        +  "inline",
        +  null
        +]
      • changedInput schema / properties / output / type
        Previous value: -"string"New value: +[
        +  "string",
        +  "null"
        +]
      • addedInput schema / properties / pay_periods_per_year / maximum
        Added value: +365
      • addedInput schema / properties / pay_periods_per_year / minimum
        Added value: +1
    • Removedpayoff_vs_invest
    • Removedrefi_breakeven
    • Removedrent_vs_buy
  3. 9 tool updates
    • Changedcalculate_cc_payoff4 fields changed
      • removedInput schema / properties / output / default
        Removed value: -null
      • changedInput schema / properties / output / description
        Previous value: -"Response verbosity: 'summary' (default) | 'inline' | 'capture'. summary returns a compact response with a data_preview block; inline returns the full payload; capture writes the full payload to this server's local disk for the chart-render pipeline and returns a capture_ref URI. capture is available on the local stdio transport only, and the hosted HTTP transport rejects it with a structured error naming 'summary' and 'inline' as the valid alternatives, since a capture_ref would be a dead link for a remote caller. Optional; defaults to 'summary' when omitted."New value: +"Valid values: 'summary' (default), 'inline'. summary returns a compact response with a data_preview block; inline returns the full payload."
      • addedInput schema / properties / output / enum
        Added value: +[
        +  "summary",
        +  "inline"
        +]
      • changedInput schema / properties / output / type
        Previous value: -[
        -  "string",
        -  "null"
        -]New value: +"string"
    • Changedcalculate_loan_payoff4 fields changed
      • removedInput schema / properties / output / default
        Removed value: -null
      • changedInput schema / properties / output / description
        Previous value: -"'summary' (default) or 'capture'. 'inline' is not a valid value for this tool on any transport. 'summary' returns the full payload including analysis.buckets[] inline. 'capture' writes the full payload to ~/.senaro/captures/ and returns capture_ref, stripping analysis from the wire response; available on the local stdio transport only, since the hosted HTTP transport rejects 'capture' and names 'summary' as the only alternative."New value: +"Valid values: 'summary' (default). 'summary' returns the full payload including analysis.buckets[] inline."
      • addedInput schema / properties / output / enum
        Added value: +[
        +  "summary"
        +]
      • changedInput schema / properties / output / type
        Previous value: -[
        -  "string",
        -  "null"
        -]New value: +"string"
    • Changedcalculate_runway4 fields changed
      • removedInput schema / properties / output / default
        Removed value: -null
      • changedInput schema / properties / output / description
        Previous value: -"'summary' (default), 'inline', or 'capture'. 'summary' returns headline scalars (depletion month or does-not-deplete) and citations with the schedule stripped. 'inline' returns the full payload including the month-by-month schedule[] for chart rendering. 'capture' writes the full payload to ~/.senaro/captures/ and returns capture_ref; available on the local stdio transport only, since the hosted HTTP transport rejects 'capture' and names 'summary' and 'inline' as the valid alternatives."New value: +"Valid values: 'summary' (default), 'inline'. 'summary' returns headline scalars (depletion month or does-not-deplete) and citations with the schedule stripped. 'inline' returns the full payload including the month-by-month schedule[] for chart rendering."
      • addedInput schema / properties / output / enum
        Added value: +[
        +  "summary",
        +  "inline"
        +]
      • changedInput schema / properties / output / type
        Previous value: -[
        -  "string",
        -  "null"
        -]New value: +"string"
    • Changedcompare_debt_consolidation6 fields changed
      • changedInput schema / properties / extra_monthly_payment / description
        Previous value: -"Extra monthly payment in dollars, $0 to $1,000,000,000, applied on top of the required minimums. Optional; defaults to 0 when omitted. fixed_payments is not a parameter of this tool, unlike calculate_cc_payoff: the keep-cards baseline is always simulated under the canonical constant rolled-forward payment (see comparison_basis in the response); supplying fixed_payments returns an unknown_parameter error."New value: +"Extra monthly payment in dollars, $0 to $1,000,000,000, applied on top of the required minimums for keep-cards and any balance transfer, and on top of the loan's own required payment for consolidation. Optional; defaults to 0 when omitted. fixed_payments is not a parameter of this tool, unlike calculate_cc_payoff: the keep-cards baseline is always simulated under the canonical constant rolled-forward payment (see comparison_basis in the response); supplying fixed_payments returns an unknown_parameter error."
      • removedInput schema / properties / output / default
        Removed value: -null
      • changedInput schema / properties / output / description
        Previous value: -"Response verbosity: 'summary' (default) | 'inline' | 'capture'. summary: compact response with a data_preview block; no heavy array exists on this response today, so summary and inline are currently identical in content. inline: full payload (currently identical to summary; will diverge once a heavy array is added for chart rendering). capture: full payload written to this server's local disk for the chart-render pipeline; capture_ref URI returned. Available on the local stdio transport only; the hosted HTTP transport rejects 'capture' with a structured error naming 'summary' and 'inline' as the valid alternatives. Optional; defaults to 'summary' when omitted."New value: +"Valid values: 'summary' (default), 'inline'. summary: compact response with a data_preview block; no heavy array exists on this response today, so summary and inline are currently identical in content. inline: full payload (currently identical to summary; will diverge once a heavy array is added for chart rendering)."
      • addedInput schema / properties / output / enum
        Added value: +[
        +  "summary",
        +  "inline"
        +]
      • changedInput schema / properties / output / type
        Previous value: -[
        -  "string",
        -  "null"
        -]New value: +"string"
      • changedInput schema / properties / windfalls / description
        Previous value: -"One-time principal payments, at most 12, same shape as calculate_cc_payoff. Optional; a JSON null is treated as omitted, the same as leaving the field out. When non-empty, the response adds windfalls_applied[] and windfalls_unused[] on keep_cards, on each balance_transfer scenario (and on each offer in balance_transfer_offers.offers[]), on consolidation_loan (single-row per windfall since the loan is a single-balance instrument), and on optimized_consolidation.keep_subset. All scenarios use with-windfalls totals so cross-option ranking stays apples-to-apples."New value: +"One-time principal payments, at most 12, same shape as calculate_cc_payoff. Optional; a JSON null is treated as omitted, the same as leaving the field out. When non-empty, the response adds windfalls_applied[] and windfalls_unused[] on keep_cards, on each balance_transfer scenario (and on each offer in balance_transfer_offers.offers[]), on consolidation_loan (single-row per windfall since the loan is a single-balance instrument), and on optimized_consolidation.keep_subset. All scenarios use with-windfalls totals so cross-option ranking stays apples-to-apples, except what_if.same_budget_accelerated, a windfall-free hypothetical about extra-payment behavior, not your actual lump-sum schedule."
    • Changedcompare_mortgage_terms1 field changed
      • changedInput schema / properties / pmi_removal_ltv_pct / description
        Previous value: -"Loan-to-value percentage at which to model borrower-requested PMI removal, 50-100. Optional; when omitted, PMI is modeled as removed at the 78% HPA automatic-termination threshold instead. When the loan's own initial loan-to-value is above 80 percent, the range where PMI applies, a value at or above that initial LTV is rejected, because the requested removal point would already be met at the first payment, so no PMI would be modeled for any month of the loan. At or below 80 percent, no such check runs. Either way PMI also stops at the statutory amortization midpoint of each option's own term (12 U.S.C. §4901(7), §4902(c)), which this value cannot postpone."New value: +"Loan-to-value percentage at which to model borrower-requested PMI removal, 50-100. Optional; when omitted, PMI is modeled as removed at the 78% HPA automatic-termination threshold instead. The rejection guard applies only when pmi_monthly is above 0 and the loan's own initial LTV is above 80 percent, the range where PMI applies: there, a value at or above that initial LTV is rejected, because the requested removal point would already be met at the first payment, so no PMI would be modeled for any month of the loan. When pmi_monthly is omitted or 0, no such check runs, whatever the LTV, and the supplied value is accepted but changes nothing in the response, because no PMI is modeled in that case. Either way PMI also stops at the statutory amortization midpoint of each option's own term (12 U.S.C. §4901(7), §4902(c)), which this value cannot postpone."
    • Changedcompare_strategies4 fields changed
      • removedInput schema / properties / output / default
        Removed value: -null
      • changedInput schema / properties / output / description
        Previous value: -"Response verbosity: 'summary' (default) | 'inline' | 'capture'. summary returns the headline comparison with a data_preview block; inline returns the full payload; capture writes the full payload to this server's local disk for the chart-render pipeline and returns a capture_ref URI. capture is available on the local stdio transport only, and the hosted HTTP transport rejects it with a structured error naming 'summary' and 'inline' as the valid alternatives, since a capture_ref would be a dead link for a remote caller. Optional; defaults to 'summary' when omitted."New value: +"Valid values: 'summary' (default), 'inline'. summary returns the headline comparison with a data_preview block; inline returns the full payload."
      • addedInput schema / properties / output / enum
        Added value: +[
        +  "summary",
        +  "inline"
        +]
      • changedInput schema / properties / output / type
        Previous value: -[
        -  "string",
        -  "null"
        -]New value: +"string"
    • Changedoptimize_401k_match4 fields changed
      • removedInput schema / properties / output / default
        Removed value: -null
      • changedInput schema / properties / output / description
        Previous value: -"Response envelope. 'summary' (default): headline scalars, period_schedule stripped. 'inline': full payload including period_schedule[]. 'capture': full payload written to ~/.senaro/captures/, capture_ref URI returned; local stdio transport only, rejected with a structured error on the hosted HTTP transport naming 'summary' and 'inline' as the valid alternatives."New value: +"Valid values: 'summary' (default), 'inline'. Response envelope. 'summary': headline scalars, period_schedule stripped. 'inline': full payload including period_schedule[]."
      • addedInput schema / properties / output / enum
        Added value: +[
        +  "summary",
        +  "inline"
        +]
      • changedInput schema / properties / output / type
        Previous value: -[
        -  "string",
        -  "null"
        -]New value: +"string"
    • Changedpayoff_vs_invest4 fields changed
      • removedInput schema / properties / output / default
        Removed value: -null
      • changedInput schema / properties / output / description
        Previous value: -"'summary' (default), 'inline', or 'capture'. 'summary' returns headline comparison scalars, milestones, and citations with monthly_schedule stripped. 'inline' returns the full payload including the month-by-month monthly_schedule[]. 'capture' writes the full payload to ~/.senaro/captures/ and returns capture_ref; available on the local stdio transport only, since the hosted HTTP transport rejects 'capture' and names 'summary' and 'inline' as the valid alternatives."New value: +"Valid values: 'summary' (default), 'inline'. 'summary' returns headline comparison scalars, milestones, and citations with monthly_schedule stripped. 'inline' returns the full payload including the month-by-month monthly_schedule[]."
      • addedInput schema / properties / output / enum
        Added value: +[
        +  "summary",
        +  "inline"
        +]
      • changedInput schema / properties / output / type
        Previous value: -[
        -  "string",
        -  "null"
        -]New value: +"string"
    • Changedrent_vs_buy5 fields changed
      • removedInput schema / properties / output / default
        Removed value: -null
      • changedInput schema / properties / output / description
        Previous value: -"'summary' (default), 'inline', or 'capture'. 'summary' returns headline scalars and per-horizon snapshots with the heavy series stripped. 'inline' returns the full payload including yearly_series[] and monthly_series[] for chart rendering. 'capture' writes the full payload to ~/.senaro/captures/ and returns capture_ref; available on the local stdio transport only, since the hosted HTTP transport rejects 'capture' and names 'summary' and 'inline' as the valid alternatives."New value: +"Valid values: 'summary' (default), 'inline'. 'summary' returns headline scalars and per-horizon snapshots with the heavy series stripped. 'inline' returns the full payload including yearly_series[] and monthly_series[] for chart rendering."
      • addedInput schema / properties / output / enum
        Added value: +[
        +  "summary",
        +  "inline"
        +]
      • changedInput schema / properties / output / type
        Previous value: -[
        -  "string",
        -  "null"
        -]New value: +"string"
      • changedInput schema / properties / pmi_removal_ltv_pct / description
        Previous value: -"Loan-balance trigger for PMI removal, as a percentage loan-to-value. Decimal from 50 to 100. Optional; omitted, it is the 78% HPA automatic-termination threshold; provided (e.g. 80), it is whichever of 78% or your value is reached first. Either way PMI also stops at the statutory amortization midpoint of the original term (12 U.S.C. §4901(7), §4902(c)), which this value cannot postpone."New value: +"Loan-balance trigger for PMI removal, as a percentage loan-to-value. Decimal from 50 to 100. Optional; omitted, it is the 78% HPA automatic-termination threshold; provided (e.g. 80), it is whichever of 78% or your value is reached first. The rejection guard applies only when down_payment_pct is below 20 and the resolved pmi_pct is above 0, the range where PMI applies: there, a value at or above the loan's own initial LTV is rejected, because the requested removal point would already be met at the first payment, so no PMI would be modeled for any month of the loan. When the resolved pmi_pct is 0, or down_payment_pct is 20 or more, no such check runs, whatever the LTV. Either way PMI also stops at the statutory amortization midpoint of the original term (12 U.S.C. §4901(7), §4902(c)), which this value cannot postpone."
  4. 16 tool updates
    • Changedanalyze_cash_advance11 fields changed
      • addedInput schema / properties / cash_advance_apr_pct
        Added value: +{
        +  "description": "Cash advance APR as a percentage. Decimal, greater than 0, at most 100. A cash advance always accrues interest immediately, so 0 is not a valid rate. REQUIRED, no default.",
        +  "type": "number"
        +}
      • addedInput schema / properties / cash_advance_balance
        Added value: +{
        +  "description": "Current balance carrying the cash advance APR. Decimal, greater than 0. REQUIRED, no default.",
        +  "type": "number"
        +}
      • addedInput schema / properties / cash_advance_fee_min
        Added value: +{
        +  "default": null,
        +  "description": "Minimum dollar cash advance fee, e.g. 10 for $10. Decimal, at least 0. Optional; defaults to 0 when omitted.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / cash_advance_fee_pct
        Added value: +{
        +  "default": null,
        +  "description": "Cash advance fee as a percentage of the advance, e.g. 5 for 5%. Decimal, at least 0 and at most 100. Optional; defaults to 0 when omitted.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / chart_title
        Added value: +{
        +  "default": null,
        +  "description": "Override for the chart title. Must not contain em-dashes or en-dashes. Max 120 characters. Optional.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / minimum_payment
        Added value: +{
        +  "default": null,
        +  "description": "Issuer-stated minimum payment. Decimal, at least 0, at most 2 decimal places. Optional; defaults to auto-calculate when omitted. When supplied, this DOES bind: the per-month mandatory payment is min(max(the issuer minimum recomputed from the balance, minimum_payment), the remaining balance, total_monthly_payment). This differs from calculate_cc_payoff's default dynamic path, where the identically-named minimum_payment is parsed and validated but never applied unless fixed_payments = true: the two tools do not share behavior for this parameter, only its name.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / purchase_apr_pct
        Added value: +{
        +  "description": "Purchase APR as a percentage, e.g. 24.99 not 0.2499. Decimal from 0 to 100. REQUIRED, no default.",
        +  "type": "number"
        +}
      • addedInput schema / properties / purchase_balance
        Added value: +{
        +  "description": "Current balance carrying the purchase APR. Decimal, at least 0. REQUIRED, no default.",
        +  "type": "number"
        +}
      • removedInput schema / properties / toolArguments
        Removed value: -{
        -  "description": "JSON object with these parameters:\n\npurchase_balance: decimal >= 0 (REQUIRED)\npurchase_apr_pct: decimal 0-100 as percentage (REQUIRED)\ncash_advance_balance: decimal > 0 (REQUIRED)\ncash_advance_apr_pct: decimal > 0, <= 100 (REQUIRED). A cash advance always accrues interest immediately, so 0 is not a valid rate.\ntotal_monthly_payment: decimal > 0, at most 2 decimal places (REQUIRED)\nminimum_payment: decimal >= 0, at most 2 decimal places (optional, default auto-calculate). When supplied, this DOES bind: the per-month mandatory payment is min(max(the issuer minimum recomputed from the balance, minimum_payment), the remaining balance, total_monthly_payment). This differs from calculate_cc_payoff's default dynamic path, where the identically-named minimum_payment is parsed and validated but never applied unless fixed_payments = true: the two tools do not share behavior for this parameter, only its name.\ncash_advance_fee_pct: decimal >= 0 (optional, default 0). e.g. 5 for 5%\ncash_advance_fee_min: decimal >= 0 (optional, default 0). e.g. 10 for $10\nchart_title: string (optional) override for the chart title. Must not contain em-dashes or en-dashes. Max 120 characters."
        -}
      • addedInput schema / properties / total_monthly_payment
        Added value: +{
        +  "description": "Total payment applied across both balances this month. Decimal, greater than 0, at most 2 decimal places. REQUIRED, no default.",
        +  "type": "number"
        +}
      • changedInput schema / required
        Previous value: -[
        -  "toolArguments"
        -]New value: +[
        +  "purchase_balance",
        +  "purchase_apr_pct",
        +  "cash_advance_balance",
        +  "cash_advance_apr_pct",
        +  "total_monthly_payment"
        +]
    • Changedanalyze_pmi_removal13 fields changed
      • addedInput schema / properties / annual_pmi_rate_pct
        Added value: +{
        +  "default": null,
        +  "description": "Annual PMI as a percentage of the current loan balance. Decimal from 0 to 5. Optional; omitting it uses the cited 0.5 default. Typical conventional-loan PMI ranges from 0.3% to 1.5%.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / annual_rate_pct
        Added value: +{
        +  "description": "Mortgage APR as a percentage. Decimal from 0 to 20. REQUIRED, no default.",
        +  "type": "number"
        +}
      • addedInput schema / properties / chart_title
        Added value: +{
        +  "default": null,
        +  "description": "Override for the chart title. Must not contain em-dashes or en-dashes. Max 120 characters. Optional.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / current_balance
        Added value: +{
        +  "description": "Today's loan balance. Decimal, greater than 0. REQUIRED, no default.",
        +  "type": "number"
        +}
      • addedInput schema / properties / current_home_value
        Added value: +{
        +  "default": null,
        +  "description": "Current market value of the home. Decimal, greater than 0. Optional. When supplied AND greater than the HPA original value (the lesser of purchase price and any closing appraisal), the response also computes the re-appraisal pathway (some lenders allow PMI removal based on current market value with a fresh appraisal).",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / extra_monthly_payment
        Added value: +{
        +  "default": null,
        +  "description": "Extra principal paid each month beyond the regular payment. Decimal, at least 0. Optional; defaults to 0 when omitted.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / loan_age_months
        Added value: +{
        +  "default": null,
        +  "description": "Months elapsed since origination. Integer from 0 to 480. Optional. Cannot exceed original_loan_term_months when both are supplied. Supplying either one without the other is rejected; both are required together. See original_loan_term_months.",
        +  "type": [
        +    "integer",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / monthly_payment
        Added value: +{
        +  "description": "Current P&I monthly payment, excluding tax, insurance, and PMI. Decimal, greater than 0. REQUIRED, no default.",
        +  "type": "number"
        +}
      • addedInput schema / properties / original_appraised_value
        Added value: +{
        +  "default": null,
        +  "description": "The home's appraised value at closing. Decimal, greater than 0. Optional. HPA sets the PMI trigger basis to the LESSER of purchase price and this appraisal (12 U.S.C. 4901); supply it when your closing appraisal came in below the purchase price.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / original_loan_term_months
        Added value: +{
        +  "default": null,
        +  "description": "The loan's original term in months. Integer from 1 to 480. Optional. Supplied together with loan_age_months, this computes the 12 U.S.C. 4902(c) statutory final-termination midpoint (12 U.S.C. 4901(7)) and bounds automatic_removal at the earlier of it and the 78% schedule date, where HPA applies and the borrower is current. Supplying either one without the other is rejected; both are required together.",
        +  "type": [
        +    "integer",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / original_purchase_price
        Added value: +{
        +  "description": "What you paid for the home (the purchase-price side of the HPA basis; when a closing appraisal is lower, see original_appraised_value). Decimal, greater than 0. REQUIRED, no default.",
        +  "type": "number"
        +}
      • removedInput schema / properties / toolArguments
        Removed value: -{
        -  "description": "JSON object with these parameters:\n\ncurrent_balance: decimal > 0 (REQUIRED). Today's loan balance.\noriginal_purchase_price: decimal > 0 (REQUIRED). What you paid for the home (the purchase-price side of the HPA basis; when a closing appraisal is lower, see original_appraised_value).\ncurrent_home_value: decimal > 0 (optional). When provided AND greater than the HPA original value (the lesser of purchase price and any closing appraisal), the response also computes the re-appraisal pathway (some lenders allow PMI removal based on current market value with a fresh appraisal).\noriginal_appraised_value: decimal > 0 (optional). The home's appraised value at closing. HPA sets the PMI trigger basis to the LESSER of purchase price and this appraisal (12 U.S.C. 4901); provide it when your closing appraisal came in below the purchase price.\nannual_pmi_rate_pct: decimal 0-5 (optional, default 0.5). Annual PMI as a percentage of the current loan balance. Typical conventional-loan PMI ranges from 0.3% to 1.5%.\nannual_rate_pct: decimal 0-20 (REQUIRED). Mortgage APR.\nmonthly_payment: decimal > 0 (REQUIRED). Current P&I monthly payment (excluding tax/insurance/PMI).\nextra_monthly_payment: decimal >= 0 (optional, default 0). Extra principal each month beyond the regular payment.\noriginal_loan_term_months: integer 1-480 (optional). The loan's original term in months. Supplied together with loan_age_months, this computes the 12 U.S.C. 4902(c) statutory final-termination midpoint (12 U.S.C. 4901(7)) and bounds automatic_removal at the earlier of it and the 78% schedule date, where HPA applies and the borrower is current. Supplying either one without the other is rejected; both are required together.\nloan_age_months: integer 0-480 (optional). Months elapsed since origination. Cannot exceed original_loan_term_months when both are supplied. Supplying either one without the other is rejected; both are required together. See original_loan_term_months.\n\nScope: conventional mortgages only. FHA loans use MIP (Mortgage Insurance Premium) with different rules. Typically MIP runs for the life of the loan when down payment < 10%. This tool does NOT model MIP.\nchart_title: string (optional) override for the chart title. Must not contain em-dashes or en-dashes. Max 120 characters."
        -}
      • changedInput schema / required
        Previous value: -[
        -  "toolArguments"
        -]New value: +[
        +  "current_balance",
        +  "original_purchase_price",
        +  "annual_rate_pct",
        +  "monthly_payment"
        +]
    • Changedcalculate_cc_payoff13 fields changed
      • addedInput schema / properties / apply_rate_cap
        Added value: +{
        +  "default": null,
        +  "description": "Whether to cap each card's APR at the regulatory ceiling before simulating. Optional; defaults to false when omitted.",
        +  "type": [
        +    "boolean",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / cards
        Added value: +{
        +  "description": "Credit cards to pay off, 1 to 20. Required: omitting the field answers contract_error MISSING_REQUIRED_FIELD, while an explicit JSON null answers parse_error (\"cards is required and must be an array.\"); the two are not the same rejection. Every card may carry segments[]. Max 100 segments total across all cards in the request. Per-card, per-type caps: 1 purchase, 1 cash_advance, 5 balance_transfer, 1 promotional, 10 installment_plan.",
        +  "items": {
        +    "properties": {
        +      "annual_fee": {
        +        "description": "Annual fee in dollars, $0 to $1,000,000,000. Optional; defaults to 0. Charged at month 1 and every twelfth month after, while the card carries a balance.",
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      },
        +      "cash_advance_apr_pct": {
        +        "description": "Cash advance APR as a percentage, greater than 0 and up to 100. Required when cash_advance_balance is greater than 0: cash advances have no promotional 0% product, so a 0% rate on a real advance balance is a data-entry error.",
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      },
        +      "cash_advance_balance": {
        +        "description": "Cash advance balance in dollars, $0 to $1,000,000,000. Optional; defaults to 0 when omitted. A POSITIVE value alongside a non-empty segments[] is rejected, the same way purchase_balance is. An explicit 0 is accepted.",
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      },
        +      "minimum_payment": {
        +        "description": "Minimum payment in dollars, $0 to $1,000,000,000 (optional, omit or set 0 to auto-calculate). Where it binds, the card is locked at max(minimum_payment, that card's issuer minimum computed at month 1), and that locked amount is paid every month. On calculate_cc_payoff it binds ONLY when fixed_payments = true; on the DEFAULT path (fixed_payments = false) it is NOT used, because the plan recomputes each card's issuer minimum from its current balance every month, and the response discloses that as MINIMUM_PAYMENT_NOT_BINDING. On compare_strategies there is no fixed_payments parameter and supplying one is rejected: the avalanche and snowball arms ALWAYS run locked, so the value DOES bind and moves both arms, while the minimum-payments-only baseline always recomputes and ignores it, disclosed as MINIMUM_PAYMENT_NOT_BINDING_IN_BASELINE.",
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      },
        +      "monthly_spend": {
        +        "description": "Recurring monthly purchase amount charged to this card's purchase balance before interest accrues, $0 to $50,000 per month. Optional; defaults to 0. Use the segment-level monthly_spend instead when supplying segments[].",
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      },
        +      "name": {
        +        "description": "Card label, e.g. 'Chase Sapphire', 120 characters or fewer. Optional; a card without a name is auto-numbered ('Card 1', 'Card 2', and so on). Echoed into per-card results and warning text. A name over the cap is replaced in error field paths by a key built from its zero-based card index, written as '#0 (name over cap)', '#1 (name over cap)' and so on.",
        +        "type": [
        +          "string",
        +          "null"
        +        ]
        +      },
        +      "plan_fees_monthly": {
        +        "description": "Recurring monthly card fee in dollars, $0 to $1,000,000,000, e.g. a pay-over-time plan fee. Optional; defaults to 0. Charged every month while the card carries a balance.",
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      },
        +      "purchase_apr_pct": {
        +        "description": "Purchase APR as a percentage, 0 to 100, e.g. 24.99 not 0.2499. Required when purchase_balance is greater than 0: an omitted APR would silently compute $0 interest.",
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      },
        +      "purchase_balance": {
        +        "description": "Purchase balance in dollars, $0 to $1,000,000,000. Required unless segments[] carries at least one segment: an omitted purchase_balance would silently report a $0, 0-month payoff. Supplying a POSITIVE value alongside a non-empty segments[] is rejected, since the two would be competing sources for the same balance. An explicit 0 is accepted.",
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      },
        +      "segments": {
        +        "description": "Per-segment breakdown of this card's balances. Optional; when it carries at least one segment it SUPERSEDES purchase_balance and cash_advance_balance, and a POSITIVE value in either of those is rejected. An empty segments[] is treated as absent, so purchase_balance is then required. At most 100 segments across the whole request. Per card, per type: 1 purchase, 1 cash_advance, 5 balance_transfer, 1 promotional, 10 installment_plan. Each item carries its own type, balance, rate and payment priority; see the type field on the item for the per-type field matrix.",
        +        "items": {
        +          "properties": {
        +            "apr_pct": {
        +              "default": null,
        +              "description": "Annual percentage rate for this segment, 0 to 100, e.g. 24.99 not 0.2499. Required when balance is greater than 0 on purchase, cash_advance, balance_transfer and promotional. On cash_advance it must be greater than 0 whenever a balance is carried. Must be 0 on installment_plan, which uses monthly_fee instead, so omitting it there is the correct shape.",
        +              "type": [
        +                "number",
        +                "null"
        +              ]
        +            },
        +            "balance": {
        +              "description": "Segment balance in dollars, $0 to $1,000,000,000. REQUIRED on every segment type: an omitted balance would silently report a $0, 0-month payoff, so it is rejected instead. An explicit 0 is valid, for example a paid-off balance_transfer segment kept for schedule continuity.",
        +              "type": "number"
        +            },
        +            "extra_payment_order": {
        +              "default": null,
        +              "description": "Extra-payment tie-break priority, 0 or more, where 0 means this segment never receives extra payment. Optional on every type, and the default for installment_plan. Only breaks a tie between segments sharing the exact same current APR, since the Credit CARD Act (TILA §164(b), 15 U.S.C. §1666c(b)) / 12 CFR 1026.53(a) always pays the highest-current-APR segment first regardless of this value.",
        +              "type": [
        +                "integer",
        +                "null"
        +              ]
        +            },
        +            "merchant_name": {
        +              "default": null,
        +              "description": "Label for an installment plan's merchant, 120 characters or fewer. Optional on installment_plan, ignored on every other type.",
        +              "type": [
        +                "string",
        +                "null"
        +              ]
        +            },
        +            "min_payment_order": {
        +              "default": null,
        +              "description": "Minimum-payment tie-break priority, 1 or more. Optional on every type. The minimum first pays any installment_plan's locked plan_payment_due in full, then the lowest-current-APR segment; this value only breaks a tie between segments sharing both the same current APR and the same installment-plan status.",
        +              "type": [
        +                "integer",
        +                "null"
        +              ]
        +            },
        +            "monthly_fee": {
        +              "default": null,
        +              "description": "Fixed fee charged each month while an installment plan's term is active, $0 or more and no greater than plan_payment_due. Optional on installment_plan. On every other type a positive value is rejected with FIELD_NOT_ALLOWED_HERE, while 0 and a negative value are accepted and dropped.",
        +              "type": [
        +                "number",
        +                "null"
        +              ]
        +            },
        +            "monthly_spend": {
        +              "default": null,
        +              "description": "Recurring monthly purchase amount posted to this segment before interest accrues, $0 to $50,000 per month. Optional on purchase. A negative value is rejected as NEGATIVE_BALANCE on every type. On every type other than purchase a positive value is rejected with FIELD_NOT_ALLOWED_HERE, since those balances do not accept new purchases; an explicit 0 is accepted.",
        +              "type": [
        +                "number",
        +                "null"
        +              ]
        +            },
        +            "plan_payment_due": {
        +              "default": null,
        +              "description": "Locked monthly payment for an installment plan, principal plus monthly_fee. Required on installment_plan, greater than $0, at least monthly_fee, and no more than $1,000,000,000. On every other type a positive value is rejected with FIELD_NOT_ALLOWED_HERE, while 0 and a negative value are accepted and dropped.",
        +              "type": [
        +                "number",
        +                "null"
        +              ]
        +            },
        +            "promo_expires_month": {
        +              "default": null,
        +              "description": "Month at which apr_pct flips to revert_apr_pct, 1 to 480. Required on balance_transfer and promotional. Ignored on purchase, cash_advance and installment_plan.",
        +              "type": [
        +                "integer",
        +                "null"
        +              ]
        +            },
        +            "remaining_payments": {
        +              "default": null,
        +              "description": "Payments left in an installment plan's stated term, 0 or more. Optional on installment_plan, ignored on every other type. 0 means the term has ALREADY ended and is valid with any balance, including a positive residual. From 1 up, the balance must be clearable within the stated term and must not be clearable a month early.",
        +              "type": [
        +                "integer",
        +                "null"
        +              ]
        +            },
        +            "revert_apr_pct": {
        +              "default": null,
        +              "description": "Rate this segment reverts to once its promo expires, 0 to 100, and at least apr_pct. Required on balance_transfer and promotional. Ignored on purchase and cash_advance. Rejected on installment_plan with FIELD_NOT_ALLOWED_HERE: an expired plan's residual keeps its existing rate rather than repricing.",
        +              "type": [
        +                "number",
        +                "null"
        +              ]
        +            },
        +            "stop_spend_month": {
        +              "default": null,
        +              "description": "Month index at which recurring spend stops, 0 or more. Read on purchase only, and range checked on every type. Omit to spend for the whole projection.",
        +              "type": [
        +                "integer",
        +                "null"
        +              ]
        +            },
        +            "type": {
        +              "description": "Segment type. REQUIRED on every segment: omitting it is rejected. Per-type field matrix. Every field below is classified for every segment type: REQUIRED (omitting it is rejected), OPTIONAL (accepted either way and read), IGNORED (accepted and never read for this type, though a value can still fail the field's own range check), or REJECTED (supplying a disallowed value is an error; the field's own rule below says exactly which values are disallowed). Value rules are stated separately from applicability, because a field can be accepted on a type and still be restricted there. The validators are the authority; this matrix describes them.\n- type: REQUIRED on every type. Values: one of purchase, cash_advance, balance_transfer, promotional, installment_plan.\n- balance: REQUIRED on every type. Values: $0 to $1,000,000,000; above $50,000 returns a HIGH_CARD_BALANCE warning, not an error.\n- apr_pct: REQUIRED on purchase, cash_advance, balance_transfer, promotional; OPTIONAL on installment_plan. Values: 0 to 100 as a percentage, e.g. 24.99 not 0.2499. The REQUIRED classification above applies when balance is greater than 0; a zero-balance segment may omit it. Must be greater than 0 on cash_advance carrying a balance. Must equal 0 on installment_plan, where omitting it is the correct shape.\n- revert_apr_pct: REQUIRED on balance_transfer, promotional; IGNORED on purchase, cash_advance; REJECTED on installment_plan. Values: 0 to 100, and at least apr_pct. Rejected on installment_plan with FIELD_NOT_ALLOWED_HERE: an expired plan's residual keeps its existing rate.\n- promo_expires_month: REQUIRED on balance_transfer, promotional; IGNORED on purchase, cash_advance, installment_plan. Values: 1 to 480.\n- monthly_fee: OPTIONAL on installment_plan; REJECTED on purchase, cash_advance, balance_transfer, promotional. Values: $0 or more, and no greater than plan_payment_due, on installment_plan. On every other type a POSITIVE value is rejected with FIELD_NOT_ALLOWED_HERE; 0 and a negative value are accepted and dropped there.\n- plan_payment_due: REQUIRED on installment_plan; REJECTED on purchase, cash_advance, balance_transfer, promotional. Values: greater than $0, at least monthly_fee, and no more than $1,000,000,000, on installment_plan. On every other type a POSITIVE value is rejected with FIELD_NOT_ALLOWED_HERE; 0 and a negative value are accepted and dropped there.\n- remaining_payments: OPTIONAL on installment_plan; IGNORED on purchase, cash_advance, balance_transfer, promotional. Values: 0 or more. 0 means the stated term has ALREADY ended and is valid with any balance. From 1 up, balance must be clearable within the stated term.\n- min_payment_order: OPTIONAL on every type. Values: 1 or more; breaks a tie only between segments sharing both the same current APR and the same installment-plan status.\n- extra_payment_order: OPTIONAL on every type. Values: 0 or more; 0 means skip. Breaks a tie only between segments sharing the exact same current APR.\n- merchant_name: OPTIONAL on installment_plan; IGNORED on purchase, cash_advance, balance_transfer, promotional. Values: 120 characters or fewer.\n- monthly_spend: OPTIONAL on purchase; REJECTED on cash_advance, balance_transfer, promotional, installment_plan. Values: $0 to $50,000 per month. A negative value is rejected as NEGATIVE_BALANCE on every type, purchase included. On every type other than purchase a POSITIVE value is rejected with FIELD_NOT_ALLOWED_HERE; an explicit 0 is accepted.\n- stop_spend_month: OPTIONAL on purchase; IGNORED on cash_advance, balance_transfer, promotional, installment_plan. Values: 0 or more; range checked on every type, read only on purchase.",
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "type",
        +            "balance"
        +          ],
        +          "type": "object"
        +        },
        +        "type": [
        +          "array",
        +          "null"
        +        ]
        +      },
        +      "stop_spend_month": {
        +        "description": "Month index at which this card's recurring spend stops, 0 or more. Optional; omit to spend for the whole projection.",
        +        "type": [
        +          "integer",
        +          "null"
        +        ]
        +      }
        +    },
        +    "type": "object"
        +  },
        +  "type": "array"
        +}
      • addedInput schema / properties / chart_title
        Added value: +{
        +  "default": null,
        +  "description": "Override for the chart title. Optional; must not contain an em dash or en dash. Max 120 characters.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / extra_monthly_payment
        Added value: +{
        +  "default": null,
        +  "description": "Extra monthly payment in dollars, $0 to $1,000,000,000, applied on top of the required minimums. Optional; defaults to 0 when omitted.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / fixed_payments
        Added value: +{
        +  "default": null,
        +  "description": "Whether to hold each card's payment constant for the whole payoff. Optional; defaults to false when omitted. When true, each card's payment is locked at max(cards[].minimum_payment, that card's issuer minimum computed at month 1), and the total monthly budget (those locked amounts plus extra_monthly_payment) is held constant for the whole payoff, so once a card is paid off its freed-up payment rolls forward onto the remaining cards. This is the ONLY mode in which cards[].minimum_payment affects calculate_cc_payoff's result; compare_strategies has no fixed_payments parameter and binds minimum_payment on both of its strategy arms regardless.",
        +  "type": [
        +    "boolean",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / full_schedule
        Added value: +{
        +  "default": null,
        +  "description": "Whether to return the full per-card month-by-month schedule. Optional; defaults to false when omitted. The per-card monthly_schedule is capped at 1000 rows total across cards and months, so later months are omitted on a long multi-card payoff; the portfolio-level monthly_totals series is always complete and is the one to read for the whole timeline.",
        +  "type": [
        +    "boolean",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / include_card_timeline
        Added value: +{
        +  "default": null,
        +  "description": "Whether to add the card_timeline block, a per-card view of when each card clears. Optional; defaults to false when omitted.",
        +  "type": [
        +    "boolean",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / output
        Added value: +{
        +  "default": null,
        +  "description": "Response verbosity: 'summary' (default) | 'inline' | 'capture'. summary returns a compact response with a data_preview block; inline returns the full payload; capture writes the full payload to this server's local disk for the chart-render pipeline and returns a capture_ref URI. capture is available on the local stdio transport only, and the hosted HTTP transport rejects it with a structured error naming 'summary' and 'inline' as the valid alternatives, since a capture_ref would be a dead link for a remote caller. Optional; defaults to 'summary' when omitted.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / per_segment
        Added value: +{
        +  "default": null,
        +  "description": "Whether each schedule row carries its per-segment breakdown. Optional; defaults to true when omitted.",
        +  "type": [
        +    "boolean",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / strategy
        Added value: +{
        +  "default": null,
        +  "description": "Payoff order: 'avalanche' pays the highest APR first, 'snowball' the smallest balance first. Optional; defaults to 'avalanche' when omitted. Matched case-insensitively, and the response echoes the canonical lowercase form actually simulated.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • removedInput schema / properties / toolArguments
        Removed value: -{
        -  "description": "JSON object with these parameters:\n\ncards (REQUIRED array, max 20):\n  - name: string (optional, auto-generated if omitted)\n  - purchase_balance: decimal >= 0 (REQUIRED unless segments[] is supplied)\n  - purchase_apr_pct: decimal 0-100 as percentage, e.g. 24.99 not 0.2499 (REQUIRED unless segments[] is supplied)\n  - cash_advance_balance: decimal >= 0 (optional, default 0)\n  - cash_advance_apr_pct: decimal > 0, <= 100 (required only if cash_advance_balance > 0)\n  - minimum_payment: decimal >= 0 (optional, omit or set 0 to auto-calculate). Binds ONLY when fixed_payments = true: the card is then locked at max(minimum_payment, that card's issuer minimum computed at month 1), and that locked amount is paid every month. On the DEFAULT path (fixed_payments = false), this value is NOT used: the plan recomputes each card's issuer minimum from its current balance every month, so the monthly budget is the sum of those recomputed minimums plus extra_monthly_payment. To hold a stated payment instead, set fixed_payments = true.\n  - annual_fee: decimal >= 0 (optional, default 0). Charged at month 1 and every 12th month thereafter while the card has a balance.\n  - plan_fees_monthly: decimal >= 0 (optional, default 0). Charged every month while the card has a balance (e.g. pay-over-time plan fee).\n  - segments: array (OPTIONAL; when present, supersedes purchase_balance / cash_advance_balance). Max 100 segments total across all cards in the request. Per-card, per-type caps: 1 purchase, 1 cash_advance, 5 balance_transfer, 1 promotional, 10 installment_plan. Each item:\n      - type: 'purchase' | 'cash_advance' | 'balance_transfer' | 'promotional' | 'installment_plan' (REQUIRED). Use 'promotional' for a balance whose 0% (or below-market) intro APR reverts to a standard rate later and is NOT a balance transfer, e.g. a 0% intro purchase APR; it validates and reverts identically to balance_transfer.\n      - balance: decimal >= 0 (REQUIRED)\n      - apr_pct: decimal 0-100 (REQUIRED for purchase / cash_advance / balance_transfer / promotional; must be 0 for installment_plan; for cash_advance, must be > 0 when balance > 0, since cash advances have no promotional 0% product)\n      - revert_apr_pct: decimal 0-100 (REQUIRED for balance_transfer / promotional; APR after promo expires; not applicable to purchase / cash_advance (silently ignored); REJECTED on installment_plan (FIELD_NOT_ALLOWED_HERE) since installment_plan has no post-term-rate concept: an expired plan's residual keeps its existing rate)\n      - promo_expires_month: int >= 1 (REQUIRED for balance_transfer / promotional; month at which apr_pct flips to revert_apr_pct)\n      - monthly_fee: decimal >= 0 (REQUIRED for installment_plan; fixed fee charged each month while the plan's term is active; stops accruing once the term elapses)\n      - plan_payment_due: decimal > 0 (REQUIRED for installment_plan; locked monthly payment, principal + monthly_fee; keeps being charged every month even after the term elapses, until the balance clears)\n      - remaining_payments: int >= 0 (optional, installment_plan only). 0 means the plan's stated term has ALREADY ended: this is the correct, valid way to model an already-expired plan carrying a residual balance, and it is accepted regardless of balance (an expired plan's residual is a real state, not a data-entry error). When remaining_payments >= 1, it must be consistent with balance / plan_payment_due / monthly_fee: net = plan_payment_due - monthly_fee must be positive, or the plan could never amortize (rejected as INSTALLMENT_PLAN_NET_NOT_POSITIVE); balance must be <= remaining_payments * net (the plan must be able to clear within its stated term) and balance must be > (remaining_payments - 1) * net (the term must not be overstated), either violation rejected as INSTALLMENT_PLAN_TERM_MISMATCH. If a plan has already ended, enter remaining_payments as 0 rather than a positive count that cannot clear the balance. An expired plan (remaining_payments reaches 0 with a positive balance still owed, whether supplied that way or reached by simulation) does not freeze and is never repriced: it keeps receiving its locked plan_payment_due every month at its existing rate until the balance clears. One aggregated PLAN_TERM_ELAPSED_WITH_BALANCE warning fires per card (never one per segment), naming every affected plan on that card and its residual, plus the card total.\n      - merchant_name: string (optional, installment_plan only)\n      - min_payment_order: int >= 1 (optional; the minimum first pays any installment_plan segment's locked plan_payment_due in full, structurally, regardless of APR or this value; among the remaining (revolving) segments it then pays down the lowest-current-APR segment first, issuer practice, not statutorily regulated; this value only breaks a tie between segments that share both the exact same current APR AND the same installment-plan status, in which case the higher-balance segment wins; two segments on the same card sharing a value emit a DUPLICATE_PAYMENT_ORDER warning; a supplied value with no APR-tie partner to ever decide emits PAYMENT_ORDER_NOT_DECISIVE)\n      - extra_payment_order: int >= 0 (optional; 0 = SKIP -- this segment never receives extra payment, the default for installment_plan; a positive value only breaks a tie between segments that share the exact same current APR, since the Credit CARD Act (TILA §164(b), 15 U.S.C. §1666c(b)) / 12 CFR §1026.53(a) always pays the highest-current-APR segment first regardless of this value; two segments on the same card sharing a positive value emit a DUPLICATE_PAYMENT_ORDER warning, duplicate 0s never warn; a supplied value with no APR-tie partner to ever decide emits PAYMENT_ORDER_NOT_DECISIVE -- this hits installment_plan hardest, since it is pinned at 0% APR (apr_pct is not read for this segment type), so a positive override on it can win the tie-break only against another segment also at exactly 0%; whenever any positive-APR segment shares the card, the plan stays ELIGIBLE and sorts LAST behind every positive-APR segment, receiving extra only once those higher-APR balances clear)\n    Response gains per-card interest_saving_balance (sum of APR-bearing segments) and installment_balance_locked (sum of installment_plan segments) when segments[] is supplied.\n\nextra_monthly_payment: decimal >= 0 (optional, default 0)\nstrategy: 'avalanche' | 'snowball' (optional, default 'avalanche')\nfixed_payments: bool (optional, default false). When true, each card's payment is locked at max(cards[].minimum_payment, that card's issuer minimum computed at month 1), and the total monthly budget (sum of those locked amounts plus extra_monthly_payment) is held constant for the whole payoff, so once a card is paid off its freed-up payment rolls forward onto the remaining cards. This is the ONLY mode in which cards[].minimum_payment affects the result; see the minimum_payment field above for what happens on the default path.\napply_rate_cap: bool (optional, default false)\nfull_schedule: bool (optional, default false). The per-card monthly_schedule is capped at 1000 rows total (cards x months); on a long multi-card payoff later months are omitted from monthly_schedule. The portfolio-level monthly_totals series is always complete, so read monthly_totals for the full timeline.\nper_segment: bool (optional, default true)\ninclude_card_timeline: bool (optional, default false)\n\nwindfalls: array of one-time principal payments (optional, default empty, max 12 items). Each item:\n  - month: int >= 0 (REQUIRED). 0 means applied before month 1's interest (same as reducing starting principal). N >= 1 applies at the END of calendar month N, so month N+1's interest is calculated on the post-windfall balance.\n  - amount: decimal > 0 (REQUIRED).\n  - label: string (optional). e.g. 'Tax refund', 'Year-end bonus'.\nWhen any windfalls are present the response gains: baseline, with_windfalls, months_saved_vs_baseline, interest_saved_vs_baseline, windfalls_applied[], windfalls_unused[]. Empty windfalls yields the same response shape as before.\n\nMin-payments-only baseline (v1.7, gap #593): when extra_monthly_payment > 0, windfalls are present, or fixed_payments = true, the response also gains:\n  - min_payments_only: { months, interest_cost, amount_paid, payoff_date } - what happens paying minimums only\n  - interest_saved_vs_min_payments: decimal - total interest saved vs paying minimums only (always >= 0)\n  - months_saved_vs_min_payments: int - months saved vs paying minimums only (when minimums never pay off, capped at MaxMonths minus with-plan months)\nTrivial minimums-only calls (no extra, no windfalls, not fixed) keep their current response shape unchanged.\n\nMinimums-only baseline series (v1.9, B2; bucketed v2.5, #1108): under the same plan-exists condition as min_payments_only above, the response also gains minimums_only_baseline: { months, granularity, buckets[], payoff_date } - the minimums-only balance trajectory bucketed through the same shared aggregator as analysis.buckets[] (same bucket row shape: bucket_index, label, plus the 17 money fields), with granularity auto-selected from THIS series' own term, the same standard rule analysis.granularity itself uses. minimums_only_baseline.granularity is NOT guaranteed to equal analysis.granularity: each series auto-selects independently from its own term, so read each series' own granularity rather than assuming they match. Equal bucket-array length is NOT a valid signal that the two granularities match either: a 24-month primary run (monthly, 24 buckets) and a 24-year minimums-only baseline (yearly, 24 buckets) land on the SAME buckets[].length with DIFFERENT granularities. granularity is the only valid mismatch signal; length is not. Computed from the SAME gated simulation as min_payments_only, so months/payoff_date agree between the two blocks. Absent under the identical trivial-call condition as min_payments_only.\n\nchart_title: string (optional) override for the chart title. Must not contain em-dashes or en-dashes. Max 120 characters.\noutput: 'summary' | 'inline' | 'capture' (optional, default 'summary')\n  'capture' is available on the local stdio transport only; the hosted HTTP transport rejects it with a structured error naming 'summary' and 'inline' as the valid alternatives."
        -}
      • addedInput schema / properties / windfalls
        Added value: +{
        +  "default": null,
        +  "description": "One-time principal payments, at most 12, e.g. a tax refund or a year-end bonus. Optional; a JSON null is treated as omitted. When non-empty the response gains baseline, with_windfalls, months_saved_vs_baseline, interest_saved_vs_baseline, windfalls_applied[] and windfalls_unused[]; an empty array returns the same shape as omitting it.",
        +  "items": {
        +    "properties": {
        +      "amount": {
        +        "description": "Dollar amount of the windfall, greater than 0. Required per entry.",
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      },
        +      "label": {
        +        "description": "Optional label for this windfall, e.g. 'Tax refund' or 'Year-end bonus'. Max 120 characters.",
        +        "type": [
        +          "string",
        +          "null"
        +        ]
        +      },
        +      "month": {
        +        "description": "Month index the windfall is applied, 0 or more. 0 means applied before month 1's interest; N >= 1 applies at the end of calendar month N. Required per entry.",
        +        "type": [
        +          "integer",
        +          "null"
        +        ]
        +      }
        +    },
        +    "type": [
        +      "object",
        +      "null"
        +    ]
        +  },
        +  "type": [
        +    "array",
        +    "null"
        +  ]
        +}
      • changedInput schema / required
        Previous value: -[
        -  "toolArguments"
        -]New value: +[
        +  "cards"
        +]
    • Changedcalculate_debt_to_income22 fields changed
      • addedInput schema / properties / additional_income
        Added value: +{
        +  "default": null,
        +  "description": "Additional monthly income: side income, rental income, or bonuses. Optional; defaults to 0 when omitted. Must be zero or more.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / annual_income
        Added value: +{
        +  "default": null,
        +  "description": "Annual gross income in dollars, divided by 12 to get monthly income. Exactly one of gross_monthly_income or annual_income is required. Must be at least $0.01, one cent, the smallest amount of money.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / existing_debts
        Added value: +{
        +  "default": null,
        +  "description": "Existing debts to include in the DTI calculation. Optional; omit it or send an empty array for no existing debts. A JSON null is rejected; omit the field instead. At most 50 debts are allowed.",
        +  "items": {
        +    "properties": {
        +      "apr_pct": {
        +        "description": "Annual percentage rate, as a percentage (0-100), e.g. 18.5 for 18.5%. Optional.",
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      },
        +      "balance": {
        +        "description": "Current balance in dollars. Optional; used for payoff cost analysis in what-if scenarios.",
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      },
        +      "monthly_payment": {
        +        "description": "Monthly payment in dollars. Required per debt; must be positive.",
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      },
        +      "months_remaining": {
        +        "description": "Months remaining on this debt. Optional; used for the 10-month rule exclusion.",
        +        "type": [
        +          "integer",
        +          "null"
        +        ]
        +      },
        +      "name": {
        +        "description": "Optional label for this debt, e.g. 'Car Loan'.",
        +        "type": [
        +          "string",
        +          "null"
        +        ]
        +      },
        +      "type": {
        +        "description": "Debt type: 'auto', 'student', 'credit_card', 'personal', 'mortgage', 'heloc', 'child_support', or 'other'. Optional, defaults to 'other' when omitted; an unrecognized value warns rather than rejects.",
        +        "type": [
        +          "string",
        +          "null"
        +        ]
        +      }
        +    },
        +    "type": [
        +      "object",
        +      "null"
        +    ]
        +  },
        +  "type": [
        +    "array",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / family_size
        Added value: +{
        +  "default": null,
        +  "description": "Household size for the VA residual income guideline. Supply family_size and property_state together, or neither. Optional; must be between 1 and 20.",
        +  "type": [
        +    "integer",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / gross_monthly_income
        Added value: +{
        +  "default": null,
        +  "description": "Gross monthly income in dollars. Exactly one of gross_monthly_income or annual_income is required. Must be at least $0.01, one cent, the smallest amount of money.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / hoa_monthly
        Added value: +{
        +  "default": null,
        +  "description": "Monthly homeowners association dues in dollars. Optional; defaults to 0 when omitted. Must be zero or more.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / home_insurance_annual
        Added value: +{
        +  "default": null,
        +  "description": "Annual home insurance in dollars. Optional; if omitted, estimated at 0.65% of the proposed home price. Must be zero or more.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / include_what_if
        Added value: +{
        +  "default": null,
        +  "description": "Whether to generate what-if scenarios showing how paying off a debt, increasing income, or reducing the home price would improve DTI. Optional; defaults to true when omitted.",
        +  "type": [
        +    "boolean",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / monthly_maintenance_and_utilities
        Added value: +{
        +  "default": null,
        +  "description": "Estimated monthly maintenance and utilities for the proposed property. 38 CFR 36.4340 calls for a realistic estimate of this figure for the property and local utility rates and sets no numeric multiplier itself, but VA underwriting guidance (the Lender's Handbook, Pamphlet 26-7) publishes a per-square-foot multiplier for this same estimate. Applying it needs the property's square footage, which this tool does not currently collect, so Senaro has no default to offer here and you supply the aggregate monthly amount instead. Supplying BOTH this field and monthly_taxes_and_retirement_withholding, together with proposed_home_price and a computable family_size/property_state, computes qualification.va.residual_income_comparison: your ACTUAL monthly residual income, its ratio to the va_residual_income_guideline figure, and whether residual_income_meets_review_waiver_margin (residual income at or above 120% of the guideline) is met. The shelter expense used here excludes any PMI (VA loans carry no monthly PMI; 38 CFR 36.4313(e) sets a funding fee instead, commonly financed into the loan; see va_funding_fee_financed_monthly for the financed-fee field). 38 CFR 36.4340(c)(3)'s review-waiver condition is CONJUNCTIVE: it also requires the back-end debt-to-income ratio (qualification.va.your_back_end) to exceed 41%, which this field does not by itself confirm. Check both fields together. Even when both hold, (c)(3) only WAIVES a second-level review requirement; it is not itself an approval. Whether this file is actually approved is an underwriting determination Senaro does not make and no input combination here determines. Missing any one of the needed inputs reads qualification.va.residual_income_comparison.status 'not_computable' with every reason named. Optional; must be zero or more.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / monthly_taxes_and_retirement_withholding
        Added value: +{
        +  "default": null,
        +  "description": "Your federal, state, and FICA tax withholding, plus any amount paid or withheld for retirement, monthly. 38 CFR 36.4340(f)(13) treats these as one class of deduction from gross income. Optional; must be zero or more.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / pmi_monthly
        Added value: +{
        +  "default": null,
        +  "description": "Monthly PMI (private mortgage insurance) in dollars. Optional; if omitted, auto-estimated at 0.5% of the loan annually when loan-to-value exceeds 80%. Feeds the conventional-basis PITI, so it moves with_proposed.front_end_dti, with_proposed.back_end_dti, with_proposed.front_end_breakdown, the conventional qualification row, and what_if.scenarios[].new_front_end_dti and new_back_end_dti. VA carries no PMI, and FHA and USDA always compute their own upfront-plus-annual mortgage insurance instead, at every loan-to-value, never this override. Must be zero or more.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / property_state
        Added value: +{
        +  "default": null,
        +  "description": "Two-letter USPS state code, or 'DC'/'PR'/'GU'/'VI'/'AS'/'MP'. Supply family_size and property_state together, or neither. Together these compute the VA residual income guideline (38 CFR 36.4340(e)) in va_residual_income_guideline: the dollar amount VA's tables require for this family size, region, and loan amount (derived from proposed_home_price; not computable without it), plus the 38 CFR 36.4340(c)(3) review-waiver figure. Computed only for family_size 1-7 and a property_state among the 50 states, DC, or PR (not GU, VI, AS, or MP; 38 CFR 36.4340(e) assigns no region to those four); outside those bounds, or without proposed_home_price, va_residual_income_guideline.status reads 'not_computable' with the reason instead. This block alone is a LOOKUP, not a verdict: it never compares against your actual residual income by itself. Optional.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / property_tax_annual
        Added value: +{
        +  "default": null,
        +  "description": "Annual property tax in dollars. Optional; if omitted, estimated at 0.88% of the proposed home price. Must be zero or more.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / proposed_debt
        Added value: +{
        +  "default": null,
        +  "description": "A proposed new debt, as an alternative to proposed_home_price. Provide at most one of proposed_debt or proposed_home_price; providing neither computes the current DTI only. Optional; a JSON null is treated as omitted, the same as leaving the field out.",
        +  "properties": {
        +    "includes_tax_insurance": {
        +      "description": "Whether monthly_payment already includes tax and insurance. Optional, defaults to true when omitted; if false and type is 'mortgage', warns that lenders use full PITI for DTI.",
        +      "type": [
        +        "boolean",
        +        "null"
        +      ]
        +    },
        +    "monthly_payment": {
        +      "description": "Monthly payment in dollars. Required; must be positive.",
        +      "type": [
        +        "number",
        +        "null"
        +      ]
        +    },
        +    "name": {
        +      "description": "Optional label for this proposed debt.",
        +      "type": [
        +        "string",
        +        "null"
        +      ]
        +    },
        +    "type": {
        +      "description": "Debt type: 'auto', 'student', 'credit_card', 'personal', 'mortgage', 'heloc', 'child_support', or 'other'. Optional, defaults to 'mortgage' when omitted; an unrecognized value is rejected.",
        +      "type": [
        +        "string",
        +        "null"
        +      ]
        +    }
        +  },
        +  "type": [
        +    "object",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / proposed_down_payment_pct
        Added value: +{
        +  "default": null,
        +  "description": "Down payment as a percent of the proposed home price, e.g. 20 for 20%. Optional; defaults to 20 when omitted. Must be between 0 and 99.9 (100% cash purchases are not supported).",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / proposed_home_price
        Added value: +{
        +  "default": null,
        +  "description": "Proposed home purchase price in dollars. Provide at most one of proposed_debt or proposed_home_price; providing neither computes the current DTI only. Auto-calculates full PITI (principal, interest, taxes, insurance). Optional; must be at least $0.01, one cent, the smallest amount of money.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / proposed_rate_pct
        Added value: +{
        +  "default": null,
        +  "description": "Proposed mortgage interest rate as a percent, e.g. 7.0 for 7.0%. Optional; the 7.0% default applies whenever this field is omitted, whether the proposal is proposed_debt or proposed_home_price. The default-rate warning fires only when proposed_home_price is used. Must be between 0 and 20.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / proposed_term_years
        Added value: +{
        +  "default": null,
        +  "description": "Proposed mortgage term in years. Optional; defaults to 30 when omitted. Must be between 1 and 40.",
        +  "type": [
        +    "integer",
        +    "null"
        +  ]
        +}
      • removedInput schema / properties / toolArguments
        Removed value: -{
        -  "description": "JSON object with these parameters:\n\ngross_monthly_income: decimal > 0 (REQUIRED; OR provide annual_income instead)\nannual_income: decimal > 0 (alternative to gross_monthly_income; divided by 12)\nadditional_income: decimal >= 0 (optional, default 0; side income, rental income, bonuses. Monthly.)\n\nexisting_debts: array of debt objects (optional, can be empty):\n  - name: string (optional label, e.g. 'Car Loan')\n  - type: 'auto' | 'student' | 'credit_card' | 'personal' | 'mortgage' | 'heloc' | 'child_support' | 'other'\n  - monthly_payment: decimal > 0 (REQUIRED per debt)\n  - balance: decimal (optional; for payoff cost analysis in what-if scenarios)\n  - apr_pct: decimal 0-100 as PERCENTAGE (optional)\n  - months_remaining: int (optional; used for 10-month rule exclusion)\n\nproposed_debt: object (optional; OR use proposed_home_price instead):\n  - name: string (optional)\n  - type: 'auto' | 'student' | 'credit_card' | 'personal' | 'mortgage' | 'heloc' | 'child_support' | 'other' (optional, default 'mortgage')\n  - monthly_payment: decimal > 0 (REQUIRED)\n  - includes_tax_insurance: bool (optional, default true; if false and type is mortgage, warns that lenders use PITI)\n\nproposed_home_price: decimal > 0 (optional; auto-calculates full PITI. Cannot combine with proposed_debt.)\nproposed_down_payment_pct: decimal 0-99.9 as PERCENTAGE (optional, default 20)\nproposed_rate_pct: decimal 0-20 as PERCENTAGE (optional, default 7.0 with warning)\nproposed_term_years: int 1-40 (optional, default 30)\n\nproperty_tax_annual: decimal >= 0 (optional. For the PITI estimate, uses 0.88% national average if omitted)\nhome_insurance_annual: decimal >= 0 (optional. Uses 0.65% national average if omitted)\npmi_monthly: decimal >= 0 (optional. Auto-estimated at 0.5% of loan when LTV > 80%. Feeds the\n  conventional-basis PITI, so it moves with_proposed.front_end_dti, with_proposed.back_end_dti,\n  with_proposed.front_end_breakdown, the conventional qualification row, and\n  what_if.scenarios[].new_front_end_dti / new_back_end_dti. VA carries no PMI, and FHA/USDA always\n  compute their own upfront-plus-annual mortgage insurance instead, at every LTV, never this\n  override)\nhoa_monthly: decimal >= 0 (optional, default 0)\n\ninclude_what_if: bool (optional, default true; generate scenarios to improve DTI)\n\ntransaction_purpose: 'purchase' | 'refinance' | 'streamlined_assist' (optional, default 'purchase')\n  Affects USDA only, and only what is disclosed. USDA's 32% PITI and 44% Total Debt figures are\n  purchase-transaction waiver conditions (HB-1-3555 11.3.A.2), disclosed rather than applied as\n  ceilings: Senaro cannot observe how the file is underwritten, so a USDA ratio overage is never 'ineligible'\n  on any transaction purpose. For a refinance, 11.3.B states debt ratios 'are not limited to the\n  maximum purchase debt ratio thresholds', so where the note fires it names both figures and states that neither applies. Streamlined-assist\n  refinances require no debt ratio calculation at all. Conventional, FHA and VA are unaffected.\n\nfamily_size: int 1-20 (optional; must be supplied together with property_state, or neither fires)\nproperty_state: two-letter USPS state code, or 'DC'/'PR'/'GU'/'VI'/'AS'/'MP' (optional; must be supplied together with family_size)\n  Together these compute the VA residual income guideline (38 CFR 36.4340(e)) in\n  va_residual_income_guideline: the dollar amount VA's tables require for this family size, region,\n  and loan amount (derived from proposed_home_price; not computable without it), plus the 38 CFR\n  36.4340(c)(3) review-waiver figure. Computed only for family_size 1-7 and a property_state among\n  the 50 states, DC, or PR (not GU, VI, AS, or MP; 38 CFR 36.4340(e) assigns no region to those\n  four); outside those bounds, or without proposed_home_price, va_residual_income_guideline.status\n  reads 'not_computable' with the reason instead. This block alone is a LOOKUP, not a verdict: it\n  never compares against your actual residual income by itself.\n\nmonthly_taxes_and_retirement_withholding: decimal >= 0 (optional; your federal, state, and FICA tax\n  withholding, PLUS any amount paid or withheld for retirement, monthly. 38 CFR 36.4340(f)(13)\n  treats these as one class of deduction from gross income.)\nmonthly_maintenance_and_utilities: decimal >= 0 (optional; estimated monthly maintenance and utilities\n  for the proposed property. 38 CFR 36.4340 calls for a realistic estimate of this figure for the\n  property and local utility rates and sets no numeric multiplier itself, but VA underwriting\n  guidance (the Lender's Handbook, Pamphlet 26-7) publishes a per-square-foot multiplier for this\n  same estimate; applying it needs the property's square footage, which this tool does not\n  currently collect, so Senaro has no default to offer here and you supply the aggregate monthly\n  amount instead.)\n  Supplying BOTH of these, together with proposed_home_price and a computable family_size/\n  property_state above, computes qualification.va.residual_income_comparison: your ACTUAL monthly\n  residual income, its ratio to the va_residual_income_guideline figure, and whether\n  residual_income_meets_review_waiver_margin (residual income at or above 120% of the guideline) is\n  met. The shelter expense used here excludes any PMI (VA loans carry no monthly PMI; 38 CFR\n  36.4313(e) sets a funding fee instead, commonly financed into the loan -- see\n  va_funding_fee_financed_monthly below). 38 CFR 36.4340(c)(3)'s review-waiver condition is\n  CONJUNCTIVE: it also requires the back-end debt-to-income ratio (qualification.va.your_back_end)\n  to exceed 41%, which this field does not by itself confirm -- check both fields together. Even\n  when both hold, (c)(3) only WAIVES a second-level review requirement; it is not itself an\n  approval. Whether this file is actually approved is an underwriting determination Senaro does\n  not make and no input combination here determines. Missing any one of the needed inputs reads\n  qualification.va.residual_income_comparison.status 'not_computable' with every reason named.\nva_funding_fee_financed_monthly: decimal >= 0 (optional; the additional monthly payment from\n  financing a VA funding fee into the loan balance, if any. 38 CFR 36.4313(e)'s applicable\n  percentage depends on down payment, prior VA-loan use, and service category, none of which\n  Senaro collects, so there is no default; if omitted while any VA figure that depends on it is\n  produced -- qualification.va.your_back_end and its verdict, any what_if VA ratio,\n  what_if.max_affordable_home.va, or the residual-income comparison above -- a\n  VA_FUNDING_FEE_NOT_MODELED warning discloses that no fee is assumed. The home-price-reduction\n  what_if scenario's hypothetical price is fixed before the fee is considered, so a supplied fee\n  is scaled to that EXACT hypothetical loan size, since 38 CFR 36.4313(e)'s fee is a percentage\n  of loan principal. what_if.max_affordable_home.va is a two-pass approximation instead, so its\n  supplied fee is scaled to an ESTIMATE of the hypothetical loan size, not the exact figure\n  reported; the PMI, tax, and insurance reservation itself is an EXACT closed-form solve, so\n  only this fee-scaling step is approximate. Without proposed_home_price there is no reference\n  loan size to scale from either way, so the raw fee is reserved unscaled instead and a\n  VA_FUNDING_FEE_NOT_SCALED warning discloses it --\n  mutually exclusive with VA_FUNDING_FEE_NOT_MODELED by construction, since one requires the fee\n  omitted and the other requires it supplied.)"
        -}
      • addedInput schema / properties / transaction_purpose
        Added value: +{
        +  "default": null,
        +  "description": "Mortgage transaction purpose: 'purchase', 'refinance', or 'streamlined_assist'. Optional, defaults to 'purchase' when omitted. Affects USDA only, and only what is disclosed. USDA's 32% PITI and 44% Total Debt figures are purchase-transaction waiver conditions (HB-1-3555 11.3.A.2), disclosed rather than applied as ceilings: Senaro cannot observe how the file is underwritten, so a USDA ratio overage is never 'ineligible' on any transaction purpose. For a refinance, 11.3.B states debt ratios 'are not limited to the maximum purchase debt ratio thresholds', so where the note fires it names both figures and states that neither applies. Streamlined-assist refinances require no debt ratio calculation at all. Conventional, FHA and VA are unaffected.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / va_funding_fee_financed_monthly
        Added value: +{
        +  "default": null,
        +  "description": "The additional monthly payment from financing a VA funding fee into the loan balance, if any. 38 CFR 36.4313(e)'s applicable percentage depends on down payment, prior VA-loan use, and service category, none of which Senaro collects, so there is no default. If omitted while any VA figure that depends on it is produced, meaning qualification.va.your_back_end and its verdict, any what_if VA ratio, what_if.max_affordable_home.va, or qualification.va.residual_income_comparison, a VA_FUNDING_FEE_NOT_MODELED warning discloses that no fee is assumed. The home-price-reduction what_if scenario's hypothetical price is fixed before the fee is considered, so a supplied fee is scaled to that EXACT hypothetical loan size, since 38 CFR 36.4313(e)'s fee is a percentage of loan principal. what_if.max_affordable_home.va is a two-pass approximation instead, so its supplied fee is scaled to an ESTIMATE of the hypothetical loan size, not the exact figure reported; the PMI, tax, and insurance reservation itself is an EXACT closed-form solve, so only this fee-scaling step is approximate. Without proposed_home_price there is no reference loan size to scale from either way, so the raw fee is reserved unscaled instead, and a VA_FUNDING_FEE_NOT_SCALED warning discloses it. This is mutually exclusive with VA_FUNDING_FEE_NOT_MODELED by construction, since one requires the fee omitted and the other requires it supplied. Optional; must be zero or more.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • removedInput schema / required
        Removed value: -[
        -  "toolArguments"
        -]
    • Changedcalculate_emergency_fund9 fields changed
      • addedInput schema / properties / chart_title
        Added value: +{
        +  "default": null,
        +  "description": "Override for the chart title. Must not contain em-dashes or en-dashes. Max 120 characters. Optional.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / current_savings
        Added value: +{
        +  "default": null,
        +  "description": "What you have in liquid emergency-accessible savings today. Decimal from 0 to $1,000,000,000. Optional; defaults to 0 when omitted.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / dependents
        Added value: +{
        +  "default": null,
        +  "description": "Number of dependents. Integer from 0 to 20. +1 target month per dependent, capped at +3. Optional; defaults to 0 when omitted.",
        +  "type": [
        +    "integer",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / has_dual_income
        Added value: +{
        +  "default": null,
        +  "description": "When true, partner income reduces the buffer by 1 month. Optional; defaults to false when omitted.",
        +  "type": [
        +    "boolean",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / job_stability
        Added value: +{
        +  "default": null,
        +  "description": "'stable_w2' | 'variable_income' | 'self_employed' | 'between_jobs'. Drives the target-months multiplier: stable_w2 salaried W-2 with consistent paycheck (+0 months); variable_income W-2 with commission/bonus/shift-based pay (+1 month); self_employed 1099 contractor/freelancer/sole proprietor (+3 months); between_jobs actively job hunting, no current paycheck (+5 months). Optional; defaults to 'stable_w2' when omitted.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / monthly_essential_expenses
        Added value: +{
        +  "description": "Rent/mortgage + utilities + food + insurance + minimum debt payments. NOT discretionary spending. Decimal from $0.01 to $1,000,000,000. REQUIRED, no default.",
        +  "type": "number"
        +}
      • addedInput schema / properties / monthly_savings_capacity
        Added value: +{
        +  "default": null,
        +  "description": "What you can contribute toward the gap each month. Drives months_to_target. Decimal, either 0 or from $0.01 to $1,000,000,000. Optional; defaults to 0 when omitted.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • removedInput schema / properties / toolArguments
        Removed value: -{
        -  "description": "JSON object with these parameters:\n\nmonthly_essential_expenses: decimal > 0 (REQUIRED). Rent/mortgage + utilities + food + insurance + minimum debt payments. NOT discretionary spending.\ncurrent_savings: decimal >= 0 (optional, default 0). What you have in liquid emergency-accessible savings today.\njob_stability: 'stable_w2' | 'variable_income' | 'self_employed' | 'between_jobs' (optional, default 'stable_w2'). Drives the target-months multiplier.\n  stable_w2: salaried W-2 with consistent paycheck (+0 months).\n  variable_income: W-2 with commission/bonus/shift-based pay (+1 month).\n  self_employed: 1099 contractor / freelancer / sole proprietor (+3 months).\n  between_jobs: actively job hunting, no current paycheck (+5 months).\ndependents: int >= 0 (optional, default 0). +1 target month per dependent, capped at +3.\nhas_dual_income: bool (optional, default false). When true, partner income reduces the buffer by 1 month.\nmonthly_savings_capacity: decimal >= 0 (optional, default 0). What you can contribute toward the gap each month. Drives months_to_target.\n\nTarget months are clamped to [3, 12]: never below the 3-month personal-finance minimum, never above 12 (excess cash is better invested than parked).\nchart_title: string (optional) override for the chart title. Must not contain em-dashes or en-dashes. Max 120 characters."
        -}
      • changedInput schema / required
        Previous value: -[
        -  "toolArguments"
        -]New value: +[
        +  "monthly_essential_expenses"
        +]
    • Changedcalculate_loan_payoff10 fields changed
      • addedInput schema / properties / annual_rate_pct
        Added value: +{
        +  "description": "Loan annual percentage rate, e.g. 6.5 not 0.065. Decimal from 0 to 100. REQUIRED, no default.",
        +  "type": "number"
        +}
      • addedInput schema / properties / chart_bucket
        Added value: +{
        +  "default": null,
        +  "description": "Time-axis granularity of analysis.buckets[]: 'auto', 'monthly', 'quarterly', 'yearly', or 'biennial'. Optional; defaults to 'auto', which selects by term length (<=24 months: monthly; <=60: quarterly; <=360: yearly; else biennial).",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / chart_title
        Added value: +{
        +  "default": null,
        +  "description": "Override for the chart title. Must not contain em-dashes or en-dashes. Max 120 characters. Optional.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / extra_monthly_payment
        Added value: +{
        +  "default": null,
        +  "description": "Extra principal paid each month beyond the regular payment. Decimal, at least 0. Optional; defaults to 0 when omitted.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / loan_type
        Added value: +{
        +  "default": null,
        +  "description": "'personal', 'auto', 'student', or 'mortgage'. Optional; defaults to 'personal' when omitted.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / output
        Added value: +{
        +  "default": null,
        +  "description": "'summary' (default) or 'capture'. 'inline' is not a valid value for this tool on any transport. 'summary' returns the full payload including analysis.buckets[] inline. 'capture' writes the full payload to ~/.senaro/captures/ and returns capture_ref, stripping analysis from the wire response; available on the local stdio transport only, since the hosted HTTP transport rejects 'capture' and names 'summary' as the only alternative.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / principal
        Added value: +{
        +  "description": "Loan principal to amortize. Decimal, greater than 0. REQUIRED, no default.",
        +  "type": "number"
        +}
      • addedInput schema / properties / term_months
        Added value: +{
        +  "description": "Loan term. Integer, greater than 0. REQUIRED, no default.",
        +  "type": "integer"
        +}
      • removedInput schema / properties / toolArguments
        Removed value: -{
        -  "description": "JSON object with these parameters:\n\nprincipal: decimal > 0 (REQUIRED)\nannual_rate_pct: decimal 0-100 as percentage, e.g. 6.5 not 0.065 (REQUIRED)\nterm_months: int > 0 (REQUIRED)\nloan_type: 'personal' | 'auto' | 'student' | 'mortgage' (optional, default 'personal')\nextra_monthly_payment: decimal >= 0 (optional, default 0)\nchart_bucket: 'auto' | 'monthly' | 'quarterly' | 'yearly' | 'biennial' (optional, default 'auto')\n  Controls the time-axis granularity of analysis.buckets[]. 'auto' selects based on term length:\n    term <= 24 months -> monthly; <= 60 -> quarterly; <= 360 -> yearly; > 360 -> biennial.\noutput: 'summary' | 'capture' (optional, default 'summary'). 'inline' is not a valid value for this tool on any transport.\n  'summary': full payload including analysis.buckets[] returned inline.\n  'capture': full payload written to ~/.senaro/captures/; capture_ref URI returned;\n             analysis field stripped from wire response. Use when passing to a chart tool.\n             Available on the local stdio transport only; the hosted HTTP transport rejects\n             'capture' with a structured error naming 'summary' as the only valid alternative.\nchart_title: string (optional) override for the chart title. Must not contain em-dashes or en-dashes. Max 120 characters."
        -}
      • changedInput schema / required
        Previous value: -[
        -  "toolArguments"
        -]New value: +[
        +  "principal",
        +  "annual_rate_pct",
        +  "term_months"
        +]
    • Changedcalculate_opportunity_cost8 fields changed
      • addedInput schema / properties / annual_return_pct
        Added value: +{
        +  "description": "Effective annual investment return, e.g. 8 not 0.08; the monthly compounding step is (1+pct/100)^(1/12)-1. Decimal from 0 to 100. REQUIRED, no default.",
        +  "type": "number"
        +}
      • addedInput schema / properties / chart_title
        Added value: +{
        +  "default": null,
        +  "description": "Override for the chart title. Must not contain em-dashes or en-dashes. Max 120 characters. Optional.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / label
        Added value: +{
        +  "default": null,
        +  "description": "What the spending is, e.g. 'coffee' or 'streaming subscriptions'. Optional.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / monthly_spending
        Added value: +{
        +  "description": "Monthly spending to price against investing. Decimal, at least $0.01. The opportunity cost of spending $0 is degenerate and is rejected. REQUIRED, no default.",
        +  "type": "number"
        +}
      • addedInput schema / properties / tax_bracket_pct
        Added value: +{
        +  "default": null,
        +  "description": "Tax bracket as a percentage, applied as a flat haircut to the investment gain. Decimal from 0 to 100. Optional.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • removedInput schema / properties / toolArguments
        Removed value: -{
        -  "description": "JSON object with these parameters:\n\nmonthly_spending: decimal, at least 0.01 (REQUIRED). The opportunity cost of spending $0 is degenerate and is rejected.\nannual_return_pct: decimal 0-100 as percentage, e.g. 8 not 0.08, an EFFECTIVE ANNUAL rate; the monthly compounding step is (1+pct/100)^(1/12)-1 (REQUIRED)\nyears: int > 0 (REQUIRED)\nlabel: string (optional). e.g. 'coffee' or 'streaming subscriptions'\ntax_bracket_pct: decimal 0-100 (optional)\nchart_title: string (optional) override for the chart title. Must not contain em-dashes or en-dashes. Max 120 characters."
        -}
      • addedInput schema / properties / years
        Added value: +{
        +  "description": "Time horizon in years. Integer, greater than 0. REQUIRED, no default.",
        +  "type": "integer"
        +}
      • changedInput schema / required
        Previous value: -[
        -  "toolArguments"
        -]New value: +[
        +  "monthly_spending",
        +  "annual_return_pct",
        +  "years"
        +]
    • Changedcalculate_runway9 fields changed
      • addedInput schema / properties / annual_inflation_rate_pct
        Added value: +{
        +  "default": null,
        +  "description": "Annual expense-inflation rate as a percentage. When greater than 0, expenses grow each month by the monthly equivalent of this annual rate; when 0, expenses are constant. Decimal, at least 0 and less than 100. Optional; defaults to 0 when omitted.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / chart_title
        Added value: +{
        +  "default": null,
        +  "description": "Reserved for the chart pipeline; validated but not yet used. Must not contain em-dashes or en-dashes. Max 120 characters. Optional.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / current_savings
        Added value: +{
        +  "description": "Liquid fund available to draw down. Decimal from 0 to 1,000,000,000; 0 is valid (an already-empty fund). REQUIRED, no default.",
        +  "type": "number"
        +}
      • addedInput schema / properties / monthly_essential_expenses
        Added value: +{
        +  "description": "Monthly outflow at the chosen expense basis. Decimal, greater than 0 and at most 1,000,000,000. REQUIRED, no default.",
        +  "type": "number"
        +}
      • addedInput schema / properties / monthly_inflows
        Added value: +{
        +  "default": null,
        +  "description": "Ongoing monthly income that continues during the drawdown (partner income, side income, unemployment benefit, severance paid monthly), modeled as a flat monthly stream; a one-time severance lump and time-limited benefits are not modeled in v1. Decimal from 0 to 1,000,000,000. Optional; defaults to 0 when omitted.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / output
        Added value: +{
        +  "default": null,
        +  "description": "'summary' (default), 'inline', or 'capture'. 'summary' returns headline scalars (depletion month or does-not-deplete) and citations with the schedule stripped. 'inline' returns the full payload including the month-by-month schedule[] for chart rendering. 'capture' writes the full payload to ~/.senaro/captures/ and returns capture_ref; available on the local stdio transport only, since the hosted HTTP transport rejects 'capture' and names 'summary' and 'inline' as the valid alternatives.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • removedInput schema / properties / toolArguments
        Removed value: -{
        -  "description": "JSON object with parameters:\n\nREQUIRED:\n  current_savings: decimal >= 0. Liquid fund available to draw down. 0 is valid (already-empty fund).\n  monthly_essential_expenses: decimal > 0. Monthly outflow at the chosen expense basis.\n\nOPTIONAL:\n  monthly_inflows: decimal >= 0 (default 0). Ongoing monthly income that continues during the\n    drawdown (partner income, side income, unemployment benefit, severance paid monthly). Modeled\n    as a flat monthly stream; a one-time severance lump and time-limited benefits are not modeled in v1.\n  annual_inflation_rate_pct: decimal [0, 100) (default 0). When > 0, expenses grow each month by the\n    monthly-equivalent of this annual rate. When 0, expenses are constant.\n  use_essential_expenses: bool (default true). true = essential-only basis (survival runway);\n    false = total-spend basis (current-pace runway). Labels the reported expense_basis.\n  chart_title: string (optional). Reserved for the chart pipeline. Must not contain em-dashes or en-dashes. Max 120 characters.\n\nENVELOPE:\n  output: 'summary' (default) | 'inline' | 'capture'\n  summary: headline scalars (depletion month / does-not-deplete) + citations; the schedule is stripped.\n  inline: full payload including the month-by-month schedule[] (for chart rendering).\n  capture: full payload written to ~/.senaro/captures/; capture_ref URI returned.\n    Available on the local stdio transport only; the hosted HTTP transport rejects 'capture'\n    with a structured error naming 'summary' and 'inline' as the valid alternatives."
        -}
      • addedInput schema / properties / use_essential_expenses
        Added value: +{
        +  "default": null,
        +  "description": "Expense basis: true means essential-only spending (survival runway), false means total spending (current-pace runway). Labels the reported expense_basis. Optional; defaults to true when omitted.",
        +  "type": [
        +    "boolean",
        +    "null"
        +  ]
        +}
      • changedInput schema / required
        Previous value: -[
        -  "toolArguments"
        -]New value: +[
        +  "current_savings",
        +  "monthly_essential_expenses"
        +]
    • Changedcompare_debt_consolidation19 fields changed
      • addedInput schema / properties / bt_apr_pct
        Added value: +{
        +  "default": null,
        +  "description": "Balance-transfer APR as a percentage, 0-100, e.g. 0 for a 0% promo. Required when include_balance_transfer is true and bt_offers is omitted. bt_offers, when supplied, overrides this and the other three bt_* scalars below. The 0-100 range applies only when include_balance_transfer is true and bt_offers is omitted; a non-number is rejected in every case.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / bt_fee_pct
        Added value: +{
        +  "default": null,
        +  "description": "Balance-transfer fee as a percentage of the transferred balance, 0-10. Optional; defaults to 3.0 when omitted. bt_offers, when supplied, overrides this. The 0-10 range applies only when include_balance_transfer is true and bt_offers is omitted; a non-number is rejected in every case.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / bt_manual_transfers
        Added value: +{
        +  "default": null,
        +  "description": "Exact per-card transfer amounts. Required when bt_transfer_strategy is 'manual'; each entry's card_name must match a name in cards[]. Optional otherwise; a JSON null is treated as omitted, the same as leaving the field out.",
        +  "items": {
        +    "properties": {
        +      "amount": {
        +        "description": "Dollar amount to transfer from this card, greater than $0, up to $1,000,000,000. Required per entry.",
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      },
        +      "card_name": {
        +        "description": "Card name; must match a name in cards[]. Required per entry. Max 120 characters.",
        +        "type": [
        +          "string",
        +          "null"
        +        ]
        +      }
        +    },
        +    "type": [
        +      "object",
        +      "null"
        +    ]
        +  },
        +  "type": [
        +    "array",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / bt_offers
        Added value: +{
        +  "default": null,
        +  "description": "Balance-transfer offers to compare head-to-head, 1 to 10, preferred over the four scalar bt_* fields above when comparing two or more offers: when supplied, bt_offers overrides bt_apr_pct, bt_promo_months, bt_regular_apr_pct, and bt_fee_pct, and their range checks are skipped. The response includes a balance_transfer_offers block with per-offer simulation results, selected_offer_index, selected_offer_label, selected_offer_reason, and all_offers_trap. Optional; a JSON null is rejected, unlike bt_manual_transfers and windfalls below, where a JSON null is treated as omitted.",
        +  "items": {
        +    "properties": {
        +      "apr_pct": {
        +        "description": "Offer APR as a percentage, 0-100, e.g. 0 for a 0% promo. Required per offer.",
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      },
        +      "fee_pct": {
        +        "description": "Balance-transfer fee for this offer, as a percentage of the transferred balance, 0-10. Optional; defaults to 3.0 when omitted.",
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      },
        +      "label": {
        +        "description": "Label for this offer. Optional; defaults to '<apr_pct>% / <promo_months>mo' when omitted. Max 120 characters.",
        +        "type": [
        +          "string",
        +          "null"
        +        ]
        +      },
        +      "promo_months": {
        +        "description": "This offer's promo period in months, 1-60. Optional; defaults to 18 when omitted.",
        +        "type": [
        +          "integer",
        +          "null"
        +        ]
        +      },
        +      "regular_apr_pct": {
        +        "description": "APR that applies after this offer's promo period ends, as a percentage, 0-100. Optional; defaults to 25.20 when omitted.",
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      }
        +    },
        +    "type": [
        +      "object",
        +      "null"
        +    ]
        +  },
        +  "type": [
        +    "array",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / bt_promo_months
        Added value: +{
        +  "default": null,
        +  "description": "Balance-transfer promo period in months, 1-60. Optional; defaults to 18 when omitted. bt_offers, when supplied, overrides this. The 1-60 range applies only when include_balance_transfer is true and bt_offers is omitted; a non-number is rejected in every case.",
        +  "type": [
        +    "integer",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / bt_regular_apr_pct
        Added value: +{
        +  "default": null,
        +  "description": "APR that applies after the promo period ends, as a percentage, 0-100. Optional; defaults to 25.20 when omitted. bt_offers, when supplied, overrides this. The 0-100 range applies only when include_balance_transfer is true and bt_offers is omitted; a non-number is rejected in every case.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / bt_transfer_limit
        Added value: +{
        +  "default": null,
        +  "description": "Cap on the total dollar amount transferred to the balance-transfer card, at least $0.01. When set, only this amount moves to the BT card; remaining balances stay on original cards, and a combined simulation runs both halves together, correctly redistributing freed minimum payments. Optional; when omitted, the entire balance is transferred (legacy behavior). Needs include_balance_transfer or bt_offers.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / bt_transfer_strategy
        Added value: +{
        +  "default": null,
        +  "description": "How to choose which balances move when bt_transfer_limit is less than your total debt: 'highest_apr_first' (transfer from highest-APR segments first, maximizes interest savings), 'highest_balance_first' (transfer largest balances first), or 'manual' (use bt_manual_transfers to specify exact amounts per card). Optional; defaults to 'highest_apr_first' when omitted. Needs bt_transfer_limit.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / cards
        Added value: +{
        +  "description": "Credit cards to include, 1 to 20. Required: omitting the field answers contract_error MISSING_REQUIRED_FIELD, while an explicit JSON null answers parse_error (\"cards must be a JSON array.\"); the two are not the same rejection. segments and stop_spend_month are not supported by this tool and are rejected; a non-zero monthly_spend, annual_fee, or plan_fees_monthly is rejected too, but zero (including an explicit 0) is accepted and dropped for all three: this tool always reads the flat purchase_balance / cash_advance_balance fields on each card below, never segments[] balances, and card-level fees and ongoing spend are not modeled here. Those five fields are supported by calculate_cc_payoff and compare_strategies instead.",
        +  "items": {
        +    "properties": {
        +      "cash_advance_apr_pct": {
        +        "description": "Cash advance APR as a percentage, greater than 0, up to 100. Required when cash_advance_balance is greater than 0.",
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      },
        +      "cash_advance_balance": {
        +        "description": "Cash advance balance in dollars, $0 to $1,000,000,000. Optional; defaults to 0 when omitted.",
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      },
        +      "minimum_payment": {
        +        "description": "Minimum payment in dollars, $0 to $1,000,000,000. Optional; 0 or omitted auto-calculates the bank minimum. Used as the locked floor for the constant keep-cards payment: each card pays max(your minimum, the bank minimum), held constant and rolled forward as cards clear.",
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      },
        +      "name": {
        +        "description": "Card label, e.g. 'Chase Sapphire'. Optional; a card without a name is auto-numbered ('Card 1', 'Card 2', ...). Max 120 characters.",
        +        "type": [
        +          "string",
        +          "null"
        +        ]
        +      },
        +      "purchase_apr_pct": {
        +        "description": "Purchase APR as a percentage, 0-100, e.g. 22.99. Required per card.",
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      },
        +      "purchase_balance": {
        +        "description": "Purchase balance in dollars, $0 to $1,000,000,000. Required per card.",
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      }
        +    },
        +    "type": "object"
        +  },
        +  "type": "array"
        +}
      • addedInput schema / properties / chart_title
        Added value: +{
        +  "default": null,
        +  "description": "Override for the chart title. Optional; must not contain an em dash or en dash. Max 120 characters.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / consolidation_loan
        Added value: +{
        +  "description": "Loan terms for the consolidation option. Required: omitting the field answers contract_error MISSING_REQUIRED_FIELD, while an explicit JSON null answers parse_error (\"consolidation_loan must be an object.\"); the two are not the same rejection.",
        +  "properties": {
        +    "annual_rate_pct": {
        +      "description": "Annual interest rate for the consolidation loan, as a percentage, 0-36, e.g. 10.99. Required.",
        +      "type": [
        +        "number",
        +        "null"
        +      ]
        +    },
        +    "include_fee_in_principal": {
        +      "description": "Whether the origination fee is rolled into the loan principal rather than paid upfront. Optional; defaults to true when omitted. A JSON null is rejected; omit the field instead of sending null.",
        +      "type": [
        +        "boolean",
        +        "null"
        +      ]
        +    },
        +    "origination_fee_flat": {
        +      "description": "Flat-dollar origination fee, $0 to $10,000. Optional; defaults to 0 when omitted, and takes precedence over origination_fee_pct when it produces a larger fee. A JSON null is rejected; omit the field instead of sending null.",
        +      "type": [
        +        "number",
        +        "null"
        +      ]
        +    },
        +    "origination_fee_pct": {
        +      "description": "Origination fee as a percentage of loan principal, 0-10. Optional; defaults to 0 when omitted. A JSON null is rejected, unlike every other optional field on this tool; omit the field instead of sending null.",
        +      "type": [
        +        "number",
        +        "null"
        +      ]
        +    },
        +    "term_months": {
        +      "description": "Consolidation loan term in months, 12-84. Required.",
        +      "type": [
        +        "integer",
        +        "null"
        +      ]
        +    }
        +  },
        +  "type": "object"
        +}
      • addedInput schema / properties / current_strategy
        Added value: +{
        +  "default": null,
        +  "description": "Your current payoff strategy, compared against the consolidation loan / balance-transfer alternative: 'avalanche' or 'snowball'. Optional; defaults to 'avalanche' when omitted. Case-sensitive.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / extra_monthly_payment
        Added value: +{
        +  "default": null,
        +  "description": "Extra monthly payment in dollars, $0 to $1,000,000,000, applied on top of the required minimums. Optional; defaults to 0 when omitted. fixed_payments is not a parameter of this tool, unlike calculate_cc_payoff: the keep-cards baseline is always simulated under the canonical constant rolled-forward payment (see comparison_basis in the response); supplying fixed_payments returns an unknown_parameter error.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / full_schedule
        Added value: +{
        +  "default": null,
        +  "description": "Whether to return the full month-by-month amortization schedule instead of the compact default. Optional; defaults to false when omitted.",
        +  "type": [
        +    "boolean",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / include_balance_transfer
        Added value: +{
        +  "default": null,
        +  "description": "Whether to run a single-offer balance-transfer scenario using the four scalar bt_* fields below. Optional; defaults to false when omitted. Ignored once bt_offers is supplied; use bt_offers when comparing two or more offers.",
        +  "type": [
        +    "boolean",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / output
        Added value: +{
        +  "default": null,
        +  "description": "Response verbosity: 'summary' (default) | 'inline' | 'capture'. summary: compact response with a data_preview block; no heavy array exists on this response today, so summary and inline are currently identical in content. inline: full payload (currently identical to summary; will diverge once a heavy array is added for chart rendering). capture: full payload written to this server's local disk for the chart-render pipeline; capture_ref URI returned. Available on the local stdio transport only; the hosted HTTP transport rejects 'capture' with a structured error naming 'summary' and 'inline' as the valid alternatives. Optional; defaults to 'summary' when omitted.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • removedInput schema / properties / toolArguments
        Removed value: -{
        -  "description": "JSON object with these parameters:\n\ncards (REQUIRED array, max 20):\n  - name: string (optional)\n  - purchase_balance: decimal >= 0 (REQUIRED)\n  - purchase_apr_pct: decimal 0-100 as percentage, e.g. 22.99 (REQUIRED)\n  - cash_advance_balance: decimal >= 0 (optional, default 0)\n  - cash_advance_apr_pct: decimal > 0, <= 100 (required if cash_advance_balance > 0)\n  - minimum_payment: decimal >= 0 (optional, 0 = auto-calculate). Used as the locked floor for the constant keep-cards payment: each card pays max(your minimum, the bank minimum), held constant and rolled forward as cards clear.\n  - segments: NOT SUPPORTED by this tool (rejected with a parse_error if supplied). This tool always reads the flat purchase_balance / cash_advance_balance fields above, never segments[] balances; supply those instead. segments[] input is supported by calculate_cc_payoff and compare_strategies.\n  - monthly_spend / stop_spend_month: NOT SUPPORTED by this tool (rejected with a validation error if supplied). Ongoing spend on cards a consolidation loan or transfer just paid off is not modeled by any arm here; supported by calculate_cc_payoff and compare_strategies.\n\nconsolidation_loan (REQUIRED object):\n  - annual_rate_pct: decimal 0-36 as percentage, e.g. 10.99 (REQUIRED)\n  - term_months: int 12-84 (REQUIRED)\n  - origination_fee_pct: decimal 0-10 (optional, default 0)\n  - origination_fee_flat: decimal (optional, default 0, takes precedence if > pct-based fee)\n  - include_fee_in_principal: bool (optional, default true)\n\nextra_monthly_payment: decimal >= 0 (optional, default 0)\ncurrent_strategy: 'avalanche' | 'snowball' (optional, default 'avalanche')\n(Note: fixed_payments is NOT a parameter of this tool, unlike calculate_cc_payoff. The keep-cards baseline is always simulated under the canonical constant rolled-forward payment, see comparison_basis in the response. Passing fixed_payments returns an unknown_parameter error.)\n\nBalance transfer, SINGLE OFFER (legacy, all optional):\ninclude_balance_transfer: bool (default false)\nbt_apr_pct: decimal 0-100 (required if include_balance_transfer: true, use 0 for 0% promo)\nbt_promo_months: int 1-60 (optional, default 18)\nbt_regular_apr_pct: decimal 0-100 (optional, default 25.20)\nbt_fee_pct: decimal 0-10 (optional, default 3.0)\n\nBalance transfer, MULTI-OFFER (preferred when comparing two or more offers):\nbt_offers: array of objects (max 10), when supplied, takes precedence over the scalar bt_* fields\n  Each object:\n  - apr_pct: decimal 0-100 (REQUIRED)\n  - promo_months: int 1-60 (optional, default 18)\n  - regular_apr_pct: decimal 0-100 (optional, default 25.20)\n  - fee_pct: decimal 0-10 (optional, default 3.0)\n  - label: string (optional, defaults to \"<apr_pct>% / <months>mo\")\nThe response includes a balance_transfer_offers block with per-offer simulation\nresults, selected_offer_index, selected_offer_label, selected_offer_reason, and all_offers_trap.\n\nPartial balance transfer (use when the BT offer has a transfer limit < your total debt):\nbt_transfer_limit: decimal >= 0.01 (optional), cap on total transferred amount.\n  When set, only this amount moves to the BT card; remaining balances stay on original cards.\n  A combined simulation runs both halves together, correctly redistributing freed minimum payments.\n  When omitted, the entire balance is transferred (legacy behavior).\nbt_transfer_strategy: 'highest_apr_first' | 'highest_balance_first' | 'manual' (optional, default 'highest_apr_first')\n  highest_apr_first: transfer from highest-APR segments first (maximizes interest savings)\n  highest_balance_first: transfer largest balances first\n  manual: use bt_manual_transfers to specify exact amounts per card\nbt_manual_transfers: array of objects (required when bt_transfer_strategy='manual')\n  Each object:\n  - card_name: string (must match a card name in cards[])\n  - amount: decimal > 0\n\nfull_schedule: bool (optional, default false, compact schedule by default)\n\nwindfalls: array of one-time principal payments (optional, default empty, max 12). Same shape as calculate_cc_payoff. Each item:\n  - month: int >= 0 (REQUIRED). 0 means applied before month 1's interest. N >= 1 applies at the END of calendar month N.\n  - amount: decimal > 0 (REQUIRED).\n  - label: string (optional). e.g. 'Tax refund', 'Year-end bonus'.\nWhen non-empty the response adds windfalls_applied[] and windfalls_unused[] on keep_cards, on each balance_transfer scenario (and on each offer in balance_transfer_offers.offers[]), on consolidation_loan (single-row per windfall since the loan is a single-balance instrument), and on optimized_consolidation.keep_subset. All scenarios use with-windfalls totals so cross-option ranking stays apples-to-apples.\n\nchart_title: string (optional) override for the chart title. Must not contain em-dashes or en-dashes. Max 120 characters.\n\nENVELOPE:\n  output: 'summary' (default) | 'inline' | 'capture'\n  summary: compact response with a data_preview block. No heavy array exists on this response today, so summary and inline are currently identical in content; the envelope is wired ahead of the future chart-render pipeline.\n  inline: full payload (currently identical to summary; will diverge once a heavy array is added for chart rendering).\n  capture: full payload written to ~/.senaro/captures/; capture_ref URI returned.\n    Available on the local stdio transport only; the hosted HTTP transport rejects 'capture'\n    with a structured error naming 'summary' and 'inline' as the valid alternatives."
        -}
      • addedInput schema / properties / windfalls
        Added value: +{
        +  "default": null,
        +  "description": "One-time principal payments, at most 12, same shape as calculate_cc_payoff. Optional; a JSON null is treated as omitted, the same as leaving the field out. When non-empty, the response adds windfalls_applied[] and windfalls_unused[] on keep_cards, on each balance_transfer scenario (and on each offer in balance_transfer_offers.offers[]), on consolidation_loan (single-row per windfall since the loan is a single-balance instrument), and on optimized_consolidation.keep_subset. All scenarios use with-windfalls totals so cross-option ranking stays apples-to-apples.",
        +  "items": {
        +    "properties": {
        +      "amount": {
        +        "description": "Dollar amount of the windfall, greater than 0. Required per entry.",
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      },
        +      "label": {
        +        "description": "Optional label for this windfall, e.g. 'Tax refund' or 'Year-end bonus'. Max 120 characters.",
        +        "type": [
        +          "string",
        +          "null"
        +        ]
        +      },
        +      "month": {
        +        "description": "Month index the windfall is applied, 0 or more. 0 means applied before month 1's interest; N >= 1 applies at the end of calendar month N. Required per entry.",
        +        "type": [
        +          "integer",
        +          "null"
        +        ]
        +      }
        +    },
        +    "type": [
        +      "object",
        +      "null"
        +    ]
        +  },
        +  "type": [
        +    "array",
        +    "null"
        +  ]
        +}
      • changedInput schema / required
        Previous value: -[
        -  "toolArguments"
        -]New value: +[
        +  "cards",
        +  "consolidation_loan"
        +]
    • Changedcompare_mortgage_terms22 fields changed
      • addedInput schema / properties / closing_cost_pct
        Added value: +{
        +  "default": null,
        +  "description": "Closing costs as a percentage of the loan amount, 0-20, points excluded. Optional; defaults to the Urban Institute loan-size regressive schedule (about 4.6% at a $97K loan down to about 1.4% at a $679K loan) when omitted. Pass 0 to model zero closing costs.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / down_payment_amount
        Added value: +{
        +  "default": null,
        +  "description": "Down payment in dollars. Optional; overrides down_payment_pct entirely, including its provenance row, when supplied. Must be at least $0 and strictly below home_price.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / down_payment_pct
        Added value: +{
        +  "default": null,
        +  "description": "Down payment as a percent of home_price, e.g. 20 for 20%. Optional; defaults to 20 when omitted. Range 0-100 applies only when down_payment_amount is absent; when down_payment_amount is supplied, down_payment_pct is ignored entirely, including in provenance.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / extra_monthly_payment
        Added value: +{
        +  "default": null,
        +  "description": "Extra principal payment in dollars, applied equally to BOTH options every month. Optional; defaults to 0 when omitted. Must be zero or more and no more than $1,000,000,000.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / filing_status
        Added value: +{
        +  "default": null,
        +  "description": "Tax filing status: 'single', 'married', or 'head_of_household'. Optional; matched case-insensitively.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / full_schedule
        Added value: +{
        +  "default": null,
        +  "description": "Whether to return the full month-by-month amortization schedule instead of the compact default. Optional; defaults to false when omitted.",
        +  "type": [
        +    "boolean",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / hoa_monthly
        Added value: +{
        +  "default": null,
        +  "description": "Monthly homeowners association dues in dollars. Optional; defaults to 0 when omitted. Must be zero or more and no more than $1,000,000,000.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / home_insurance_annual
        Added value: +{
        +  "default": null,
        +  "description": "Annual home insurance in dollars. Optional; defaults to 0 when omitted. Must be zero or more.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / home_price
        Added value: +{
        +  "description": "Home purchase price in dollars. Required. Must be positive and no more than $1,000,000,000.",
        +  "type": "number"
        +}
      • addedInput schema / properties / invest_the_difference
        Added value: +{
        +  "default": null,
        +  "description": "Whether both options deploy the same total budget every month: the higher option's P&I plus any extra_monthly_payment plus the month-1 PMI both carry. The cheaper-mortgage holder invests the payment gap each month; an option that stops paying PMI earlier invests the freed cash; the option with the lower upfront points cost invests the difference at month 0; after payoff the full budget goes to investments. See comparison_basis in the response. Optional; defaults to true when omitted.",
        +  "type": [
        +    "boolean",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / investment_return_pct
        Added value: +{
        +  "default": null,
        +  "description": "Assumed investment return on the invested payment gap, as a percentage, an EFFECTIVE ANNUAL rate; the monthly compounding step is (1+pct/100)^(1/12)-1. Optional; defaults to the cited Senaro long-run S&P 500 nominal return, about 10%, when omitted. Range 0-30.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / option_a
        Added value: +{
        +  "description": "The first fixed-rate mortgage option to compare. Required.",
        +  "properties": {
        +    "annual_rate_pct": {
        +      "description": "Annual interest rate for this option, as a percentage, 0-20, e.g. 6.25. Required.",
        +      "type": [
        +        "number",
        +        "null"
        +      ]
        +    },
        +    "is_arm": {
        +      "description": "Whether this option is an adjustable-rate mortgage. Optional; defaults to false when omitted. ARM analysis is not yet supported: true on either option is rejected.",
        +      "type": [
        +        "boolean",
        +        "null"
        +      ]
        +    },
        +    "label": {
        +      "description": "Display label for this option, e.g. '30-year fixed'. Optional; auto-generated when omitted. At most 120 characters.",
        +      "type": [
        +        "string",
        +        "null"
        +      ]
        +    },
        +    "points": {
        +      "description": "Discount points bought, each equal to 1% of the loan amount, 0-4. Optional; defaults to 0 when omitted.",
        +      "type": [
        +        "number",
        +        "null"
        +      ]
        +    },
        +    "points_rate_reduction_pct": {
        +      "description": "Interest-rate reduction per discount point, in percentage points, 0-1.0. Optional; defaults to 0.25 when omitted.",
        +      "type": [
        +        "number",
        +        "null"
        +      ]
        +    },
        +    "term_years": {
        +      "description": "Mortgage term in years, 1-40. Required.",
        +      "type": [
        +        "integer",
        +        "null"
        +      ]
        +    }
        +  },
        +  "type": "object"
        +}
      • addedInput schema / properties / option_b
        Added value: +{
        +  "description": "The second fixed-rate mortgage option to compare, same shape as option_a. Required. Must differ from option_a on at least one of term_years, annual_rate_pct, or points.",
        +  "properties": {
        +    "annual_rate_pct": {
        +      "description": "Annual interest rate for this option, as a percentage, 0-20, e.g. 6.25. Required.",
        +      "type": [
        +        "number",
        +        "null"
        +      ]
        +    },
        +    "is_arm": {
        +      "description": "Whether this option is an adjustable-rate mortgage. Optional; defaults to false when omitted. ARM analysis is not yet supported: true on either option is rejected.",
        +      "type": [
        +        "boolean",
        +        "null"
        +      ]
        +    },
        +    "label": {
        +      "description": "Display label for this option, e.g. '30-year fixed'. Optional; auto-generated when omitted. At most 120 characters.",
        +      "type": [
        +        "string",
        +        "null"
        +      ]
        +    },
        +    "points": {
        +      "description": "Discount points bought, each equal to 1% of the loan amount, 0-4. Optional; defaults to 0 when omitted.",
        +      "type": [
        +        "number",
        +        "null"
        +      ]
        +    },
        +    "points_rate_reduction_pct": {
        +      "description": "Interest-rate reduction per discount point, in percentage points, 0-1.0. Optional; defaults to 0.25 when omitted.",
        +      "type": [
        +        "number",
        +        "null"
        +      ]
        +    },
        +    "term_years": {
        +      "description": "Mortgage term in years, 1-40. Required.",
        +      "type": [
        +        "integer",
        +        "null"
        +      ]
        +    }
        +  },
        +  "type": "object"
        +}
      • addedInput schema / properties / pmi_monthly
        Added value: +{
        +  "default": null,
        +  "description": "Monthly PMI (private mortgage insurance) in dollars, charged when down payment is below 20%. Optional; defaults to 0 when omitted. Must be zero or more and no more than $1,000,000,000.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / pmi_removal_ltv_pct
        Added value: +{
        +  "default": null,
        +  "description": "Loan-to-value percentage at which to model borrower-requested PMI removal, 50-100. Optional; when omitted, PMI is modeled as removed at the 78% HPA automatic-termination threshold instead. When the loan's own initial loan-to-value is above 80 percent, the range where PMI applies, a value at or above that initial LTV is rejected, because the requested removal point would already be met at the first payment, so no PMI would be modeled for any month of the loan. At or below 80 percent, no such check runs. Either way PMI also stops at the statutory amortization midpoint of each option's own term (12 U.S.C. §4901(7), §4902(c)), which this value cannot postpone.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / property_tax_annual
        Added value: +{
        +  "default": null,
        +  "description": "Annual property tax in dollars, for true monthly cost. Optional; defaults to 0 when omitted. Must be zero or more.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / standard_deduction
        Added value: +{
        +  "default": null,
        +  "description": "Standard deduction in dollars, compared against itemized mortgage-interest deductions. Optional; defaults to the IRS basic standard deduction for tax_year and filing_status when omitted (TY2026: 16100 single, 32200 married, 24150 head_of_household; TY2025: 15750 single, 31500 married, 23625 head_of_household; source Rev. Proc. 2025-32). Must be zero or more.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / tax_bracket_pct
        Added value: +{
        +  "default": null,
        +  "description": "Marginal tax bracket as a percentage, 0-50. Optional; enables after-tax investment return and mortgage interest deduction analysis when supplied. The after-tax comparison credits each option's annual deduction savings to its investments at year end.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / tax_year
        Added value: +{
        +  "default": null,
        +  "description": "Tax year, 2025 or 2026, selecting the IRS standard-deduction table for the itemize-vs-standard analysis. Optional; defaults to 2026 when omitted.",
        +  "type": [
        +    "integer",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / time_horizon_years
        Added value: +{
        +  "default": null,
        +  "description": "Number of years to project the invest-the-difference comparison, 1-40. Optional; defaults to the maximum of both options' term_years when omitted.",
        +  "type": [
        +    "integer",
        +    "null"
        +  ]
        +}
      • removedInput schema / properties / toolArguments
        Removed value: -{
        -  "description": "JSON object with these parameters:\n\nhome_price: decimal > 0, <= 1,000,000,000 (REQUIRED)\ndown_payment_pct: decimal 0-100 as percentage (optional, default 20)\ndown_payment_amount: decimal >= 0 (optional; overrides down_payment_pct if provided)\n\noption_a (REQUIRED object):\n  - label: string (optional; auto-generated if omitted)\n  - annual_rate_pct: decimal 0-20 as percentage, e.g. 6.25 (REQUIRED)\n  - term_years: int 1-40 (REQUIRED)\n  - is_arm: bool (optional, default false; ARM not yet supported)\n  - points: decimal 0-4 (optional, default 0; discount points bought, each = 1% of loan)\n  - points_rate_reduction_pct: decimal 0-1.0, PERCENTAGE POINTS reduction per point (optional, default 0.25)\n\noption_b (REQUIRED object):\n  - same shape as option_a\n  - Must differ from option_a on at least one of: term_years, annual_rate_pct, or points\n\nproperty_tax_annual: decimal >= 0 (optional, default 0; for true monthly cost)\nhome_insurance_annual: decimal >= 0 (optional, default 0)\npmi_monthly: decimal >= 0, <= 1,000,000,000 (optional, default 0; PMI if < 20% down)\npmi_removal_ltv_pct: decimal 50-100 (optional). When omitted, PMI is modeled as removed at the 78% HPA automatic-termination threshold. Provide a value (e.g. 80) to model borrower-requested removal at that LTV (whichever of 78% or your value is reached first). Either way PMI also stops at the statutory amortization midpoint of each option's own term (12 U.S.C. §4901(7), §4902(c)), which this value cannot postpone.\nhoa_monthly: decimal >= 0 (optional, default 0)\n\ninvest_the_difference: bool (optional, default true). When true, both options deploy the same total budget every month: the higher option's P&I plus any extra_monthly_payment plus the month-1 PMI both carry. The cheaper-mortgage holder invests the payment gap each month; an option that stops paying PMI earlier invests the freed cash; the option with the lower upfront points cost invests the difference at month 0; after payoff the full budget goes to investments. See comparison_basis in the response.\ninvestment_return_pct: decimal 0-30 as percentage, an EFFECTIVE ANNUAL rate; the monthly compounding step is (1+pct/100)^(1/12)-1 (optional; defaults to the cited Senaro long-run S&P 500 nominal return, about 10%)\ntax_bracket_pct: decimal 0-50 as percentage (optional; enables after-tax investment return and mortgage interest deduction analysis; the after-tax comparison credits each option's annual deduction savings to its investments at year end)\nstandard_deduction: decimal (optional. Defaults to the IRS basic standard deduction for tax_year + filing_status. TY2026: 16100 single, 32200 married, 24150 head_of_household. TY2025: 15750 single, 31500 married, 23625 head_of_household. Source: Rev. Proc. 2025-32)\nfiling_status: 'single' | 'married' | 'head_of_household' (optional)\ntax_year: int, 2025 or 2026 (optional, default 2026; selects the IRS standard-deduction table for the itemize-vs-standard analysis)\n\nextra_monthly_payment: decimal >= 0, <= 1,000,000,000 (optional, default 0; extra principal applied equally to BOTH options)\ntime_horizon_years: int 1-40 (optional; default: max of both term_years)\nfull_schedule: bool (optional, default false; compact amortization by default)\nclosing_cost_pct: decimal 0-20 as percentage of loan amount (optional, default: Urban Institute loan-size regressive schedule (~4.6% at $97K loan down to ~1.4% at $679K), points excluded. Pass 0 to model zero closing costs)"
        -}
      • changedInput schema / required
        Previous value: -[
        -  "toolArguments"
        -]New value: +[
        +  "home_price",
        +  "option_a",
        +  "option_b"
        +]
    • Changedcompare_strategies7 fields changed
      • addedInput schema / properties / apply_rate_cap
        Added value: +{
        +  "default": null,
        +  "description": "Whether to cap each card's APR at the regulatory ceiling before simulating. Optional; defaults to false when omitted.",
        +  "type": [
        +    "boolean",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / cards
        Added value: +{
        +  "description": "Credit cards to compare, 1 to 20, the same shape calculate_cc_payoff takes. Required: omitting the field answers contract_error MISSING_REQUIRED_FIELD, while an explicit JSON null answers parse_error (\"cards is required and must be an array.\"); the two are not the same rejection. Every card may carry segments[]. Max 100 segments total across all cards in the request. Per-card, per-type caps: 1 purchase, 1 cash_advance, 5 balance_transfer, 1 promotional, 10 installment_plan.",
        +  "items": {
        +    "properties": {
        +      "annual_fee": {
        +        "description": "Annual fee in dollars, $0 to $1,000,000,000. Optional; defaults to 0. Charged at month 1 and every twelfth month after, while the card carries a balance.",
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      },
        +      "cash_advance_apr_pct": {
        +        "description": "Cash advance APR as a percentage, greater than 0 and up to 100. Required when cash_advance_balance is greater than 0: cash advances have no promotional 0% product, so a 0% rate on a real advance balance is a data-entry error.",
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      },
        +      "cash_advance_balance": {
        +        "description": "Cash advance balance in dollars, $0 to $1,000,000,000. Optional; defaults to 0 when omitted. A POSITIVE value alongside a non-empty segments[] is rejected, the same way purchase_balance is. An explicit 0 is accepted.",
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      },
        +      "minimum_payment": {
        +        "description": "Minimum payment in dollars, $0 to $1,000,000,000 (optional, omit or set 0 to auto-calculate). Where it binds, the card is locked at max(minimum_payment, that card's issuer minimum computed at month 1), and that locked amount is paid every month. On calculate_cc_payoff it binds ONLY when fixed_payments = true; on the DEFAULT path (fixed_payments = false) it is NOT used, because the plan recomputes each card's issuer minimum from its current balance every month, and the response discloses that as MINIMUM_PAYMENT_NOT_BINDING. On compare_strategies there is no fixed_payments parameter and supplying one is rejected: the avalanche and snowball arms ALWAYS run locked, so the value DOES bind and moves both arms, while the minimum-payments-only baseline always recomputes and ignores it, disclosed as MINIMUM_PAYMENT_NOT_BINDING_IN_BASELINE.",
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      },
        +      "monthly_spend": {
        +        "description": "Recurring monthly purchase amount charged to this card's purchase balance before interest accrues, $0 to $50,000 per month. Optional; defaults to 0. Use the segment-level monthly_spend instead when supplying segments[].",
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      },
        +      "name": {
        +        "description": "Card label, e.g. 'Chase Sapphire', 120 characters or fewer. Optional; a card without a name is auto-numbered ('Card 1', 'Card 2', and so on). Echoed into per-card results and warning text. A name over the cap is replaced in error field paths by a key built from its zero-based card index, written as '#0 (name over cap)', '#1 (name over cap)' and so on.",
        +        "type": [
        +          "string",
        +          "null"
        +        ]
        +      },
        +      "plan_fees_monthly": {
        +        "description": "Recurring monthly card fee in dollars, $0 to $1,000,000,000, e.g. a pay-over-time plan fee. Optional; defaults to 0. Charged every month while the card carries a balance.",
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      },
        +      "purchase_apr_pct": {
        +        "description": "Purchase APR as a percentage, 0 to 100, e.g. 24.99 not 0.2499. Required when purchase_balance is greater than 0: an omitted APR would silently compute $0 interest.",
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      },
        +      "purchase_balance": {
        +        "description": "Purchase balance in dollars, $0 to $1,000,000,000. Required unless segments[] carries at least one segment: an omitted purchase_balance would silently report a $0, 0-month payoff. Supplying a POSITIVE value alongside a non-empty segments[] is rejected, since the two would be competing sources for the same balance. An explicit 0 is accepted.",
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      },
        +      "segments": {
        +        "description": "Per-segment breakdown of this card's balances. Optional; when it carries at least one segment it SUPERSEDES purchase_balance and cash_advance_balance, and a POSITIVE value in either of those is rejected. An empty segments[] is treated as absent, so purchase_balance is then required. At most 100 segments across the whole request. Per card, per type: 1 purchase, 1 cash_advance, 5 balance_transfer, 1 promotional, 10 installment_plan. Each item carries its own type, balance, rate and payment priority; see the type field on the item for the per-type field matrix.",
        +        "items": {
        +          "properties": {
        +            "apr_pct": {
        +              "default": null,
        +              "description": "Annual percentage rate for this segment, 0 to 100, e.g. 24.99 not 0.2499. Required when balance is greater than 0 on purchase, cash_advance, balance_transfer and promotional. On cash_advance it must be greater than 0 whenever a balance is carried. Must be 0 on installment_plan, which uses monthly_fee instead, so omitting it there is the correct shape.",
        +              "type": [
        +                "number",
        +                "null"
        +              ]
        +            },
        +            "balance": {
        +              "description": "Segment balance in dollars, $0 to $1,000,000,000. REQUIRED on every segment type: an omitted balance would silently report a $0, 0-month payoff, so it is rejected instead. An explicit 0 is valid, for example a paid-off balance_transfer segment kept for schedule continuity.",
        +              "type": "number"
        +            },
        +            "extra_payment_order": {
        +              "default": null,
        +              "description": "Extra-payment tie-break priority, 0 or more, where 0 means this segment never receives extra payment. Optional on every type, and the default for installment_plan. Only breaks a tie between segments sharing the exact same current APR, since the Credit CARD Act (TILA §164(b), 15 U.S.C. §1666c(b)) / 12 CFR 1026.53(a) always pays the highest-current-APR segment first regardless of this value.",
        +              "type": [
        +                "integer",
        +                "null"
        +              ]
        +            },
        +            "merchant_name": {
        +              "default": null,
        +              "description": "Label for an installment plan's merchant, 120 characters or fewer. Optional on installment_plan, ignored on every other type.",
        +              "type": [
        +                "string",
        +                "null"
        +              ]
        +            },
        +            "min_payment_order": {
        +              "default": null,
        +              "description": "Minimum-payment tie-break priority, 1 or more. Optional on every type. The minimum first pays any installment_plan's locked plan_payment_due in full, then the lowest-current-APR segment; this value only breaks a tie between segments sharing both the same current APR and the same installment-plan status.",
        +              "type": [
        +                "integer",
        +                "null"
        +              ]
        +            },
        +            "monthly_fee": {
        +              "default": null,
        +              "description": "Fixed fee charged each month while an installment plan's term is active, $0 or more and no greater than plan_payment_due. Optional on installment_plan. On every other type a positive value is rejected with FIELD_NOT_ALLOWED_HERE, while 0 and a negative value are accepted and dropped.",
        +              "type": [
        +                "number",
        +                "null"
        +              ]
        +            },
        +            "monthly_spend": {
        +              "default": null,
        +              "description": "Recurring monthly purchase amount posted to this segment before interest accrues, $0 to $50,000 per month. Optional on purchase. A negative value is rejected as NEGATIVE_BALANCE on every type. On every type other than purchase a positive value is rejected with FIELD_NOT_ALLOWED_HERE, since those balances do not accept new purchases; an explicit 0 is accepted.",
        +              "type": [
        +                "number",
        +                "null"
        +              ]
        +            },
        +            "plan_payment_due": {
        +              "default": null,
        +              "description": "Locked monthly payment for an installment plan, principal plus monthly_fee. Required on installment_plan, greater than $0, at least monthly_fee, and no more than $1,000,000,000. On every other type a positive value is rejected with FIELD_NOT_ALLOWED_HERE, while 0 and a negative value are accepted and dropped.",
        +              "type": [
        +                "number",
        +                "null"
        +              ]
        +            },
        +            "promo_expires_month": {
        +              "default": null,
        +              "description": "Month at which apr_pct flips to revert_apr_pct, 1 to 480. Required on balance_transfer and promotional. Ignored on purchase, cash_advance and installment_plan.",
        +              "type": [
        +                "integer",
        +                "null"
        +              ]
        +            },
        +            "remaining_payments": {
        +              "default": null,
        +              "description": "Payments left in an installment plan's stated term, 0 or more. Optional on installment_plan, ignored on every other type. 0 means the term has ALREADY ended and is valid with any balance, including a positive residual. From 1 up, the balance must be clearable within the stated term and must not be clearable a month early.",
        +              "type": [
        +                "integer",
        +                "null"
        +              ]
        +            },
        +            "revert_apr_pct": {
        +              "default": null,
        +              "description": "Rate this segment reverts to once its promo expires, 0 to 100, and at least apr_pct. Required on balance_transfer and promotional. Ignored on purchase and cash_advance. Rejected on installment_plan with FIELD_NOT_ALLOWED_HERE: an expired plan's residual keeps its existing rate rather than repricing.",
        +              "type": [
        +                "number",
        +                "null"
        +              ]
        +            },
        +            "stop_spend_month": {
        +              "default": null,
        +              "description": "Month index at which recurring spend stops, 0 or more. Read on purchase only, and range checked on every type. Omit to spend for the whole projection.",
        +              "type": [
        +                "integer",
        +                "null"
        +              ]
        +            },
        +            "type": {
        +              "description": "Segment type. REQUIRED on every segment: omitting it is rejected. Per-type field matrix. Every field below is classified for every segment type: REQUIRED (omitting it is rejected), OPTIONAL (accepted either way and read), IGNORED (accepted and never read for this type, though a value can still fail the field's own range check), or REJECTED (supplying a disallowed value is an error; the field's own rule below says exactly which values are disallowed). Value rules are stated separately from applicability, because a field can be accepted on a type and still be restricted there. The validators are the authority; this matrix describes them.\n- type: REQUIRED on every type. Values: one of purchase, cash_advance, balance_transfer, promotional, installment_plan.\n- balance: REQUIRED on every type. Values: $0 to $1,000,000,000; above $50,000 returns a HIGH_CARD_BALANCE warning, not an error.\n- apr_pct: REQUIRED on purchase, cash_advance, balance_transfer, promotional; OPTIONAL on installment_plan. Values: 0 to 100 as a percentage, e.g. 24.99 not 0.2499. The REQUIRED classification above applies when balance is greater than 0; a zero-balance segment may omit it. Must be greater than 0 on cash_advance carrying a balance. Must equal 0 on installment_plan, where omitting it is the correct shape.\n- revert_apr_pct: REQUIRED on balance_transfer, promotional; IGNORED on purchase, cash_advance; REJECTED on installment_plan. Values: 0 to 100, and at least apr_pct. Rejected on installment_plan with FIELD_NOT_ALLOWED_HERE: an expired plan's residual keeps its existing rate.\n- promo_expires_month: REQUIRED on balance_transfer, promotional; IGNORED on purchase, cash_advance, installment_plan. Values: 1 to 480.\n- monthly_fee: OPTIONAL on installment_plan; REJECTED on purchase, cash_advance, balance_transfer, promotional. Values: $0 or more, and no greater than plan_payment_due, on installment_plan. On every other type a POSITIVE value is rejected with FIELD_NOT_ALLOWED_HERE; 0 and a negative value are accepted and dropped there.\n- plan_payment_due: REQUIRED on installment_plan; REJECTED on purchase, cash_advance, balance_transfer, promotional. Values: greater than $0, at least monthly_fee, and no more than $1,000,000,000, on installment_plan. On every other type a POSITIVE value is rejected with FIELD_NOT_ALLOWED_HERE; 0 and a negative value are accepted and dropped there.\n- remaining_payments: OPTIONAL on installment_plan; IGNORED on purchase, cash_advance, balance_transfer, promotional. Values: 0 or more. 0 means the stated term has ALREADY ended and is valid with any balance. From 1 up, balance must be clearable within the stated term.\n- min_payment_order: OPTIONAL on every type. Values: 1 or more; breaks a tie only between segments sharing both the same current APR and the same installment-plan status.\n- extra_payment_order: OPTIONAL on every type. Values: 0 or more; 0 means skip. Breaks a tie only between segments sharing the exact same current APR.\n- merchant_name: OPTIONAL on installment_plan; IGNORED on purchase, cash_advance, balance_transfer, promotional. Values: 120 characters or fewer.\n- monthly_spend: OPTIONAL on purchase; REJECTED on cash_advance, balance_transfer, promotional, installment_plan. Values: $0 to $50,000 per month. A negative value is rejected as NEGATIVE_BALANCE on every type, purchase included. On every type other than purchase a POSITIVE value is rejected with FIELD_NOT_ALLOWED_HERE; an explicit 0 is accepted.\n- stop_spend_month: OPTIONAL on purchase; IGNORED on cash_advance, balance_transfer, promotional, installment_plan. Values: 0 or more; range checked on every type, read only on purchase.",
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "type",
        +            "balance"
        +          ],
        +          "type": "object"
        +        },
        +        "type": [
        +          "array",
        +          "null"
        +        ]
        +      },
        +      "stop_spend_month": {
        +        "description": "Month index at which this card's recurring spend stops, 0 or more. Optional; omit to spend for the whole projection.",
        +        "type": [
        +          "integer",
        +          "null"
        +        ]
        +      }
        +    },
        +    "type": "object"
        +  },
        +  "type": "array"
        +}
      • addedInput schema / properties / chart_title
        Added value: +{
        +  "default": null,
        +  "description": "Override for the chart title. Optional; must not contain an em dash or en dash. Max 120 characters.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / extra_monthly_payment
        Added value: +{
        +  "default": null,
        +  "description": "Extra monthly payment in dollars, $0 to $1,000,000,000, applied on top of the required minimums. Optional; defaults to 0 when omitted.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / output
        Added value: +{
        +  "default": null,
        +  "description": "Response verbosity: 'summary' (default) | 'inline' | 'capture'. summary returns the headline comparison with a data_preview block; inline returns the full payload; capture writes the full payload to this server's local disk for the chart-render pipeline and returns a capture_ref URI. capture is available on the local stdio transport only, and the hosted HTTP transport rejects it with a structured error naming 'summary' and 'inline' as the valid alternatives, since a capture_ref would be a dead link for a remote caller. Optional; defaults to 'summary' when omitted.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • removedInput schema / properties / toolArguments
        Removed value: -{
        -  "description": "JSON object with these parameters:\n\ncards (REQUIRED array, max 20): same format as calculate_cc_payoff\n  - name, purchase_balance, purchase_apr_pct (REQUIRED)\n  - cash_advance_balance, cash_advance_apr_pct (optional)\n  - minimum_payment (OPTIONAL, omit or 0 to auto-calculate)\n  - annual_fee: decimal >= 0 (optional, default 0). Charged at month 1 and every 12th month thereafter.\n  - plan_fees_monthly: decimal >= 0 (optional, default 0). Charged every month while the card has a balance.\n  - segments: array (OPTIONAL; when present, supersedes purchase_balance / cash_advance_balance). Max 100 segments total across all cards in the request. Per-card, per-type caps: 1 purchase, 1 cash_advance, 5 balance_transfer, 1 promotional, 10 installment_plan. Same per-segment shape as calculate_cc_payoff; see that tool's segments[] documentation for the full field list.\n\nextra_monthly_payment: decimal >= 0 (optional, default 0)\n(Note: fixed_payments is NOT a parameter of this tool, unlike calculate_cc_payoff. Avalanche and snowball are always compared under the canonical constant rolled-forward payment, see comparison_basis in the response. Passing fixed_payments returns an unknown_parameter error.)\napply_rate_cap: bool (optional, default false)\nchart_title: string (optional) override for the chart title. Must not contain em-dashes or en-dashes. Max 120 characters.\noutput: 'summary' | 'inline' | 'capture' (optional, default 'summary')\n  'capture' is available on the local stdio transport only; the hosted HTTP transport rejects it with a structured error naming 'summary' and 'inline' as the valid alternatives."
        -}
      • changedInput schema / required
        Previous value: -[
        -  "toolArguments"
        -]New value: +[
        +  "cards"
        +]
    • Changedcompound_interest11 fields changed
      • addedInput schema / properties / annual_rate_pct
        Added value: +{
        +  "description": "Annual interest rate as a percentage, e.g. 7 not 0.07. Decimal from 0 to 100. REQUIRED, no default.",
        +  "type": "number"
        +}
      • addedInput schema / properties / apply_default_inflation
        Added value: +{
        +  "default": null,
        +  "description": "When true and inflation_rate_pct is 0, auto-selects inflation via MacroeconomicDefaults.ForHorizon. Ignored when inflation_rate_pct is greater than 0 or rate_convention is 'real'. Optional; defaults to false when omitted.",
        +  "type": [
        +    "boolean",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / chart_title
        Added value: +{
        +  "default": null,
        +  "description": "Override for the chart title. Must not contain em-dashes or en-dashes. Max 120 characters. Optional.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / compounds_per_year
        Added value: +{
        +  "default": null,
        +  "description": "Compounding periods per year. Integer, greater than 0. Optional; defaults to 12 when omitted.",
        +  "type": [
        +    "integer",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / inflation_rate_pct
        Added value: +{
        +  "default": null,
        +  "description": "Inflation rate as a percentage. Decimal: -1 (deprecated suppress, same as 0), 0 (no real-value overlay), or greater than 0 and less than 100 (explicit percentage, e.g. 3.5). Optional; defaults to 0 when omitted.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / monthly_contribution
        Added value: +{
        +  "default": null,
        +  "description": "Additional contribution added each month. Decimal, at least 0. Optional; defaults to 0 when omitted.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / principal
        Added value: +{
        +  "description": "Starting balance. Decimal, at least 0. REQUIRED, no default.",
        +  "type": "number"
        +}
      • addedInput schema / properties / rate_convention
        Added value: +{
        +  "default": null,
        +  "description": "'nominal' or 'real'. 'real' unconditionally suppresses the inflation overlay; use when annual_rate_pct is already inflation-adjusted. Optional; defaults to 'nominal' when omitted.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • removedInput schema / properties / toolArguments
        Removed value: -{
        -  "description": "JSON object with these parameters:\n\nprincipal: decimal >= 0 (REQUIRED)\nannual_rate_pct: decimal 0-100 as percentage, e.g. 7 not 0.07 (REQUIRED)\nyears: int >= 1 (REQUIRED)\nmonthly_contribution: decimal >= 0 (optional, default 0)\ncompounds_per_year: int > 0 (optional, default 12)\ninflation_rate_pct: decimal >= 0 (optional, default 0 = NO real-value overlay; >0 = explicit percentage e.g. 3.5; -1 = deprecated suppress, same as 0)\napply_default_inflation: bool (optional, default false; when true and inflation_rate_pct=0, auto-selects inflation via MacroeconomicDefaults.ForHorizon; ignored when inflation_rate_pct>0 or rate_convention='real')\nrate_convention: 'nominal'|'real' (optional, default 'nominal'; 'real' unconditionally suppresses the inflation overlay; use when annual_rate_pct is already inflation-adjusted)\nchart_title: string (optional) override for the chart title. Must not contain em-dashes or en-dashes. Max 120 characters."
        -}
      • addedInput schema / properties / years
        Added value: +{
        +  "description": "Time horizon in years. Integer, at least 1. REQUIRED, no default.",
        +  "type": "integer"
        +}
      • changedInput schema / required
        Previous value: -[
        -  "toolArguments"
        -]New value: +[
        +  "principal",
        +  "annual_rate_pct",
        +  "years"
        +]
    • Changedoptimize_401k_match11 fields changed
      • addedInput schema / properties / annual_salary
        Added value: +{
        +  "description": "Annual gross salary. Decimal > 0. REQUIRED, no default.",
        +  "type": "number"
        +}
      • addedInput schema / properties / chart_title
        Added value: +{
        +  "default": null,
        +  "description": "Override for the chart title. Reserved for the chart pipeline. Must not contain em-dashes or en-dashes. Max 120 characters. Optional.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / contribution_pct
        Added value: +{
        +  "description": "Current employee contribution as a percent of gross pay. Decimal in [0, 100]. REQUIRED, no default.",
        +  "type": "number"
        +}
      • addedInput schema / properties / has_true_up
        Added value: +{
        +  "default": null,
        +  "description": "Whether the plan provides an annual true-up. false is the conservative assumption: surfaces front-loading forfeiture risk. Optional; defaults to false when omitted.",
        +  "type": [
        +    "boolean",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / match_preset
        Added value: +{
        +  "default": null,
        +  "description": "Named match-formula preset: 'safe_harbor_basic' (100% of first 3% + 50% of next 2%), 'safe_harbor_enhanced_simple' (100% of first 4%), 'qaca' (100% of first 1% + 50% of next 5%), 'fifty_pct_of_first_six_pct' (50% of first 6%). Exactly one of match_preset or match_tiers is required. Optional; omit when supplying match_tiers.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / match_tiers
        Added value: +{
        +  "default": null,
        +  "description": "Custom match-formula tier list. Example: [{\"match_frac\": 1.0, \"up_to_deferral_pct\": 3}, {\"match_frac\": 0.5, \"up_to_deferral_pct\": 2}] = safe_harbor_basic. Exactly one of match_preset or match_tiers is required. Optional; omit when supplying match_preset.",
        +  "items": {
        +    "properties": {
        +      "match_frac": {
        +        "description": "Employer match fraction for this tier (0.5 = 50%). Must be in (0, 1].",
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      },
        +      "up_to_deferral_pct": {
        +        "description": "Width of this tier as a percent of pay (e.g. 3 = the first 3% of pay). Must be > 0.",
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      }
        +    },
        +    "type": [
        +      "object",
        +      "null"
        +    ]
        +  },
        +  "type": [
        +    "array",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / output
        Added value: +{
        +  "default": null,
        +  "description": "Response envelope. 'summary' (default): headline scalars, period_schedule stripped. 'inline': full payload including period_schedule[]. 'capture': full payload written to ~/.senaro/captures/, capture_ref URI returned; local stdio transport only, rejected with a structured error on the hosted HTTP transport naming 'summary' and 'inline' as the valid alternatives.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / participant_age
        Added value: +{
        +  "default": null,
        +  "description": "Determines which catch-up limit applies (age 50+, or age 60-63 SECURE 2.0 super catch-up). Integer >= 0. Optional; omit when age is unknown or participant is under 50.",
        +  "type": [
        +    "integer",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / pay_periods_per_year
        Added value: +{
        +  "description": "Pay periods per year (12=monthly, 24=semi-monthly, 26=biweekly, 52=weekly). Integer in [1, 365]. REQUIRED, no default.",
        +  "type": "integer"
        +}
      • removedInput schema / properties / toolArguments
        Removed value: -{
        -  "description": "JSON object with parameters:\n\nREQUIRED:\n  annual_salary: decimal > 0. Annual gross salary.\n  pay_periods_per_year: integer in [1, 365]. Pay periods per year (12=monthly, 24=semi-monthly, 26=biweekly, 52=weekly).\n  contribution_pct: decimal in [0, 100]. Current employee contribution as a percent of gross pay.\n\nMATCH FORMULA (exactly one required):\n  match_preset: one of 'safe_harbor_basic' (100% of first 3% + 50% of next 2%), 'safe_harbor_enhanced_simple'\n    (100% of first 4%), 'qaca' (100% of first 1% + 50% of next 5%), 'fifty_pct_of_first_six_pct' (50% of first 6%).\n  match_tiers: array of tier objects [{ match_frac: decimal (0,1], up_to_deferral_pct: decimal > 0 }, ...].\n    match_frac is the employer fraction (0.5 = 50%). up_to_deferral_pct is the tier width as a percent of pay.\n    Example: [{ match_frac: 1.0, up_to_deferral_pct: 3 }, { match_frac: 0.5, up_to_deferral_pct: 2 }] = safe_harbor_basic.\n\nOPTIONAL:\n  has_true_up: bool (default false). Whether the plan provides an annual true-up.\n    false is the conservative assumption: surfaces front-loading forfeiture risk.\n  participant_age: integer >= 0 (optional). Determines which catch-up limit applies (age 50+, or age 60-63 SECURE 2.0 super catch-up).\n    Omit when age is unknown or participant is under 50.\n  chart_title: string (optional). Reserved for the chart pipeline. Must not contain em-dashes or en-dashes. Max 120 characters.\n\nENVELOPE:\n  output: 'summary' (default) | 'inline' | 'capture'\n  summary: headline scalars (match captured/forfeited, full-match threshold, front-load flag, applied_caps, citations);\n    the per-period period_schedule is stripped.\n  inline: full payload including the period_schedule[] (for chart rendering).\n  capture: full payload written to ~/.senaro/captures/; capture_ref URI returned.\n    Available on the local stdio transport only; the hosted HTTP transport rejects 'capture'\n    with a structured error naming 'summary' and 'inline' as the valid alternatives."
        -}
      • changedInput schema / required
        Previous value: -[
        -  "toolArguments"
        -]New value: +[
        +  "annual_salary",
        +  "pay_periods_per_year",
        +  "contribution_pct"
        +]
    • Changedpayoff_vs_invest16 fields changed
      • addedInput schema / properties / chart_title
        Added value: +{
        +  "default": null,
        +  "description": "Override for the chart title. Must not contain em-dashes or en-dashes. Max 120 characters. Optional.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / debt_apr_pct
        Added value: +{
        +  "description": "Debt annual percentage rate, e.g. 6.5 not 0.065. Decimal from 0 to 100. REQUIRED, no default.",
        +  "type": "number"
        +}
      • addedInput schema / properties / debt_balance
        Added value: +{
        +  "description": "Current debt balance. Decimal, greater than 0. REQUIRED, no default.",
        +  "type": "number"
        +}
      • addedInput schema / properties / debt_type
        Added value: +{
        +  "description": "Debt type: 'credit_card', 'auto', 'student', 'personal', or 'mortgage'. REQUIRED, no default.",
        +  "type": "string"
        +}
      • addedInput schema / properties / extra_monthly
        Added value: +{
        +  "description": "Extra monthly amount available for debt payoff or investing, the amount in question. Decimal, greater than 0. REQUIRED, no default.",
        +  "type": "number"
        +}
      • addedInput schema / properties / full_schedule
        Added value: +{
        +  "default": null,
        +  "description": "Whether to return the full month-by-month schedule. Optional; defaults to false (compact schedule) when omitted.",
        +  "type": [
        +    "boolean",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / investment_return_pct
        Added value: +{
        +  "default": null,
        +  "description": "Expected investment return, an EFFECTIVE ANNUAL rate; the monthly compounding step is (1+pct/100)^(1/12)-1. Decimal from 0 to 30. Optional; defaults to the cited long-run S&P 500 nominal return, about 10%, when omitted.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / investment_tax_advantaged
        Added value: +{
        +  "default": null,
        +  "description": "Whether the investment is tax-advantaged, e.g. 401k or IRA. Optional; defaults to false when omitted.",
        +  "type": [
        +    "boolean",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / investment_volatility_pct
        Added value: +{
        +  "default": null,
        +  "description": "Investment volatility. Decimal. Optional; adds a volatility risk note when supplied.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / minimum_payment
        Added value: +{
        +  "default": null,
        +  "description": "Minimum monthly payment. Decimal, at least 0. Optional; auto-calculated for amortizing loans when omitted or 0.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / output
        Added value: +{
        +  "default": null,
        +  "description": "'summary' (default), 'inline', or 'capture'. 'summary' returns headline comparison scalars, milestones, and citations with monthly_schedule stripped. 'inline' returns the full payload including the month-by-month monthly_schedule[]. 'capture' writes the full payload to ~/.senaro/captures/ and returns capture_ref; available on the local stdio transport only, since the hosted HTTP transport rejects 'capture' and names 'summary' and 'inline' as the valid alternatives.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / tax_bracket_pct
        Added value: +{
        +  "default": null,
        +  "description": "Tax bracket, e.g. 22 not 0.22. Decimal from 0 to 100. Optional; enables an after-tax comparison when supplied.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / term_months_remaining
        Added value: +{
        +  "default": null,
        +  "description": "Months remaining on the debt. Integer, greater than 0. Required for non-credit-card debt types; optional for credit_card.",
        +  "type": [
        +    "integer",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / time_horizon_years
        Added value: +{
        +  "default": null,
        +  "description": "Projection horizon. Integer, greater than 0. Optional; defaults to the greater of the payoff horizon or 10 years when omitted.",
        +  "type": [
        +    "integer",
        +    "null"
        +  ]
        +}
      • removedInput schema / properties / toolArguments
        Removed value: -{
        -  "description": "JSON object with these parameters:\n\ndebt_type: 'credit_card' | 'auto' | 'student' | 'personal' | 'mortgage' (REQUIRED)\ndebt_balance: decimal > 0 (REQUIRED)\ndebt_apr_pct: decimal 0-100 as percentage, e.g. 6.5 not 0.065 (REQUIRED)\nextra_monthly: decimal > 0. The amount in question (REQUIRED)\nminimum_payment: decimal >= 0 (optional; auto-calculated for loans when 0)\nterm_months_remaining: int > 0. Required for non-credit-card debt types\ninvestment_return_pct: decimal 0-30 as percentage, an EFFECTIVE ANNUAL rate; the monthly compounding step is (1+pct/100)^(1/12)-1 (optional; defaults to the cited long-run S&P 500 nominal return, about 10%)\ninvestment_volatility_pct: decimal (optional; adds volatility risk note)\ntax_bracket_pct: decimal 0-100 as percentage (optional; enables after-tax comparison)\ninvestment_tax_advantaged: bool (optional, default false; set true for 401k/IRA)\ntime_horizon_years: int > 0 (optional; default max(payoff years, 10))\nfull_schedule: bool (optional, default false; returns compact schedule by default)\nchart_title: string (optional) override for the chart title. Must not contain em-dashes or en-dashes. Max 120 characters.\n\nENVELOPE:\n  output: 'summary' (default) | 'inline' | 'capture'\n  summary: headline comparison scalars + milestones + citations; monthly_schedule is stripped.\n  inline: full payload including the month-by-month monthly_schedule[] (for chart rendering).\n  capture: full payload written to ~/.senaro/captures/; capture_ref URI returned.\n    Available on the local stdio transport only; the hosted HTTP transport rejects 'capture'\n    with a structured error naming 'summary' and 'inline' as the valid alternatives."
        -}
      • changedInput schema / required
        Previous value: -[
        -  "toolArguments"
        -]New value: +[
        +  "debt_type",
        +  "debt_balance",
        +  "debt_apr_pct",
        +  "extra_monthly"
        +]
    • Changedrefi_breakeven14 fields changed
      • addedInput schema / properties / chart_title
        Added value: +{
        +  "default": null,
        +  "description": "Reserved for the chart pipeline; validated but not yet used. Must not contain em-dashes or en-dashes. Max 120 characters. Optional.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / closing_costs
        Added value: +{
        +  "default": null,
        +  "description": "Explicit closing costs in dollars, excluding points; total upfront cost is closing_costs plus the points cost. Decimal from 0 to 1,000,000,000. Optional; omitting it uses the cited default of 0.67% of the loan (LodeStar 2026). An IMMEDIATE break-even requires total upfront cost to be zero (or non-positive) AND monthly_savings to be non-negative, i.e. closing_costs AND points both 0, not closing_costs alone; a zero-total-cost refi into a worse deal (negative monthly_savings) reports NEAR_ZERO_OR_NEGATIVE_SAVINGS instead.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / compute_economic_break_even
        Added value: +{
        +  "default": null,
        +  "description": "Whether to compute the economic (net-worth crossover) break-even. When false, only the cash-flow break-even and interest delta are returned, and economic_break_even reports NOT_REQUESTED. Optional; defaults to true when omitted.",
        +  "type": [
        +    "boolean",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / current_annual_rate_pct
        Added value: +{
        +  "description": "Current loan's annual rate as a percentage, e.g. 6.5 not 0.065. Decimal from 0 to 20. REQUIRED, no default.",
        +  "type": "number"
        +}
      • addedInput schema / properties / current_balance
        Added value: +{
        +  "description": "Outstanding principal you would refinance. Decimal, greater than 0 and at most 1,000,000,000. REQUIRED, no default.",
        +  "type": "number"
        +}
      • addedInput schema / properties / current_monthly_payment
        Added value: +{
        +  "default": null,
        +  "description": "Your actual statement P&I payment. Decimal, greater than 0 and at most 1,000,000,000. Optional; when supplied it overrides the formula-derived payment, so match your statement.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / investment_return_pct
        Added value: +{
        +  "default": null,
        +  "description": "Annual return used for the economic (invest-the-savings) break-even, an EFFECTIVE ANNUAL rate; the monthly compounding step is (1+pct/100)^(1/12)-1. Decimal from 0 to 30. Optional; omitting it uses the cited long-run S&P 500 nominal total-return default, about 10%; call list_defaults for the exact current value.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / new_annual_rate_pct
        Added value: +{
        +  "description": "The offered refinance rate as a percentage. Decimal from 0 to 20. REQUIRED, no default.",
        +  "type": "number"
        +}
      • addedInput schema / properties / new_term_months
        Added value: +{
        +  "description": "The new loan term in months. Integer from 1 to 480. REQUIRED, no default.",
        +  "type": "integer"
        +}
      • addedInput schema / properties / points
        Added value: +{
        +  "default": null,
        +  "description": "Discount points paid at closing, where 1.0 means 1% of the loan. Decimal from 0 to 4. Optional; defaults to 0 when omitted.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / remaining_term_months
        Added value: +{
        +  "description": "Months left on the current loan. Integer from 1 to 480. REQUIRED, no default.",
        +  "type": "integer"
        +}
      • addedInput schema / properties / roll_costs_into_loan
        Added value: +{
        +  "default": null,
        +  "description": "Whether closing costs and points are added to the new principal instead of paid upfront; cash-flow break-even then reports COSTS_ROLLED_INTO_LOAN. Optional; defaults to false when omitted.",
        +  "type": [
        +    "boolean",
        +    "null"
        +  ]
        +}
      • removedInput schema / properties / toolArguments
        Removed value: -{
        -  "description": "JSON object with these parameters:\n\nREQUIRED:\n  current_balance: decimal > 0. Outstanding principal you would refinance.\n  current_annual_rate_pct: decimal [0, 20] as a percentage, e.g. 6.5 not 0.065.\n  remaining_term_months: int [1, 480]. Months left on the current loan.\n  new_annual_rate_pct: decimal [0, 20] as a percentage. The offered rate.\n  new_term_months: int [1, 480]. The new loan term.\n\nOPTIONAL:\n  current_monthly_payment: decimal > 0. Your actual statement P&I payment; overrides the\n    formula-derived payment when supplied (match your statement).\n  closing_costs: decimal >= 0. Explicit closing costs in dollars; overrides the cited\n    default (0.67% of loan, LodeStar 2026). Excludes points -- total upfront cost is\n    closing_costs + points cost. An IMMEDIATE break-even requires total upfront cost to be\n    zero (or non-positive) AND monthly_savings to be non-negative, i.e. closing_costs AND\n    points both 0, not closing_costs alone; a zero-total-cost refi into a worse deal\n    (negative monthly_savings) reports NEAR_ZERO_OR_NEGATIVE_SAVINGS instead.\n  points: decimal [0, 4] (default 0). Discount points at closing (1.0 = 1% of loan).\n  roll_costs_into_loan: bool (default false). When true, closing costs + points are added to\n    the new principal instead of paid upfront; cash-flow break-even then reports COSTS_ROLLED_INTO_LOAN.\n  investment_return_pct: decimal [0, 30], an EFFECTIVE ANNUAL rate; the monthly compounding step\n    is (1+pct/100)^(1/12)-1 (default: ~10.0%, the cited long-run S&P 500 nominal total-return default; call\n    list_defaults for the exact current value). Annual return used for the economic\n    (invest-the-savings) break-even.\n  compute_economic_break_even: bool (default true). When false, only the cash-flow break-even\n    and interest delta are returned (economic break-even reports NOT_REQUESTED).\n  chart_title: string (optional). Reserved for the chart pipeline. Must not contain em-dashes or en-dashes. Max 120 characters."
        -}
      • changedInput schema / required
        Previous value: -[
        -  "toolArguments"
        -]New value: +[
        +  "current_balance",
        +  "current_annual_rate_pct",
        +  "remaining_term_months",
        +  "new_annual_rate_pct",
        +  "new_term_months"
        +]
    • Changedrent_vs_buy35 fields changed
      • addedInput schema / properties / age_65_plus_count
        Added value: +{
        +  "default": null,
        +  "description": "Number of filers aged 65 or older, each adding the additional standard deduction. Integer from 0 to 2. Only used when apply_tax_benefit is true. Optional; defaults to 0 when omitted.",
        +  "type": [
        +    "integer",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / annual_gross_income
        Added value: +{
        +  "default": null,
        +  "description": "Annual gross income, the MAGI proxy for the bracket and the SALT cap. Decimal from 0 to 1,000,000,000. Required when apply_tax_benefit is true; otherwise not used.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / apply_tax_benefit
        Added value: +{
        +  "default": null,
        +  "description": "Whether to apply the federal tax benefit model (TY2025/TY2026 SALT caps, mortgage interest deduction, PMI deductibility). When true, filing_status, tax_year and annual_gross_income are required. Optional; defaults to false when omitted. The §121 home-sale gain exclusion applies only at sale points 24 months or more after purchase (IRC §121(a) 2-of-5-year test); year 1 of yearly_series, months 1-23 in monthly_series, and any horizon under 24 months use no exclusion. Separately, IRC §1222(3) makes a holding period of 12 months or less short-term, taxed at ordinary rates instead of the long-term rates otherwise applied, on the home-sale gain and the renter's portfolio gain alike (24 months vs. 12 months are two different thresholds); this tool applies that split exactly to the home-sale gain, and to the renter's portfolio at annual grain: prior years' contributions are compounded together as one long-term lot rather than aged individually, so months inside a year understate short-term gain.",
        +  "type": [
        +    "boolean",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / basis_capitalizable_pct_override
        Added value: +{
        +  "default": null,
        +  "description": "Percentage of the buy-side closing cost that is capitalizable into the §121 adjusted basis (IRS Pub 523 split: abstract fees, title search, recording fees, survey fees, transfer taxes, owner's title insurance). Decimal from 0 to 100; 0 means a HomePrice-only basis, 100 means the full closing cost is in basis. Optional; defaults to 50% (Senaro deterministic midpoint) when omitted. Supply your actual HUD-1 split when available.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / blind_count
        Added value: +{
        +  "default": null,
        +  "description": "Number of blind filers, each adding the additional standard deduction. Integer from 0 to 2. Only used when apply_tax_benefit is true. Optional; defaults to 0 when omitted.",
        +  "type": [
        +    "integer",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / charitable
        Added value: +{
        +  "default": null,
        +  "description": "Charitable contributions. Decimal from 0 to 1,000,000,000; TY2026 and later apply a 0.5%-of-AGI floor per OBBBA §70425. Only used when apply_tax_benefit is true. Optional; defaults to 0 when omitted.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / chart_title
        Added value: +{
        +  "default": null,
        +  "description": "Override for the chart title. Must not contain em-dashes or en-dashes. Max 120 characters. Optional.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / down_payment_pct
        Added value: +{
        +  "default": null,
        +  "description": "Down payment as a percentage of the home price. Decimal from 0 to 100; 0 is valid (zero-down programs). Optional; defaults to 20% when omitted.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / filing_status
        Added value: +{
        +  "default": null,
        +  "description": "Tax filing status: 'Single', 'MFJ', 'MFS' or 'HoH'. Required when apply_tax_benefit is true; otherwise not used.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / hoa_monthly
        Added value: +{
        +  "default": null,
        +  "description": "Monthly HOA dues on the BUY path. Decimal from 0 to 1,000,000,000. Optional; defaults to 0 when omitted.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / home_appreciation_pct
        Added value: +{
        +  "default": null,
        +  "description": "Annual home price appreciation as a percentage. Decimal from -50 to 50. Optional; defaults to the cited 4.25% (FHFA HPI) when omitted; override for your market.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / home_insurance_pct
        Added value: +{
        +  "default": null,
        +  "description": "Annual home insurance as a percentage of home value. Decimal from 0 to 15. Optional; defaults to 0.65% when omitted; override for your market.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / home_price
        Added value: +{
        +  "description": "Home purchase price. Decimal, greater than 0 and at most 1,000,000,000. REQUIRED, no default.",
        +  "type": "number"
        +}
      • addedInput schema / properties / horizon_years
        Added value: +{
        +  "default": null,
        +  "description": "Projection horizon in years. Integer from 1 to 40. Optional; when omitted, the response returns snapshots at years 5, 10 and 30.",
        +  "type": [
        +    "integer",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / inflation_pct
        Added value: +{
        +  "default": null,
        +  "description": "Inflation as a percentage, for the real_terms toggle. Decimal, at least 0 and less than 100. Optional; when omitted, a nominal run uses 2.5%, and a real_terms run uses the horizon-based default: 2.0% for a horizon of 7 years or less, 2.6% for 8 to 19 years, 2.5% for 20 years or more.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / investment_return_pct
        Added value: +{
        +  "default": null,
        +  "description": "RENT path annual return as a percentage, an EFFECTIVE ANNUAL rate; the monthly compounding step is (1+pct/100)^(1/12)-1. Decimal from -50 to 30. Optional; defaults to 10.0% when omitted.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / loan_origination_date
        Added value: +{
        +  "default": null,
        +  "description": "Mortgage origination date, YYYY-MM-DD. Selects the mortgage interest deduction cap: on or before 2017-12-15 the $1M cap, after it the $750k cap (Single and MFJ; MFS caps are half). Only used when apply_tax_benefit is true. Optional; omitting it uses the post-2017 cap.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / loan_term_months
        Added value: +{
        +  "default": null,
        +  "description": "Mortgage term in months. Integer from 1 to 480. Optional; defaults to 360 (a 30-year fixed) when omitted.",
        +  "type": [
        +    "integer",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / maintenance_pct
        Added value: +{
        +  "default": null,
        +  "description": "Annual maintenance as a percentage of home value. Decimal from 0 to 20. Optional; defaults to the cited 1.5% (Harvard JCHS) when omitted; override for your market.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / monthly_rent
        Added value: +{
        +  "description": "Monthly rent on the RENT path. Decimal from 0 to 1,000,000,000; 0 is valid (a free-housing baseline). REQUIRED, no default.",
        +  "type": "number"
        +}
      • addedInput schema / properties / mortgage_rate_pct
        Added value: +{
        +  "description": "Mortgage annual rate as a percentage, e.g. 6.75 for 6.75%. Decimal, greater than 0 and at most 100. Never cached; provide the current rate. REQUIRED, no default.",
        +  "type": "number"
        +}
      • addedInput schema / properties / other_itemizable
        Added value: +{
        +  "default": null,
        +  "description": "Other itemizable deductions, such as medical expenses above 7.5% of AGI or casualty losses. Decimal from 0 to 1,000,000,000. Only used when apply_tax_benefit is true. Optional; defaults to 0 when omitted.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / output
        Added value: +{
        +  "default": null,
        +  "description": "'summary' (default), 'inline', or 'capture'. 'summary' returns headline scalars and per-horizon snapshots with the heavy series stripped. 'inline' returns the full payload including yearly_series[] and monthly_series[] for chart rendering. 'capture' writes the full payload to ~/.senaro/captures/ and returns capture_ref; available on the local stdio transport only, since the hosted HTTP transport rejects 'capture' and names 'summary' and 'inline' as the valid alternatives.",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / pmi_annual
        Added value: +{
        +  "default": null,
        +  "description": "Annual PMI premium in dollars while PMI is active. Decimal from 0 to 1,000,000,000. Only used when apply_tax_benefit is true. Optional; when omitted it is derived from pmi_pct and the original loan amount.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / pmi_pct
        Added value: +{
        +  "default": null,
        +  "description": "Annual PMI rate as a percentage of the loan. Decimal from 0 to 5. Optional; defaults to 0.5% when omitted. The removal threshold is set by pmi_removal_ltv_pct.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / pmi_removal_ltv_pct
        Added value: +{
        +  "default": null,
        +  "description": "Loan-balance trigger for PMI removal, as a percentage loan-to-value. Decimal from 50 to 100. Optional; omitted, it is the 78% HPA automatic-termination threshold; provided (e.g. 80), it is whichever of 78% or your value is reached first. Either way PMI also stops at the statutory amortization midpoint of the original term (12 U.S.C. §4901(7), §4902(c)), which this value cannot postpone.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / property_tax_pct
        Added value: +{
        +  "default": null,
        +  "description": "Annual property tax as a percentage of home value. Decimal from 0 to 10. Optional; defaults to 0.88% when omitted; override for your market.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / purchase_year_points
        Added value: +{
        +  "default": null,
        +  "description": "Discount points paid at origination, as a percentage of the loan. Decimal from 0 to 4. Counted as cash paid at closing on every call; when apply_tax_benefit is true they are also deducted in year 1. Optional; defaults to 0 when omitted.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / real_terms
        Added value: +{
        +  "default": null,
        +  "description": "Whether to deflate the output series to a real-terms view. Optional; defaults to false when omitted.",
        +  "type": [
        +    "boolean",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / rent_growth_pct
        Added value: +{
        +  "default": null,
        +  "description": "Annual rent escalation as a percentage. Decimal from -50 to 50. Optional; defaults to the cited 3.4% (BLS CPI ROPR) when omitted; override for your market.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / sell_side_pct
        Added value: +{
        +  "default": null,
        +  "description": "Sell-side transaction cost as a percentage of the sale price. Decimal from 0 to 25. Optional; defaults to the cited 7.5% (Redfin post-NAR) when omitted; override for your market.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / state_local_tax
        Added value: +{
        +  "default": null,
        +  "description": "State and local tax paid (state income or sales tax), for the SALT cap. Decimal from 0 to 1,000,000,000. Only used when apply_tax_benefit is true. Optional; defaults to 0 when omitted.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / tax_year
        Added value: +{
        +  "default": null,
        +  "description": "Tax year: 2025 or 2026. Integer. Required when apply_tax_benefit is true; otherwise not used.",
        +  "type": [
        +    "integer",
        +    "null"
        +  ]
        +}
      • removedInput schema / properties / toolArguments
        Removed value: -{
        -  "description": "JSON object with parameters:\n\nTIER 0 - REQUIRED (the instant answer renders from these three):\n  home_price: decimal > 0\n  monthly_rent: decimal >= 0 (0 is valid for free-housing baseline)\n  mortgage_rate_pct: decimal in (0, 100]; e.g. 6.75 for 6.75%. Never cached; provide current rate.\n\nTIER 1 - PRE-FILLED (defaults are cited; omit to use the default):\n  down_payment_pct: decimal [0, 100] (default: 20%). 0 is valid (zero-down programs).\n  loan_term_months: int > 0 (default: 360 = 30-year fixed)\n  horizon_years: int [1, 40] (default: returns snapshots at years 5, 10, 30)\n  hoa_monthly: decimal >= 0 (default: 0)\n\nTIER 2 - SOFT OVERRIDES (defaults from cited national sources; override for your market):\n  home_appreciation_pct: decimal. Annual home price appreciation % (default: 4.25%, FHFA HPI).\n  rent_growth_pct: decimal. Annual rent escalation % (default: 3.4%, BLS CPI ROPR).\n  maintenance_pct: decimal. Annual maintenance as % of home value (default: 1.5%, Harvard JCHS).\n  sell_side_pct: decimal. Sell-side transaction cost % of sale price (default: 7.5%, Redfin post-NAR).\n  property_tax_pct: decimal. Annual property tax as % of home value (default: 0.88%).\n  home_insurance_pct: decimal. Annual insurance as % of home value (default: 0.65%).\n  pmi_pct: decimal. Annual PMI rate as % of loan (default: 0.5%). Removal threshold is set by pmi_removal_ltv_pct (below).\n  pmi_removal_ltv_pct: decimal 50-100 (optional). Sets the loan-balance trigger for PMI removal: omitted, it is the 78% HPA automatic-termination threshold; provided (e.g. 80), it is whichever of 78% or your value is reached first. Either way PMI also stops at the statutory amortization midpoint of the original term (12 U.S.C. §4901(7), §4902(c)), which this value cannot postpone.\n  investment_return_pct: decimal. RENT path annual return %, an EFFECTIVE ANNUAL rate; the monthly compounding step is (1+pct/100)^(1/12)-1 (default: 10.0%).\n  inflation_pct: decimal. Inflation % for real-terms toggle (default: 2.5%).\n  real_terms: bool. Deflate the output series for real-terms view (default: false).\n  basis_capitalizable_pct_override: decimal [0, 100]. Fraction of the buy-side closing cost that is\n    capitalizable into the §121 adjusted basis (IRS Pub 523 split: abstract fees, title search, recording fees,\n    survey fees, transfer taxes, owner's title insurance). Default: 50% (Senaro deterministic midpoint).\n    Supply your actual HUD-1 split when available. 0 = HomePrice-only basis; 100 = full closing cost in basis.\n\nTIER 3 - TAX REFINEMENT (only used when apply_tax_benefit = true):\n  apply_tax_benefit: bool (default: false). The §121 home-sale gain exclusion applies only at sale\n    points 24 months or more after purchase (IRC §121(a) 2-of-5-year test); year 1 of yearly_series,\n    months 1-23 in monthly_series, and any horizon under 24 months use no exclusion. Separately,\n    IRC §1222(3) makes a holding period of 12 months or less short-term, taxed at ordinary rates\n    instead of the long-term rates otherwise applied, on the home-sale gain and the renter's\n    portfolio gain alike (24 months vs. 12 months are two different thresholds); this tool applies that split exactly to the\n    home-sale gain, and to the renter's portfolio at annual grain: prior years' contributions are\n    compounded together as one long-term lot rather than aged individually, so months inside a year\n    understate short-term gain.\n  filing_status: 'Single' | 'MFJ' | 'MFS' | 'HoH' (REQUIRED when apply_tax_benefit = true)\n  tax_year: 2025 | 2026 (REQUIRED when apply_tax_benefit = true)\n  annual_gross_income: decimal >= 0 (REQUIRED when apply_tax_benefit = true; MAGI proxy for bracket/SALT)\n  state_local_tax: decimal >= 0 (optional, default 0; SALT for the SALT cap, state income or sales tax paid)\n  charitable: decimal >= 0 (optional, default 0; charitable contributions. TY2026+: 0.5%-AGI floor per OBBBA §70111)\n  other_itemizable: decimal >= 0 (optional, default 0; medical above 7.5% AGI, casualty losses, etc.)\n  age_65_plus_count: int 0 to 2 (optional; adds additional standard deduction per qualifying filer)\n  blind_count: int 0 to 2 (optional; adds additional standard deduction per qualifying blind filer)\n  loan_origination_date: string YYYY-MM-DD (optional; pre-12/15/2017 gets $1M cap, post gets $750k)\n  pmi_annual: decimal >= 0 (optional; if omitted, derived from pmi_pct x average loan balance)\n  purchase_year_points: decimal >= 0 (optional; discount points at origination, % of loan; paid in cash at closing and deducted in year 1)\n\nENVELOPE:\n  output: 'summary' (default) | 'inline' | 'capture'\n  summary: headline scalars + per-horizon snapshots; heavy series stripped (chart-friendly)\n  inline: full payload including yearly_series[] + monthly_series[] (for chart rendering)\n  capture: full payload written to ~/.senaro/captures/; capture_ref URI returned.\n    Available on the local stdio transport only; the hosted HTTP transport rejects 'capture'\n    with a structured error naming 'summary' and 'inline' as the valid alternatives.\n\nchart_title: string (optional) override for the chart title. Must not contain em-dashes or en-dashes. Max 120 characters."
        -}
      • changedInput schema / required
        Previous value: -[
        -  "toolArguments"
        -]New value: +[
        +  "home_price",
        +  "monthly_rent",
        +  "mortgage_rate_pct"
        +]
  5. 18 tool updates
    • First observedanalyze_cash_advance
    • First observedanalyze_pmi_removal
    • First observedcalculate_cc_payoff
    • First observedcalculate_debt_to_income
    • First observedcalculate_emergency_fund
    • First observedcalculate_loan_payoff
    • First observedcalculate_opportunity_cost
    • First observedcalculate_runway
    • First observedcompare_debt_consolidation
    • First observedcompare_mortgage_terms
    • First observedcompare_strategies
    • First observedcompound_interest
    • First observedlist_defaults
    • First observedoptimize_401k_match
    • First observedpayoff_vs_invest
    • First observedrefi_breakeven
    • First observedrent_vs_buy
    • First observedserver_info

Publisher details

Operator
Senaro · Publisher source
Operator website
https://senaro.ai
Vendor relationship
First-party
Trust center
Not available
Restrictions
None. Open endpoint with no authentication, no API key, no sign-up, no paid plan, no admin approval, and no regional restriction.

Related MCP Connectors

Related MCP Servers

  • A
    license
    B
    quality
    C
    maintenance
    Provides 77 deterministic financial calculators, live market data, and a meta-advisor that chains tools into prioritized plans from plain-language descriptions.
    77
    22 PyPI
    6
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    Enables users to look up and search Australian SMSF rules — contribution caps, total super balance and Division 296 thresholds, pension minimums and lodgment deadlines — and to compute contribution headroom, every answer carrying an ato.gov.au or legislation.gov.au citation. It is read-only and deterministic, with no model calls or network access at call time.
    6
    MIT
Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.

Resources