Retirement Lab MCP documentation
Run hypothetical retirement simulations across countries, taxes, spending, and market assumptions.
Endpoint: https://retirementlab.app/api/mcp
Retirement Lab is a financial calculator. Every result is a hypothetical projection based on the inputs and assumptions supplied, not financial, tax, investment, or legal advice and not a forecast or guarantee.
Connect a client
OAuth (primary)
Add https://retirementlab.app/api/mcp as a remote MCP server. A compatible client
discovers Retirement Lab’s OAuth metadata, opens a browser, and asks you to
approve the mcp scope. Access and refresh tokens are opaque and
hashed at rest. Disconnect a client from Connected apps in the
Retirement Lab account menu; its next MCP call will fail with 401.
- Protected-resource metadata: https://retirementlab.app/.well-known/oauth-protected-resource/api/mcp
- Authorization-server metadata: https://retirementlab.app/.well-known/oauth-authorization-server
API key (advanced fallback)
- Sign in at Retirement Lab.
- Open the account menu, choose MCP API keys, add a label, and issue the key.
- Copy the cleartext once. The server stores only its SHA-256 hash.
- Configure a Streamable HTTP client with URL
https://retirementlab.app/api/mcpand headerAuthorization: Bearer <key>. - Rotate or revoke the key from the same account screen. Rotation immediately invalidates the previous value.
Never send an API key to another host or place it in a prompt, source file, or shared transcript.
Supported clients and transport
Retirement Lab uses stateless Streamable HTTP and supports MCP protocol
revisions 2025-03-26 and 2025-06-18. It is designed for
remote-connector clients with OAuth, including Claude on the web and Claude
Desktop. Remote connectors already added on the web can be used from Claude
mobile. Claude Code and other MCP clients can connect when they support
Streamable HTTP plus OAuth, or a client-specific static bearer header for the
API-key fallback.
Generic setup values:
{
"name": "Retirement Lab",
"transport": "streamable_http",
"url": "https://retirementlab.app/api/mcp",
"authentication": "oauth"
}
Currency contract
- A simulation’s
countryis its country of tax residence. - Country never determines the units of monetary input values.
- Root-level
input_currencydeclares those units as an ISO 4217 code. - All monetary values in one payload must use the same currency.
- The server converts once into the tax-residence country’s calculator currency and returns the rate and provenance it used.
For explicit monetary sweep values, use sweep_currency. For a
JSON Merge Patch containing money, use patch_currency. Do not infer
currency from a symbol, country, citizenship, or income-source country.
Supported countries and tax regimes
This list is rendered from the calculator’s tax-policy registry:
Australia, Austria, Belgium, Brazil, Canada, Chile, Costa Rica, Cyprus, Czech Republic, France, Germany, Greece, Hungary, Italy, Japan, Malaysia, Malta, Mexico, Netherlands, Panama, Portugal, Singapore, South Africa, Spain, Switzerland, Thailand, UAE, UK, US, Uruguay, Vietnam
The default regime is the ordinary resident tax model for the selected country. These additional time-bounded regimes are rendered from the implementation registry:
| Regime | Country | Input key | Modeled duration |
|---|---|---|---|
Cyprus_NonDom | Cyprus | non-dom | 17 years |
Greece_Pensioner | Greece | foreign-pensioner-flat-7pct | 15 years |
Italy_Pensioner | Italy | pensioner-flat-7pct | 10 years |
Malta_NonDom | Malta | non-dom | 17 years |
NHR | Portugal | nhr | 10 years |
Call get_supported_inputs at the start of a session for the
current machine-readable countries, regimes, currencies, portfolio presets,
assumptions version, wrapper IDs, and a minimal valid payload.
Account wrapper discovery
wrapper_ids_by_country lists the account wrappers available in each
country. A Roth conversion must name a stable wrapper id, never a bucket name
such as tax_deferred, and get_supported_inputs is the
source of valid IDs.
| Country | ID | Label | Bucket |
|---|---|---|---|
| Australia | superannuation | Superannuation | tax_deferred |
| Australia | taxable | Taxable account | taxable |
| Austria | taxable_generic | Taxable brokerage | taxable |
| Austria | tax_deferred_generic | Tax-deferred account | tax_deferred |
| Austria | tax_free_generic | Tax-free account | tax_free |
| Belgium | taxable_generic | Taxable brokerage | taxable |
| Belgium | tax_deferred_generic | Tax-deferred account | tax_deferred |
| Belgium | tax_free_generic | Tax-free account | tax_free |
| Brazil | taxable_generic | Taxable brokerage | taxable |
| Brazil | tax_deferred_generic | Tax-deferred account | tax_deferred |
| Brazil | tax_free_generic | Tax-free account | tax_free |
| Canada | rrsp | RRSP | tax_deferred |
| Canada | tfsa | TFSA | tax_free |
| Canada | non_registered | Non-registered account | taxable |
| Chile | taxable_generic | Taxable brokerage | taxable |
| Chile | tax_deferred_generic | Tax-deferred account | tax_deferred |
| Chile | tax_free_generic | Tax-free account | tax_free |
| Costa Rica | taxable_generic | Taxable brokerage | taxable |
| Costa Rica | tax_deferred_generic | Tax-deferred account | tax_deferred |
| Costa Rica | tax_free_generic | Tax-free account | tax_free |
| Cyprus | taxable_generic | Taxable brokerage | taxable |
| Cyprus | tax_deferred_generic | Tax-deferred account | tax_deferred |
| Cyprus | tax_free_generic | Tax-free account | tax_free |
| Czech Republic | taxable_generic | Taxable brokerage | taxable |
| Czech Republic | tax_deferred_generic | Tax-deferred account | tax_deferred |
| Czech Republic | tax_free_generic | Tax-free account | tax_free |
| France | per | PER | tax_deferred |
| France | pea | PEA | tax_free |
| France | assurance_vie | Assurance-vie | tax_deferred |
| France | livret_a | Livret A | tax_free |
| France | fr_taxable_brokerage | Taxable brokerage | taxable |
| Germany | riester | Riester | tax_deferred |
| Germany | ruerup | Ruerup | tax_deferred |
| Germany | bav | bAV | tax_deferred |
| Germany | de_taxable_brokerage | Taxable brokerage | taxable |
| Greece | taxable_generic | Taxable brokerage | taxable |
| Greece | tax_deferred_generic | Tax-deferred account | tax_deferred |
| Greece | tax_free_generic | Tax-free account | tax_free |
| Hungary | taxable_generic | Taxable brokerage | taxable |
| Hungary | tax_deferred_generic | Tax-deferred account | tax_deferred |
| Hungary | tax_free_generic | Tax-free account | tax_free |
| Italy | fondo_pensione | Fondo pensione | tax_deferred |
| Italy | pip | PIP | tax_deferred |
| Italy | pir | PIR | tax_free |
| Italy | it_taxable_brokerage | Taxable brokerage | taxable |
| Japan | idec | iDeCo | tax_deferred |
| Japan | nisa | NISA | tax_free |
| Japan | corporate_dc | Corporate DC | tax_deferred |
| Japan | jp_taxable_brokerage | Taxable brokerage | taxable |
| Malaysia | taxable_generic | Taxable brokerage | taxable |
| Malaysia | tax_deferred_generic | Tax-deferred account | tax_deferred |
| Malaysia | tax_free_generic | Tax-free account | tax_free |
| Malta | taxable_generic | Taxable brokerage | taxable |
| Malta | tax_deferred_generic | Tax-deferred account | tax_deferred |
| Malta | tax_free_generic | Tax-free account | tax_free |
| Mexico | taxable_generic | Taxable brokerage | taxable |
| Mexico | tax_deferred_generic | Tax-deferred account | tax_deferred |
| Mexico | tax_free_generic | Tax-free account | tax_free |
| Netherlands | taxable_generic | Taxable brokerage | taxable |
| Netherlands | tax_deferred_generic | Tax-deferred account | tax_deferred |
| Netherlands | tax_free_generic | Tax-free account | tax_free |
| Panama | taxable_generic | Taxable brokerage | taxable |
| Panama | tax_deferred_generic | Tax-deferred account | tax_deferred |
| Panama | tax_free_generic | Tax-free account | tax_free |
| Portugal | ppr | PPR | tax_deferred |
| Portugal | pt_taxable_brokerage | Taxable brokerage | taxable |
| Singapore | taxable_generic | Taxable brokerage | taxable |
| Singapore | tax_deferred_generic | Tax-deferred account | tax_deferred |
| Singapore | tax_free_generic | Tax-free account | tax_free |
| South Africa | taxable_generic | Taxable brokerage | taxable |
| South Africa | tax_deferred_generic | Tax-deferred account | tax_deferred |
| South Africa | tax_free_generic | Tax-free account | tax_free |
| Spain | plan_de_pensiones | Plan de Pensiones | tax_deferred |
| Spain | ppa | PPA | tax_deferred |
| Spain | pias | PIAS | tax_free |
| Spain | es_taxable_brokerage | Taxable brokerage | taxable |
| Switzerland | taxable_generic | Taxable brokerage | taxable |
| Switzerland | tax_deferred_generic | Tax-deferred account | tax_deferred |
| Switzerland | tax_free_generic | Tax-free account | tax_free |
| Thailand | taxable_generic | Taxable brokerage | taxable |
| Thailand | tax_deferred_generic | Tax-deferred account | tax_deferred |
| Thailand | tax_free_generic | Tax-free account | tax_free |
| UAE | taxable_generic | Taxable brokerage | taxable |
| UAE | tax_deferred_generic | Tax-deferred account | tax_deferred |
| UAE | tax_free_generic | Tax-free account | tax_free |
| UK | sipp | SIPP | tax_deferred |
| UK | isa | ISA | tax_free |
| UK | gia | General Investment Account | taxable |
| US | 401k | 401(k) | tax_deferred |
| US | traditional_ira | Traditional IRA | tax_deferred |
| US | roth_ira | Roth IRA | tax_free |
| US | roth_401k | Roth 401(k) | tax_free |
| US | hsa | HSA | tax_free |
| US | taxable_brokerage | Taxable brokerage | taxable |
| Uruguay | taxable_generic | Taxable brokerage | taxable |
| Uruguay | tax_deferred_generic | Tax-deferred account | tax_deferred |
| Uruguay | tax_free_generic | Tax-free account | tax_free |
| Vietnam | taxable_generic | Taxable brokerage | taxable |
| Vietnam | tax_deferred_generic | Tax-deferred account | tax_deferred |
| Vietnam | tax_free_generic | Tax-free account | tax_free |
Generic wrapper IDs: taxable_generic (taxable), tax_deferred_generic (tax_deferred), tax_free_generic (tax_free). A generic ID requires a
corresponding explicit account when accounts are provided.
taxable, tax_deferred, and tax_free
are bucket classifications, not wrapper IDs.
Annual savings window
Validation rule: A positive annual_savings requires retirement_year to be greater than initial_year. The MCP error returned before enqueue states the same rule.
Relocation discovery
Only same-currency relocation events are currently supported, such as
Spain to Portugal (EUR).
For different-currency tax residences, use compare_countries
to run separate hypothetical projections.
Asynchronous run lifecycle
- Enqueue: call
run_simulation,compare_countries,sensitivity_sweep, orrun_sweep. Save the returnedrun_idandpoll_after_ms. - Wait or poll: call
wait_for_runwith that id, or pollget_run_result, until status issucceededorfailed. - Inspect: call
get_run_resultwith selectors such assweep_points,comparison,yearly_percentiles, orsimulation_snapshot. The stored result reports what the hypothetical simulation shows; it does not rerun compute.
Succeeded sweeps return
compact sweep_points by default, and
sweep_points is also an explicit
get_run_result selector.
Idempotent enqueue
Reusing an
idempotency_key replays a stored enqueue for 15 minutes. After
that window the key has expired, so the same key is a new enqueue rather than
a replay guarantee.
Inspect an immutable share
get_share returns compact headline results by default and omits
the full configuration and percentile time series.
{
"token_or_url": "example-token"
}
Request canonical inputs or year-by-year bands explicitly:
{
"token_or_url": "example-token",
"selectors": [
"simulation_snapshot"
]
}
{
"token_or_url": "example-token",
"selectors": [
"yearly_percentiles"
]
}
Current quotas and tier limitations
The values below are read from the same settings used by the runtime. They can change as the Public Beta is tuned.
| Limit | Free | Pro / 30-day Pass |
|---|---|---|
| Daily MCP compute units | 10 | 1000 |
| Simulation paths per payload | 10,000 | 100,000 |
| Explicit sweep values | 10 | 100 |
| Simulation horizon | 75 years | 120 years |
| Country comparison | Up to 2 countries | Subject to sweep caps |
Compute requests also use a distributed limit of
60 requests per 60 minutes, a
1,000,000-byte compact-result cap, and a
30-second maximum per wait_for_run call.
An account can keep up to 5 active API keys.
OAuth and API keys for one user draw from the same user-level compute allowance.
Product feature gates still apply; for example, a comparison with three or more
countries requires Pro or a 30-day Pass.
Complete example workflows
These inline examples deliberately omit simulation_mode. For MCP
simulation and sweep calls, omission defaults to
monte_carlo_iid; set another supported mode explicitly to override it.
1. Run a retirement simulation
First call get_supported_inputs. Then call
run_simulation with these arguments:
{
"input_currency": "USD",
"simulation": {
"initial_year": 2026,
"age": 45,
"plan_to_age": 95,
"initial_net_worth": 1000000,
"baseline_annual_expenses": 60000,
"additional_expenses": [],
"additional_income": [],
"country": "US",
"avg_return": 0.07,
"volatility": 0.12,
"approximate_yield_rate": 0.02,
"taxable_balance": 500000,
"tax_deferred_balance": 350000,
"tax_free_balance": 150000,
"taxable_cost_basis": 350000
},
"idempotency_key": "example-retirement-run-1"
}
Use the returned id in wait_for_run:
{
"run_id": 123,
"max_wait_seconds": 10
}
If still queued or running, wait for poll_after_ms and repeat.
When it succeeds, inspect compact results:
{
"run_id": 123,
"selectors": [
"yearly_percentiles"
]
}
2. Compare countries or tax-residence assumptions
Call compare_countries with the same USD-denominated financial
inputs for each tax residence. A three-country comparison requires Pro or a
30-day Pass.
{
"input_currency": "USD",
"base_payload": {
"simulation": {
"initial_year": 2026,
"age": 45,
"plan_to_age": 95,
"initial_net_worth": 1000000,
"baseline_annual_expenses": 60000,
"additional_expenses": [],
"additional_income": [],
"country": "US",
"avg_return": 0.07,
"volatility": 0.12,
"approximate_yield_rate": 0.02,
"taxable_balance": 500000,
"tax_deferred_balance": 350000,
"tax_free_balance": 150000,
"taxable_cost_basis": 350000
}
},
"countries": [
"US",
"Spain",
"Portugal"
],
"idempotency_key": "example-country-comparison-1"
}
Wait for the returned run id, then call get_run_result with:
{
"run_id": 124,
"selectors": [
"comparison"
]
}
Each row is a hypothetical projection for the same supplied inputs under that tax-residence assumption. It is not a relocation instruction.
Cost-of-living scaling
can change the spending basis for each country point, and
applied_assumptions.cost_of_living is the receipt for what was applied.
failure_rate_without_tax removes taxes from an already transformed point;
it does not undo FX conversion, COL-adjusted spending, or other explicit point
transforms.
3. Run a sensitivity sweep and inspect the result
Call sensitivity_sweep to vary one supported assumption:
{
"input_currency": "USD",
"payload": {
"simulation": {
"initial_year": 2026,
"age": 45,
"plan_to_age": 95,
"initial_net_worth": 1000000,
"baseline_annual_expenses": 60000,
"additional_expenses": [],
"additional_income": [],
"country": "US",
"avg_return": 0.07,
"volatility": 0.12,
"approximate_yield_rate": 0.02,
"taxable_balance": 500000,
"tax_deferred_balance": 350000,
"tax_free_balance": 150000,
"taxable_cost_basis": 350000
}
},
"variable": "retirement_year",
"values": [
"2040",
"2045",
"2050"
],
"idempotency_key": "example-retirement-age-sweep-1"
}
Wait for the returned run id, then inspect the stored sweep:
{
"run_id": 125,
"selectors": [
"sweep_points"
]
}
Summarize only the failure probabilities and other values the simulation returns, given these assumptions.
Completed-run web records
When presenting a completed run, include its history_url as the
account's saved web record. Succeeded result tools describe only manifested,
same-account web capabilities. Public-share availability names
create_share_link only when available and always requires the
user's request; it does not create a share automatically.
Link campaigns distinguish surfaces: history uses run_result,
result attribution uses funnel_attribution, shares use
share_link, and downloads use run_export. They are not
impression, click, or CTR events.
Tool catalog and safety behavior
This catalog is generated from the registered MCP tools. Public-share creation is explicitly marked as a state-changing action that crosses a public boundary.
- Render Projection
render_projectionRead-onlyAfter reading or waiting for a completed run or immutable share, call this once to render its stored hypothetical projection. It never starts or polls compute.
- Run Retirement Simulation
run_simulationChanges account state; may cross a public/external boundaryEnqueue a hypothetical projection; based on your inputs, the simulation shows status and polling details after REST authorization checks run. For an inline simulation, omitting simulation_mode defaults to monte_carlo_iid.
- Run Parameter Sweep
run_sweepChanges account state; may cross a public/external boundaryGeneral sweep interface for the broader sweep-variable set and engine-generated points. Enqueue a hypothetical projection after REST tier and run-limit checks; use compare_countries for country comparisons. For an inline simulation, omitting simulation_mode defaults to monte_carlo_iid.
- Compare Tax Residences
compare_countriesChanges account state; may cross a public/external boundaryCompare supported countries by enqueueing a country sweep for a hypothetical projection; based on your inputs, the simulation shows status and polling details. For an inline simulation, omitting simulation_mode defaults to monte_carlo_iid.
- Run Sensitivity Sweep
sensitivity_sweepChanges account state; may cross a public/external boundaryPreferred convenience wrapper for explicit values of its supported single non-country variables. Enqueue a hypothetical projection and return status and polling details. For an inline simulation, omitting simulation_mode defaults to monte_carlo_iid.
- Inspect Simulation Result
get_run_resultRead-onlyReturn compact stored results for a run; the simulation shows probabilities, terminal-wealth percentiles, and selected details based on your inputs. Succeeded sweeps include compact sweep_points by default; sweep_points is also an explicit sweep selector. Use the yearly_percentiles selector for year-by-year percentile bands of the hypothetical projection, suitable for charting.
- Read Saved Scenario
get_scenarioRead-onlyRead a private mutable saved scenario and its normalized calculator inputs.
- Read Shared Result
get_shareRead-only; may cross a public/external boundaryRead a compact immutable public hypothetical-result share summary. The default omits the full configuration and year-by-year arrays. Select simulation_snapshot for canonical inputs or yearly_percentiles for aligned years and p10/p50/p90 arrays. To change its inputs, create a private variant from the share directly.
- List Saved Scenarios
list_scenariosRead-onlyList private saved calculator scenarios, which may later be updated or cloned.
- List Simulation Runs
list_runsRead-onlyList private immutable hypothetical-run summaries without loading result or simulation payloads.
- List Shared Results
list_sharesRead-onlyList immutable public hypothetical-result share snapshots owned by this account.
- Create Saved Scenario
create_scenarioChanges account state; may cross a public/external boundaryCreate a new mutable saved calculator scenario from a validated simulation payload.
- Update Saved Scenario
update_scenarioChanges account stateApply JSON Merge Patch to saved calculator inputs using optimistic concurrency. Runs and public shares cannot be updated.
- Clone Scenario
clone_scenarioChanges account stateCreate a new mutable saved calculator scenario from a scenario, immutable run, or immutable public share snapshot.
- Create Scenario Variant
create_variantChanges account state; may cross a public/external boundaryApply JSON Merge Patch to a scenario, run, or public share; optionally save and enqueue the resulting hypothetical projection. Lineage is persisted.
- Preview Exit Tax
preview_exit_taxRead-only; may cross a public/external boundaryPreview a proposed relocation exit-tax calculation; based on your inputs, the hypothetical projection shows estimated tax impact without enqueueing compute.
- Preview Roth Conversion Tax
preview_conversion_taxRead-only; may cross a public/external boundaryPreview a Roth conversion tax calculation; based on your inputs, the hypothetical projection shows estimated tax impact without enqueueing compute.
- Wait for Simulation Run
wait_for_runRead-onlyPoll a stored run for a bounded period and return what the simulation shows once available, or a still-running envelope. Use the yearly_percentiles selector for year-by-year percentile bands of the hypothetical projection, suitable for charting.
- Save Run as Scenario
save_scenarioChanges account stateSave an unnamed run as a named scenario when the user asks to keep, name, or share it; otherwise runs stay unnamed and appear in the web app's run history. The history_url opens the stored hypothetical projection in the web app.
- Create Public Share Link
create_share_linkChanges account state; may cross a public/external boundaryCreate an externally visible, unlisted, immutable hypothetical-result share URL from exactly one completed run_id or the latest clean completed run for config_name. Anyone with the URL can view it; this never reruns a scenario.
- Cancel Simulation Run
cancel_runChanges account stateCancel a queued or running MCP-originated hypothetical projection owned by this account.
- List Supported Inputs
get_supported_inputsRead-onlyList supported calculator inputs, including countries, tax regimes, simulation modes, and the current assumptions version. The response also includes a ready-to-adapt minimal example payload and a short list of common input traps, including that omitted simulation_mode defaults to monte_carlo_iid for MCP runs.
Data handling
- OAuth and API-key calls run as your Retirement Lab account and can read or change only resources the account can access.
- Scenario inputs and simulation results are stored so you can inspect and rerun them. Authenticated account data remains until account deletion.
- OAuth tokens and API keys are opaque and stored only as hashes. Cleartext API keys are displayed once.
- Tool-call analytics record an internal user id, tool name, result status, tier, and coarse authentication method. They do not store raw credentials, email addresses, prompts, or financial payloads for adoption measurement.
- A link created by
create_share_linkis unlisted but public: anyone with it can view the immutable shared result.
See the Retirement Lab Privacy Policy for collected data, processors, retention, export, deletion, and contact details.
Active-user metric: A distinct non-test Retirement Lab user with at least one MCP tool-call analytics event in the rolling window. OAuth and API-key breakdowns may overlap when one user uses both methods; no credential secrets or email addresses are stored in this metric.
Troubleshooting
- OAuth does not start or fails
- Confirm the endpoint is exactly
https://retirementlab.app/api/mcp, allow the browser sign-in window, and retry from the client. If a prior connection is stale, disconnect it under Connected apps and connect again. - Invalid inputs
- Call
get_supported_inputs, use an exact country label, includeinput_currency, and keep all money in that payload in the declared currency. Read structured validation details before retrying. - A run is still queued or running
- This is expected: simulations are asynchronous. Honor
poll_after_msand callwait_for_runorget_run_resultagain. Do not enqueue a duplicate; reuse anidempotency_keywhen retrying an enqueue. - Quota or tier error
- Read the returned cap, remaining daily compute units, and reset timing. Reduce paths, sweep values, or countries, or wait for the quota window. A tier error describes the product capability required.
- Expired or revoked credentials
- For OAuth, restart the connection flow. For an API key, issue or rotate a key in the account menu and replace the old bearer value. Revoked values cannot be restored.
Support
Visit support or contact support@cedarwelllabs.com and include the client name, approximate time, tool name, and non-secret error text. Never send an API key, OAuth token, or complete financial payload. The availability of inbound routing does not establish how outbound replies are configured.