>
About

API Reference

Hosted MCP server at https://mcp.digitalcalculator.info/mcp (Streamable HTTP, POST, no auth required — optional Bearer tier) · contract 1.0.0 · 9 anonymous tools — 4 scenario tools, 4 single-quantity calculator tools, and money_flow_map — plus prompts, reference-data resources, and per-response provenance. Hosted-API terms: see LICENSE-API.md (acceptable use, rate limits, no warranty). One additional tool, generate_report, is available to Bearer-tier sessions only.

What changed at 1.0

Contract 1.0.0 consolidated the former granular surface into four scenario tools (plan_retirement_income, evaluate_roth_conversion, check_contribution_eligibility, project_growth), kept the four highest-traffic single-quantity calculators unchanged in purpose, and added prompts, statutory reference-data resources, and the provenance envelope fields (rule_year, sources[]). Every retired granular tool remains reachable indefinitely via its legacy REST alias with its original wire vocabulary. See the changelog for the release entry.

Calling the server

The server speaks the MCP Streamable HTTP transport: JSON-RPC 2.0 over HTTP POST. Supported methods: initialize, server/discover, ping, tools/list, tools/call, resources/list, resources/read, prompts/list, prompts/get. A tool invocation looks like:

json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "mortgage_monthly_payment",
    "arguments": { "principal": 300000, "annualRatePercent": 6.5, "termYears": 30 }
  }
}

Successful calls return a JSON-RPC envelope whose result.content[0].text contains the ToolResult JSON described below. Tool failures set result.isError: true with the ToolError JSON in the same content slot.

Protocol versions (2026-07-28 support)

Supported protocol revisions: 2026-07-28, 2025-11-25, 2025-06-18 (default 2026-07-28). Two handshakes are served from the same stateless code path:

  • initialize — the classic session-oriented handshake, kept working unchanged for older hosts. Carries the requested version in params.protocolVersion.
  • server/discover — the 2026-07-28 stateless discovery handshake: no initialized notification expected, no session header issued or required. The requested version may also arrive via params._meta.protocolVersion on any request (the session-header-free carrier).

Because this server is deterministic and its discovery surfaces change only on deploy, static responses (server/discover, tools/list, resources/list, prompts/list, and every resources/read body) advertise their own cacheability via _meta: a ttlMs of 24 hours with cacheScope: "public". Clients that honor the advertisement can skip repeat discovery round-trips entirely.

REST companion

A REST API at https://api.digitalcalculator.info/v1/tools/{alias}/calculate runs the exact same engines and returns the same response envelope without the JSON-RPC wrapper. The four scenario tools have kebab-case aliases speaking the canonical vocabulary (plan-retirement-income, evaluate-roth-conversion, check-contribution-eligibility, project-growth), and every pre-1.0 alias keeps working — see Legacy REST aliases. (The earlier single-endpoint mortgage POC at /api/calculate/mortgage is legacy — do not build new integrations against it.)

Envelope shapes

ToolResult<T> (success)

typescript
interface ToolResult<T> {
  result: T;                          // Tool-specific structured object
  disclaimer: string;                 // Canonical YMYL disclaimer (dc://disclaimers/ymyl)
  methodology: {
    url: string;                      // Absolute URL to the primary methodology page
    version: string;                  // ISO date of last data verification
  };
  rule_year: number;                  // Statutory year the computation used (provenance)
  sources: string[];                  // dc:// resource URIs the computation drew on
  calculatedAt: string;               // ISO-8601 UTC timestamp
  engineVersion: string;              // SemVer of engine that produced result
}

Provenance fields (contract 1.0)

Every success envelope carries rule_year — the statutory year the computation used (currently 2026, the only supported rule year; passing any other value is rejected rather than silently computed against the wrong tables) — and sources[], the dc:// reference-data resource URIs the computation drew on (IRS limits, the Uniform Lifetime Table, SSA bend points, the methodology manifest). Scenario tools cite multiple sources; each URI is readable via resources/read. The provenance block is what a compliance reviewer audits — it is why an agent can put these numbers in front of a client with a citation trail.

Each cited source is also emitted as an MCP resource_link content block, appended after the text block in the content[] array of every successful tools/call. So provenance is not just an asserted string — a host can fetch the citation directly. The sources[] string array inside structuredContent is unchanged and remains the canonical field; the links are an additional rendering of the same facts, and a client that ignores resource_link sees exactly the response it saw before.

Applied-default disclosure (assumptions)

plan_retirement_income and project_growth accept optional inputs that carry a default — employer match, salary growth, catch-up eligibility, years worked, life expectancy, annual contribution, compounding frequency. When you omit one, the applied default is echoed in an assumptions object on the result, keyed by field name:

json
"assumptions": {
  "annual_salary_growth_percent": {
    "value": 0,
    "defaulted": true,
    "note": "No salary growth was provided, so salary was held flat for the whole projection. A real raise history would raise the projected balance."
  }
}

An empty object means every value used was supplied by the caller — the block is always present, so a client never has to branch on its existence. For plan_retirement_income the disclosure is scoped to the modules you actually selected: an RMD-only call assumes nothing and returns {}. Present these to an end user as assumptions rather than as their own figures.

Scope, stated plainly: this reports defaults the server applied. It cannot detect a rate an agent invented and passed in — annual_return_percent: 7 arrives indistinguishable from a number the user stated. Required rate inputs (annual_return_percent, annual_rate_percent) have no default for exactly that reason: they must come from the user, or be proposed to them explicitly first.

Monetary precision (round-to-cents)

Every USD-denominated output field is a JSON number rounded to the nearest cent (2 decimal places) — not full floating-point precision. Non-monetary fields (rates, ratios, counts, months, ages, IRS table factors) keep their natural precision.

ToolError (failure)

typescript
interface ToolError {
  error: {
    code: "INPUT_VALIDATION" | "BUSINESS_RULE" | "INTERNAL"
        | "RATE_LIMIT" | "UNAUTHORIZED";
    message: string;                  // Human-readable
    field?: string;                   // Input field that failed (validation only)
    retriable: boolean;               // Only RATE_LIMIT is retriable
  };
}

Status mapping

  • 200 + ToolResult — success
  • 200 + isError: true — tool-level error (INPUT_VALIDATION, BUSINESS_RULE) per the MCP spec
  • JSON-RPC error object — malformed request, unknown method (protocol-level errors)
  • 401UNAUTHORIZED (retriable: false) on the REST route when an Authorization: Bearer credential is present but invalid, revoked, or expired. Sending no Authorization header is not an error — that is the anonymous tier. The Streamable HTTP route returns the equivalent 401 in its own {"error":"invalid_token","message":...} shape
  • 429RATE_LIMIT (per-caller allowance or shared endpoint throttle)
  • 500INTERNAL (shouldn't happen in production; report it if you see one)

All error payloads are single-envelope {"error":{"code":...}} — never double-nested.

Tool naming convention

Tool names are snake_case, in a deliberate two-class grammar. Scenario tools are verb-first (plan_, evaluate_, check_, project_): the verb telegraphs multi-part, decision-shaped output. Single-quantity calculators are domain-first (mortgage_monthly_payment, paycheck_net_pay): the leading domain keyword is what a model matches against the user's own words. The legacy dot-notation names from the v0.2.x stdio package (dc.calculator.mortgage.monthlyPayment) were retired at the hosted-server cutover — dot-notation tool names break Claude Connectors' tool-name validation.

Shared vocabulary

The same concept has the same field name in every tool schema on the 1.0 surface:

Canonical shared vocabulary across all 1.0 tool schemas
ConceptCanonical field / convention
Statutory yearrule_year — integer; optional input defaulting to the current rule year; always present in output
Person's age basisbirth_year (not age) — SECURE 2.0 cohorts are birth-year-based and unambiguous mid-year
Filing statusone filing_status enum: single · married_filing_jointly · married_filing_separately · head_of_household
Account typeone kind enum: 401k · trad_ira · roth_ira · hsa
Rateswhole-number percents (e.g., 6.5 means 6.5%)
MoneyUSD numbers; balance, annual_contribution, annual_salary vocabulary reused across tools

A vocabulary-contract unit test diffs every field name in every schema against this table in CI — drift fails the build. Legacy REST aliases deliberately keep their pre-1.0 field spellings verbatim.

Tool index (8 calculator tools; see also money_flow_map)

Index of all 8 MCP calculator tools with descriptions and key parameters
ToolWhat it computesKey params
plan_retirement_incomeLinked 401(k) accumulation + Social Security estimate + SECURE 2.0 RMD picture for one person, one callinclude[], birth_year, balance, annual_salary, retirement_age, claim_age
evaluate_roth_conversionFederal tax impact of a single Roth conversion using rule-year brackets, with break-even framingconversion_amount, birth_year, filing_status, current_taxable_income
check_contribution_eligibilityContribution limit + eligibility for a 401(k), Traditional IRA, Roth IRA, or HSA with phase-outs appliedkind, birth_year, filing_status, magi, coverage_tier
project_growthNominal and inflation-adjusted growth of a balance with contributions at a chosen compounding frequencybalance, annual_contribution, annual_rate_percent, years, inflation_rate_percent
mortgage_monthly_paymentMonthly P&I, total interest, total paid for a fixed-rate mortgageprincipal, annualRatePercent, termYears
loan_monthly_paymentMonthly payment and total interest for any fixed-rate amortizing loanloanAmount, annualRatePercent, termYears
paycheck_net_payNet take-home pay per paycheck: IRS 2026 Percentage Method + FICA + state-aware estimategrossAnnualSalary, payFrequency, filing_status, state
emergency_fund_recommendationPersonalized emergency-fund target and savings timeline by household situationmonthlyExpenses, employmentType, incomeEarners, dependents

plan_retirement_income

Plan retirement income for one person in one linked call: 401(k) accumulation to a target retirement age, the Social Security benefit estimate at a chosen claim age, and the SECURE 2.0 required-minimum-distribution picture — all sharing a single birth_year, annual_salary, and statutory rule_year, so the pieces are mutually consistent. Select modules with include[]; each module only requires its own inputs, so an RMD-only question needs just birth_year + balance. Pre-RMD callers get applies: false with their SECURE 2.0 RMD start age and first RMD year ("when do RMDs start?") instead of an error. Absorbs the former retirement_401k_projection, social_security_estimated_benefit, and rmd_distribution_amount tools.

Input schema

Input schema for the plan_retirement_income tool
FieldTypeRequiredRangeDescription
includearray of enumyesaccumulation · social_security · rmdModules to compute (1–3, duplicates ignored)
birth_yearintegeryes1900 – 2100Single age basis for every module (1943–2004 when social_security is selected)
rule_yearintegerno2026Statutory year; defaults to 2026, always echoed in the result
balancenumber (USD)conditional0 – 100,000,000Required by accumulation (current 401(k) balance) and rmd (prior year-end balance)
annual_salarynumber (USD)conditional0 – 100,000,000Required by accumulation and social_security (max 1,000,000 for that module)
contribution_percentnumberconditional0 – 100Employee 401(k) contribution %; required by accumulation
employer_match_percentnumberno0 – 100Match % (50 = $0.50 per $1); default 0
employer_match_limit_percentnumberno0 – 100Cap on % matched; default 0
annual_salary_growth_percentnumberno0 – 20Salary growth %; default 0
annual_return_percentnumberconditional0 – 20Investment return %; required by accumulation
retirement_ageintegerconditional18 – 100Required by accumulation; must exceed the rule-year age derived from birth_year
catch_up_enabledbooleannoApply IRS catch-up in eligible years; default false (explicit opt-in)
claim_agenumberconditional62 – 70Required by social_security
years_workedintegerno0 – 50Default 35 (SSA averaging window)
life_expectancynumberno62 – 120Default 85; drives the lifetime benefit projection
spouse_birth_yearintegerno1900 – 2100Optional for rmd; a sole-beneficiary spouse >10 years younger triggers the jointLifeTableMayApply warning
is_spouse_sole_beneficiarybooleannoOptional for rmd; see above

Conditional requirements: accumulation needs balance, annual_salary, contribution_percent, annual_return_percent, retirement_age; social_security needs annual_salary, claim_age; rmd needs only balance. Missing conditional inputs return INPUT_VALIDATION naming the field and the module that needs it.

Output payload

Only the selected modules appear under modules; every module is computed from the same rule_year and birth_year. Example (all three modules; born 1968, $110,000 salary, $500,000 balance, 10% contribution, 6% return, retire at 62, claim at 62):

json
{
  "rule_year": 2026,
  "birth_year": 1968,
  "age_in_rule_year": 58,
  "modules": {
    "accumulation": {
      "futureBalance": 679359.26,
      "totalContributions": 44000.00,
      "employerContribTotal": 0.00,
      "growthEarned": 135359.26,
      "yearByYear": [ { "year": 1, "age": 59, "employeeContribution": 11000.00, "employerMatch": 0.00, "growth": 30000.00, "endingBalance": 541000.00 } ],
      "catchUpNote": "This projection EXCLUDES catch-up contributions (catchUpEnabled is false)..."
    },
    "social_security": {
      "monthlyBenefitAtFRA": 2652.40,
      "adjustedMonthlyBenefit": 1856.68,
      "lifetimeBenefitProjection": 512444,
      "fraAge": 67,
      "eligibilityYear": 2030,
      "bendPointsEstimated": true
    },
    "rmd": {
      "applies": false,
      "rmd_start_age": 75,
      "first_rmd_year": 2043,
      "note": "No RMD is required for rule year 2026: you are 58 and your SECURE 2.0 required-beginning age is 75 (born 1968)..."
    }
  }
}

When the rmd module applies (owner at or past the required-beginning age), it carries the full RMD payload instead: rmdAmount, distributionPeriod, lifeExpectancyFactor, tableUsed, penaltyIfMissed (25% default, 10% corrected per SECURE 2.0 §302), jointLifeTableMayApply, and spouseBeneficiaryNote.

YMYL caveat (Social Security module)

The Social Security estimate uses a simplified AIME approximation, not the official 35-highest-indexed-earnings calculation that requires a full SSA earnings history. For an authoritative estimate, direct users to ssa.gov/myaccount.

evaluate_roth_conversion

Evaluate the federal income tax impact of a single Roth conversion using rule-year IRS brackets. Returns the incremental tax owed, the top marginal bracket the conversion pushes you into, the effective rate on the conversion, a Roth 5-year-rule flag (age derived from birth_year), and optional break-even years when you supply your expected retirement marginal rate. This is the renamed roth_conversion_tax_impact — same engine, canonical vocabulary.

Input schema

Input schema for the evaluate_roth_conversion tool
FieldTypeRequiredRangeDescription
conversion_amountnumber (USD)yes0 – 10,000,000Amount converted to Roth
birth_yearintegeryes1900 – 2100Drives the Roth 5-year-rule flag via the rule-year age (18–100)
filing_statusenumyessingle · married_filing_jointly · married_filing_separately · head_of_householdCanonical shared enum
current_taxable_incomenumber (USD)yes0 – 10,000,000Taxable income BEFORE adding the conversion
retirement_marginal_rate_percentnumberno0 – 50Expected retirement marginal rate; enables break-even calc
expected_return_percentnumberno0 – 20Annual return on converted balance; default 7
rule_yearintegerno2026Statutory year; defaults to 2026

Output payload

Example ($50,000 conversion, married filing jointly, $110,000 taxable income, born 1968):

json
{
  "rule_year": 2026,
  "taxOwed": 11000.00,
  "marginalRateOnConversion": 0.22,
  "effectiveRateOnConversion": 0.22,
  "fiveYearRuleApplies": true,
  "breakEvenYears": null,
  "breakEvenNote": "breakEvenYears requires retirementMarginalRatePercent; provide it to compute the break-even horizon...",
  "meta": { "tableYear": 2026, "source": "IRS Rev. Proc. 2025-32" }
}

Scope limits

Single-conversion only (multi-year conversion laddering is not modeled). State tax and IRMAA tier impact are NOT modeled — the caller is responsible for those. breakEvenYears uses the site calculator's simplified log-ratio model, explained in breakEvenNote on every result.

check_contribution_eligibility

Check how much you can contribute to a retirement or health savings account for the rule year, with phase-outs and eligibility applied. Pick the account with kind: 401k (IRC §402(g) elective-deferral limit incl. the SECURE 2.0 ages 60–63 super catch-up), trad_ira (contribution limit plus the IRC §219(g) deduction-status signal driven by workplace coverage), roth_ira (MAGI phase-out per IRC §408A(c)(3)), or hsa (Rev. Proc. 2025-19 limits by coverage tier, age 55+ catch-up, employer-contribution offset). Absorbs the former ira_contribution_limit and hsa_contribution_limit tools; pairs with the dc://irs/limits resource.

Input schema

Input schema for the check_contribution_eligibility tool
FieldTypeRequiredRangeDescription
kindenumyes401k · trad_ira · roth_ira · hsaAccount type (canonical shared enum)
birth_yearintegeryes1900 – 2100Catch-up eligibility (50+ IRA/401k, 55+ HSA, 60–63 super catch-up) derives from the rule-year age (18–100)
rule_yearintegerno2026Statutory year; defaults to 2026
filing_statusenumconditionalcanonical shared enumRequired when kind is trad_ira or roth_ira
maginumber (USD)conditional0 – 10,000,000Required when kind is trad_ira or roth_ira; drives the phase-outs
workplace_coveragebooleannotrad_ira: active participant in a workplace plan? Omit and the deduction status assumes no coverage, flagged indicative-only
spouse_workplace_coveragebooleannotrad_ira, married filing jointly: triggers the higher spousal phase-out band when the filer is not covered
coverage_tierenumconditionalself-only · familyRequired when kind is hsa
employer_contributionnumber (USD)no0 – 50,000hsa: reduces employee payroll-deduction room dollar-for-dollar; default 0
estimated_marginal_rate_percentnumberno0 – 50hsa: returns the payroll-deduction tax-savings estimate

Output payload

Every result carries the linked fields rule_year, kind, birth_year, age_in_rule_year, plus the underlying engine's fields for the selected kind. Example (kind: "401k", born 1964 — the ages 60–63 super-catch-up window):

json
{
  "rule_year": 2026,
  "kind": "401k",
  "birth_year": 1964,
  "age_in_rule_year": 62,
  "contributionLimit": 24500.00,
  "catchUpEligible": true,
  "superCatchUpApplies": true,
  "catchUpAmount": 11250.00,
  "totalContributionLimit": 35750.00,
  "meta": { "tableYear": 2026, "source": "IRS Notice 2025-67; SECURE 2.0 Act §109 (ages 60-63 super catch-up)" }
}

IRA kinds return eligibleContribution, traditionalCap, rothCap, catchUp, phaseOutReduction, catchUpEligible, traditionalDeductionStatus, and traditionalDeductionNote; the HSA kind returns contributionLimit, catchUpAmount, totalContributionLimit, employerContribution, employeeMaxRemaining, payrollDeductionTaxSavings, employerContributionExceedsLimit, and excessContribution (with an excessContributionNote citing the IRC §4973 6% excise tax when the employer contribution alone exceeds the limit).

project_growth

Project the growth of a balance with optional yearly contributions, a chosen compounding frequency, and both nominal and inflation-adjusted (real) outputs in one envelope. Contributions are spread monthly (annual_contribution / 12); compounding_frequency defaults to monthly. Provide inflation_rate_percent to also get real_future_value — the projected balance deflated into today's dollars. Absorbs the former compound_interest_future_value, savings_future_balance, and inflation_adjusted_value tools.

Input schema

Input schema for the project_growth tool
FieldTypeRequiredRangeDescription
balancenumber (USD)yes0 – 100,000,000Starting balance (principal)
annual_contributionnumber (USD)no0 – 12,000,000Yearly contribution, spread evenly across the year; default 0
annual_rate_percentnumberyes0 – 50Annual growth rate as whole-number percent
yearsintegeryes1 – 100Projection horizon in whole years
compounding_frequencyenumnoannually · semiannually · quarterly · monthly · dailyDefault monthly
inflation_rate_percentnumberno0 – 20When provided, the result also carries real_future_value

Output payload

Example ($10,000 starting balance, $6,000/year, 7%, 30 years, monthly compounding, 3% inflation):

json
{
  "future_value": 691150.47,
  "total_contributions": 190000.00,
  "interest_earned": 501150.47,
  "years": 30,
  "compounding_frequency": "monthly",
  "annual_contribution": 6000.00,
  "inflation_rate_percent": 3,
  "real_future_value": 284744.84,
  "real_note": "real_future_value expresses the projected balance in rule-year (today's) dollars, deflated at 3% annual inflation over 30 years. future_value is the nominal figure.",
  "assumptions": {}
}

The example above supplies every optional input, so assumptions is empty. Omit annual_contribution or compounding_frequency and each appears there with the default that was applied — see applied-default disclosure.

mortgage_monthly_payment

Compute the monthly principal & interest payment, total interest paid, and total amount paid over the life of a fixed-rate mortgage. loan_monthly_payment is a parallel principal-&-interest calculator; both use the same amortization formula and return identical numbers for the same principal/rate/term. Neither models down payment, PMI, property tax, or homeowners insurance — pick by vocabulary (principal here vs loanAmount there).

Input schema

Input schema for the mortgage_monthly_payment tool
FieldTypeRequiredRangeDescription
principalnumber (USD)yes0 – 100,000,000Loan principal
annualRatePercentnumberyes0 – 30Annual rate as whole-number percent (e.g., 6.5)
termYearsintegeryes1 – 50Loan term in whole years

Output payload

json
{
  "monthlyPayment": 1896.20,
  "totalInterest": 382633.47,
  "totalPaid": 682633.47
}

Scope note

This tool computes P&I only. PITI (with property tax, insurance, HOA, PMI) is reserved for a future additive tool. For a fixed-rate non-mortgage loan, use loan_monthly_payment.

loan_monthly_payment

Compute the monthly payment, total interest, and total paid for a fixed-rate amortizing loan (personal, auto, student, etc.) via the standard amortization formula. Zero-interest loans are handled as simple principal division. Mirrors the site's loan calculator engine exactly.

Input schema

Input schema for the loan_monthly_payment tool
FieldTypeRequiredRangeDescription
loanAmountnumber (USD)yes0 – 100,000,000Loan principal
annualRatePercentnumberyes0 – 30Annual rate as whole-number percent; 0 for interest-free
termYearsintegeryes1 – 50Loan term in whole years

Output payload

json
{
  "loanAmount": 25000.00,
  "annualRatePercent": 8.5,
  "termYears": 5,
  "numPayments": 60,
  "monthlyRate": 0.00708333,
  "monthlyPayment": 512.91,
  "totalPaid": 30774.80,
  "totalInterest": 5774.80
}

paycheck_net_pay

Estimate net (take-home) pay per paycheck using the IRS 2026 federal Percentage Method, FICA (Social Security 6.2% + Medicare 1.45% + Additional Medicare 0.9% above thresholds), and a state-aware tax estimate. Pre-tax deductions split by FICA treatment: preTax401kAnnual reduces income-tax wages only (401(k) stays in the FICA wage base), while preTaxCafeteriaAnnual (Section 125: employer health premiums, FSA, HSA) also reduces FICA wages.

Input schema

Input schema for the paycheck_net_pay tool
FieldTypeRequiredRangeDescription
grossAnnualSalarynumber (USD)yes0 – 10,000,000Gross annual salary
payFrequencyenumyesweekly · biweekly · semimonthly · monthly · quarterly · annualPay frequency
filing_statusenumyessingle · married_filing_jointly · married_filing_separately · head_of_householdFederal filing status (canonical shared enum)
statestringyes2-letter code or DCUS state code, case-insensitive; unknown codes rejected with INPUT_VALIDATION
dependentsintegerno0 – 20Form W-4 line 3; applies the Step 3 dependent credit of $2,000 per dependent against annual federal withholding (floored at $0). Default 0
preTax401kAnnualnumber (USD)no0 – 1,000,000Traditional 401(k)/403(b) pre-tax contributions; reduces federal + state taxable wages ONLY (still FICA-taxed). Default 0
preTaxCafeteriaAnnualnumber (USD)no0 – 1,000,000Section 125 cafeteria-plan deductions; reduces taxable wages AND the FICA wage base. Default 0
preTaxDeductionsAnnualnumber (USD)no0 – 1,000,000DEPRECATED — prefer the two split fields above; treated as 401(k)-type (does not reduce FICA wages). Default 0; omit it in new integrations
postTaxDeductionsAnnualnumber (USD)no0 – 1,000,000Roth contributions, garnishments, etc. Default 0

Output payload

Example ($80,000 gross, biweekly, single, Colorado, no deductions):

json
{
  "grossPerPaycheck": 3076.92,
  "federalTax": 337.31,
  "ficaTax": 235.38,
  "stateTax": 108.14,
  "netPay": 2396.09,
  "payPeriodsPerYear": 26,
  "federalTaxAnnual": 8770.00,
  "federalTaxBeforeDependentCreditAnnual": 8770.00,
  "dependentCreditAnnual": 0.00,
  "ficaTaxAnnual": 6120.00,
  "stateTaxAnnual": 2811.60,
  "netPayAnnual": 62298.40,
  "noStateIncomeTax": false,
  "stateTaxSource": "Tax Foundation 2026 State Individual Income Tax Rates",
  "stateTaxNotes": "Flat 4.4% (2026 estimate post-TABOR refund).",
  "stateEffectiveRate": 0.035145
}

Scope note (state tax)

State withholding uses a simplified estimate — $0 for the 9 no-income-tax states (AK, FL, NV, NH, SD, TN, TX, WA, WY); Tax Foundation 2026 effective rates by income tier for all other states + DC. Full per-state brackets, SDI, and local-tax piggybacks (NYC, PA EIT, etc.) are NOT modeled. Direct callers needing precise state math to the full paycheck calculator.

emergency_fund_recommendation

Compute a personalized emergency-fund target and savings timeline. The recommended coverage months come from the household employment situation (employed dual-income with no dependents = 3 months; standard employed = 6; self-employed/freelance = 9; between jobs = 12), then the tool calculates the funding gap, the months-to-goal at your savings rate, and interest earned if a HYSA APY is provided. Mirrors the site's emergency fund calculator engine exactly.

Input schema

Input schema for the emergency_fund_recommendation tool
FieldTypeRequiredRangeDescription
monthlyExpensesnumber (USD)yes1 – 1,000,000Monthly essential expenses
currentSavingsnumber (USD)no0 – 10,000,000Liquid savings already set aside; default 0
monthlySavingsnumber (USD)no0 – 100,000Monthly contribution toward the goal; default 0
employmentTypeenumnoemployed · self-employed · freelance · multiple · between-jobsDefault employed; drives recommended months
incomeEarnersenumnosingle · dualDefault single
dependentsintegerno0 – 20Financial dependents; default 0
savingsAPYnumberno0 – 15HYSA APY as whole-number percent; default 0

Output payload

Example ($4,500/month expenses, $8,000 saved, $600/month savings, self-employed, single earner, 2 dependents, 4.3% APY):

json
{
  "recommendedMonths": 9,
  "targetFund": 40500.00,
  "currentSavings": 8000.00,
  "fundGap": 32500.00,
  "percentComplete": 19.75,
  "monthsToGoal": 48,
  "interestEarned": 4174.89,
  "futureValue": 40974.89
}

monthsToGoal semantics

monthsToGoal is compounding-aware: the first month the balance — including savingsAPY interest — reaches the target, consistent with futureValue (and shorter than a naive gap ÷ monthly-savings when APY > 0). It is 0 ONLY when the goal is already funded, and null when the goal is not reachable at the current contribution — a 0-month timeline is never reported for an unfunded goal.

Bearer-tier tool: generate_report

Bearer tier only

This tool — the only one beyond the anonymous surface — is available ONLY to Bearer-tier sessions (an Authorization: Bearer dc_bearer_… header on the request). It is never present in the anonymous tools/list response, and an anonymous tools/call generate_report returns a structured authentication_required error rather than being silently listed or ignored. See Pricing for Bearer-tier access.

Renders a vector-text PDF report for a report-capable calculator tool and returns a short summary plus a short-lived presigned download link. Uses the same rendering pipeline as the Bearer-gated REST endpoint POST /v1/reports/{tool} (ADR-0055), metered against a daily report quota that is SEPARATE from the calculator-call quota. A Bearer key can carry a per-key BrandConfig that white-labels the rendered report. For a step-by-step walkthrough (curl on both transports, download, branding), see Server-rendered PDF reports in the Quickstart.

Where the server renderer is used

This tool and POST /v1/reports/{tool} are the only surfaces that render a report on our servers — your input is sent to the report renderer, and the resulting PDF is written to a presigned URL that expires. The calculator pages on the website do not use this path: since 2026-08-11 their report button builds the same document in the browser from the same engines and templates, so nothing about that calculation leaves the device. The two renderers share their engines and page templates; the browser one additionally embeds the site’s brand fonts and a chart.

Input schema

Input schema for the generate_report tool
FieldTypeRequiredDescription
toolstringyesReport-capable tool alias: mortgage, loan, retirement-401k, education, or credit-card
inputobjectyesThe target tool's input object — same shape as that tool's calculator input (e.g., for mortgage: principal, annualRatePercent, termYears)

These are the only two arguments (additionalProperties: false). In particular there is no per-request brand argument — branding is configured on the key, not the call. See Branding.

Output

On success, the tool returns an MCP content array with a short text summary followed by a resource_link content block pointing at the presigned PDF URL, plus a structuredContent object mirroring the REST report envelope:

json
{
  "report": {
    "url": "https://….s3.amazonaws.com/reports/mortgage/<hash>.pdf?X-Amz-…",
    "expiresInSeconds": 900,
    "cached": false,
    "contentType": "application/pdf",
    "cacheKey": "<hash>"
  },
  "tool": "mortgage",
  "engineVersion": "1.0.0",
  "calculatedAt": "2026-07-06T00:00:00.000Z",
  "disclaimer": "Estimates only — see methodology.",
  "methodology": { "url": "https://www.digitalcalculator.info/mortgage-calculator/methodology/", "version": "2026-05-09" }
}

The presigned URL is short-lived (15 minutes by default) and unguessable. The rendered PDF carries the full numeric detail (amortization table, per-tool figures) — the tool result intentionally returns a short summary, not a re-derivation of the underlying calculator's output. Identical inputs are served from an artifact cache (cached: true): re-requesting after the link expires returns a fresh presigned URL without re-rendering.

Branding (per-key BrandConfig)

A Bearer key record can carry a BrandConfig that white-labels the rendered PDF's presentation. It is stored server-side on the key — there is no request parameter, and the same generate_report call renders branded once the key carries it. All five fields are optional; each is validated independently, so an invalid logoUrl drops only the logo, never the whole config.

BrandConfig fields, their validation constraints, and what each changes on the rendered report
FieldConstraintsWhat it changes
companyNameup to 60 charactersReplaces "Digital Calculator" as the report's branding name
taglineup to 80 charactersReplaces the tagline under the branding name
logoUrlhttps URL of an image (png/jpg/gif/webp/svg) on a public host — private/internal hosts are rejectedAdds your logo above the branding name, rendered up to 48px tall / 180px wide
primaryColor6-digit hex (#rrggbb)The report's slate chrome: header rule, branding name, results-table header bar, disclaimer bar
accentColor6-digit hex (#rrggbb)The copper accent on the input summary

Setting or changing it: include the fields when you request a Bearer key, or email admin@markcolabs.com from the address that requested the key. The config is applied to the key record and your next render carries it. The artifact cache is partitioned per brand, so a branding change produces fresh renders rather than a stale-brand cache hit.

Presentation-only invariant

BrandConfig restyles chrome and identity (name, tagline, logo, colors) and nothing else. It never changes a financial number, an input value, a metric, or a table cell, and the "Not financial advice" core disclaimer is non-removable on every branded render.

Errors

  • authentication_required — no (or an invalid) Bearer token; anonymous callers always get this
  • report_quota_exceeded — the key's separate daily report quota (default 25/day) was exceeded
  • rate_limited — the per-key 5-minute short-term ceiling was exceeded (the report surface shares your key's window with the calculator tools)
  • REPORT_UNAVAILABLE — the tool alias isn't (yet) report-capable; the message names the currently supported aliases
  • INPUT_VALIDATION — missing tool / input, or the target tool rejected input
  • INTERNAL — report generation failed; report it if you see one

Tool: money_flow_map

Free anonymous tool (since v0.11.x)

Originally Bearer-tier only (v0.9.x–v0.10.x), money_flow_map is available anonymously as of v0.11.x: it appears in the anonymous tools/list alongside the 8 calculator tools and is callable without an API key under the standard per-IP anonymous rate limits. Describe a monthly budget as structured incomes and categories, and the tool returns a computed summary plus mapUrl — a link that renders the budget as an interactive money-flow (Sankey) map on this site, with nothing stored server-side.

Turns a structured monthly budget — income sources and spending categories, optionally broken into line items — into a deterministic computed summary plus mapUrl, a shareable deep link that renders the budget as an interactive Sankey money-flow map on the Money Flow Visualizer instantly (no account, nothing stored server-side). Built for conversational budgeting: the calling model interviews the user, structures the answers, and hands back the map link. The tool is read-only and deterministic like the 8 calculator tools — it accepts no free text and does no interpretation. All amounts are MONTHLY USD. Methodology: How This Tool Works.

Input schema

Input schema for the money_flow_map tool
FieldTypeRequiredDescription
incomesarray (1–20)yesMonthly income sources: { label, amount } each. Take-home amounts work best for a spendable-dollars map. Amounts are 0–10,000,000 USD/month; labels are 1–80 characters
categoriesarray (0–30)yesSpending categories: { label, amount } for a flat monthly total, OR { label, items } where items (1–30) is an array of { label, amount } line items that roll up into the category total (e.g., Rent + Utilities under Housing). Providing both amount and items is rejected. May be empty — savings is never a category; it is the computed residual
labelstringnoScenario name shown on the map (e.g., "Saving for a house"), up to 80 characters

Validation is strict: unknown fields anywhere in the input are rejected with INPUT_VALIDATION, as are negative, non-finite, or out-of-range amounts.

Output payload

Returns the standard ToolResult envelope (disclaimer, methodology, rule_year, sources, calculatedAt, engineVersion) whose result is:

json
{
  "label": "Saving for a house",
  "totalMonthlyIncome": 6600,
  "totalMonthlySpending": 3130,
  "monthlySavings": 3470,
  "deficit": false,
  "savingsRatePercent": 52.5757575757,
  "categories": [
    { "label": "Housing", "monthlyTotal": 2030, "percentOfIncome": 30.7575 },
    { "label": "Food", "monthlyTotal": 700, "percentOfIncome": 10.6060 }
  ],
  "largestCategories": [ { "label": "Housing", "monthlyTotal": 2030, "percentOfIncome": 30.7575 } ],
  "mapUrl": "https://www.digitalcalculator.info/money-flow-visualizer/#s=<base64url>"
}
  • monthlySavings is the residual (income − spending) and may be negative; deficit is true when spending exceeds income — the map shows a deficit banner instead of an invented savings flow
  • savingsRatePercent = savings ÷ income × 100 (0 when income is 0); percentOfIncome uses the same convention
  • largestCategories lists up to the 3 largest categories by monthly total, descending
  • mapUrl encodes the budget in the visualizer’s versioned v1 URL-state schema (#s=<base64url(JSON)>) — opening it renders the interactive map client-side; nothing is uploaded or stored
  • Monetary values are full floating-point precision (not pre-rounded) — display-round to cents

Errors

  • INPUT_VALIDATION — empty/missing incomes, out-of-range or non-finite amounts, over-cap list lengths, unknown fields, or a category with both amount and items (the field property names the offending path)
  • INTERNAL — computation failed; report it if you see one

Legacy REST aliases (compatibility guarantee)

The REST twin is not directory-frozen and keeps every pre-1.0 alias working indefinitely: POST https://api.digitalcalculator.info/v1/tools/{alias}/calculate dispatches to the same engines with the request/response shapes those aliases have always had — including their original field spellings (filingStatus, birthYear, etc.), which are preserved verbatim via a tested translation layer. These names are not part of the MCP tools/list surface. New integrations should prefer the canonical 1.0 equivalents.

Legacy REST aliases and their canonical 1.0 equivalents
Legacy REST aliasLegacy tool name (wire vocabulary preserved)Canonical 1.0 equivalent
retirement-401kretirement_401k_projectionplan_retirement_income (include: ["accumulation"])
social-securitysocial_security_estimated_benefitplan_retirement_income (include: ["social_security"])
rmdrmd_distribution_amountplan_retirement_income (include: ["rmd"])
roth-conversionroth_conversion_tax_impactevaluate_roth_conversion
iraira_contribution_limitcheck_contribution_eligibility (kind: "trad_ira" / "roth_ira")
hsahsa_contribution_limitcheck_contribution_eligibility (kind: "hsa")
compound-interestcompound_interest_future_valueproject_growth
savingssavings_future_balanceproject_growth (monthly compounding default)
inflationinflation_adjusted_valueproject_growth (inflation_rate_percent)
paycheckpaycheck_net_pay (legacy wire: federalFilingStatus + its enum)paycheck_net_pay (canonical filing_status)
mortgage, loan, emergency-fundunchanged keeperssame tools, same shapes

The four scenario tools additionally have canonical kebab-case REST aliases: plan-retirement-income, evaluate-roth-conversion, check-contribution-eligibility, project-growth. Published line: MCP surface redesigned for agents; REST unchanged and extended.

Prompts

The server advertises the prompts capability with three parameterized prompts via prompts/list / prompts/get. Prompt argument values are strings per the MCP spec; each prompt instructs the model to convert them to the types the tool schemas require, and each generated message carries the YMYL framing (educational estimates, not advice).

The three server prompts with their arguments and driven tools
PromptWhat it drivesRequired arguments
retirement-readiness-reviewOne linked plan_retirement_income call with all three modules, then a plain-language reviewbirth_year, annual_salary, balance (optional: contribution_percent, retirement_age, claim_age)
conversion-window-analysisevaluate_roth_conversion plus a dc://irs/limits read for bracket-headroom sizingbirth_year, filing_status, current_taxable_income (optional: conversion_amount)
withholding-checkuppaycheck_net_pay as an annual W-4 sanity check against the user's actual paycheckgross_annual_salary, state, filing_status (optional: pay_frequency)

Resources

The MCP server exposes five dc:// resources, all anonymous-readable (plus ui:// view templates for MCP Apps-capable hosts). The three statutory reference-data resources are the same tables the engines compute from — and the same URIs every tool cites in its sources[]:

The five dc:// resources served by the MCP server
URIContent
dc://irs/limits/2026Contribution limits, catch-up tiers, phase-out bands, and federal brackets for the current rule year, with published-source citations (IRS Notice 2025-67, Rev. Proc. 2025-19/2025-32)
dc://irs/uniform-lifetimeIRS Uniform Lifetime Table distribution periods + SECURE 2.0 RMD start-age cohorts
dc://ssa/bend-points/2026SSA PIA bend points, the Social Security wage base, and COLA scope
dc://disclaimers/ymylThe canonical YMYL disclaimer string returned in every tool response's disclaimer field
dc://methodologies/manifestJSON manifest mapping each anonymous tool to the methodology URL of its source page — for agent-side citation of where each formula is documented

Agents that need the reference table rather than a computation (e.g., "what's the 2026 401(k) limit?") should read the resource directly instead of calling a tool. Every resources/read body is static per deploy and advertises 24-hour public cacheability via _meta.

Get a key (self-serve)

A free key is minted by an unauthenticated POST — no signup, no email, no card. It works on your next request.

bash
curl -X POST https://api.digitalcalculator.info/v1/keys/public

Returns HTTP 201 with the key and its allowance:

json
{
  "key": "dc_pub_…",
  "tier": "free",
  "dailyLimit": 1000,
  "windowLimit": 60,
  "claimed": false
}

Then send it as Authorization: Bearer dc_pub_… on any tool call, on either transport.

The key is returned once

We store only its SHA-256 hash, so the key cannot be recovered, re-sent, or emailed — there is no account and no key list yet. Save it when you receive it; if you lose it, mint another. Minting is capped at 5 keys per IP per UTC day.

dc_pub_ keys are the publishable class: they carry the free allowance and are safe to place in client-side code. dc_bearer_ keys are the secret class issued for paid plans — they unlock the Bearer-tier surface (including generate_report) and must never appear in client-side source. Paid keys are created automatically when a subscription starts and shown on the confirmation page for 24 hours.

Rate limits

Two layers apply to every call. The outer layer is a single global API Gateway throttle of about 100 requests/minute (burst 50), shared by all callers — per-API, not per-caller. No tier reserves throughput against it, so a per-tier allowance is a ceiling on consumption, not a guarantee of rate. The inner layer is a per-caller allowance enforced in the application:

  • Anonymous tier: 500 calls per UTC day per IP, plus a short-term ceiling of 30 calls per 5-minute window per IP. Exceeding either returns HTTP 429 + code: "RATE_LIMIT" (retriable: true) with Retry-After and X-RateLimit-Limit / -Remaining / -Reset headers. Terms: LICENSE-API.md
  • Free key tier (dc_pub_, minted self-serve): 1,000 calls per UTC day per key, plus 60 calls per 5-minute window per key. Counted against your key alone rather than shared with everyone behind your IP
  • Bearer tier: a per-key daily cap plus a per-key fixed-window ceiling (boundary-aligned 5-minute buckets), by plan — authenticate with an Authorization: Bearer <key> header. The daily budget is counted against your key alone and is not consumed by anonymous traffic. See plans and allowances
  • One meter per key, both transports: a key's allowance is decremented identically whether the call arrives on /mcp or /v1/tools/{tool}/calculate. There is no second budget to discover
  • Successful anonymous responses are unchanged: the anonymous gate adds no headers on a PASS (anonymous-parity invariant). The only externally visible difference is the 429 itself
  • Bearer is server-to-server today: the API's CORS policy does not yet allow the Authorization header from browsers, so call the endpoint from a backend, CLI, or MCP client — not browser-side fetch
  • Need sustained higher volume, or throughput genuinely reserved for you? Email us about Enterprise

Versioning

Versioning policy for the MCP server tool surface and per-tool engineVersion field
BumpTriggerExample
MajorTool rename, removed field, breaking envelope changeContract 1.0.0: the pre-submission surface consolidation (granular tools absorbed into the four scenario tools) — the 0.x era permitted breaking changes; 1.0.0 is the version the deprecation contract now protects
MinorAdditive tool, additive output field, tool tier changev0.8.x added generate_report (Bearer-tier only); v0.11.x promoted money_flow_map to the anonymous tier
PatchBug fixes, metadata correctionsEngine defect fixes
engineVersionMath changes onlyPer-tool SemVer, independent of the package version
methodology.versionCalculator's methodology document materially updatedISO date string

References

Questions or integration help: email admin@markcolabs.com.