Solve the whole household at once rather than account by account
One command on the Household and UMA coordination server, callable over MCP as optimize_household. Every result carries the method it used, the rule pack it was pinned to, what is still missing and whether coverage was complete, for a licensed professional to review.
What it needs, and why
Every input is checked before anything is calculated. subject_ref, document_ref, as_of, prices, idempotency_key and cash_target are required; the rest refine the answer.
| Input | What it asks for | Why it matters | If you omit it |
|---|---|---|---|
subject_ref | Account or household id you are entitled to read | The account or household the analysis is scoped to. Naming one is a request to read it, never a grant. | required |
document_ref | Path to the normalized holdings export to analyse | Account metadata, lots and basis come from the export, and nothing resolves until one has been read. | required |
as_of | Analysis date, YYYY-MM-DD | Holding periods and the 30-day wash-sale window are both measured from the analysis date. | required |
prices | Price per share by security id, as decimal strings | A gain cannot be computed without a price per security, and no price is ever fetched or guessed here. | required |
idempotency_key | Caller-chosen key | Repeating the key returns the first run instead of running again, so a retry cannot spend a gain budget twice. | required |
cash_target | Cash to raise across the household | The objective is proceeds by a date. The gain budget is the constraint on reaching it, not the goal. | required |
withdrawal_account | Account the cash is raised from | Cash is raised from a named account, which decides whose lots are sold and whose budget is spent. | optional |
gain_budget | Cap on realized gains, per tax unit | A cap on realized gains is a decision rather than a default, and supplying one requires saying how it is measured. | optional |
netting_basis | Whether the gain budget is measured net of losses or on gains alone one of net_gains, gross_gains | Gross and net readings of the same budget give different answers, so the server will not pick one for you. | optional |
period | Tax period the budget applies to, e.g. "2026" | A budget belongs to a tax period, and gains already realized in that period consume the allowance. | optional |
planned_purchases | Purchases already planned but not yet placed | A purchase a manager intends but has not placed can still create a wash sale, and only the caller knows about it. | optional |
Requests it answers
- “How should a whole household harvest together?”
- “Can a household-wide harvest be proven optimal?”
An agent with these skills loaded picks the command from phrasing like that, asks for whatever input is missing, and reports the figures with the method that produced them.
Run it
TAXAGENT_TENANT_ID=... TAXAGENT_PRINCIPAL_ID=... TAXAGENT_ACCOUNTS=... scripts/mcp_server.sh --server householdThe same command is exposed by taxagent-household, so Claude, Claude Code, Codex, Cursor and any other MCP client can call it directly. Analysis only. No tool in this server places orders, modifies custodian records or files returns. Two engines with different claims: optimize_household solves the whole household at once and, where the search exhausts, proves no other selection does better; coordinate_household serves accounts in a declared order, reserves budget as it goes, and is never optimal. Optimality is over the lots, prices and constraints supplied and for one objective, not a judgement about what the household should do. Budgets are allocated per tax unit: a household spanning several tax units gets several budgets and never one pooled allowance.
Scripted cases that exercise it
2 of the 71 console cases call this command on a real server over JSON-RPC and assert the outcome. Every one runs on every build, and all of them are listed on the case inventory.
| Case | What it asks | What it shows | Outcome |
|---|---|---|---|
household-joint-optimal | Solved jointly, with a proof | No ordering at all. Where the search exhausts, the run proves no other selection raises more cash within the budget — which the sequential coordinator can never say. | completed_with_limitations |
household-joint-infeasible-is-proven | An unreachable target, proven unreachable | Because each tax unit was searched exhaustively, the shortfall is a proof rather than a failure to find a plan. | INFEASIBLE_CONSTRAINTS |
Limits it inherits from taxagent-household
- Sequential allocation. Results depend on the declared unit order and are not a joint optimum.
- Budgets are allocated per tax unit; a household spanning several tax units gets several budgets, never one.
- Cross-unit wash-sale prevention covers accounts in scope and planned purchases supplied with the request. Unseen accounts and unreported manager intentions remain outside it.
- Solved per tax unit and summed. That decomposition is exact, not an approximation: a gain budget belongs to one taxpayer, so no selection in one unit changes what is available in another and the objective is additive. A household spanning several tax units still gets several budgets and never one pooled allowance.
- Wash-sale scope follows the taxpayer and is handled before the solve, by excluding lots whose loss would be disallowed. Accounts outside the snapshot remain unseen.
- Optimality is over the supplied lots and constraints. It says nothing about which account should hold which exposure, which is an allocation decision this engine does not make.
- Beyond 40 budget-consuming lots in a tax unit the search is not exhausted and the status degrades to feasible with a reported gap.
The server, its other 1 command and what it deliberately leaves alone are on the Household and UMA coordination page.