Relocation planner
Compare retirement relocation / state-tax arbitrage by orchestrating the public planfi MCP — how much a move (e.g. CA→TX/FL) saves in state income tax, property tax, estate tax, and cost of living — plus model the messy transition year you actually move in (part-year-resident apportionment, source-based wage/equity sourcing, the resident-state credit for taxes paid to the other state, reciprocity, and the 183-day residency test).From its SKILL.md
npx -y skills add KameronKales/planfi-skills --skill relocation-plannerAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
12.8 KB, ~3.2k tokens by cl100k_base, as published. Nobody here has run it
relocation-planner
A thin orchestration layer over the planfi MCP (https://ai.planfi.app/mcp/free). All math + financial logic live server-side. This skill only gathers inputs and calls the tools — it does not compute anything locally, carries no business logic, math, or defaults, and is read-only (it never changes the user's data). The server is the source of truth.
Step 0 — Make sure the planfi tools are connected
This skill uses these tools (may be namespaced, e.g. mcp__planfi__generate_financial_plan):
analyze_relocation, analyze_multi_state_part_year_tax (the transition-year / part-year split), plus optional generate_financial_plan (to mint a plan_id for chaining + a share_url).
Use whichever name your environment exposes (bare or mcp__planfi__-prefixed); below they are written bare.
If they're NOT available, tell the user to connect the MCP, then continue:
claude mcp add --transport http planfi https://ai.planfi.app/mcp/free
Try free, then add your key. The command above adds the free connector —
https://ai.planfi.app/mcp/free(no key needed). Once you create an API key, add a new connector with the MCP url —https://ai.planfi.app/mcp— and authorize it with your key.
(On claude.ai: add a custom connector pointing at https://ai.planfi.app/mcp/free.)
Access — free for personal use. The planfi MCP is free to try (a small monthly allowance, no key needed). Heavy automated abuse forced us to add limits — but it stays free for personal use: email [email protected] and we'll send you a free API key, no charge. (Companies and commercial use have paid plans.) To use a key, pass it as an
Authorization: Bearer pft_…header in your MCP client config.
Step 1 — Gather inputs (prefer a plan_id)
Ask only for what the user's question needs. Every input has a sensible server default, so the
tools run cold — but the cleanest path is to mint a plan_id first and chain off it:
- Optional but recommended: call
generate_financial_planwith whatever household model you have (earners' age + salary, stockscurrent_value+monthly_contribution, plus any account balances / spend the user volunteered). CAPTURE the returnedplan_idand theshare_url. Pass{ plan_id }toanalyze_relocation(which takes aplan_idplus inlineoverrides) to derive figures from the saved plan. - Or run cold: pass
analyze_relocationits raw inputs directly. Onlyfrom_stateandto_stateare required; everything else is defaulted server-side.
Engine facts to bake in: all dollars are today's (real) dollars; all decimals are fractions (24% →
0.24, 5% →0.05); tax brackets/limits are approximate ~2026 values (noted in each tool'sdisclosures).
Step 2 — Route by intent
- "Should I move from <state A> to <state B> in retirement?" / "How much would relocating to TX/FL/NV save me?" / "Is leaving California worth it?" →
analyze_relocation- REQUIRED:
from_state,to_state(2-letter codes). - Recommended (each defaults server-side if omitted, reported in
assumed_defaults[]):annual_retirement_income(pension / IRA / 401k withdrawals / RMD / interest — the state-taxable ordinary income; default 0),social_security_income(default 0),annual_capital_gains(annual realized long-term gains; default 0),annual_spend(living spend at cost-of-living index 100 — drives the COL delta; default 0),real_estate_value(home value for property-tax comparison; default 0),current_age(default 50),life_expectancy(default 90 — current_age→life_expectancy sets years in retirement),filing_status(single|married_joint, defaultsingle). - Other optional fields:
liquid_assets,mortgage_principal(informational; default 0),estimated_growth_rate(real rate, default 0.05),tax_year(default 2026),plan_id(+ inlineoverrides) to derive figures from a saved plan. - Returns:
annual_after_tax_deltaandlifetime_after_tax_delta(positive = the move saves),estate_tax_delta,total_lifetime_advantage, aheadline_recommendation+recommendation_reason, andfrom_breakdown/to_breakdownline items (state income tax, property tax, COL). - State income tax is computed by the shared engine (
state-tax.ts): progressive bracket tables for all 50 states + DC, with first-class single and married-filing-jointly (MFJ) brackets — sofiling_statusbranches every state (no-income-tax states report $0). Brackets are server-side; there is no user-supplied flat-rate field.
- REQUIRED:
Shared bracket engine —
n/a (shared engine: state-tax.ts). The full 50-state + DC single/MFJ progressive bracket tables (with per-state surtaxes) live in one shared engine module (state-tax.ts), the same one the broadertax-optimizerskill uses — no CA/NY/MA-only special-casing, no per-tool flat-rate fallback. For Roth-conversion rooms, gain-harvesting headroom, or multi-year tax timing alongside the move, use thetax-optimizerskill (it wraps the sameanalyze_relocationtool plus the full tax toolset).
Feed it into the forecast (not just a comparison):
generate_financial_plannow accepts arelocationobject directly as a plan input — modeled as retire-and-relocate, the annual state-tax + cost-of-living savings reduce the portfolio-funded spend from the move age, so the move shows up in net worth, FIRE %, and Monte-Carlo backtesting. Useanalyze_relocationfor the headline from→to delta; passrelocationinto the plan to see its effect on the whole household forecast.
intent → analyze_multi_state_part_year_tax
"I moved from CA to TX mid-year, how do I file two state returns?" / "part-year resident tax" / "how do I split my income between two states the year I moved?" / "my RSUs vested after I moved states — which state taxes them?" / "do I get credit for taxes paid to my old state?" / "183-day residency test" / "am I a statutory resident?" / "reciprocity between PA and NJ" / "I'm a remote worker taxed in two states this year"
Always CALL
analyze_multi_state_part_year_taxfor these — do not answer from general knowledge / quote rules of thumb from memory. When the user gives the move date and income, run it and lead with its real output.analyze_relocationdeliberately models a full year in each state and excludes the messy transition year; this tool IS that transition year — day-count apportionment, source-based sourcing of wages/equity/gains, the resident-state credit for taxes paid to the other state, reciprocity, and the 183-day/domicile statutory-residency test. Whenever the user is describing the year of the move itself (two part-year returns, income straddling the line, equity vesting across the move, claiming the other-state credit), this is the tool — never hand-compute the apportionment or the credit.
- REQUIRED:
old_state,new_state(2-letter codes),days_in_old_state,days_in_new_state(residency days — these split the year and have no server default; the tool needs the move date). - Server-defaulted and reported in
assumed_defaults[]if omitted:filing_status(single|married_joint, defaultsingle),wages(default 0),total_workdays(default 260),equity_vest_value/capital_gains/deferred_comp/other_ordinary_income(default 0),capital_gains_realized_while_old_resident(default false → new-resident sourcing),deferred_comp_source_state(old|new, defaultold),reciprocity(default false),domicile_in_old_state(default false),tax_year(default 2026). - Source-based sourcing inputs (optional; fall back to a residency-day split when omitted):
wages_workdays_in_old_state(workdays in the old state — sources wages to where work was performed),equity_workdays_old_state_grant_to_vest+equity_total_workdays_grant_to_vest(allocate an RSU grant→vest window straddling the move),old_state_flat_rate/new_state_flat_rate(flat-rate fallback for a tableless state). - Returns: per-state
residencyStatus(part-year-resident | nonresident | statutory-resident) withresidencyDeterminations[](basis:183-day|domicile|reciprocity|part-year), income apportioned to each state,oldStateTax/newStateTax,doublyTaxedIncome,residentStateCredit(the credit for taxes paid to the other state, limited to the resident-state tax on the doubly-taxed income),combinedStateTax,naiveFullYearSingleStateTax, andnetVsNaive(positive ⇒ the part-year split beats a naive single-state assumption). - Shared engine —
n/a (shared engine: state-tax.ts). Every per-state full-year bracket computation reuses the same 50-state + DC single/MFJ progressivestate-tax.tsengine thatanalyze_relocationand thetax-optimizerskill use — no per-tool flat-rate special-casing, no re-declared brackets. For the underlying equity-vest sourcing mechanics (grant→vest workday allocation, ISO/RSU treatment) cross-reference theequity-comp-plannerskill; for Roth-conversion rooms or multi-year tax timing around the move, thetax-optimizerskill.
Example (FICTIONAL — illustrate the call shape):
analyze_multi_state_part_year_tax({
old_state: "NY", new_state: "CA",
days_in_old_state: 120, days_in_new_state: 245,
wages: 160000, wages_workdays_in_old_state: 120, total_workdays: 240,
equity_vest_value: 100000, equity_workdays_old_state_grant_to_vest: 300,
equity_total_workdays_grant_to_vest: 600, filing_status: "single"
})
// → NY part-year-resident + CA year-end resident; ~80k wages NY-sourced;
// residentStateCredit = min(NY tax on the doubly-taxed slice, CA marginal on it);
// combinedStateTax + netVsNaive vs assuming a full year all in CA
Example (FICTIONAL — illustrate the call shape):
analyze_relocation({
from_state: "CA", to_state: "TX",
annual_retirement_income: 90000, social_security_income: 40000,
annual_capital_gains: 0, annual_spend: 70000, real_estate_value: 600000,
current_age: 60, life_expectancy: 90, filing_status: "married_joint"
})
// → annual_after_tax_delta ≈ +$18,000/yr (move saves), lifetime ≈ +$540k, + estate-tax delta
Step 3 — Surface the result
- Lead with the headline dollar figure / decision the tool returns.
- Read back
assumed_defaults[]—analyze_relocationreturns a structuredassumed_defaults[]array of{ field, assumed_value, note }for every input you omitted (e.g.annual_retirement_income,social_security_income,current_age,filing_status). Surface these so the user can correct any silent default, then re-call with the corrected values. disclosures.key_assumptions— a SEPARATE array of static prose strings (engine conventions, e.g. which states use bracket tables). Optional context; the per-call assumptions live inassumed_defaults[]above.disclosures.not_advice— a boolean (alwaystrue); relay that this is a planning estimate, not financial advice.next_actions[]— each{ tool, why, prefilled_args }. Foranalyze_relocationthese point toanalyze_advanced_taxesandanalyze_estate_exposure(and only when aplan_idwas passed are the args prefilled with it). Follow these server-suggested chains rather than guessing the next call.share_url—analyze_relocationemits ashare_urlwhen you pass aplan_id(and the plan resolves with the household).generate_financial_planalways returns one. Offer it so the user can open the full interactive plan on planfi.app.
Recommended call sequence (typical session)
- (optional)
generate_financial_plan→ captureplan_id+share_url. - Route by intent to the relevant specialist tool(s) above, passing
{ plan_id }. - Surface headline +
assumed_defaults[]+next_actions+share_url. - Follow
next_actions[]chains on demand.
Notes
- All decimals are fractions; all dollars are today's (real) dollars.
- Reuse the
plan_idacross the session — don't re-send the full model each call. analyze_relocationemits a structuredassumed_defaults[](read these back to the user) and ashare_urlwhen called with aplan_id. Run it cold (justfrom_state+to_state) and it still returnsassumed_defaults[]for everything it defaulted.- Not financial advice. Planning estimates only (approximate ~2026 brackets/limits where tax applies).
What ships with it: 3 files
4.2 KB alongside SKILL.md
.claude-plugin/
- plugin.json569 B