Run several accounts or UMA sleeves against one shared gain budget per tax unit, reserving budget so no two runs spend the same allowance and screening cross-sleeve wash sales
One command on the Household and UMA coordination server, callable over MCP as coordinate_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 | Shared 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 do I share one gain budget across accounts?”
- “How do UMA sleeves avoid washing each other's losses?”
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
3 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-coordinated | Two accounts, one shared budget | Budget is reserved per tax unit as each account is served, so no two runs spend the same allowance. | completed_with_limitations |
household-uma-wash | One sleeve would wash another sleeve's loss | Same account, same taxpayer, two managers. Neither did anything wrong and the loss would still be disallowed, so it is caught first. | completed_with_limitations |
household-gap | Falling short, and the ceiling it was measured against | Sequential allocation depends on the order units are served, so the run reports an LP bound on what any allocation could have raised. | 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.