Skip to main content
Glama
AlvisoOculus

OptionsAhoy: Stock Equity and Tax Optimizer

OptionsAhoy MCP Server

Glama quality score npm version MCPSafe security grade MCP calls in the last 30 days

Independently verified by third parties. Glama: third-party MCP-directory quality score (tool docs, behavior, completeness). · npm: published with build provenance, a signed SLSA attestation that this package was built from this repo by GitHub Actions (verify with npm audit signatures). · MCPSafe: independent 5-model-consensus security scan (AIVSS), Grade A with zero findings.

Validated against trusted sources (checks we run ourselves, against references we do not control, and that you can reproduce). Computation: every 2026 federal tax constant matches its IRS Rev. Proc. 2025-32 / Internal Revenue Code value, and 14 worked federal cases (ordinary income, long-term capital gains, and the Alternative Minimum Tax including the incentive stock option bargain element) reproduce to the cent against the independently-maintained PSL Tax-Calculator, a tax model we did not write. State income tax is cross-checked the same way: 16 cases across California, New York, New Jersey, Pennsylvania, and Massachusetts reproduce to the cent against OpenTaxSolver, an independent state tax engine we also did not write. The headline answer is recomputed live in your browser.

Tested and hardened. Input safety: requests are validated against the published schema; bad inputs return a clear 400 with the offending field named, never a crash or a wrong number, and the live API is re-checked by a robustness suite after every deploy. · Test suite: the calculation engine is covered by more than a thousand automated tests across the federal and 50-state tax logic, AMT credit recovery, and option pricing; a failing test blocks the release.

Live usage: MCP calls over the last 30 days, served straight from the server's own telemetry (/api/v1/stats, aggregate counts only, no PII).

Deterministic equity-compensation tax math that any Model Context Protocol (MCP) client can call: incentive stock option (ISO) exercise schedules under the alternative minimum tax (AMT), non-qualified stock option (NSO) and restricted stock unit (RSU) decisions, qualified small business stock (QSBS) qualification, single-stock concentration, protective-put hedging, and equity-funding goals. Relevant federal tax code plus all 50 states and DC, 2026 brackets. Built by AlphaLatitude Inc., the company behind OptionsAhoy.

Why not just ask the model? We benchmarked five frontier large language models (LLMs), 3 runs each, 15 trials total, on the same multi-year ISO exercise problem. Every trial overstated the after-tax result of its own proposed schedule, by 2x to 20x. Multi-year scheduling has a search space larger than is practical to work through in-context; these tools return the verifiable answer instead. Live benchmark, updated for the latest models: optionsahoy.com/benchmark. Raw responses and scoring: llm-iso-benchmark. Full write-up: But can it do taxes though?

Install in one line

The hosted endpoint is https://optionsahoy.com/mcp (HTTP, no auth, no account). Quickest paths:

Client

Install

Any MCP client

Add https://optionsahoy.com/mcp as a remote HTTP server, or npx add-mcp https://optionsahoy.com/mcp

Claude Desktop

Download optionsahoy.mcpb and double-click it

19 clients via Smithery

npx @smithery/cli install alphalatitude/optionsahoy --client claude

Local stdio (npm)

npx -y optionsahoy-mcp

Full install matrix (Gemini CLI extension, config-file JSON, REST API, Google Cloud Agent Registry): optionsahoy.com/for-agents.

Related MCP server: QuantOracle

The eight tools

Tool name

What it computes

amt_iso_optimize

Multi-year ISO exercise schedule that maximizes after-tax net final value at the planning horizon, modeling AMT credit recovery, grant expiration, and the post-termination exercise window

nso_calculate

After-tax payout on an NSO exercise (federal, state, FICA), comparing sell-at-exercise vs hold for long-term capital gains

rsu_sell_vs_hold

RSU vest decision: sell at vest vs hold for long-term capital gains, including the gap between 22% supplemental withholding and your marginal bracket

concentration_analyze

Single-stock concentration risk (drawdown exposure at 30/50/70% downside), comparing after-tax sell-down, hold, and hedge strategies

protective_put_price

Protective put, zero-cost collar, and put spread pricing via Black-Scholes: annualized hedge cost, maximum loss, upside cap, protected band, floor-hit probability, and which structure it recommends

qsbs_check

Section 1202 QSBS qualification across the six statutory tests, with the OBBBA 2026 tiered exclusion and per-state conformity

equity_funding_plan

Multi-year, multi-stack sell schedule to hit a target after-tax amount by a deadline; returns four named plans plus the full risk/wealth frontier

rsu_lot_optimize

Which vested RSU lots to sell, and on which dates, to divest a target share fraction at the lowest computed tax: specific-lot identification, long-term deferral, and multi-year bracket spreading with in-plan loss carryforward, versus a FIFO sell order

The ISO optimizer searches its full discretized candidate space and refines share by share, matching a brute-force maximum to the cent on a published tractable case (see the proof); the planners run deterministic bracket-aware searches and the calculators return exact results. Deterministic computation, not a language-model guess. Coverage spans the relevant federal tax code (ordinary brackets, long-term capital gains, AMT with credit recovery, FICA, NIIT) plus all 50 states and DC (state ordinary brackets, LTCG treatment, state AMT for CA, CO, CT, MN). Same engine as the in-browser calculators at optionsahoy.com/tools; the API response carries the same computed figures as clicking through the tool.

What a call looks like (all eight tools)

One real call per tool, captured from https://optionsahoy.com/mcp and committed under docs/examples/, so every figure below is auditable against the response it came from. The inputs are deliberately explicit (no ticker, every date pinned), so re-running scripts/capture-readme-examples.mts reproduces the same numbers. Each block shows the ask, the arguments that carry the scenario, and what came back.

amt_iso_optimize

"I have 50,000 vested ISOs at a $4 strike and the stock is at $90. Married filing jointly, $300,000 of income, California. Should I exercise the whole block now or spread it out?"

{"shares": 50000, "strike": 4, "fmv": 90, "horizon": 4, "filingStatus": "married_joint",
 "ordinaryIncome": 300000, "stateCode": "CA", "grantDate": "2023-03-15",
 "expectedGrowth": 0.1, "volatilityDrag": 0.2, "cashReturnRate": 0.05, …}

The optimized schedule exercises 1,401 / 1,339 / 1,280 / 45,980 shares across the four years and ends at a net final value of $1,623,234, against $1,565,849 for exercising the whole block today. The AMT crossover sits at 699 shares: the first $60,185 of bargain element costs no AMT at all. (raw; the same position as the site's published worked example, priced with the explicit growth and drag above)

nso_calculate

"5,000 NSOs at an $8 strike, stock at $75. Do I sell at exercise or hold a year for long-term gains? Single, $250,000 income, California."

{"shares": 5000, "strike": 8, "currentPrice": 75, "expectedSalePrice": 90, "holdYears": 1,
 "holdFunding": "sell-to-cover", "volatility": 0.3, "ordinaryIncome": 250000,
 "filingStatus": "single", "stateCode": "CA", …}

The exercise itself is a $335,000 bargain element taxed $159,618, leaving $175,382 if you sell everything on the spot. Holding a year with a sell-to-cover projects $193,943 against $184,209 for selling now and investing the proceeds, a $9,734 edge for holding. (raw)

rsu_sell_vs_hold

"1,000 RSUs vesting at $200. Sell at vest or hold another year and a half? Married filing jointly, $300,000 income, New York."

{"shares": 1000, "currentPrice": 200, "expectedSalePrice": 220, "holdYears": 1.5,
 "volatility": 0.25, "ordinaryIncome": 300000, "filingStatus": "married_joint",
 "stateCode": "NY", "stillEmployed": true, …}

The vest costs $73,896 in total tax, of which $55,716 is federal against only $44,000 withheld at the flat supplemental rate: that gap is the April bill people do not see coming. Selling at vest and investing projects $136,247 at the end of that hold versus $130,817 for holding, so holding loses $5,430 here. (raw)

concentration_analyze

"$750,000 of my $2.25M is in one tech stock I bought in 2022 for $150,000. How exposed am I, and what does selling down cost?"

{"positionValue": 750000, "costBasis": 150000, "acquisitionDate": "2022-01-15",
 "sector": "tech_software", "totalAssets": 2250000, "expectedPositionReturn": 0.12,
 "volatility": 0.35, "ordinaryIncome": 350000, "filingStatus": "single", "stateCode": "CA", …}

The position is 33% of net worth, which the tool bands as "Concentrated", and a 50% drawdown in that one name would cost $375,000. Selling it down over three years pays $196,709 in tax against $208,368 for selling in a single year. (raw)

protective_put_price

"I want downside protection on a $500,000 tech position for a year, with a floor about 20% below spot. Put, collar, or put spread?"

{"positionValue": 500000, "sector": "tech_software", "volatility": 0.35,
 "protectionLevel": 0.2, "tenorYears": 1, "spreadRiskLevel": 0.1, "expectedReturn": 0.08}

A bare put struck at $400,000 costs $19,348 a year, 3.87% of the position. The zero-cost collar buys the same floor for a net premium of roughly zero by capping upside at $724,518, which it puts a 15.7% chance on reaching, and that is the structure it recommends. (raw) This call passes an explicit sigma, so every leg prices at that one number; pass a ticker instead and each leg prices at its own strike's implied volatility off that stock's live option chain, which costs more for a floor this far out of the money.

qsbs_check

"I bought founder stock in March 2020 and I am selling in March 2026 for a $5M gain. Is it QSBS, and how much is tax-free?"

{"acquisitionDate": "2020-03-01", "saleDate": "2026-03-15", "entityType": "us-c-corp",
 "acquisitionMethod": "original-issuance", "assetCategory": "under-50m",
 "industry": "tech-software", "adjustedBasis": 50000, "expectedGain": 5000000, …}

Verdict qualifies: all six statutory tests pass at 6.0 years held, so 100% of the $5,000,000 gain is excludable under the $10,000,000 per-issuer cap, worth $1,190,000 in federal tax. California does not conform, so the same gain is fully taxable by the state. (raw)

equity_funding_plan

"I need $400,000 after tax by June 2029 for a house. I hold 3,500 shares at $120 across two lots. What do I sell, and when?"

{"targetAfterTax": 400000, "targetDate": "2029-06-30", "stacks": [{"currentPrice": 120,
 "expectedAnnualGrowth": 0.08, "lots": [{"shares": 2000, "costBasisPerShare": 50,
 "acquisitionDate": "2022-01-15"}, …]}], "ordinaryIncome": 350000, "stateCode": "CA", …}

Selling everything today falls short: it nets $391,574 against the $400,000 goal. The recommended staged plan sells 3,314 of the 3,500 shares over the years to the deadline, lands at $400,075 after tax with $63,740 of tax paid, and beats a single sale in the target year by $12,976. (raw)

rsu_lot_optimize

"I hold 3,000 vested RSU shares from three vests, the stock is at $180, and I want to cut the position in half over the next two years. Which lots go?"

{"lots": [{"vestDate": "2022-08-15", "shares": 1200, "costBasisPerShare": 95},
 {"vestDate": "2024-02-15", "shares": 1000, "costBasisPerShare": 130}, …],
 "currentPrice": 180, "divestFraction": 0.5, "horizonYears": 2, "ordinaryIncome": 200000, …}

To divest 1,500 of the 3,000 shares it pairs the underwater newest lot against long-term gains from an older one, so the whole divestment costs $2,935 in tax and keeps $267,065 after tax, $29,942 more than selling the same fraction in FIFO order. (raw)

Every example above passes volatility and growth explicitly. In a real call you can instead pass ticker: volatility resolves from the published implied-vol snapshot as of the last market close, and growth from the cached trailing-CAGR snapshot. If either cannot be resolved the call returns an error naming the exact field rather than a guessed number, and protective_put_price echoes volatilitySource (explicit, chain, ticker, or sector-default) plus pricingMode (chain-skew or flat) so you always know which sigma was priced and whether the legs were priced at their own strikes.

These captures ran in the 2026 tax year. Figures move in a later tax year for the three tools whose schedules run forward from today's date (amt_iso_optimize, equity_funding_plan, rsu_lot_optimize); everything else is pinned by the dates in the arguments.

Use it in your agent framework (Python)

If you build agents in Python rather than calling the MCP endpoint directly, OptionsAhoy ships installable tool packages for the major agent frameworks. Each one wraps the same calculators behind the framework's native tool interface. All are published on PyPI and all are keyless: no OptionsAhoy account, no API key.

Framework

Install

Import

Example

LangChain

pip install optionsahoy-langchain

from langchain_optionsahoy import get_optionsahoy_tools

equity_agent.py

LlamaIndex

pip install llama-index-tools-optionsahoy

from llama_index.tools.optionsahoy import OptionsAhoyToolSpec

equity_agent.py

CrewAI

pip install crewai-optionsahoy

from crewai_optionsahoy import get_optionsahoy_tools

equity_crew.py

Plain Python client

pip install optionsahoy

from optionsahoy import OptionsAhoyClient

basic_client.py

The three framework adapters pull in the keyless optionsahoy client automatically. There is also an OpenBB Workspace agent (a FastAPI application built on the OptionsAhoy client) for use inside OpenBB Workspace. Source and runnable examples for all of the above live under integrations/python.

More ways to build

However your agent is built, there is a drop-in piece. All are public and keyless.

Building block

What it is

Vercel AI SDK tools

A TypeScript package (optionsahoy-ai-sdk) exposing all eight calculators as Vercel AI SDK tool() definitions, ready to spread into generateText / streamText.

Instruction kits

Editor rules and skills for Cursor, Windsurf, Claude Skills, and Claude Code subagents, so your coding agent calls the OptionsAhoy tools for equity-compensation questions.

Coding recipes

Copy-paste Python recipes, one self-contained file per question, calling the keyless API with only requests. Also in integrations/recipes.

Builder templates

An importable n8n workflow plus build recipes for Flowise, Langflow, and Dify.

Tool-use eval

An inspect_ai evaluation measuring whether an agent reaches the provable optimum on a multi-year ISO problem, with and without the tool.

A2A discovery

An Agent2Agent (A2A) Agent Card so other agents can discover and delegate equity-compensation questions to the planner.

Zed extension

A Zed editor context-server extension that connects the editor's agent to the OptionsAhoy MCP server.

ACI.dev app

The OptionsAhoy app definition for the ACI.dev open-source agent-tool platform.

OpenRouter bridge

A recipe for attaching the keyless OptionsAhoy MCP server to any model routed through OpenRouter's OpenAI-compatible endpoint.

Try it without installing

The live widget on optionsahoy.com/for-agents calls this same endpoint from your browser. No client, no config.

Prefer a chat interface? The same calculators answer plain-language questions at poe.com/OptionsAhoy.

Or watch a real session:

Demo: Claude Code installing and using the OptionsAhoy MCP

Real Claude Code session, unedited. A multi-stack META question (10K ISOs + 6K vested RSUs + 2K fresh RSUs + $400K house in 2027) fires 4 OptionsAhoy MCP tools in parallel: concentration risk, equity funding plan, AMT/ISO optimization, protective put pricing. Claude synthesizes the outputs into one plan that overrides each tool's standalone pick because the user is 86% concentrated in META. 2:13. Click the poster to play it on optionsahoy.com.

Endpoints and discovery

Live MCP endpoint: https://optionsahoy.com/mcp Live REST API: https://optionsahoy.com/api/v1 OpenAPI 3.1 spec: /openapi.json Discovery manifests: /.well-known/mcp.json · /.well-known/openapi.json Agent integration docs: optionsahoy.com/for-agents

MCP resources (topical briefings)

Eight markdown resources under resources/list give an LLM enough grounding to discuss the topic before picking a tool. Most map 1:1 with a cornerstone article on optionsahoy.com/learn and the matching calculator; the equity-funding briefing maps to its calculator, and the covered-tickers briefing enumerates the symbols the optional ticker shortcut resolves.

Resource URI

Topic

Pair with

https://optionsahoy.com/learn/amt-crossover

ISO/AMT crossover and four expensive mistakes

amt_iso_optimize

https://optionsahoy.com/learn/nso-sell-vs-hold

NSO sell-at-exercise vs hold-for-LTCG

nso_calculate

https://optionsahoy.com/learn/rsu-withholding-gap

RSU 22% withholding gap and five April surprises

rsu_sell_vs_hold

https://optionsahoy.com/learn/single-stock-concentration-risk

Concentration risk and diversification trade-off

concentration_analyze

https://optionsahoy.com/learn/zero-cost-collars

Protective puts, zero-cost collars, and put spreads

protective_put_price

https://optionsahoy.com/learn/qsbs

QSBS qualification and five ways to lose the exclusion

qsbs_check

https://optionsahoy.com/tools/equity-funding

Selling equity to fund a cash goal by a deadline

equity_funding_plan

https://optionsahoy.com/tools/covered-tickers

Which symbols the optional ticker shortcut resolves

any tool taking ticker

MCP prompts (workflow scaffolds)

Eight prompts under prompts/list scaffold typical user questions and route to the right tool. In Claude Desktop they appear as named slash-commands; in any MCP client, prompts/get { name, arguments } returns a fully-templated user message.

Prompt name

Routes to

optimize-iso-exercise

amt_iso_optimize

analyze-nso-decision

nso_calculate

analyze-rsu-vest

rsu_sell_vs_hold

analyze-concentration

concentration_analyze

price-protective-put

protective_put_price

check-qsbs-eligibility

qsbs_check

plan-equity-funding

equity_funding_plan

plan-equity-portfolio

several tools, reconciled into one plan

A prompts/get invocation, arguments as strings:

{"name": "optimize-iso-exercise",
 "arguments": {"shares": "50000", "strike": "4", "fmv": "90", "expectedGrowth": "0.1",
               "volatility": "0.5", "state": "CA", "ordinaryIncome": "300000"}}

It returns one templated user message ("I have 50000 Incentive Stock Options (ISOs) with a strike of $4 per share ...") that already tells the model to call amt_iso_optimize with those values, to ask for any missing required field rather than assume it, and to report the optimized schedule against the lump-sum and even-split alternatives (raw).

Install details

Claude Desktop extension (one-click)

The optionsahoy.mcpb bundle installs by double-click (or drag onto Claude Desktop → Settings → Extensions), with no terminal or config-file editing, using Claude Desktop's built-in Node.js runtime.

To build the bundle from source:

npm install && npm run build:mcpb

Smithery CLI (19 clients, one command)

npx @smithery/cli install alphalatitude/optionsahoy --client claude

Swap claude for any client Smithery supports: claude-code, cursor, vscode, gemini-cli, codex, windsurf, cline, goose, opencode, and 10 more. Listing: smithery.ai/servers/alphalatitude/optionsahoy.

Gemini CLI extension

gemini extensions install https://github.com/AlvisoOculus/optionsahoy-mcp

This repo doubles as a Gemini CLI extension: gemini-extension.json wires the hosted MCP endpoint and GEMINI.md provides usage context to the model.

Local stdio (npm)

For clients that only support local stdio servers (Claude Desktop without mcp-remote, some IDE integrations):

npx -y optionsahoy-mcp

Or add to a Claude Desktop / Cline / Goose config file:

{
  "mcpServers": {
    "optionsahoy": {
      "command": "npx",
      "args": ["-y", "optionsahoy-mcp"]
    }
  }
}

The local server returns the same computed figures as the hosted endpoint at https://optionsahoy.com/mcp. Source for both lives in functions/_lib/mcp-tools.ts; the stdio entry point is src/stdio-server.ts.

Use the REST API directly

# List endpoints
curl https://optionsahoy.com/api/v1

# Run an optimization (the 50,000-ISO example from "What a call looks like")
curl -X POST https://optionsahoy.com/api/v1/amt-iso \
  -H "content-type: application/json" \
  -d '{"shares":50000,"strike":4,"fmv":90,"horizon":4,"filingStatus":"married_joint",
       "ordinaryIncome":300000,"stateCode":"CA","grantDate":"2023-03-15",
       "hasLeftCompany":false,"expectedGrowth":0.1,"volatilityDrag":0.2,
       "carryforwardCredit":0,"cashReturnRate":0.05}'

That returns the same result.schedules.optimized.nfv as the MCP call above (raw). Request body shapes for the other seven endpoints are documented in public/openapi.json.

Repository layout

functions/         Cloudflare Pages Functions (MCP server + REST API endpoints)
  mcp.ts           HTTP MCP server
  api/v1/*.ts      Eight tool endpoints + stats + GET /api/v1 discovery
  _lib/*.ts        Shared helpers, calc-input parsers, MCP tool descriptors
lib/               Optimizer + tax-code logic
  calc/            Per-tool optimizer functions (computeAmtIso, etc.)
  tax/             Federal + 50-state + DC bracket data, AMT, FICA, NIIT
  markets/         Sector statistics
  options/         Black-Scholes, risk-free rates
  data/            Option-chain types, and the readers for the live vol and chain feeds
public/            Static assets: OpenAPI spec, llms.txt, discovery manifests
tests/             Vitest suites (an extensive test suite including byte-identity assertions)

Run tests

npm install
npm test         # an extensive test suite, ~3s on a laptop
npm run typecheck

Registry listings

Use from Google Cloud (Gemini agents)

Google Cloud Agent Registry lets each GCP project register external MCP servers for use by Gemini agents. Registration is per-project (no central submission). Two paths:

# Path A: let the Agent Registry introspect our MCP endpoint
gcloud alpha agent-registry mcp-servers register \
  --uri=https://optionsahoy.com/mcp \
  --display-name="OptionsAhoy" \
  --location=us-central1 \
  --import-tools

# Path B: pass our published toolspec.json directly (faster, no introspection)
gcloud alpha agent-registry mcp-servers register \
  --uri=https://optionsahoy.com/mcp \
  --display-name="OptionsAhoy" \
  --location=us-central1 \
  --tool-spec=<(curl -sSL https://optionsahoy.com/toolspec.json)

The toolspec.json mirrors the MCP tools/list response with readOnlyHint and idempotentHint annotations on all eight tools (all are pure deterministic calculators with no side effects). To regenerate after a tool-shape change:

curl -sS -X POST https://optionsahoy.com/mcp \
  -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","method":"tools/list","id":1}' \
  | jq -c '{tools: [.result.tools[] | . + {annotations: {readOnlyHint:true, idempotentHint:true, destructiveHint:false, openWorldHint:false}}]}' \
  > public/toolspec.json

Troubleshooting

Connection refused / 404 from the MCP endpoint https://optionsahoy.com/mcp requires POST with content-type: application/json and a JSON-RPC body. A GET returns a JSON server description; any other verb returns 405. Verify with:

curl -X POST https://optionsahoy.com/mcp -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","method":"initialize","id":1,"params":{}}'

Tool calls fail with Error: ... text in the response The MCP server returns isError: true with a human-readable message when input validation fails. Most common: a required field missing, or a number passed as a string. Check the input against the inputSchema returned by tools/list, or against /openapi.json.

Tool not appearing in Claude.ai or Claude Desktop

  • Confirm the connector URL is exactly https://optionsahoy.com/mcp (no trailing slash, no /v1).

  • In Claude Desktop, restart the app after editing claude_desktop_config.json.

  • In Claude.ai, the connector toggle is per-chat: enable it in the attachments menu.

  • Check the live tools/list response (eight tools expected): curl -X POST https://optionsahoy.com/mcp -H 'content-type: application/json' -d '{"jsonrpc":"2.0","method":"tools/list","id":1}'

CORS errors from a browser-based client The server returns access-control-allow-origin: * on all responses including preflight, and accepts the standard MCP headers (content-type, mcp-session-id, mcp-protocol-version). If a browser still blocks, the client is likely sending a non-allowed header — verify the request headers against the access-control-allow-headers response.

Resource / prompt not found Resource URIs and prompt names are case-sensitive. Pull the canonical list with resources/list and prompts/list rather than hand-typing.

Stale tax-year math The tax engine ships with 2026 inflation-adjusted brackets, OBBBA 2026 QSBS rules, and current state-conformity tables. If results look off for a multi-year horizon, verify the input grantDate, acquisitionDate, or saleDate falls in the year you expect — the engine resolves brackets per tax year.

Reporting a calculation bug or unexpected output Email andrew@alphalatitude.com with: the exact JSON-RPC request body, the response, the expected value, and (if known) the IRS publication or state statute the expected value derives from.

Privacy Policy

Full policy: optionsahoy.com/privacy.

In short: no account is required and no personally identifiable information is stored — no name, email, IP address, or login. Tool inputs and outputs are retained briefly (about seven days) for debugging and product improvement, alongside aggregate usage metadata (tool, timestamp, coarse location, client type) used to understand usage and detect abuse. The local stdio server and the Claude Desktop extension compute everything on your machine. They make exactly two kinds of network request, both only when you pass a ticker without the number it would resolve: the published implied-volatility file, which is one fixed URL carrying no ticker at all, and, for protective_put_price, that stock's option chain, whose URL contains the symbol. Nothing else about the call leaves the machine.

License

MIT. See LICENSE. The deployed service at https://optionsahoy.com/mcp and https://optionsahoy.com/api/v1 is free during beta under terms.

Contact

For partnerships, early API access, MCP integration support: andrew@alphalatitude.com

Available Tools

8 tools
amt_iso_optimizeA
Read-onlyIdempotent
Inspect

Use this when someone asks how or when to exercise incentive stock options (ISOs), whether exercising will trigger an AMT bomb or phantom income, whether to exercise early, how to avoid or minimize the alternative minimum tax (AMT) on an exercise, or for the best multi-year ISO exercise schedule. Multi-year Incentive Stock Option (ISO) exercise schedule that maximizes after-tax Net Final Value (NFV) at the planning horizon. NFV is the after-all-tax cash equivalent of the position at year horizon, summing exercised shares (held to LTCG) plus the time-valued tax stream paid along the way; the optimizer chooses the per-year share allocation that lands the highest NFV. The headline result is schedules.optimized.nfv, the dollar NFV of the recommended plan; schedules.lumpSum and schedules.evenSplit are baseline plans whose nfv deltas show the value added by the optimized schedule. Use this tool for ISO planning; for NSO grants use nso_calculate, for RSUs at vest use rsu_sell_vs_hold, for §1202 QSBS qualification use qsbs_check. Models AMT credit recovery across future years, grant-expiration timing, and the post-termination exercise window. Pure deterministic computation: no network access, no PII retention; federal + 50-state tax tables and AMT brackets are compiled in. The recommended schedule comes from searching the full discretized candidate space and refining share by share; on a published tractable case it matches a brute-force maximum to the cent (see https://optionsahoy.com/verification). departedRecommendation, when present, is scanned rather than searched exhaustively, so it can land a few shares off the exact optimum. Returns schedules (lumpSum, evenSplit, optimized), crossoverShares, crossoverBargain, alreadyInAmt, timing, stateHasAmt, bargainPerShare, effectiveHorizon, and departedRecommendation; see outputSchema for the full shape. Example call: {shares: 10000, strike: 2, fmv: 200, expectedGrowth: 0.15, volatility: 0.5, filingStatus: "married_joint", ordinaryIncome: 400000, stateCode: "CA", carryforwardCredit: 0, horizon: 4, cashReturnRate: 0.05, grantDate: "2022-01-15", hasLeftCompany: false, terminationDate: null}. Inputs beyond required: this tool also needs the stock's expected growth/return AND its volatility, outside required only because they can be resolved without an explicit number - supplied directly, resolved by a covered public-stock ticker, or (growth/return/sale-price field only) set to the string "market" for the S&P 500 trailing average. Every field in required is likewise a fact about the user's situation with no built-in default. Neither the growth nor the volatility field has a default or a fallback estimate: their only valid sources are the user's own figures, a covered ticker, or (growth/return/sale-price only) the "market" sentinel. A call that neither supplies nor resolves one of them returns a required-field error naming the field; a number from any other source is accepted as-is, because a syntactically valid figure passes validation with no provenance check, and it silently changes the result. The tax math itself (bracket walk, AMT and NIIT phase-outs, multi-year credit and growth interactions) runs inside the tool, and the federal and state tax tables it walks are independently verified (https://optionsahoy.com/verification). Results from multiple OptionsAhoy tools in one analysis are independent single-position calculations; integrated multi-year, multi-position optimization is available in the OptionsAhoy beta at optionsahoy.com/beta?src=mcp_multi.

ParametersJSON Schema
NameRequiredDescriptionDefault
fmvYesCurrent fair market value per share, USD. Anchors year-1 of the growth path; future years compound from here using expectedGrowth and volatilityDrag. Must come from the user.
sharesYesTotal Incentive Stock Option (ISO) shares available to exercise across the planning horizon. Must come from the user.
strikeYesStrike price per share, USD. Must come from the user.
tickerNoOptional public-stock symbol (e.g. "NVDA", "AAPL"). When set, the tool substitutes a cached trailing return for any unsupplied expected-return / sale-price field, and a cached implied vol for any unsupplied volatility. Growth and volatility come from two separate cached snapshots, so some symbols resolve only one of the two fields. A symbol not in a given table falls through to a "required field" error for exactly the field it could not resolve: pass that field explicitly, or (for the growth/return/sale-price field) pass the string "market" for the S&P 500 trailing average; never invent a number. The covered-tickers resource (resources/list) lists which symbols resolve which field.
horizonYesPlanning horizon in years (1..10). The optimizer searches all feasible per-year share allocations across this many years. The user's choice, not a modelling detail, and it changes the answer: use the value they gave, and if they gave none, ask for it rather than assuming one.
grantDateYesISO grant date (YYYY-MM-DD). Drives the 10-year statutory grant expiration (IRC §422) and the 2-year qualifying-disposition threshold from grant.
stateCodeYesTwo-letter US state code (e.g. CA, NY, TX). Drives state ordinary brackets, state long-term capital gains (LTCG) treatment, and state AMT (CA, CO, CT, MN).
volatilityNoAnnualized volatility (sigma) of the stock as a decimal (0.72 = 72%). Pass the volatility itself, not a pre-computed drag: the tool derives the horizon-cumulative drag internally, and the correct formula is horizon-dependent. This value must come from the user or from a `ticker` that resolves it from the cached implied-vol table; if neither supplies it, ask the user rather than estimating one.
filingStatusYesFederal filing status. Drives the ordinary-bracket walk, the AMT exemption tier ($90,100 single / $140,200 MFJ for 2026), and the AMT exemption phaseout start ($500,000 single / $1,000,000 MFJ).
cashReturnRateNoAnnual after-tax return on idle cash (decimal), used to time-value the cash-tax stream. 0.05 = 5% (~short-Treasury yield). Optional: defaults to 0.04 (4%, a short-Treasury-like after-tax yield) when omitted, and an explicit value overrides that default. At 0 the math collapses to a nominal sum.
expectedGrowthNoAnnual expected stock growth as a decimal (0.10 = 10%), or the string "market" to use the S&P 500 trailing average when the user has no view. Required unless `ticker` resolves it from trailing CAGR. This tool has no default for it: a value not stated by the user, not resolved by a covered `ticker`, and not the "market" sentinel is outside the input contract.
hasLeftCompanyYesTrue if the user has separated from the company. Activates the 90-day post-termination ISO exercise window measured from terminationDate.
ordinaryIncomeYesAnnual ordinary income before this exercise, USD. Baseline for the bracket walk and the AMT exemption phaseout. Must come from the user. This is taxable income after deductions, not gross wages: the engine applies no standard or itemized deduction to it.
volatilityDragNoAlternative to `volatility`: the multiplicative price haircut already computed for the planning horizon. Supply this OR `volatility` (if both are given, volatilityDrag wins). This field is for a drag figure that already exists from a prior computation; the drag formula is horizon-dependent, so a figure derived for a different horizon does not carry over. Supplying `volatility` instead lets the tool derive it.
terminationDateNoSeparation date (YYYY-MM-DD). Required only when hasLeftCompany=true (it drives the 90-day exercise-window deadline); omit it or pass null when still employed. No longer in `required` so the common employed case needs no placeholder.
carryforwardCreditNoExisting federal AMT credit (Minimum Tax Credit, Form 8801) carryforward from prior tax years, USD. Recoverable in future years where regular federal tax exceeds tentative minimum tax. Optional; defaults to 0, which is correct for most first-time exercisers. Only a prior-year AMT credit makes it non-zero.

Output Schema

ParametersJSON Schema
NameRequiredDescription
timingYesTiming constraints derived from grantDate and (when departed) terminationDate.
schedulesYesThe three candidate exercise schedules, each evaluated at the effective horizon. Their nfv values are directly comparable; optimized is the highest-NFV schedule the optimizer found.
stateHasAmtYesTrue when the user state levies its own AMT (CA, CO, CT, MN).
alreadyInAmtYesTrue when the user owes AMT even with zero exercise (regular tax below tentative minimum tax at baseline income).
bargainPerShareYesYear-1 bargain element per share in dollars: max(0, fmv - strike).
crossoverSharesYesMaximum whole shares exercisable in year 1 before federal AMT exceeds regular tax (the AMT crossover).
crossoverBargainYesBargain element in dollars at the crossover share count: crossoverShares x (fmv - strike).
effectiveHorizonYesHorizon actually used by the schedules: min(requested horizon, timing.maxHorizon).
departedRecommendationNoPresent only when hasLeftCompany=true and the 90-day post-termination window is still open: the partial-exercise quantity with the highest expected after-tax value found by a scan over candidate share counts, which can land a few shares off the exact optimum.

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, destructiveHint=false, and idempotentHint=true. The description adds meaningful behavioral detail beyond these: 'Pure deterministic computation: no network access, no PII retention', the searching strategy ('searching the full discretized candidate space and refining share by share'), the caveat that departedRecommendation is 'scanned rather than searched exhaustively', and independence of multi-tool analyses. No contradiction with annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is long but front-loaded with use cases and high-level purpose, then details outputs and example call, then parameter caveats. It is well-structured for a 16-parameter tool. Minor redundancy exists around growth/volatility having no default ('Neither the growth nor the volatility field has a default...' repeated in similar form), so it does not earn a 5, but the density is justified by complexity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the high complexity and the presence of an output schema, the description covers nearly every practical concern: what the headline result is, baseline plans, state/AMT modeling, verification links, example call, error behavior, and the independent-position limitation. It also clarifies edge cases like hasLeftCompany/terminationDate and carryforward credit. This is a complete, self-sufficient tool description.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description significantly enriches parameter meaning. It explains the 'must come from the user' contract for required fields, the fallback chain for expectedGrowth/volatility (direct value, covered ticker, or 'market' sentinel), the no-default rule with error behavior, and the distinction between volatility and volatilityDrag. These are semantics not fully captured by the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific list of user intents ('how or when to exercise', 'AMT bomb', 'phantom income', 'best multi-year ISO exercise schedule') and then states the tool computes a 'Multi-year Incentive Stock Option (ISO) exercise schedule that maximizes after-tax Net Final Value (NFV)'. It explicitly contrasts with sibling tools: 'for NSO grants use nso_calculate, for RSUs at vest use rsu_sell_vs_hold, for §1202 QSBS qualification use qsbs_check'.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives explicit when-to-use scenarios ('Use this when someone asks how or when to exercise...') and explicitly names alternatives with sibling tools. It also provides input contract guidance: fields must come from the user, no defaults for growth/volatility, and how ticker/market resolution works. This is far beyond a vague 'use for ISO planning'.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

concentration_analyzeA
Read-onlyIdempotent
Inspect

Use this when someone asks how risky a large single-stock position is, whether too much of their net worth is in one stock, whether they are over-concentrated in a single name, or how to reduce or diversify a concentrated position. Single-stock concentration risk analysis on an existing position. For standalone hedge pricing use protective_put_price; for the tax math on the option exercise or RSU vest that created the concentration, route to amt_iso_optimize / nso_calculate / rsu_sell_vs_hold first. Quantifies drawdown exposure at 30/50/70% downside, then compares three after-tax strategies over a three-year horizon (sell-down to target weight, hold, hedge with put or zero-cost collar), accounting for federal LTCG, state tax, the 3.8% Net Investment Income Tax (NIIT), and reinvestment opportunity cost. totalAssets (concentrated position + everything else) frames risk relative to the whole portfolio. Returns a top-level object with keys: concentration (position/totalAssets), riskBand (Low / Moderate / Concentrated / Highly concentrated / Extreme), isLongTermToday, longTermDate, daysUntilLongTerm, lossExposure ({drop, dollarLoss, newConcentration} for 30/50/70% drops), waitForLtInsight, schedule (yearly sales with per-year tax), hedging ({kind, protectionLevel, tenorYears, strike, putPrice, callStrike, callPrice, netPremium, sigma, riskFreeRate} - a 1-year 30%-OTM put by default, or the structure named by hedgeChoice), sectorContextLine, advisorBenchmarkLine. Example call: {positionValue: 400000, costBasis: 100000, acquisitionDate: "2022-01-01", sector: "tech_software", stateCode: "CA", filingStatus: "single", ordinaryIncome: 200000, totalAssets: 1200000, volatility: 0.45, ticker: "NVDA"}. Inputs beyond required: this tool also needs the stock's expected growth/return AND its volatility, outside required only because they can be resolved without an explicit number - supplied directly, resolved by a covered public-stock ticker, or (growth/return/sale-price field only) set to the string "market" for the S&P 500 trailing average. Every field in required is likewise a fact about the user's situation with no built-in default. Neither the growth nor the volatility field has a default or a fallback estimate: their only valid sources are the user's own figures, a covered ticker, or (growth/return/sale-price only) the "market" sentinel. A call that neither supplies nor resolves one of them returns a required-field error naming the field; a number from any other source is accepted as-is, because a syntactically valid figure passes validation with no provenance check, and it silently changes the result. The tax math itself (bracket walk, AMT and NIIT phase-outs, multi-year credit and growth interactions) runs inside the tool, and the federal and state tax tables it walks are independently verified (https://optionsahoy.com/verification). Results from multiple OptionsAhoy tools in one analysis are independent single-position calculations; integrated multi-year, multi-position optimization is available in the OptionsAhoy beta at optionsahoy.com/beta?src=mcp_multi.

ParametersJSON Schema
NameRequiredDescriptionDefault
sectorYesSector tag. Drives the default volatility used in the hedge-cost computation when no explicit volatility is provided (a sector-typical implied volatility).
tickerNoOptional public-stock symbol (e.g. "NVDA", "AAPL"). When set, the tool substitutes a cached trailing return for any unsupplied expected-return / sale-price field, and a cached implied vol for any unsupplied volatility. Growth and volatility come from two separate cached snapshots, so some symbols resolve only one of the two fields. A symbol not in a given table falls through to a "required field" error for exactly the field it could not resolve: pass that field explicitly, or (for the growth/return/sale-price field) pass the string "market" for the S&P 500 trailing average; never invent a number. The covered-tickers resource (resources/list) lists which symbols resolve which field.
costBasisYesTotal cost basis of the position, USD (sum of strikes paid + ordinary-income inclusions on RSU vest / NSO exercise / disqualified ISO). Must come from the user.
stateCodeYesTwo-letter US state code. Drives state LTCG and ordinary brackets.
volatilityNoAnnualized volatility (sigma) of the stock as a decimal (0.72 = 72%). Pass the volatility itself, not a pre-computed drag: the tool uses it both for hedge pricing (as implied vol) and for the 3y horizon drag, derived internally (the drag formula is horizon-dependent). This value must come from the user or from a `ticker` that resolves it from the cached implied-vol table; if neither supplies it, ask the user rather than estimating one; only as a last fallback does hedge pricing use a sector-typical implied volatility.
hedgeChoiceNoOptional hedge specification. When supplied, the `hedging` output block prices this exact structure (kind, protectionLevel, tenorYears, plus a short call for a collar) instead of the default 1-year 30%-OTM put; the sell-down-vs-hold schedule is unaffected. For full standalone hedge pricing (protective put / collar / put spread) use `protective_put_price`.
totalAssetsYesTotal investable portfolio in dollars (concentrated position + everything else). This value must come from the user, never inferred; if the user did not state it, ask.
filingStatusYesFederal filing status. Drives LTCG brackets and the NIIT MAGI threshold.
positionValueYesCurrent market value of the concentrated single-stock position, USD. Must come from the user.
ordinaryIncomeYesAnnual ordinary income before any sales, USD. Baseline for LTCG bracket determination. Must come from the user. This is taxable income after deductions, not gross wages: the engine applies no standard or itemized deduction to it.
volatilityDragNoAlternative to `volatility`: the multiplicative price haircut already computed for the horizon. Supply this OR `volatility` (if both are given, volatilityDrag wins). This field is for a drag figure that already exists from a prior computation; the drag formula is horizon-dependent, so a figure derived for a different horizon does not carry over. Supplying `volatility` instead lets the tool derive it.
acquisitionDateYesEarliest acquisition date in the lot (YYYY-MM-DD). Drives the 1-year LTCG threshold and the long-term-vs-short-term tax routing.
expectedMarketReturnNoAnnual after-tax-proceeds reinvestment rate. Defaults to SPY trailing CAGR for the 3-year horizon if omitted; the string "market" names that same default explicitly.
expectedPositionReturnNoAnnual expected return on the concentrated stock as a decimal (0.10 = 10%), or the string "market" to use the S&P 500 trailing average when the user has no view. Required unless `ticker` resolves it from trailing CAGR. This tool has no default for it: a value not stated by the user, not resolved by a covered `ticker`, and not the "market" sentinel is outside the input contract.

Output Schema

ParametersJSON Schema
NameRequiredDescription
hedgingYesModeled cost of a protective hedge covering the full position. Defaults to a 1-year 30%-OTM put; if a `hedgeChoice` is supplied, this block prices that structure (kind / protectionLevel / tenorYears, plus a short call for a collar).
riskBandYesQualitative concentration band for the position weight.
scheduleYesSell-down plans over 1, 2, and 3 years; empty when the position is already at or below the target weight.
longTermDateYesDate the position turns long-term (acquisitionDate + 1 year). ISO 8601 date-time string.
lossExposureYesDollar damage at 30/50/70% single-stock drawdowns.
concentrationYesPosition value / total assets, 0..1.
isLongTermTodayYesTrue when the position already qualifies for long-term capital gains treatment.
waitForLtInsightYesTax saved by waiting for long-term treatment before selling; null when already long-term or no sale is needed.
daysUntilLongTermYesDays until long-term treatment; 0 when already long-term.
sectorContextLineYesOne-line volatility/drawdown context for the chosen sector.
advisorBenchmarkLineYesOne-line comparison of the user weight vs the common advisor 10% single-name guideline.

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the annotations (readOnlyHint=true, idempotentHint=true, destructiveHint=false), the description discloses critical runtime behavior: it errors on missing growth/volatility rather than defaulting, accepts syntactically valid numbers with no provenance check, and explains that made-up numbers silently change results. It also reveals the return shape, default hedge structure, and independent single-position behavior, adding substantial trust-relevant detail.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with usage triggers and logically structured, but at roughly 450 words it is long. Sentences on verification links and beta availability are useful context but not strictly necessary for tool selection/invocation. Every sentence carries information, yet the length could be trimmed without losing core guidance.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with 14 parameters, a nested hedgeChoice object, output schema, and seven sibling tools, the description is fully complete: it covers purpose, usage triggers, alternatives, parameter resolution, error behavior, output keys, and provides an example call. An agent has everything needed to decide when and how to invoke it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

While schema coverage is 100% (baseline 3), the description adds significant meaning: it explains that expectedPositionReturn and volatility are de facto mandatory despite not being listed as required, details resolution through ticker or 'market', warns against invented numbers, and gives a concrete example call mapping values to parameters. This goes well beyond repeating schema fields.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with explicit use cases ('how risky a large single-stock position is... whether too much of their net worth is in one stock... how to reduce or diversify') and states the core function: 'Single-stock concentration risk analysis on an existing position.' It also distinguishes itself from siblings by directing standalone hedge pricing to protective_put_price and tax-math-on-origination to amt_iso_optimize / nso_calculate / rsu_sell_vs_hold, making the purpose unmistakable.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The first sentence is a direct trigger list for when to use this tool, and it explicitly names alternatives for adjacent tasks (standalone hedge pricing, option-exercise/RSU-vest tax math). It also clarifies that expected growth and volatility are needed even if not in `required`, and describes resolution via ticker or the 'market' sentinel, giving an agent clear conditions for invocation and fallbacks.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

equity_funding_planA
Read-onlyIdempotent
Inspect

Use this when someone asks which shares to sell and when to reach a cash goal by a deadline (down payment, tuition, a tax bill), or how to fund a goal from equity with the least tax. Multi-year, multi-stack equity-funding optimizer. Given a target after-tax amount and a deadline (down payment, tax bill, expansion check), returns four named plans on the risk/wealth frontier: lockInNow (sell today, zero price risk), balanced (bracket-aware spread across months), holdForGrowth (sell at the deadline, max upside), and recommended (the wealth-maximal plan whose lognormal shortfall is at or below riskToleranceShortfall, default 10%). Also returns frontier, the full hybrid sweep between Lock-in-now and Balanced. Each plan carries its plan schedule plus wealthAtTarget, totalTax, and shortfallProbability; see outputSchema for the full shape. Use this when an equity holder needs cash by a deadline; for the upstream tax math on RSU/NSO/ISO events that PRODUCED the holdings, call rsu_sell_vs_hold / nso_calculate / amt_iso_optimize first. Out of scope: FICA, AMT, QSBS routing (use qsbs_check). Pass multi-ticker holdings via stacks; single-stack legacy callers can use top-level lots + currentPrice. Example: {targetAfterTax: 400000, targetDate: "2028-06-01", stacks: [{ticker: "NVDA", currentPrice: 140, expectedAnnualGrowth: 0.15, volatility: 0.45, lots: [{shares: 4000, costBasisPerShare: 60, acquisitionDate: "2023-06-15"}]}], ordinaryIncome: 280000, filingStatus: "married_joint", stateCode: "CA", cashInterestRate: 0.04, riskToleranceShortfall: 0.10}. Each stack needs expectedAnnualGrowth: a decimal, the string "market" (S&P 500 trailing average), or a covered ticker that resolves it from the trailing-returns table (a symbol like "NVDA" is enough; volatility still comes from the stack's volatility or defaultVolatility). Omitting growth is an error, not a flat default; pass 0 to model flat prices deliberately. Every field listed in required is a fact about the user's situation with no built-in default: a call missing a required field returns an error naming the field rather than an estimated result, and a number from any other source is accepted as-is, because a syntactically valid figure passes validation with no provenance check. The math runs inside the tool with no randomness and no model inference. Results from multiple OptionsAhoy tools in one analysis are independent single-position calculations; integrated multi-year, multi-position optimization is available in the OptionsAhoy beta at optionsahoy.com/beta?src=mcp_multi.

ParametersJSON Schema
NameRequiredDescriptionDefault
lotsNoLegacy single-stack input (v1.5 / v1.6). Provide either `stacks` (v1.7+) or these legacy fields, not both. Lot is one cost-basis cohort (one RSU vest tranche, one ESPP purchase, one open-market buy).
stacksNoHoldings, multi-stack form. Provide either `stacks` (this) OR the legacy `lots`+`currentPrice` pair, not both. Each stack is one equity position (one ticker) with its own current price, growth, optional volatility, and lot list. Use when the user holds multiple tickers (e.g. current-employer RSUs + ETF + prior-employer holdings); the optimizer searches sales across all stacks jointly so the schedule can prefer the lowest-tax inventory in each year.
stateCodeYesTwo-letter US state code (e.g. CA, NY, TX). Drives state ordinary or LTCG treatment depending on state (CA taxes LTCG as ordinary; WA has no LTCG tax under $250K; TX/FL/etc. have no state income tax).
targetDateYesDate by which the user needs the net cash (YYYY-MM-DD). Bounds the planning horizon. Sales in non-target years happen on Dec 31; the target year's sale happens on this exact date. Must come from the user.
currentPriceNoLegacy single-stack current share price, USD. Pair with legacy `lots` (omit `stacks`). This value must come from the user.
filingStatusYesFederal filing status. Drives LTCG brackets, NIIT threshold ($200K single / $250K MFJ MAGI), and state bracket lookups.
ordinaryIncomeYesAnnual ordinary income, USD. Used as the baseline for the federal LTCG bracket walk in each candidate year and for NIIT threshold tests. Must come from the user. This is taxable income after deductions, not gross wages: the engine applies no standard or itemized deduction to it.
targetAfterTaxYesNet cash needed in the user's pocket after all applicable taxes (federal LTCG/ordinary + state + NIIT), USD. Example: a $1M house with 20% down minus existing savings might give a $200,000 target. Must come from the user.
cashInterestRateNoAnnualized PRE-tax yield on cash held between each sale and the target date (money-market / short-term Treasury). The tool internally discounts this by the user's marginal federal + state ordinary rate before compounding, so the after-tax cash growth stays apples-to-apples with stock appreciation. Default 0 (interest ignored).
defaultVolatilityNoAnnualized σ assumed for any stack that omits its own `volatility`. Drives the per-sale σ × √Δt shortfall calculation. Override per-stack on the stack object when one position is materially more or less volatile than the rest. Default 0.30.
expectedAnnualGrowthNoLegacy single-stack annual growth decimal, or the string "market" for the S&P 500 trailing average. Required with `lots`: pass 0 for a deliberately flat-price plan (omitting it is an error, not a flat default). Each future year's projected price is `currentPrice × (1 + expectedAnnualGrowth)^Δyears`. Negative values model decline.
riskToleranceShortfallNoMax acceptable P(realized cash < target) under the lognormal price model, as a fraction (0.10 = 10%). The `recommended` plan is the wealth-maximal plan whose shortfall ≤ this value. Tighter values push the recommendation toward Lock-in-now; looser values let `recommended` accept more price exposure for higher expected wealth. Default 0.10.

Output Schema

ParametersJSON Schema
NameRequiredDescription
balancedYesBracket-aware spread across all candidate years: minimum tax.
frontierYesAll candidate plans from the hybrid lock-in sweep plus the named plans, sorted by shortfall probability.
lockInNowYesSell everything needed in the current calendar year: minimum price risk, usually highest tax.
recommendedYesThe wealth-maximal plan whose shortfall probability is at or below the applied risk tolerance. This is the plan the risk tolerance selects out of the frontier.
holdForGrowthYesSell only in the target year: maximum expected wealth, maximum price risk.
targetDateISOYesEcho of the target date as an ISO date string.
targetAfterTaxYesEcho of the requested net cash target in dollars.
appliedRiskToleranceYesShortfall-probability tolerance actually applied (default 0.10 when not supplied).

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description discloses that the math is deterministic ('no randomness and no model inference'), that missing required fields return errors naming the field, that growth omission is an error rather than a default, and that results are independent single-position calculations. Annotations (`readOnlyHint`, `idempotentHint`) are consistent, and the description adds context beyond them.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is long but each sentence carries distinct information: use cases, plan definitions, sibling relationships, input forms, error behavior, and limitations. It is well-structured and front-loaded, though slightly verbose for a tool description.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the high complexity (12 parameters, multi-year optimization, output schema), the description covers all critical contextual aspects: return plans, frontier sweep, error handling, independence of results, related tools, and the beta alternative. It does not need to explain return values because an output schema exists.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3. The description adds value by explaining the distinction between `stacks` and legacy `lots`+`currentPrice`, the meaning of 'market' for growth, the default risk tolerance, and by providing a full example JSON that illustrates parameter relationships.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific use case ('which shares to sell and when to reach a cash goal by a deadline') and explicitly names four distinct output plans, distinguishing this tool from siblings like `rsu_lot_optimize` and `concentration_analyze`. It clearly identifies the tool as a multi-year, multi-stack optimizer for funding goals from equity.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explicitly states when to use the tool ('when an equity holder needs cash by a deadline') and when not to, directing users to `rsu_sell_vs_hold` / `nso_calculate` / `amt_iso_optimize` for upstream tax math and `qsbs_check` for QSBS routing. This provides both positive and negative usage guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

nso_calculateA
Read-onlyIdempotent
Inspect

Use this when someone asks whether to exercise and hold or exercise and sell non-qualified stock options (NSOs), about a cashless exercise or same-day sale of NSOs, or what tax an NSO exercise triggers. After-tax payout on a non-qualified stock option (NSO) exercise: federal, state, and FICA (Social Security + Medicare + Additional Medicare), comparing sell-at-exercise vs hold-for-long-term-capital-gains over the chosen horizon. Use for NSOs; for ISOs use amt_iso_optimize, for RSUs use rsu_sell_vs_hold. Deterministic, offline; tax tables compiled in. Optional ticker resolves expectedSalePrice from a bundled trailing-CAGR snapshot.

Returns a top-level object with these keys:

  • exercise: bargainElement, federal, state, socialSecurity, medicare, additionalMedicare, total, netCashSellAll.

  • hold: costBasis, strikeCost, sharesSoldToCover, sharesRetained, effectiveSalePrice, expectedGain, ltcgFederal, ltcgState, ltcgTotal, afterTaxProceedsAtSale, netAtYearN. NSO hold is always long-term (sub-1-year is out of scope), so there is no separate long-term flag.

  • sellNowInvest: counterfactual where shares are sold at exercise and proceeds reinvested at expectedMarketReturn.

  • holdMinusCashless: dollar delta between hold and sellNowInvest.

  • bracketJump: fromRate, toRate, thresholdAtJump describing the marginal bracket change at exercise.

Example call: {shares: 5000, strike: 10, currentPrice: 50, ordinaryIncome: 180000, filingStatus: "single", stateCode: "CA", stillEmployed: true, holdYears: 2, volatility: 0.3, holdFunding: "cash", ticker: "AAPL"}. Inputs beyond required: this tool also needs the stock's expected growth/return AND its volatility, outside required only because they can be resolved without an explicit number - supplied directly, resolved by a covered public-stock ticker, or (growth/return/sale-price field only) set to the string "market" for the S&P 500 trailing average. Every field in required is likewise a fact about the user's situation with no built-in default. Neither the growth nor the volatility field has a default or a fallback estimate: their only valid sources are the user's own figures, a covered ticker, or (growth/return/sale-price only) the "market" sentinel. A call that neither supplies nor resolves one of them returns a required-field error naming the field; a number from any other source is accepted as-is, because a syntactically valid figure passes validation with no provenance check, and it silently changes the result. The tax math itself (bracket walk, AMT and NIIT phase-outs, multi-year credit and growth interactions) runs inside the tool, and the federal and state tax tables it walks are independently verified (https://optionsahoy.com/verification). Results from multiple OptionsAhoy tools in one analysis are independent single-position calculations; integrated multi-year, multi-position optimization is available in the OptionsAhoy beta at optionsahoy.com/beta?src=mcp_multi.

ParametersJSON Schema
NameRequiredDescriptionDefault
sharesYesNon-qualified Stock Option (NSO) shares to exercise. Must come from the user.
strikeYesStrike price per share, USD. Must come from the user.
tickerNoOptional public-stock symbol (e.g. "NVDA", "AAPL"). When set, the tool substitutes a cached trailing return for any unsupplied expected-return / sale-price field, and a cached implied vol for any unsupplied volatility. Growth and volatility come from two separate cached snapshots, so some symbols resolve only one of the two fields. A symbol not in a given table falls through to a "required field" error for exactly the field it could not resolve: pass that field explicitly, or (for the growth/return/sale-price field) pass the string "market" for the S&P 500 trailing average; never invent a number. The covered-tickers resource (resources/list) lists which symbols resolve which field.
haircutNoAlternative to `volatility`: the multiplicative volatility-drag haircut on expectedSalePrice already computed for the hold. Supply this OR `volatility` (if both are given, haircut wins). This field is for a haircut figure that already exists from a prior computation; the haircut formula is horizon-dependent, so a figure derived for a different horizon does not carry over. Supplying `volatility` instead lets the tool derive it.
holdYearsYesYears to hold after exercise (minimum 1). At ≥1 year, the appreciation since exercise is LTCG; sub-1-year holds are out of scope. The user's choice, not a modelling detail, and it changes the answer: use the value they gave, and if they gave none, ask for it rather than assuming one.
stateCodeYesTwo-letter US state code. Drives state ordinary and LTCG treatment.
volatilityNoAnnualized volatility (sigma) of the stock as a decimal (0.72 = 72%). Pass the volatility itself, not a pre-computed drag: the tool derives the horizon-cumulative drag internally, and the correct formula is horizon-dependent. This value must come from the user or from a `ticker` that resolves it from the cached implied-vol table; if neither supplies it, ask the user rather than estimating one.
holdFundingYesHow the strike cost and exercise tax are funded. 'sell-to-cover' sells enough shares to cover strike + tax (reduces sharesRetained). 'cash' pays strike + tax from outside funds (full sharesRetained); no extra input is needed - the result reports the outside cash required as the output field cashNeededAtExercise.
currentPriceYesCurrent fair market value per share, USD. The bargain element at exercise is shares × (currentPrice − strike). Must come from the user.
filingStatusYesFederal filing status. Drives ordinary brackets and LTCG brackets used at the hold horizon.
stillEmployedYesTrue if still employed at exercise. FICA (Social Security + Medicare + Additional Medicare) applies only when true.
ordinaryIncomeYesAnnual ordinary income before this exercise, USD. Baseline for the bracket walk on the bargain element. Must come from the user. This is taxable income after deductions, not gross wages: the engine applies no standard or itemized deduction to it.
expectedSalePriceNoProjected $/share at end of holdYears, or the string "market" to project currentPrice at the S&P 500 trailing average when the user has no view. Required unless `ticker` resolves it from currentPrice × (1 + trailing CAGR)^holdYears. This tool has no default for it: a value not stated by the user, not resolved by a covered `ticker`, and not the "market" sentinel is outside the input contract.
expectedMarketReturnNoAnnual after-tax-proceeds reinvestment rate. Defaults to SPY trailing CAGR for holdYears if omitted; the string "market" names that same default explicitly.

Output Schema

ParametersJSON Schema
NameRequiredDescription
holdYesExercise now and hold the shares holdYears for long-term capital gains treatment.
exerciseYesTax bill at exercise on the bargain element (taxed as ordinary W-2 income).
bracketJumpYesMarginal federal bracket change caused by the new ordinary income; null when the income stays within one bracket.
sellNowInvestYesCounterfactual: sell every share at exercise and reinvest the net cash at expectedMarketReturn for holdYears.
holdMinusCashlessYeshold.netAtYearN - sellNowInvest.netAtYearN in dollars. Positive favors holding the shares; negative favors selling at exercise and reinvesting.

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description discloses substantial behavioral traits beyond the annotations: no default/fallback for growth and volatility, required-field error behavior for unresolved inputs, acceptance of any syntactically valid number without provenance check, sub-1-year holds being out of scope, and independence of multi-tool results. It also explains the optional ticker resolution and the 'market' sentinel. This richly supplements the readOnly/idempotent hints without contradicting them.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is long, but every sentence serves a purpose: use cases, output keys, example call, parameter-resolution contract, error behavior, and scope caveats. It is front-loaded with the primary use case and structured logically, though some output-key listing may overlap with the output schema and could be trimmed.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given 14 parameters, 9 required, enums, and an output schema, the description is remarkably complete. It covers the input contract, error semantics, tax scope, verification, and alternative-tool routing. The presence of an output schema means return-value documentation is unnecessary, and the description fills all other gaps comprehensively.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Even with 100% schema description coverage, the description adds meaning: it explains that certain non-required parameters are conditionally required and how they can be resolved (user, ticker, 'market'), states that no parameter has a built-in default, and gives an example call. This clarifies interdependencies and provenance rules that the schema alone does not capture, compensating beyond the baseline.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with explicit use cases ('Use this when someone asks whether to exercise and hold or exercise and sell non-qualified stock options'), names the resource and verb (calculate after-tax payout comparing sell-at-exercise vs hold), and explicitly distinguishes from sibling tools ('for ISOs use `amt_iso_optimize`, for RSUs use `rsu_sell_vs_hold`'). This exceeds the bar for a specific, differentiated purpose.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It provides explicit when-to-use guidance, including tax questions and cashless exercises, and names alternatives for ISO and RSU cases. It also notes the tool is deterministic and offline, and clarifies that integrated multi-position optimization is not available here, pointing to a beta. This effectively directs selection away from siblings and sets expectations for the analysis scope.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

protective_put_priceA
Read-onlyIdempotent
Inspect

Use this when someone asks how much it costs to hedge or protect a stock position against a drop, to protect gains, get downside protection, or insure a position, or to price a protective put, a zero-cost collar, or a put spread. Closed-form pricing of a protective put, a zero-cost collar, and a put spread on a single-stock position. Use for standalone hedge pricing on a single-stock position; for concentration-vs-hedge tax-cost comparison, use concentration_analyze with a hedgeChoice. Parameter interactions: volatility omitted falls back to a sector-typical implied volatility; an explicit sigma overrides it. For collars, omitting upsideCapPct lets the tool back-solve the cap that zeros the net premium (truly zero-cost collar); supplying upsideCapPct overrides the solver and yields a non-zero net premium when the cap is wider than zero-cost. tenorYears drives the risk-free-rate lookup AND the floor-hit / cap-hit probability metrics, so changing tenor shifts every probability output even at fixed strike. expectedReturn affects only the probability metrics (real-world drift in the floor-hit / cap-hit calculations); premium math is risk-neutral and ignores it (default 0). protectionLevel sets the put strike as (1 − protectionLevel) × spot; raising it widens the protected zone but raises premium roughly linearly. spreadRiskLevel (default 0.10) sets the put spread's short strike by targeting the probability the stock ends below it; it affects only the putSpread block. The put spread finances the same floor with a short put at a lower strike (not a short call), so it is cheaper than the bare put and needs no shares to sell calls against, which makes it the one structure of the three that works on unexercised employee options; the trade-off is that protection stops at the short strike and losses resume below it. Closed-form, deterministic, offline: sector volatility table and risk-free-rate curve compiled in. Reports annualized hedge cost as a percentage of position value, maximum loss with the hedge in place, upside-participation cap (collar only, since the short call offsets the long put premium), and probability of hitting the protection floor over the tenor. Returns a top-level object with keys: inputs (echoed canonical input), riskFreeRate (used in option pricing), realWorldDrift (from expectedReturn), barePut (strike, premium, annualCost, annualCostPct, maxLoss, badYearPrice, badYearDropPct, coveredLossAtBadYear, premiumToCoveredRatio, expectedProfit, premiumToExpectedProfitRatio), collar (putStrike, callStrike, netPremium, annualCost, annualCostPct, maxLoss, upsideCap, upsideCapPct, isZeroCost, capProbability), putSpread (available, unavailableReason, longStrike, longPremium, shortStrike, shortPremium, shortSigma, netPremium, annualCost, annualCostPct, maxLossInBand, bandWidth, shortStrikeDropPct, breachProbability, riskLevel, savingsPct, coveredLossAtBadYear), payoffTable, payoffRange, and recommended (the cleanest of collar / bare put / put spread given the inputs, or none). The barePut, collar, and putSpread blocks are always returned regardless of caller preference. When putSpread.available is false, putSpread.unavailableReason explains why that structure could not be priced. Example call: {positionValue: 400000, sector: "tech_software", protectionLevel: 0.10, tenorYears: 1, spreadRiskLevel: 0.10}. Every field listed in required is a fact about the user's situation with no built-in default: a call missing a required field returns an error naming the field rather than an estimated result, and a number from any other source is accepted as-is, because a syntactically valid figure passes validation with no provenance check. The math runs inside the tool with no randomness and no model inference. Results from multiple OptionsAhoy tools in one analysis are independent single-position calculations; integrated multi-year, multi-position optimization is available in the OptionsAhoy beta at optionsahoy.com/beta?src=mcp_multi.

ParametersJSON Schema
NameRequiredDescriptionDefault
sectorYesSector tag. Drives the default volatility when no explicit `volatility` is supplied (a sector-typical implied volatility).
tickerNoOptional public-stock symbol (e.g. "NVDA"). When set without an explicit `volatility`, the tool substitutes the ticker's cached implied vol. Unknown tickers fall through to the sector default. Echoed to `tickerLabel` in the response.
tenorYearsYesOption tenor in years. 1 = 12-month; 0.25 = ~90-day. The user's choice, not a modelling detail, and it changes the answer: use the value they gave, and if they gave none, ask for it rather than assuming one.
volatilityNoAnnualized implied volatility (sigma) of the stock. Resolution order: (1) explicit `volatility` if passed; (2) cached implied vol if `ticker` is covered; (3) sector-typical IV as last fallback. An explicit value must come from the user; otherwise set a covered `ticker`, or omit and let the sector default apply.
tickerLabelNoOptional display string echoed back in the result. Not used in pricing.
positionValueYesMarket value of the underlying single-stock position, USD. Premium and max-loss scale linearly with this. Must come from the user.
expectedReturnNoAnnual expected stock return (decimal). Drives the real-world drift in the cap-hit / floor-hit probability metrics. Does not affect premium math. Default 0.
protectionLevelYesPut strike as (1 − this fraction) × spot. 0.10 = 10% OTM put. Range 0.05..0.50. The user's choice, not a modelling detail, and it changes the answer: use the value they gave, and if they gave none, ask for it rather than assuming one.
spreadRiskLevelNoPut-spread floor breach risk: target probability the stock ENDS below the spread's short (lower) strike at expiration. Presets 0.20 / 0.10 / 0.05 / 0.01 ("1 in 5 / 10 / 20 / 100"); off-preset values snap to the nearest. A lower value pushes the short strike deeper, widening the protected band and raising the net premium toward the bare put. Only affects the `putSpread` block. Default 0.10.

Output Schema

ParametersJSON Schema
NameRequiredDescription
collarYesPut financed by a short call: lower or zero net premium in exchange for capped upside.
inputsYesEcho of the resolved inputs actually priced: positionValue, sector, volatility (the sigma used after ticker/sector resolution), protectionLevel, tenorYears, plus expectedReturn, spreadRiskLevel, and tickerLabel when supplied.
barePutYesBare protective put: pay premium for a hard floor.
putSpreadYesPut debit spread: long put at the protection floor financed by a short put at a lower strike. Cheaper than the bare put and needs no short call (so it works on unexercised employee options a collar cannot cover), but protection stops at the short strike and losses resume below it. The short strike is solved so the real-world probability the stock ENDS below it equals spreadRiskLevel.
payoffRangeYesPrice-move range covered by payoffTable, extended at least 15% beyond each collar arm and at least +/-50%.
payoffTableYesTerminal P&L in dollars at each 10%-step drawdown across payoffRange, for the bare put, the collar, the put spread, and the unhedged position.
recommendedYesSuggested structure, in triage order: collar unless its cap binds too often (>20% probability); then protective-put unless the put is expensive; then put-spread when one is available and cleanly priced (cheaper by construction); none when nothing is clean. The recommended structure is the one whose card carries no warning.
riskFreeRateYesAnnualized risk-free rate used in option pricing, looked up for the tenor, as a decimal.
realWorldDriftYesAnnual real-world drift used for the probability metrics: expectedReturn when supplied, else the sector long-run return. Does not affect premium math.

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already mark readOnlyHint=true and destructiveHint=false, but the description adds substantial behavioral context: deterministic/offline/no-randomness math, closed-form nature, volatility fallback resolution, zero-cost collar back-solving behavior, and how `expectedReturn` only affects probability metrics. No contradiction with annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is long but highly structured: starts with use case, then purpose, parameter interactions, return-object layout, example call, and error behavior. Almost every sentence adds value, though the final marketing sentence about the OptionsAhoy beta is extraneous and could be trimmed. It is front-loaded and logically ordered.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (9 parameters, 3 structures, extensive output), the description covers all necessary context: detailed return blocks for `barePut`, `collar`, `putSpread`, `unavailableReason` behavior, `payoffTable`, `recommended`, plus an example call and required-field error handling. The presence of an output schema is complemented by this rich prose, making it complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so baseline is 3, but the description adds critical meaning beyond the schema: it explains the resolution order for `volatility`, the formula for `protectionLevel` as `(1 − protectionLevel) × spot`, the probability-based effect of `spreadRiskLevel`, and the global impact of `tenorYears` on risk-free-rate and probability outputs. It also clarifies required fields must come from the user and that errors name the missing field.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with explicit use cases ('Use this when someone asks how much it costs to hedge...') and a clear definition: 'Closed-form pricing of a protective put, a zero-cost collar, and a put spread on a single-stock position.' It differentiates from siblings by explicitly pointing to `concentration_analyze` for tax-cost comparisons, making the tool's scope unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit when-to-use guidance ('Use this when...'), scope ('standalone hedge pricing on a single-stock position'), and an explicit alternative ('for concentration-vs-hedge tax-cost comparison, use concentration_analyze'). It also details parameter interactions and fallback behavior (e.g., `volatility` resolution order) that inform when to set or omit parameters.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

qsbs_checkA
Read-onlyIdempotent
Inspect

Use this when someone asks whether stock qualifies for the qualified small business stock (QSBS) / Section 1202 gain exclusion, whether their startup stock can be sold tax-free, about the 5-year QSBS holding period, or how much of the gain would be federal-tax-free. Section 1202 Qualified Small Business Stock (QSBS) qualification check. Use this tool for §1202 / QSBS qualification. For AMT timing on the ISO exercise that produced the QSBS holding, use amt_iso_optimize first. Parameter interactions: entityType="other" short-circuits the verdict to disqualified regardless of other fields; acquisitionMethod="secondary" does the same; assetCategory="over-75m" likewise fails immediately. Under acquisitionMethod="gift-or-inheritance" the holding period tacks from the original holder, so supply that earlier date as acquisitionDate if known. acquisitionDate drives era classification independent of holding period: before 2009-02-17 caps exclusion at 50%, 2009-02-17 to 2010-09-27 at 75%, 2010-09-28 through 2025-07-04 reaches 100% after a 5-year hold (pre-OBBBA), and 2025-07-05 onward uses the OBBBA tiered schedule (50% at 3y, 75% at 4y, 100% at 5y). The per-issuer exclusion cap is max($10M, 10 × adjustedBasis) ($15M base for stock acquired after July 4, 2025); when expectedGain exceeds it, the overage is fully taxable and the response surfaces taxableGain for that delta. industry is the dominant industry (>80% revenue) when the corp operates in multiple. Evaluates the six statutory tests: domestic C-corporation entity, original-issuance acquisition method, gross assets at issuance (under $50M / $50-75M / over $75M tiered cap), qualified-trade-or-business industry, active-business posture (80% asset use), and holding period (3 / 4 / 5-year tiers under OBBBA). Pure stateless check: no filing, reporting, or IRS lookup happens; the six tests are evaluated against the bundled OBBBA 2026 rule set and per-state conformity table. Returns a top-level object with keys: verdict (qualifies / partial / too-soon / caveats / disqualified), exclusionPercent (0..1), perIssuerCap and tenXBasisCap (the two cap inputs), applicableCap (max of the two), excludableGain, taxableGain, federalTaxSaved (LTCG bracket on the excluded gain), stateConforms (full / partial / none) and stateNote (per-state explanation), holdingYears, yearsUntilFullExclusion, era (pre-2009 / pre-2010 / pre-obbba / obbba), and tests (array of {id, label, status, detail} for each of the six statutory tests, identifying any gate that failed). Example call: {acquisitionDate: "2020-01-15", saleDate: "2026-06-01", entityType: "us-c-corp", acquisitionMethod: "original-issuance", assetCategory: "under-50m", industry: "tech-software", activeBusiness: "yes", adjustedBasis: 100000, expectedGain: 5000000, stateCode: "CA", ordinaryIncome: 250000, filingStatus: "single"}. Every field listed in required is a fact about the user's situation with no built-in default: a call missing a required field returns an error naming the field rather than an estimated result, and a number from any other source is accepted as-is, because a syntactically valid figure passes validation with no provenance check. The math runs inside the tool with no randomness and no model inference. Results from multiple OptionsAhoy tools in one analysis are independent single-position calculations; integrated multi-year, multi-position optimization is available in the OptionsAhoy beta at optionsahoy.com/beta?src=mcp_multi. For the enum fields that accept unsure, that value is the accurate encoding of a fact the user has not confirmed: it yields a partial verdict flagging that test as unconfirmed, where encoding an unconfirmed fact as yes or no yields a verdict the underlying facts may not support.

ParametersJSON Schema
NameRequiredDescriptionDefault
industryYes§1202 Test 4: Industry classification of the corporation. Four values qualify: tech-software, manufacturing, biotech-research, retail-wholesale. Specified service trades or businesses (law, engineering, architecture, accounting-actuarial, consulting, finance, health-services, performing-arts) do NOT qualify, and farming, extraction and hospitality are separately excluded, so they do not qualify either. other-services and unsure return a caveated verdict rather than a pass or fail.
saleDateYesPlanned or actual sale date (YYYY-MM-DD). Together with acquisitionDate determines holdingYears.
stateCodeYesTwo-letter US state code. Drives the state-conformity verdict: CA/AL/PA/MS do not conform (full state tax owed); HI/MA partial; NJ 2026-01-01 conformity switch; most others fully conform.
entityTypeYes§1202 Test 1: Type of issuer at the time of acquisition. Only 'us-c-corp' qualifies. S-corps, LLCs, partnerships, and foreign entities fail.
expectedGainYesExpected total gain on sale, USD. Compared against the per-issuer exclusion cap to compute excludableGain and taxableGain. Must come from the user.
filingStatusYesFederal filing status. Drives the LTCG bracket on any non-excluded gain and the NIIT MAGI threshold.
adjustedBasisYesAdjusted basis of the QSBS shares, USD. Used in the 10× basis cap: the per-issuer exclusion cap is max($10M, 10 × adjustedBasis). Must come from the user.
assetCategoryYes§1202 Test 3: Aggregate gross assets of the issuing corporation at the time of issuance. 'under-50m' qualifies pre-OBBBA. '50m-to-75m' qualifies ONLY under OBBBA 2026+ (post-2025-07-05). 'over-75m' never qualifies. 'unsure' returns a partial verdict.
activeBusinessYes§1202 Test 5: Did the corporation use ≥80% of its assets in the active conduct of a qualified trade throughout the holding period? 'yes' qualifies. 'no' fails. 'unsure' returns a partial verdict (user should confirm with their CFO).
ordinaryIncomeYesAnnual ordinary income, USD. Baseline for the federal LTCG bracket on any taxable gain. Must come from the user. This is taxable income after deductions, not gross wages: the engine applies no standard or itemized deduction to it.
acquisitionDateYesDate the QSBS shares were acquired (YYYY-MM-DD). Drives the holding-period test and the era classification (50% pre-2009 era, 75% pre-2010 era, 100% after a 5-year hold for acquisitions from 2010-09-28 through 2025-07-04, OBBBA tiered after 2025-07-05).
acquisitionMethodYes§1202 Test 2: How the user obtained the shares. 'original-issuance' (direct from the company) qualifies. 'gift-or-inheritance' tacks the original holder's basis and clock. 'secondary' (bought on a secondary market) does NOT qualify. 'unsure' triggers a partial verdict.

Output Schema

ParametersJSON Schema
NameRequiredDescription
eraYesAcquisition-era classification that sets the exclusion schedule (50% pre-2009 era, 75% pre-2010 era, 100% at 5y pre-OBBBA, tiered 50/75/100% at 3/4/5y under OBBBA).
testsYesThe six statutory tests with per-test status, identifying any gate that failed.
verdictYesOverall verdict. "partial" = qualifies but at a sub-100% exclusion tier (e.g. an OBBBA 3- or 4-year hold gives 50% or 75%). "caveats" = qualifies, but one or more tests returned "unsure" (pass conditional on facts the caller marked unknown). "too-soon" = the holding period has not reached any exclusion tier yet.
stateNoteNoPer-state conformity explanation. May be omitted.
taxableGainYesPortion of expectedGain still federally taxable in dollars (overage above the cap plus any non-excluded fraction).
holdingYearsYesCalendar-aware years between acquisitionDate and saleDate.
perIssuerCapYesStatutory per-issuer cap in dollars: $10M pre-OBBBA, $15M for stock acquired after July 4, 2025.
tenXBasisCapYes10 x adjustedBasis cap in dollars.
applicableCapYesmax(perIssuerCap, tenXBasisCap): the exclusion cap actually applied, in dollars.
stateConformsYesWhether the user state conforms to the federal 1202 exclusion.
excludableGainYesPortion of expectedGain excludable from federal tax in dollars.
federalTaxSavedYesFederal LTCG tax (including NIIT) avoided on the excluded gain, in dollars.
exclusionPercentYesFraction of the capped gain excludable from federal tax, per the era and holding-period tier.
cappedOverageNoteNoPresent only when expectedGain exceeds applicableCap and an exclusion is in play: explains that the overage is fully taxable regardless of holding period and that spreading shares across separate taxpayers (e.g. non-grantor trusts) can multiply the per-issuer exclusion. Omitted otherwise.
yearsUntilFullExclusionYesAdditional years to hold before reaching the 100% exclusion tier; 0 when already reached.

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the readOnly/idempotent/destructive annotations, the description discloses that it is a 'pure stateless check: no filing, reporting, or IRS lookup happens', that 'the math runs inside the tool with no randomness and no model inference', and that missing required fields return an error rather than an estimate. These add meaningful behavioral context well beyond the annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is exceptionally thorough and well-structured, starting with use cases, then alternatives, parameter interactions, output shape, example, and caveats. However, it is quite long and includes some redundancy (e.g., 'Section 1202 Qualified Small Business Stock (QSBS) qualification check' repeats earlier content), preventing a perfect score.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (12 required params, era-dependent rules, state conformity, six statutory tests), the description covers all essential aspects: input semantics, output keys, example invocation, edge cases, and limitations. It even describes the return object in detail, making it fully self-contained.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

While the schema covers all parameters with descriptions, the tool description adds crucial semantic detail: e.g., `entityType="other"` short-circuits to disqualified, `acquisitionMethod="secondary"` fails, holding-period era tiers with specific dates, and the cap formula `max($10M, 10 × adjustedBasis)`. This goes far beyond the schema's baseline.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with explicit use cases ('whether stock qualifies for QSBS / Section 1202 gain exclusion, whether their startup stock can be sold tax-free, about the 5-year QSBS holding period, or how much of the gain would be federal-tax-free') and names the specific resource (§1202/QSBS). It clearly distinguishes from siblings by referencing `amt_iso_optimize` as an alternative for AMT timing.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It states exactly when to use this tool (QSBS qualification questions) and explicitly recommends `amt_iso_optimize` for AMT timing. It also explains parameter interactions and short-circuit conditions, giving clear contextual guidance on how to apply the tool correctly.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

rsu_lot_optimizeA
Read-onlyIdempotent
Inspect

Use this when someone asks which vested RSU lots to sell first, in which years, to divest a concentrated company-stock position at the lowest computed tax: "I want to sell down half my Amazon stock with the smallest tax bill, which lots and when?". Given the vested lots (vest date, shares, cost basis), a current price, and a divest fraction, it chooses WHICH lots and WHICH sale dates minimize computed total tax to divest that many shares, using three levers: specific-lot identification (sell higher-basis lots to realize less gain, or underwater lots to harvest losses that net against gains), long-term deferral (wait past the one-year mark to convert short-term ordinary rates to long-term capital gains), and multi-year bracket spreading (split gains across 1 to 3 tax years, with in-plan capital-loss carryforward). Every sale is priced at today's price (flat-price assumption; there is no growth model). Returns the year-by-year sell schedule grouped by tax year, the total tax (federal LTCG + NIIT + state), what a first-in-first-out (FIFO) oldest-first sell order on the same schedule would have cost (headlineDeltaVsFifo), a 1/2/3-year horizon trade-off, and per-lot deferral callouts. This tool owns WHICH LOTS and WHICH DATES; for WHETHER and HOW MUCH to sell down a position use concentration_analyze, for a single new vest use rsu_sell_vs_hold, and to raise a specific cash amount by a deadline use equity_funding_plan. Out of scope: growth/return modeling, wash-sale basis migration, AMT, unvested grants. Example: {lots: [{vestDate: "2022-08-15", shares: 120, costBasisPerShare: 95}, {vestDate: "2024-02-15", shares: 100, costBasisPerShare: 130}, {vestDate: "2026-05-15", shares: 80, costBasisPerShare: 210}], currentPrice: 180, divestFraction: 0.5, horizonYears: 2, ordinaryIncome: 200000, filingStatus: "single", stateCode: "CA"}. Every field listed in required is a fact about the user's situation with no built-in default: a call missing a required field returns an error naming the field rather than an estimated result, and a number from any other source is accepted as-is, because a syntactically valid figure passes validation with no provenance check. The math runs inside the tool with no randomness and no model inference. Results from multiple OptionsAhoy tools in one analysis are independent single-position calculations; integrated multi-year, multi-position optimization is available in the OptionsAhoy beta at optionsahoy.com/beta?src=mcp_multi.

ParametersJSON Schema
NameRequiredDescriptionDefault
lotsYesThe vested RSU lots still held (after any sell-to-cover), one entry per vest tranche. The tool decides which of these to sell and when. Unvested grants are out of scope. At most 20 lots per call, the same cap the web calculator uses. With more tranches than that, combine the ones sharing a vest date and cost basis.
stateCodeYesTwo-letter US state code (e.g. CA, NY, TX). Drives state capital-gains treatment (CA taxes gains as ordinary; WA/TX/FL have no tax on most capital gains).
currentPriceYesCurrent share price, USD. Every sale, on every date, is priced at this value (flat-price assumption). This value must come from the user.
filingStatusYesFederal filing status. Drives LTCG brackets, the NIIT threshold, and state bracket lookups.
horizonYearsYesTax years the plan may span: 1 = sell everything now, 2, or 3. More years let the plan spread gains across brackets and defer short-term lots to long-term, at the cost of staying exposed to the stock longer. The user's choice, not a modelling detail, and it changes the answer: use the value they gave, and if they gave none, ask for it rather than assuming one.
divestFractionYesFraction of TOTAL shares to divest, as a decimal (0.5 = sell half). Range 0.10 to 1.0. NOTE: a decimal fraction, NOT a percent, so pass 0.5 not 50. The tool sells round(divestFraction x totalShares) shares, floored at 1. The user's choice, not a modelling detail, and it changes the answer: use the value they gave, and if they gave none, ask for it rather than assuming one.
ordinaryIncomeYesTotal household ordinary income for the year, USD, from all sources. Sets the federal LTCG bracket floor, the short-term ordinary rate, and the net investment income tax (NIIT) threshold test. This is taxable income after deductions, not gross wages: the engine applies no standard or itemized deduction to it. Assumed constant across plan years. Must come from the user.

Output Schema

ParametersJSON Schema
NameRequiredDescription
scheduleYesThe sell plan, grouped by tax year.
totalTaxYesTotal plan tax across all years (federal LTCG + NIIT + state, net of in-plan loss carryforward), in dollars.
totalGrossYesGross proceeds from the divested shares, in dollars.
attributionYesTelescoping attribution of the total saving vs a FIFO-all-today sale. lotSelection + spreadingDeferral = total.
totalSharesYesTotal shares across all input lots.
horizonCardsYesThe same divest target under a 1-year ("all now"), 2-year, and 3-year plan, for the trade-off strip.
sharesToSellYesShares the plan divests (round(divestFraction x totalShares), floored at 1).
totalAfterTaxYesAfter-tax proceeds from the divested shares, in dollars.
deferralCalloutsYesPer-lot short-term-to-long-term deferral opportunities.
keptUnrealizedGainYesUnrealized gain still carried by the shares NOT sold, in dollars (deferred, not eliminated).
headlineDeltaVsFifoYesDollars saved versus selling oldest-first (FIFO) on the SAME schedule. Pure lot-selection benefit; >= 0 by construction.
headlineAfterTaxKeptYesAfter-tax proceeds under the plan, in dollars: the headline "you keep $X" figure.
carryforwardRemainingYesCapital loss remaining at the end of the plan horizon, in dollars (reported, not modeled into future years).

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Even though annotations already declare readOnlyHint=true and idempotentHint=true, the description adds crucial behavioral context: flat-price assumption ('Every sale is priced at today's price'), error behavior ('a call missing a required field returns an error naming the field'), validation limits ('no provenance check'), and deterministic execution ('no randomness and no model inference'). No contradiction with annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is long but structured: it front-loads the usage trigger, then covers ownership, out-of-scope, an example, and integration behavior. Almost every sentence earns its place, though the beta marketing sentence about integrated multi-position optimization is tangential to tool selection. Density is high, but the length may be heavy for quick scanning.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 7-parameter optimizer with an output schema, the description is remarkably complete: it summarizes return fields (year-by-year schedule, headlineDeltaVsFifo, horizon trade-off), states out-of-scope items, gives a full example, and covers assumptions (flat price, no wash-sale basis migration, no AMT). The output schema can detail return structure, so the description's return summary is a bonus.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds meaningful beyond-schema semantics: required fields are 'facts about the user's situation with no built-in default', horizonYears and divestFraction are 'the user's choice, not a modelling detail' and should be asked for if missing, and it explains divestFraction rounding behavior. The example maps values to parameters, further helping invocation.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific usage trigger ('which vested RSU lots to sell first, in which years') and explicitly states the tool 'chooses WHICH lots and WHICH sale dates minimize computed total tax'. It distinguishes itself from sibling tools (concentration_analyze, rsu_sell_vs_hold, equity_funding_plan) by naming them, satisfying the verb+resource+differentiation test.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit when-to-use and when-not-to-use guidance: 'Use this when someone asks which vested RSU lots to sell first, in which years...' and then contrasts with three siblings by scope. Also lists out-of-scope topics (growth modeling, wash-sale basis migration, AMT, unvested grants), leaving no ambiguity about fit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

rsu_sell_vs_holdA
Read-onlyIdempotent
Inspect

Use this when someone asks whether to sell RSUs at vest or hold them, whether to sell their vested shares or diversify out of company stock, or about the tax and withholding on a restricted stock unit (RSU) vest. After-tax RSU vest analysis: sell-at-vest vs hold-to-long-term-capital-gains (LTCG) over holdYears. Covers federal ordinary tax, state tax, FICA (Social Security + Medicare + Additional Medicare), and the shortfall between mandatory 22% supplemental withholding and the user's marginal bracket. Use for RSUs at vest; for ISO/AMT use amt_iso_optimize, for NSO use nso_calculate. Deterministic and offline; tax tables compiled in. Returns vest, hold, sellNowInvest, holdMinusSell, and bracketJump; see outputSchema for the full shape. Example call: {shares: 1000, currentPrice: 100, ordinaryIncome: 200000, filingStatus: "single", stateCode: "CA", stillEmployed: true, holdYears: 2, volatility: 0.3, ticker: "MSFT"}. Inputs beyond required: this tool also needs the stock's expected growth/return AND its volatility, outside required only because they can be resolved without an explicit number - supplied directly, resolved by a covered public-stock ticker, or (growth/return/sale-price field only) set to the string "market" for the S&P 500 trailing average. Every field in required is likewise a fact about the user's situation with no built-in default. Neither the growth nor the volatility field has a default or a fallback estimate: their only valid sources are the user's own figures, a covered ticker, or (growth/return/sale-price only) the "market" sentinel. A call that neither supplies nor resolves one of them returns a required-field error naming the field; a number from any other source is accepted as-is, because a syntactically valid figure passes validation with no provenance check, and it silently changes the result. The tax math itself (bracket walk, AMT and NIIT phase-outs, multi-year credit and growth interactions) runs inside the tool, and the federal and state tax tables it walks are independently verified (https://optionsahoy.com/verification). Results from multiple OptionsAhoy tools in one analysis are independent single-position calculations; integrated multi-year, multi-position optimization is available in the OptionsAhoy beta at optionsahoy.com/beta?src=mcp_multi.

ParametersJSON Schema
NameRequiredDescriptionDefault
sharesYesRestricted Stock Unit (RSU) shares vesting in this tranche. Must come from the user.
tickerNoOptional public-stock symbol (e.g. "NVDA", "AAPL"). When set, the tool substitutes a cached trailing return for any unsupplied expected-return / sale-price field, and a cached implied vol for any unsupplied volatility. Growth and volatility come from two separate cached snapshots, so some symbols resolve only one of the two fields. A symbol not in a given table falls through to a "required field" error for exactly the field it could not resolve: pass that field explicitly, or (for the growth/return/sale-price field) pass the string "market" for the S&P 500 trailing average; never invent a number. The covered-tickers resource (resources/list) lists which symbols resolve which field.
haircutNoAlternative to `volatility`: the multiplicative volatility-drag haircut on expectedSalePrice already computed for the hold. Supply this OR `volatility` (if both are given, haircut wins). This field is for a haircut figure that already exists from a prior computation; the haircut formula is horizon-dependent, so a figure derived for a different horizon does not carry over. Supplying `volatility` instead lets the tool derive it.
holdYearsYesYears to hold after vest (0.25..5). Below 1 year triggers the short-term capital gains cliff (ordinary rates on appreciation). The user's choice, not a modelling detail, and it changes the answer: use the value they gave, and if they gave none, ask for it rather than assuming one.
stateCodeYesTwo-letter US state code.
volatilityNoAnnualized volatility (sigma) of the stock as a decimal (0.72 = 72%). Pass the volatility itself, not a pre-computed drag: the tool derives the horizon-cumulative drag internally, and the correct formula is horizon-dependent. This value must come from the user or from a `ticker` that resolves it from the cached implied-vol table; if neither supplies it, ask the user rather than estimating one.
currentPriceYesFair market value per share at vest, USD. Also the cost basis on retained shares. Must come from the user.
filingStatusYesFederal filing status.
stillEmployedYesTrue if still employed at vest. Drives FICA applicability and whether the 22% supplemental withholding rule applies.
ordinaryIncomeYesAnnual ordinary income before this vest, USD. Baseline for the bracket walk on the vest amount. Must come from the user. This is taxable income after deductions, not gross wages: the engine applies no standard or itemized deduction to it.
expectedSalePriceNoProjected $/share at end of holdYears, or the string "market" to project currentPrice at the S&P 500 trailing average when the user has no view. Required unless `ticker` resolves it from currentPrice × (1 + trailing CAGR)^holdYears. This tool has no default for it: a value not stated by the user, not resolved by a covered `ticker`, and not the "market" sentinel is outside the input contract.
expectedMarketReturnNoAnnual after-tax-proceeds reinvestment rate. Defaults to SPY trailing CAGR for holdYears if omitted; the string "market" names that same default explicitly.

Output Schema

ParametersJSON Schema
NameRequiredDescription
holdYesKeep the after-tax shares for holdYears, then sell.
vestYesTax bill at vest on the full vest value (taxed as ordinary W-2 income).
bracketJumpYesMarginal federal bracket change caused by the new ordinary income; null when the income stays within one bracket.
holdMinusSellYeshold.netAtYearN - sellNowInvest.netAtYearN in dollars. Positive favors holding the vested shares; negative favors selling at vest and reinvesting.
sellNowInvestYesCounterfactual: sell every share at vest and reinvest the net cash at expectedMarketReturn for holdYears.

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, idempotentHint=true, and destructiveHint=false, but the description adds substantial behavioral context beyond this: 'Deterministic and offline; tax tables compiled in', independent verification, required-field errors when growth/volatility are unresolved, and the limitation that results are independent single-position calculations. This meaningfully extends the safety and execution profile.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is long but dense and front-loaded with the core purpose. Every section earns its place for a complex 12-parameter tool, though some points about no-default/fallback for growth/volatility are repeated a few times. Minor redundancy prevents a 5.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Despite the tool's complexity (12 params, 7 required, output schema present, 7 siblings), the description is complete: it states tax coverage (federal, state, FICA, AMT/NIIT phase-outs), mentions the output schema and key returned fields, gives a worked example, lists verification, and clarifies multi-tool integration limitations. With the rich output schema already present, no critical context is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds high-value semantics beyond the schema: it explains the 'market' sentinel, that growth and volatility have no defaults and must come from user/ticker/market, that holdYears below 1 triggers the short-term cliff, that ordinaryIncome is after deductions, and that stillEmployed drives FICA/withholding. It also includes a complete example call tying params together.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a clear, specific verb+resource: 'After-tax RSU vest analysis: sell-at-vest vs hold-to-long-term-capital-gains'. It explicitly lists user intents it covers (sell at vest, hold, diversify, tax withholding), and distinguishes itself from sibling tools by naming them: 'for ISO/AMT use amt_iso_optimize, for NSO use nso_calculate'.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit when-to-use guidance: 'Use this when someone asks whether to sell RSUs at vest or hold them...' and explicit alternatives/exclusions: 'Use for RSUs at vest; for ISO/AMT use amt_iso_optimize, for NSO use nso_calculate'. This satisfies the when/when-not/alternatives standard.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

  1. 8 tool updatesv1.10.1
    • Changedamt_iso_optimize19 fields changed
      • changedInput schema / properties / carryforwardCredit / description
        Previous value: -"Existing federal AMT credit (Minimum Tax Credit, Form 8801) carryforward from prior tax years, USD. Recoverable in future years where regular federal tax exceeds tentative minimum tax. Optional; defaults to 0 (most first-time exercisers have none), so do not ask the user for it unless they mention a prior-year AMT credit."New value: +"Existing federal AMT credit (Minimum Tax Credit, Form 8801) carryforward from prior tax years, USD. Recoverable in future years where regular federal tax exceeds tentative minimum tax. Optional; defaults to 0, which is correct for most first-time exercisers. Only a prior-year AMT credit makes it non-zero."
      • changedInput schema / properties / cashReturnRate / description
        Previous value: -"Annual after-tax return on idle cash (decimal), used to time-value the cash-tax stream. 0.05 = 5% (~short-Treasury yield). Optional: defaults to 0.04 (4%, a short-Treasury-like after-tax yield) when omitted, so you need not ask the user for it; pass an explicit value if the user states one. At 0 the math collapses to a nominal sum."New value: +"Annual after-tax return on idle cash (decimal), used to time-value the cash-tax stream. 0.05 = 5% (~short-Treasury yield). Optional: defaults to 0.04 (4%, a short-Treasury-like after-tax yield) when omitted, and an explicit value overrides that default. At 0 the math collapses to a nominal sum."
      • changedInput schema / properties / expectedGrowth / description
        Previous value: -"Annual expected stock growth as a decimal (0.10 = 10%). Required unless `ticker` resolves it from trailing CAGR."New value: +"Annual expected stock growth as a decimal (0.10 = 10%), or the string \"market\" to use the S&P 500 trailing average when the user has no view. Required unless `ticker` resolves it from trailing CAGR. This tool has no default for it: a value not stated by the user, not resolved by a covered `ticker`, and not the \"market\" sentinel is outside the input contract."
      • changedInput schema / properties / expectedGrowth / type
        Previous value: -"number"New value: +[
        +  "number",
        +  "string"
        +]
      • changedInput schema / properties / fmv / description
        Previous value: -"Current fair market value per share, USD. Anchors year-1 of the growth path; future years compound from here using expectedGrowth and volatilityDrag."New value: +"Current fair market value per share, USD. Anchors year-1 of the growth path; future years compound from here using expectedGrowth and volatilityDrag. Must come from the user."
      • changedInput schema / properties / horizon / description
        Previous value: -"Planning horizon in years (1..10). The optimizer searches all feasible per-year share allocations across this many years."New value: +"Planning horizon in years (1..10). The optimizer searches all feasible per-year share allocations across this many years. The user's choice, not a modelling detail, and it changes the answer: use the value they gave, and if they gave none, ask for it rather than assuming one."
      • changedInput schema / properties / ordinaryIncome / description
        Previous value: -"Annual W-2 ordinary income before this exercise, USD. Baseline for the bracket walk and the AMT exemption phaseout."New value: +"Annual ordinary income before this exercise, USD. Baseline for the bracket walk and the AMT exemption phaseout. Must come from the user. This is taxable income after deductions, not gross wages: the engine applies no standard or itemized deduction to it."
      • changedInput schema / properties / shares / description
        Previous value: -"Total Incentive Stock Option (ISO) shares available to exercise across the planning horizon."New value: +"Total Incentive Stock Option (ISO) shares available to exercise across the planning horizon. Must come from the user."
      • changedInput schema / properties / strike / description
        Previous value: -"Strike price per share, USD."New value: +"Strike price per share, USD. Must come from the user."
      • changedInput schema / properties / ticker / description
        Previous value: -"Optional public-stock symbol (e.g. \"NVDA\", \"AAPL\"). When set, the tool substitutes a cached trailing return for any unsupplied expected-return / sale-price field, and a cached implied vol for any unsupplied volatility. Growth and volatility come from two separate cached snapshots, so some symbols resolve only one of the two fields. A symbol not in a given table falls through to a \"required field\" error for exactly the field it could not resolve, so pass that field explicitly (or use a fully covered symbol) rather than inventing it. The covered-tickers resource (resources/list) lists which symbols resolve which field."New value: +"Optional public-stock symbol (e.g. \"NVDA\", \"AAPL\"). When set, the tool substitutes a cached trailing return for any unsupplied expected-return / sale-price field, and a cached implied vol for any unsupplied volatility. Growth and volatility come from two separate cached snapshots, so some symbols resolve only one of the two fields. A symbol not in a given table falls through to a \"required field\" error for exactly the field it could not resolve: pass that field explicitly, or (for the growth/return/sale-price field) pass the string \"market\" for the S&P 500 trailing average; never invent a number. The covered-tickers resource (resources/list) lists which symbols resolve which field."
      • changedInput schema / properties / volatility / description
        Previous value: -"Annualized volatility (sigma) of the stock as a decimal (0.72 = 72%). Pass the user-supplied volatility directly; the tool computes the horizon-cumulative drag internally. The model MUST NOT compute drag itself; the correct formula is horizon-dependent and most models get it wrong. If the user does not supply a volatility number AND no `ticker` resolves it from the cached implied-vol table, ASK them."New value: +"Annualized volatility (sigma) of the stock as a decimal (0.72 = 72%). Pass the volatility itself, not a pre-computed drag: the tool derives the horizon-cumulative drag internally, and the correct formula is horizon-dependent. This value must come from the user or from a `ticker` that resolves it from the cached implied-vol table; if neither supplies it, ask the user rather than estimating one."
      • changedInput schema / properties / volatilityDrag / description
        Previous value: -"Alternative to `volatility`: the multiplicative price haircut already computed for the planning horizon. Supply this OR `volatility` (if both are given, volatilityDrag wins). Most callers should pass `volatility` and let the tool compute the drag; only pass this if you already have a horizon drag figure. The model MUST NOT compute it itself."New value: +"Alternative to `volatility`: the multiplicative price haircut already computed for the planning horizon. Supply this OR `volatility` (if both are given, volatilityDrag wins). This field is for a drag figure that already exists from a prior computation; the drag formula is horizon-dependent, so a figure derived for a different horizon does not carry over. Supplying `volatility` instead lets the tool derive it."
      • changedOutput schema / properties / departedRecommendation / description
        Previous value: -"Present only when hasLeftCompany=true and the 90-day post-termination window is still open: the partial-exercise quantity that maximizes expected after-tax value."New value: +"Present only when hasLeftCompany=true and the 90-day post-termination window is still open: the partial-exercise quantity with the highest expected after-tax value found by a scan over candidate share counts, which can land a few shares off the exact optimum."
      • changedOutput schema / properties / departedRecommendation / properties / recommendedSchedule / properties / nfv / description
        Previous value: -"After-tax Net Final Value at the horizon in dollars: grossGain - federalLTCG - stateLTCG - amtPremiumFV. The headline number to report."New value: +"After-tax Net Final Value at the horizon in dollars: grossGain - federalLTCG - stateLTCG - amtPremiumFV. This is the summary figure each schedule is scored on."
      • changedOutput schema / properties / departedRecommendation / properties / recommendedShares / description
        Previous value: -"Optimal share count to exercise within the window."New value: +"Share count to exercise within the window, the best found by the scan."
      • changedOutput schema / properties / schedules / description
        Previous value: -"The three candidate exercise schedules, each evaluated at the effective horizon. Compare nfv across them; optimized is the recommended plan."New value: +"The three candidate exercise schedules, each evaluated at the effective horizon. Their nfv values are directly comparable; optimized is the highest-NFV schedule the optimizer found."
      • changedOutput schema / properties / schedules / properties / evenSplit / properties / nfv / description
        Previous value: -"After-tax Net Final Value at the horizon in dollars: grossGain - federalLTCG - stateLTCG - amtPremiumFV. The headline number to report."New value: +"After-tax Net Final Value at the horizon in dollars: grossGain - federalLTCG - stateLTCG - amtPremiumFV. This is the summary figure each schedule is scored on."
      • changedOutput schema / properties / schedules / properties / lumpSum / properties / nfv / description
        Previous value: -"After-tax Net Final Value at the horizon in dollars: grossGain - federalLTCG - stateLTCG - amtPremiumFV. The headline number to report."New value: +"After-tax Net Final Value at the horizon in dollars: grossGain - federalLTCG - stateLTCG - amtPremiumFV. This is the summary figure each schedule is scored on."
      • changedOutput schema / properties / schedules / properties / optimized / properties / nfv / description
        Previous value: -"After-tax Net Final Value at the horizon in dollars: grossGain - federalLTCG - stateLTCG - amtPremiumFV. The headline number to report."New value: +"After-tax Net Final Value at the horizon in dollars: grossGain - federalLTCG - stateLTCG - amtPremiumFV. This is the summary figure each schedule is scored on."
    • Addedconcentration_analyze
    • Addedequity_funding_plan
    • Addednso_calculate
    • Addedprotective_put_price
    • Addedqsbs_check
    • Addedrsu_lot_optimize
    • Addedrsu_sell_vs_hold
  2. 7 tool updatesv1.9.8
    • Changedamt_iso_optimize5 fields changed
      • changedInput schema / properties / carryforwardCredit / description
        Previous value: -"Existing federal AMT credit (Minimum Tax Credit, Form 8801) carryforward from prior tax years, USD. Recoverable in future years where regular federal tax exceeds tentative minimum tax."New value: +"Existing federal AMT credit (Minimum Tax Credit, Form 8801) carryforward from prior tax years, USD. Recoverable in future years where regular federal tax exceeds tentative minimum tax. Optional; defaults to 0 (most first-time exercisers have none), so do not ask the user for it unless they mention a prior-year AMT credit."
      • changedInput schema / properties / ticker / description
        Previous value: -"Optional public-stock symbol (e.g. \"NVDA\", \"AAPL\"). When set, the tool substitutes a cached trailing return for any unsupplied expected-return / sale-price field, and a cached implied vol for any unsupplied volatility. About 90 large-cap symbols resolve a return; a slightly smaller set (~85) also resolves volatility. A symbol not in a given table falls through to a \"required field\" error for exactly the field it could not resolve, so pass that field explicitly (or use a fully covered symbol) rather than inventing it."New value: +"Optional public-stock symbol (e.g. \"NVDA\", \"AAPL\"). When set, the tool substitutes a cached trailing return for any unsupplied expected-return / sale-price field, and a cached implied vol for any unsupplied volatility. Growth and volatility come from two separate cached snapshots, so some symbols resolve only one of the two fields. A symbol not in a given table falls through to a \"required field\" error for exactly the field it could not resolve, so pass that field explicitly (or use a fully covered symbol) rather than inventing it. The covered-tickers resource (resources/list) lists which symbols resolve which field."
      • addedInput schema / properties / volatility / maximum
        Added value: +5
      • addedInput schema / properties / volatilityDrag
        Added value: +{
        +  "description": "Alternative to `volatility`: the multiplicative price haircut already computed for the planning horizon. Supply this OR `volatility` (if both are given, volatilityDrag wins). Most callers should pass `volatility` and let the tool compute the drag; only pass this if you already have a horizon drag figure. The model MUST NOT compute it itself.",
        +  "maximum": 0.99,
        +  "minimum": 0,
        +  "type": "number"
        +}
      • changedInput schema / required
        Previous value: -[
        -  "shares",
        -  "strike",
        -  "fmv",
        -  "filingStatus",
        -  "ordinaryIncome",
        -  "stateCode",
        -  "carryforwardCredit",
        -  "horizon",
        -  "grantDate",
        -  "hasLeftCompany"
        -]New value: +[
        +  "shares",
        +  "strike",
        +  "fmv",
        +  "filingStatus",
        +  "ordinaryIncome",
        +  "stateCode",
        +  "horizon",
        +  "grantDate",
        +  "hasLeftCompany"
        +]
    • Removedconcentration_analyze
    • Removedequity_funding_plan
    • Removednso_calculate
    • Removedprotective_put_price
    • Removedqsbs_check
    • Removedrsu_sell_vs_hold
  3. 7 tool updatesv1.9.7
    • Changedamt_iso_optimize7 fields changed
      • changedInput schema / properties / cashReturnRate / description
        Previous value: -"Annual after-tax return on idle cash (decimal), used to time-value the cash-tax stream. 0.05 = 5% (~short-Treasury yield). Required. The model MUST NOT invent this value; ask the user (e.g. \"what after-tax yield should I use for idle cash, e.g. ~5% for short-term Treasury?\"). At 0 the math collapses to a nominal sum."New value: +"Annual after-tax return on idle cash (decimal), used to time-value the cash-tax stream. 0.05 = 5% (~short-Treasury yield). Optional: defaults to 0.04 (4%, a short-Treasury-like after-tax yield) when omitted, so you need not ask the user for it; pass an explicit value if the user states one. At 0 the math collapses to a nominal sum."
      • addedInput schema / properties / stateCode / enum
        Added value: +[
        +  "AK",
        +  "AL",
        +  "AR",
        +  "AZ",
        +  "CA",
        +  "CO",
        +  "CT",
        +  "DC",
        +  "DE",
        +  "FL",
        +  "GA",
        +  "HI",
        +  "IA",
        +  "ID",
        +  "IL",
        +  "IN",
        +  "KS",
        +  "KY",
        +  "LA",
        +  "MA",
        +  "MD",
        +  "ME",
        +  "MI",
        +  "MN",
        +  "MO",
        +  "MS",
        +  "MT",
        +  "NC",
        +  "ND",
        +  "NE",
        +  "NH",
        +  "NJ",
        +  "NM",
        +  "NV",
        +  "NY",
        +  "OH",
        +  "OK",
        +  "OR",
        +  "PA",
        +  "RI",
        +  "SC",
        +  "SD",
        +  "TN",
        +  "TX",
        +  "UT",
        +  "VA",
        +  "VT",
        +  "WA",
        +  "WI",
        +  "WV",
        +  "WY"
        +]
      • removedInput schema / properties / stateCode / pattern
        Removed value: -"^[A-Z]{2}$"
      • changedInput schema / properties / terminationDate / description
        Previous value: -"Separation date (YYYY-MM-DD) when hasLeftCompany=true; null when still employed. Together with hasLeftCompany, drives the 90-day exercise window deadline."New value: +"Separation date (YYYY-MM-DD). Required only when hasLeftCompany=true (it drives the 90-day exercise-window deadline); omit it or pass null when still employed. No longer in `required` so the common employed case needs no placeholder."
      • changedInput schema / properties / ticker / description
        Previous value: -"Optional public-stock symbol (e.g. \"NVDA\", \"AAPL\"). When set, the tool substitutes a cached trailing return for any unsupplied expected-return / sale-price field AND a cached implied vol for any unsupplied volatility, instead of requiring the caller to invent either. Most large-cap public symbols are covered; unknown tickers fall through to \"required field\" errors so the model knows to ask the user."New value: +"Optional public-stock symbol (e.g. \"NVDA\", \"AAPL\"). When set, the tool substitutes a cached trailing return for any unsupplied expected-return / sale-price field, and a cached implied vol for any unsupplied volatility. About 90 large-cap symbols resolve a return; a slightly smaller set (~85) also resolves volatility. A symbol not in a given table falls through to a \"required field\" error for exactly the field it could not resolve, so pass that field explicitly (or use a fully covered symbol) rather than inventing it."
      • changedInput schema / properties / volatility / description
        Previous value: -"Annualized volatility (sigma) of the stock as a decimal (0.72 = 72%). Pass the user-supplied volatility directly; the tool computes the horizon-cumulative drag internally. The model MUST NOT compute drag itself — the correct formula is horizon-dependent and most models get it wrong. If the user does not supply a volatility number AND no `ticker` resolves it from the cached implied-vol table, ASK them."New value: +"Annualized volatility (sigma) of the stock as a decimal (0.72 = 72%). Pass the user-supplied volatility directly; the tool computes the horizon-cumulative drag internally. The model MUST NOT compute drag itself; the correct formula is horizon-dependent and most models get it wrong. If the user does not supply a volatility number AND no `ticker` resolves it from the cached implied-vol table, ASK them."
      • changedInput schema / required
        Previous value: -[
        -  "shares",
        -  "strike",
        -  "fmv",
        -  "filingStatus",
        -  "ordinaryIncome",
        -  "stateCode",
        -  "carryforwardCredit",
        -  "horizon",
        -  "cashReturnRate",
        -  "grantDate",
        -  "hasLeftCompany",
        -  "terminationDate"
        -]New value: +[
        +  "shares",
        +  "strike",
        +  "fmv",
        +  "filingStatus",
        +  "ordinaryIncome",
        +  "stateCode",
        +  "carryforwardCredit",
        +  "horizon",
        +  "grantDate",
        +  "hasLeftCompany"
        +]
    • Changedconcentration_analyze19 fields changed
      • changedInput schema / properties / hedgeChoice / description
        Previous value: -"Optional hedge specification. When provided, adds a hedged scenario to the sell-down-vs-hold comparison and computes the post-tax NFV of the hedged hold. Omit to compare only sell-down vs. hold."New value: +"Optional hedge specification. When supplied, the `hedging` output block prices this exact structure (kind, protectionLevel, tenorYears, plus a short call for a collar) instead of the default 1-year 30%-OTM put; the sell-down-vs-hold schedule is unaffected. For full standalone hedge pricing (protective put / collar / put spread) use `protective_put_price`."
      • changedInput schema / properties / hedgeChoice / properties / kind / description
        Previous value: -"Hedge instrument: 'put' (bare protective put — pay premium for downside protection) or 'collar' (put financed by a short call — caps upside in exchange for lower or zero net premium)."New value: +"Hedge instrument: 'put' (bare protective put, pay premium for downside protection) or 'collar' (put financed by a short call, caps upside in exchange for lower or zero net premium)."
      • changedInput schema / properties / sector / description
        Previous value: -"Sector tag. Drives the default volatility used in the hedge-cost computation when no explicit volatility is provided. See lib/markets/sector-stats.ts for the per-sector annualVol table; this tool applies IV_OVER_RV_MULTIPLIER (1.20) to the realized vol to approximate implied vol."New value: +"Sector tag. Drives the default volatility used in the hedge-cost computation when no explicit volatility is provided (a sector-typical implied volatility)."
      • addedInput schema / properties / stateCode / enum
        Added value: +[
        +  "AK",
        +  "AL",
        +  "AR",
        +  "AZ",
        +  "CA",
        +  "CO",
        +  "CT",
        +  "DC",
        +  "DE",
        +  "FL",
        +  "GA",
        +  "HI",
        +  "IA",
        +  "ID",
        +  "IL",
        +  "IN",
        +  "KS",
        +  "KY",
        +  "LA",
        +  "MA",
        +  "MD",
        +  "ME",
        +  "MI",
        +  "MN",
        +  "MO",
        +  "MS",
        +  "MT",
        +  "NC",
        +  "ND",
        +  "NE",
        +  "NH",
        +  "NJ",
        +  "NM",
        +  "NV",
        +  "NY",
        +  "OH",
        +  "OK",
        +  "OR",
        +  "PA",
        +  "RI",
        +  "SC",
        +  "SD",
        +  "TN",
        +  "TX",
        +  "UT",
        +  "VA",
        +  "VT",
        +  "WA",
        +  "WI",
        +  "WV",
        +  "WY"
        +]
      • removedInput schema / properties / stateCode / pattern
        Removed value: -"^[A-Z]{2}$"
      • changedInput schema / properties / ticker / description
        Previous value: -"Optional public-stock symbol (e.g. \"NVDA\", \"AAPL\"). When set, the tool substitutes a cached trailing return for any unsupplied expected-return / sale-price field AND a cached implied vol for any unsupplied volatility, instead of requiring the caller to invent either. Most large-cap public symbols are covered; unknown tickers fall through to \"required field\" errors so the model knows to ask the user."New value: +"Optional public-stock symbol (e.g. \"NVDA\", \"AAPL\"). When set, the tool substitutes a cached trailing return for any unsupplied expected-return / sale-price field, and a cached implied vol for any unsupplied volatility. About 90 large-cap symbols resolve a return; a slightly smaller set (~85) also resolves volatility. A symbol not in a given table falls through to a \"required field\" error for exactly the field it could not resolve, so pass that field explicitly (or use a fully covered symbol) rather than inventing it."
      • changedInput schema / properties / volatility / description
        Previous value: -"Annualized volatility (sigma) of the stock as a decimal (0.72 = 72%). Pass the user-supplied volatility directly; the tool uses it both for hedge pricing (as implied vol) and for the 3y horizon drag, computed internally. The model MUST NOT compute drag itself — the correct formula is horizon-dependent and most models get it wrong. If the user does not supply a volatility number AND no `ticker` resolves it from the cached implied-vol table, ASK them; only as a last fallback does hedge pricing use sector_stats.annualVol × 1.20."New value: +"Annualized volatility (sigma) of the stock as a decimal (0.72 = 72%). Pass the user-supplied volatility directly; the tool uses it both for hedge pricing (as implied vol) and for the 3y horizon drag, computed internally. The model MUST NOT compute drag itself; the correct formula is horizon-dependent and most models get it wrong. If the user does not supply a volatility number AND no `ticker` resolves it from the cached implied-vol table, ASK them; only as a last fallback does hedge pricing fall back to a sector-typical implied volatility."
      • addedInput schema / properties / volatilityDrag
        Added value: +{
        +  "description": "Alternative to `volatility`: the multiplicative price haircut already computed for the horizon. Supply this OR `volatility` (if both are given, volatilityDrag wins). Most callers should pass `volatility` and let the tool compute the drag; only pass this if you already have a horizon drag figure. The model MUST NOT compute it itself.",
        +  "maximum": 0.99,
        +  "minimum": 0,
        +  "type": "number"
        +}
      • changedOutput schema / properties / hedging / description
        Previous value: -"Black-Scholes cost of a 1-year 30%-OTM protective put covering the full position."New value: +"Modeled cost of a protective hedge covering the full position. Defaults to a 1-year 30%-OTM put; if a `hedgeChoice` is supplied, this block prices that structure (kind / protectionLevel / tenorYears, plus a short call for a collar)."
      • addedOutput schema / properties / hedging / properties / callPrice
        Added value: +{
        +  "description": "Collar short-call premium in dollars received. Omitted for a put.",
        +  "type": "number"
        +}
      • addedOutput schema / properties / hedging / properties / callStrike
        Added value: +{
        +  "description": "Collar short-call strike in dollars ((1 + upsideCapPct) x position value). Omitted for a put.",
        +  "type": "number"
        +}
      • addedOutput schema / properties / hedging / properties / kind
        Added value: +{
        +  "description": "Structure priced: \"put\" (default) or \"collar\" when a hedgeChoice with kind:\"collar\" and upsideCapPct was supplied.",
        +  "enum": [
        +    "put",
        +    "collar"
        +  ],
        +  "type": "string"
        +}
      • addedOutput schema / properties / hedging / properties / netPremium
        Added value: +{
        +  "description": "Net premium paid in dollars: putPrice for a put, max(0, putPrice - callPrice) for a collar.",
        +  "type": "number"
        +}
      • addedOutput schema / properties / hedging / properties / protectionLevel
        Added value: +{
        +  "description": "Floor as a fraction below spot (0.30 = a 30%-OTM put). Echoes hedgeChoice.protectionLevel, else 0.30.",
        +  "type": "number"
        +}
      • changedOutput schema / properties / hedging / properties / putPrice / description
        Previous value: -"Put premium in dollars for the 1-year tenor."New value: +"Gross long-put premium in dollars for the tenor."
      • changedOutput schema / properties / hedging / properties / sigma / description
        Previous value: -"Annualized volatility used in pricing (explicit/ticker implied vol, else sector realized vol x 1.20)."New value: +"Annualized volatility used in pricing (explicit or ticker-implied vol, else a sector-typical implied volatility)."
      • changedOutput schema / properties / hedging / properties / strike / description
        Previous value: -"Put strike in dollars (70% of position value)."New value: +"Long put strike in dollars ((1 - protectionLevel) x position value)."
      • addedOutput schema / properties / hedging / properties / tenorYears
        Added value: +{
        +  "description": "Hedge tenor in years. Echoes hedgeChoice.tenorYears, else 1.",
        +  "type": "number"
        +}
      • changedOutput schema / properties / hedging / required
        Previous value: -[
        -  "strike",
        -  "putPrice",
        -  "sigma",
        -  "riskFreeRate"
        -]New value: +[
        +  "kind",
        +  "protectionLevel",
        +  "tenorYears",
        +  "strike",
        +  "putPrice",
        +  "netPremium",
        +  "sigma",
        +  "riskFreeRate"
        +]
    • Changedequity_funding_plan5 fields changed
      • addedInput schema / anyOf
        Added value: +[
        +  {
        +    "required": [
        +      "stacks"
        +    ]
        +  },
        +  {
        +    "required": [
        +      "lots",
        +      "currentPrice"
        +    ]
        +  }
        +]
      • changedInput schema / properties / stacks / description
        Previous value: -"Multi-stack input. Each stack is one equity position (one ticker) with its own current price, growth, optional volatility, and lot list. Use when the user holds multiple tickers (e.g. current-employer RSUs + ETF + prior-employer holdings); the optimizer searches sales across all stacks jointly so the schedule can prefer the lowest-tax inventory in each year."New value: +"Holdings, multi-stack form. Provide either `stacks` (this) OR the legacy `lots`+`currentPrice` pair, not both. Each stack is one equity position (one ticker) with its own current price, growth, optional volatility, and lot list. Use when the user holds multiple tickers (e.g. current-employer RSUs + ETF + prior-employer holdings); the optimizer searches sales across all stacks jointly so the schedule can prefer the lowest-tax inventory in each year."
      • addedInput schema / properties / stateCode / enum
        Added value: +[
        +  "AK",
        +  "AL",
        +  "AR",
        +  "AZ",
        +  "CA",
        +  "CO",
        +  "CT",
        +  "DC",
        +  "DE",
        +  "FL",
        +  "GA",
        +  "HI",
        +  "IA",
        +  "ID",
        +  "IL",
        +  "IN",
        +  "KS",
        +  "KY",
        +  "LA",
        +  "MA",
        +  "MD",
        +  "ME",
        +  "MI",
        +  "MN",
        +  "MO",
        +  "MS",
        +  "MT",
        +  "NC",
        +  "ND",
        +  "NE",
        +  "NH",
        +  "NJ",
        +  "NM",
        +  "NV",
        +  "NY",
        +  "OH",
        +  "OK",
        +  "OR",
        +  "PA",
        +  "RI",
        +  "SC",
        +  "SD",
        +  "TN",
        +  "TX",
        +  "UT",
        +  "VA",
        +  "VT",
        +  "WA",
        +  "WI",
        +  "WV",
        +  "WY"
        +]
      • removedInput schema / properties / stateCode / pattern
        Removed value: -"^[A-Z]{2}$"
      • removedInput schema / properties / today
        Removed value: -{
        -  "description": "Reference \"now\" for year classification and projection compounding. Defaults to the server's current date; tests pass an explicit value for deterministic results.",
        -  "format": "date",
        -  "type": "string"
        -}
    • Changednso_calculate4 fields changed
      • addedInput schema / properties / stateCode / enum
        Added value: +[
        +  "AK",
        +  "AL",
        +  "AR",
        +  "AZ",
        +  "CA",
        +  "CO",
        +  "CT",
        +  "DC",
        +  "DE",
        +  "FL",
        +  "GA",
        +  "HI",
        +  "IA",
        +  "ID",
        +  "IL",
        +  "IN",
        +  "KS",
        +  "KY",
        +  "LA",
        +  "MA",
        +  "MD",
        +  "ME",
        +  "MI",
        +  "MN",
        +  "MO",
        +  "MS",
        +  "MT",
        +  "NC",
        +  "ND",
        +  "NE",
        +  "NH",
        +  "NJ",
        +  "NM",
        +  "NV",
        +  "NY",
        +  "OH",
        +  "OK",
        +  "OR",
        +  "PA",
        +  "RI",
        +  "SC",
        +  "SD",
        +  "TN",
        +  "TX",
        +  "UT",
        +  "VA",
        +  "VT",
        +  "WA",
        +  "WI",
        +  "WV",
        +  "WY"
        +]
      • removedInput schema / properties / stateCode / pattern
        Removed value: -"^[A-Z]{2}$"
      • changedInput schema / properties / ticker / description
        Previous value: -"Optional public-stock symbol (e.g. \"NVDA\", \"AAPL\"). When set, the tool substitutes a cached trailing return for any unsupplied expected-return / sale-price field AND a cached implied vol for any unsupplied volatility, instead of requiring the caller to invent either. Most large-cap public symbols are covered; unknown tickers fall through to \"required field\" errors so the model knows to ask the user."New value: +"Optional public-stock symbol (e.g. \"NVDA\", \"AAPL\"). When set, the tool substitutes a cached trailing return for any unsupplied expected-return / sale-price field, and a cached implied vol for any unsupplied volatility. About 90 large-cap symbols resolve a return; a slightly smaller set (~85) also resolves volatility. A symbol not in a given table falls through to a \"required field\" error for exactly the field it could not resolve, so pass that field explicitly (or use a fully covered symbol) rather than inventing it."
      • changedInput schema / properties / volatility / description
        Previous value: -"Annualized volatility (sigma) of the stock as a decimal (0.72 = 72%). Pass the user-supplied volatility directly; the tool computes the horizon-cumulative drag internally. The model MUST NOT compute drag itself — the correct formula is horizon-dependent and most models get it wrong. If the user does not supply a volatility number AND no `ticker` resolves it from the cached implied-vol table, ASK them."New value: +"Annualized volatility (sigma) of the stock as a decimal (0.72 = 72%). Pass the user-supplied volatility directly; the tool computes the horizon-cumulative drag internally. The model MUST NOT compute drag itself; the correct formula is horizon-dependent and most models get it wrong. If the user does not supply a volatility number AND no `ticker` resolves it from the cached implied-vol table, ASK them."
    • Changedprotective_put_price14 fields changed
      • changedInput schema / properties / expectedReturn / description
        Previous value: -"Annual expected stock return (decimal). Drives risk-neutral drift in the cap-hit / floor-hit probability metrics. Does not affect premium math. Default 0."New value: +"Annual expected stock return (decimal). Drives the real-world drift in the cap-hit / floor-hit probability metrics. Does not affect premium math. Default 0."
      • changedInput schema / properties / sector / description
        Previous value: -"Sector tag. Drives the default volatility when no explicit `volatility` is supplied. Lookup table is in lib/markets/sector-stats.ts."New value: +"Sector tag. Drives the default volatility when no explicit `volatility` is supplied (a sector-typical implied volatility)."
      • addedInput schema / properties / spreadRiskLevel
        Added value: +{
        +  "description": "Put-spread floor breach risk: target probability the stock ENDS below the spread's short (lower) strike at expiration. Presets 0.20 / 0.10 / 0.05 / 0.01 (\"1 in 5 / 10 / 20 / 100\"); off-preset values snap to the nearest. A lower value pushes the short strike deeper, widening the protected band and raising the net premium toward the bare put. Only affects the `putSpread` block. Default 0.10.",
        +  "maximum": 0.2,
        +  "minimum": 0.01,
        +  "type": "number"
        +}
      • changedOutput schema / description
        Previous value: -"Protective put and zero-cost collar pricing. All dollar amounts are USD."New value: +"Protective put, zero-cost collar, and put-spread pricing on a single-stock position. All dollar amounts are USD."
      • changedOutput schema / properties / inputs / description
        Previous value: -"Echo of the resolved inputs actually priced: positionValue, sector, volatility (the sigma used after ticker/sector resolution), protectionLevel, tenorYears, plus expectedReturn and tickerLabel when supplied."New value: +"Echo of the resolved inputs actually priced: positionValue, sector, volatility (the sigma used after ticker/sector resolution), protectionLevel, tenorYears, plus expectedReturn, spreadRiskLevel, and tickerLabel when supplied."
      • addedOutput schema / properties / inputs / properties / spreadRiskLevel
        Added value: +{
        +  "description": "Put spread floor breach risk echoed from the request (snapped to a supported preset). Omitted when not supplied.",
        +  "type": "number"
        +}
      • changedOutput schema / properties / payoffTable / description
        Previous value: -"Terminal P&L in dollars at each 10%-step drawdown across payoffRange, for the bare put, the collar, and the unhedged position."New value: +"Terminal P&L in dollars at each 10%-step drawdown across payoffRange, for the bare put, the collar, the put spread, and the unhedged position."
      • addedOutput schema / properties / payoffTable / items / properties / spreadPnl
        Added value: +{
        +  "description": "Position + put-spread P&L in dollars at this move. null (serialized from NaN) when putSpread.available is false.",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
      • changedOutput schema / properties / payoffTable / items / required
        Previous value: -[
        -  "drawdownPct",
        -  "barePutPnl",
        -  "collarPnl",
        -  "unhedgedPnl"
        -]New value: +[
        +  "drawdownPct",
        +  "barePutPnl",
        +  "collarPnl",
        +  "spreadPnl",
        +  "unhedgedPnl"
        +]
      • addedOutput schema / properties / putSpread
        Added value: +{
        +  "description": "Put debit spread: long put at the protection floor financed by a short put at a lower strike. Cheaper than the bare put and needs no short call (so it works on unexercised employee options a collar cannot cover), but protection stops at the short strike and losses resume below it. The short strike is solved so the real-world probability the stock ENDS below it equals spreadRiskLevel.",
        +  "properties": {
        +    "annualCost": {
        +      "description": "Net premium annualized, in dollars per year.",
        +      "type": "number"
        +    },
        +    "annualCostPct": {
        +      "description": "Annualized net premium as a fraction of position value.",
        +      "type": "number"
        +    },
        +    "available": {
        +      "description": "False when no useful spread exists at these inputs: the 1-in-N short strike lands at/above the floor (floor already deep for this risk level) or the short leg does not reduce cost. When false, render unavailableReason instead of the numbers.",
        +      "type": "boolean"
        +    },
        +    "bandWidth": {
        +      "description": "Width of the protected band in dollars: longStrike - shortStrike (the spread max payout).",
        +      "type": "number"
        +    },
        +    "breachProbability": {
        +      "description": "Achieved real-world probability (0..1) the stock ends below the short strike; approximately spreadRiskLevel after the solve.",
        +      "type": "number"
        +    },
        +    "coveredLossAtBadYear": {
        +      "description": "Dollars the spread pays at the bad-year price, capped at bandWidth; 0 when the bad-year drop never reaches the floor.",
        +      "type": "number"
        +    },
        +    "longPremium": {
        +      "description": "Long put premium in dollars for the full tenor (same as barePut.premium).",
        +      "type": "number"
        +    },
        +    "longStrike": {
        +      "description": "Long put strike in dollars (same floor as the bare put).",
        +      "type": "number"
        +    },
        +    "maxLossInBand": {
        +      "description": "Loss in dollars if the stock ends anywhere inside the protected band (floor holds): position - longStrike + netPremium. Below the short strike, losses resume dollar-for-dollar on top of this.",
        +      "type": "number"
        +    },
        +    "netPremium": {
        +      "description": "Net debit in dollars: long premium - short premium, floored at 0.",
        +      "type": "number"
        +    },
        +    "riskLevel": {
        +      "description": "The spreadRiskLevel preset the solve targeted (0.20 / 0.10 / 0.05 / 0.01), after snapping.",
        +      "type": "number"
        +    },
        +    "savingsPct": {
        +      "description": "Fraction of the bare put premium rebated by the short leg: shortPremium / longPremium.",
        +      "type": "number"
        +    },
        +    "shortPremium": {
        +      "description": "Short put premium in dollars received for the full tenor.",
        +      "type": "number"
        +    },
        +    "shortSigma": {
        +      "description": "Annualized sigma used to price the short leg, as a decimal (equals volatility in flat-sigma mode).",
        +      "type": "number"
        +    },
        +    "shortStrike": {
        +      "description": "Short put strike in dollars, solved so P(end below it) = spreadRiskLevel.",
        +      "type": "number"
        +    },
        +    "shortStrikeDropPct": {
        +      "description": "Short strike as a drawdown from spot, as a fraction of position value.",
        +      "type": "number"
        +    },
        +    "unavailableReason": {
        +      "description": "Why the spread is unavailable; null when available. 'floor' = the solved short strike sits at/above the protection floor (or within 1% of position of it). 'no-rebate' = the short leg does not strictly reduce cost.",
        +      "enum": [
        +        "floor",
        +        "no-rebate",
        +        null
        +      ],
        +      "type": [
        +        "string",
        +        "null"
        +      ]
        +    }
        +  },
        +  "required": [
        +    "available",
        +    "unavailableReason",
        +    "longStrike",
        +    "longPremium",
        +    "shortStrike",
        +    "shortPremium",
        +    "shortSigma",
        +    "netPremium",
        +    "annualCost",
        +    "annualCostPct",
        +    "maxLossInBand",
        +    "bandWidth",
        +    "shortStrikeDropPct",
        +    "breachProbability",
        +    "riskLevel",
        +    "savingsPct",
        +    "coveredLossAtBadYear"
        +  ],
        +  "type": "object"
        +}
      • changedOutput schema / properties / recommended / description
        Previous value: -"Suggested structure: collar unless its cap binds too often (>20% probability); protective-put when the put is reasonably priced; none when neither is clean."New value: +"Suggested structure, in triage order: collar unless its cap binds too often (>20% probability); then protective-put unless the put is expensive; then put-spread when one is available and cleanly priced (cheaper by construction); none when nothing is clean. The recommended structure is the one whose card carries no warning."
      • changedOutput schema / properties / recommended / enum
        Previous value: -[
        -  "collar",
        -  "protective-put",
        -  "none"
        -]New value: +[
        +  "collar",
        +  "protective-put",
        +  "put-spread",
        +  "none"
        +]
      • changedOutput schema / properties / riskFreeRate / description
        Previous value: -"Annualized risk-free rate used in Black-Scholes, looked up for the tenor, as a decimal."New value: +"Annualized risk-free rate used in option pricing, looked up for the tenor, as a decimal."
      • changedOutput schema / required
        Previous value: -[
        -  "inputs",
        -  "riskFreeRate",
        -  "realWorldDrift",
        -  "barePut",
        -  "collar",
        -  "payoffTable",
        -  "payoffRange",
        -  "recommended"
        -]New value: +[
        +  "inputs",
        +  "riskFreeRate",
        +  "realWorldDrift",
        +  "barePut",
        +  "collar",
        +  "putSpread",
        +  "payoffTable",
        +  "payoffRange",
        +  "recommended"
        +]
    • Changedqsbs_check7 fields changed
      • changedInput schema / properties / acquisitionDate / description
        Previous value: -"Date the QSBS shares were acquired (YYYY-MM-DD). Drives the holding-period test and the era classification (50% pre-2009, 75% 2009-2010, 100% 2010-2025-07-04, OBBBA tiered after 2025-07-05)."New value: +"Date the QSBS shares were acquired (YYYY-MM-DD). Drives the holding-period test and the era classification (50% pre-2009 era, 75% pre-2010 era, 100% after a 5-year hold for acquisitions from 2010-09-28 through 2025-07-04, OBBBA tiered after 2025-07-05)."
      • addedInput schema / properties / stateCode / enum
        Added value: +[
        +  "AK",
        +  "AL",
        +  "AR",
        +  "AZ",
        +  "CA",
        +  "CO",
        +  "CT",
        +  "DC",
        +  "DE",
        +  "FL",
        +  "GA",
        +  "HI",
        +  "IA",
        +  "ID",
        +  "IL",
        +  "IN",
        +  "KS",
        +  "KY",
        +  "LA",
        +  "MA",
        +  "MD",
        +  "ME",
        +  "MI",
        +  "MN",
        +  "MO",
        +  "MS",
        +  "MT",
        +  "NC",
        +  "ND",
        +  "NE",
        +  "NH",
        +  "NJ",
        +  "NM",
        +  "NV",
        +  "NY",
        +  "OH",
        +  "OK",
        +  "OR",
        +  "PA",
        +  "RI",
        +  "SC",
        +  "SD",
        +  "TN",
        +  "TX",
        +  "UT",
        +  "VA",
        +  "VT",
        +  "WA",
        +  "WI",
        +  "WV",
        +  "WY"
        +]
      • removedInput schema / properties / stateCode / pattern
        Removed value: -"^[A-Z]{2}$"
      • changedOutput schema / properties / era / description
        Previous value: -"Acquisition-era classification that sets the exclusion schedule (50% pre-2009, 75% 2009-2010, 100% at 5y pre-OBBBA, tiered 50/75/100% at 3/4/5y under OBBBA)."New value: +"Acquisition-era classification that sets the exclusion schedule (50% pre-2009 era, 75% pre-2010 era, 100% at 5y pre-OBBBA, tiered 50/75/100% at 3/4/5y under OBBBA)."
      • changedOutput schema / properties / perIssuerCap / description
        Previous value: -"The $10M statutory per-issuer cap in dollars."New value: +"Statutory per-issuer cap in dollars: $10M pre-OBBBA, $15M for stock acquired after July 4, 2025."
      • changedOutput schema / properties / tests / description
        Previous value: -"The eight statutory tests with per-test status, so an agent can show exactly which gate failed."New value: +"The six statutory tests with per-test status, so an agent can show exactly which gate failed."
      • changedOutput schema / properties / verdict / description
        Previous value: -"Overall verdict. \"partial\"/\"caveats\" mean some tests came back unsure; \"too-soon\" means the holding period has not reached an exclusion tier yet."New value: +"Overall verdict. \"partial\" = qualifies but at a sub-100% exclusion tier (e.g. an OBBBA 3- or 4-year hold gives 50% or 75%). \"caveats\" = qualifies, but one or more tests returned \"unsure\" (pass conditional on facts the caller marked unknown). \"too-soon\" = the holding period has not reached any exclusion tier yet."
    • Changedrsu_sell_vs_hold4 fields changed
      • addedInput schema / properties / stateCode / enum
        Added value: +[
        +  "AK",
        +  "AL",
        +  "AR",
        +  "AZ",
        +  "CA",
        +  "CO",
        +  "CT",
        +  "DC",
        +  "DE",
        +  "FL",
        +  "GA",
        +  "HI",
        +  "IA",
        +  "ID",
        +  "IL",
        +  "IN",
        +  "KS",
        +  "KY",
        +  "LA",
        +  "MA",
        +  "MD",
        +  "ME",
        +  "MI",
        +  "MN",
        +  "MO",
        +  "MS",
        +  "MT",
        +  "NC",
        +  "ND",
        +  "NE",
        +  "NH",
        +  "NJ",
        +  "NM",
        +  "NV",
        +  "NY",
        +  "OH",
        +  "OK",
        +  "OR",
        +  "PA",
        +  "RI",
        +  "SC",
        +  "SD",
        +  "TN",
        +  "TX",
        +  "UT",
        +  "VA",
        +  "VT",
        +  "WA",
        +  "WI",
        +  "WV",
        +  "WY"
        +]
      • removedInput schema / properties / stateCode / pattern
        Removed value: -"^[A-Z]{2}$"
      • changedInput schema / properties / ticker / description
        Previous value: -"Optional public-stock symbol (e.g. \"NVDA\", \"AAPL\"). When set, the tool substitutes a cached trailing return for any unsupplied expected-return / sale-price field AND a cached implied vol for any unsupplied volatility, instead of requiring the caller to invent either. Most large-cap public symbols are covered; unknown tickers fall through to \"required field\" errors so the model knows to ask the user."New value: +"Optional public-stock symbol (e.g. \"NVDA\", \"AAPL\"). When set, the tool substitutes a cached trailing return for any unsupplied expected-return / sale-price field, and a cached implied vol for any unsupplied volatility. About 90 large-cap symbols resolve a return; a slightly smaller set (~85) also resolves volatility. A symbol not in a given table falls through to a \"required field\" error for exactly the field it could not resolve, so pass that field explicitly (or use a fully covered symbol) rather than inventing it."
      • changedInput schema / properties / volatility / description
        Previous value: -"Annualized volatility (sigma) of the stock as a decimal (0.72 = 72%). Pass the user-supplied volatility directly; the tool computes the horizon-cumulative drag internally. The model MUST NOT compute drag itself — the correct formula is horizon-dependent and most models get it wrong. If the user does not supply a volatility number AND no `ticker` resolves it from the cached implied-vol table, ASK them."New value: +"Annualized volatility (sigma) of the stock as a decimal (0.72 = 72%). Pass the user-supplied volatility directly; the tool computes the horizon-cumulative drag internally. The model MUST NOT compute drag itself; the correct formula is horizon-dependent and most models get it wrong. If the user does not supply a volatility number AND no `ticker` resolves it from the cached implied-vol table, ASK them."
  4. 1 tool updatev1.9.5
    • Changedqsbs_check1 field changed
      • addedOutput schema / properties / cappedOverageNote
        Added value: +{
        +  "description": "Present only when expectedGain exceeds applicableCap and an exclusion is in play: explains that the overage is fully taxable regardless of holding period and that spreading shares across separate taxpayers (e.g. non-grantor trusts) can multiply the per-issuer exclusion. Omitted otherwise.",
        +  "type": "string"
        +}
  5. 7 tool updatesv1.9.2
    • Changedamt_iso_optimize3 fields changed
      • changedInput schema / properties / ticker / description
        Previous value: -"Optional public-stock symbol (e.g. \"NVDA\", \"AAPL\"). When set, the tool substitutes the ticker's trailing CAGR for any unsupplied expected-return / sale-price field instead of requiring the caller to invent one. ~90 symbols covered; unknown tickers fall through to \"required field\" errors so the model knows to ask the user."New value: +"Optional public-stock symbol (e.g. \"NVDA\", \"AAPL\"). When set, the tool substitutes a cached trailing return for any unsupplied expected-return / sale-price field AND a cached implied vol for any unsupplied volatility, instead of requiring the caller to invent either. Most large-cap public symbols are covered; unknown tickers fall through to \"required field\" errors so the model knows to ask the user."
      • changedInput schema / properties / volatility / description
        Previous value: -"Annualized volatility (sigma) of the stock as a decimal (0.72 = 72%). Pass the user-supplied volatility directly; the tool computes the horizon-cumulative drag internally. The model MUST NOT compute drag itself — the correct formula is horizon-dependent and most models get it wrong. If the user does not supply a volatility number, ASK them."New value: +"Annualized volatility (sigma) of the stock as a decimal (0.72 = 72%). Pass the user-supplied volatility directly; the tool computes the horizon-cumulative drag internally. The model MUST NOT compute drag itself — the correct formula is horizon-dependent and most models get it wrong. If the user does not supply a volatility number AND no `ticker` resolves it from the cached implied-vol table, ASK them."
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "description": "ISO/AMT exercise optimization result. All dollar amounts are USD.",
        +  "properties": {
        +    "alreadyInAmt": {
        +      "description": "True when the user owes AMT even with zero exercise (regular tax below tentative minimum tax at baseline income).",
        +      "type": "boolean"
        +    },
        +    "bargainPerShare": {
        +      "description": "Year-1 bargain element per share in dollars: max(0, fmv - strike).",
        +      "type": "number"
        +    },
        +    "crossoverBargain": {
        +      "description": "Bargain element in dollars at the crossover share count: crossoverShares x (fmv - strike).",
        +      "type": "number"
        +    },
        +    "crossoverShares": {
        +      "description": "Maximum whole shares exercisable in year 1 before federal AMT exceeds regular tax (the AMT crossover).",
        +      "type": "integer"
        +    },
        +    "departedRecommendation": {
        +      "description": "Present only when hasLeftCompany=true and the 90-day post-termination window is still open: the partial-exercise quantity that maximizes expected after-tax value.",
        +      "properties": {
        +        "curve": {
        +          "description": "Share-count vs after-tax-value curve sampled uniformly across [0, total shares], for charting.",
        +          "items": {
        +            "properties": {
        +              "exerciseTax": {
        +                "description": "AMT cost in dollars at this share count.",
        +                "type": "number"
        +              },
        +              "netValue": {
        +                "description": "Expected after-tax value in dollars at this share count.",
        +                "type": "number"
        +              },
        +              "shares": {
        +                "description": "Exercised share count at this sample point.",
        +                "type": "number"
        +              }
        +            },
        +            "required": [
        +              "shares",
        +              "netValue",
        +              "exerciseTax"
        +            ],
        +            "type": "object"
        +          },
        +          "type": "array"
        +        },
        +        "fullExerciseNetValue": {
        +          "description": "Expected after-tax value in dollars at the hold horizon if all shares are exercised.",
        +          "type": "number"
        +        },
        +        "fullExerciseShares": {
        +          "description": "Total shares available (the exercise-everything alternative).",
        +          "type": "integer"
        +        },
        +        "fullExerciseTax": {
        +          "description": "AMT cost in dollars of exercising all shares.",
        +          "type": "number"
        +        },
        +        "futureFmvPerShare": {
        +          "description": "Projected FMV per share in dollars at the hold horizon.",
        +          "type": "number"
        +        },
        +        "holdYears": {
        +          "description": "Post-exercise hold horizon in years used for the comparison.",
        +          "type": "number"
        +        },
        +        "recommendedExerciseTax": {
        +          "description": "AMT cost in dollars at the recommended share count.",
        +          "type": "number"
        +        },
        +        "recommendedNetValue": {
        +          "description": "Expected after-tax value in dollars at the hold horizon for the recommended count.",
        +          "type": "number"
        +        },
        +        "recommendedSchedule": {
        +          "description": "Year-by-year tax schedule for the recommended share count.",
        +          "properties": {
        +            "amtPremiumFV": {
        +              "description": "Future-valued AMT premium stream (exercise tax paid above the no-exercise baseline, compounded at cashReturnRate to the horizon) in dollars.",
        +              "type": "number"
        +            },
        +            "baselineRegularTax": {
        +              "description": "Tax owed with no exercise at all (regular federal + state on ordinary income, summed across the horizon) in dollars.",
        +              "type": "number"
        +            },
        +            "creditEarned": {
        +              "description": "Federal AMT credit generated across the horizon in dollars.",
        +              "type": "number"
        +            },
        +            "creditRecovered": {
        +              "description": "Federal AMT credit recovered across the horizon in dollars.",
        +              "type": "number"
        +            },
        +            "creditRemaining": {
        +              "description": "Federal AMT credit still unrecovered at the horizon in dollars.",
        +              "type": "number"
        +            },
        +            "exerciseTax": {
        +              "description": "totalTax minus baselineRegularTax: the marginal tax cost of exercising, in dollars.",
        +              "type": "number"
        +            },
        +            "federalLTCG": {
        +              "description": "Federal long-term capital gains tax (including NIIT) on grossGain in dollars.",
        +              "type": "number"
        +            },
        +            "grossGain": {
        +              "description": "shares x (projected FMV at horizon - strike): the LTCG-eligible gain in dollars.",
        +              "type": "number"
        +            },
        +            "label": {
        +              "description": "Which candidate plan this schedule represents.",
        +              "enum": [
        +                "lump_sum",
        +                "even_split",
        +                "optimized"
        +              ],
        +              "type": "string"
        +            },
        +            "nfv": {
        +              "description": "After-tax Net Final Value at the horizon in dollars: grossGain - federalLTCG - stateLTCG - amtPremiumFV. The headline number to report.",
        +              "type": "number"
        +            },
        +            "stateLTCG": {
        +              "description": "State long-term capital gains tax on grossGain in dollars.",
        +              "type": "number"
        +            },
        +            "totalTax": {
        +              "description": "Total cash tax paid across the horizon in dollars.",
        +              "type": "number"
        +            },
        +            "years": {
        +              "description": "Per-year detail, one entry per year of the effective horizon.",
        +              "items": {
        +                "description": "Exercise and tax detail for one calendar year of the schedule.",
        +                "properties": {
        +                  "amtOwedFederal": {
        +                    "description": "Federal AMT owed above regular tax this year in dollars.",
        +                    "type": "number"
        +                  },
        +                  "amtOwedState": {
        +                    "description": "State AMT owed above regular state tax this year in dollars.",
        +                    "type": "number"
        +                  },
        +                  "bargain": {
        +                    "description": "Bargain element recognized this year in dollars: shares x (projected FMV - strike).",
        +                    "type": "number"
        +                  },
        +                  "cashTax": {
        +                    "description": "Total cash tax paid this year in dollars: federal + state, net of credit recovery.",
        +                    "type": "number"
        +                  },
        +                  "creditRecovered": {
        +                    "description": "Federal AMT credit applied (recovered) this year in dollars.",
        +                    "type": "number"
        +                  },
        +                  "regularFederal": {
        +                    "description": "Regular federal income tax for the year in dollars (ordinary income only, before AMT).",
        +                    "type": "number"
        +                  },
        +                  "regularState": {
        +                    "description": "Regular state income tax for the year in dollars.",
        +                    "type": "number"
        +                  },
        +                  "shares": {
        +                    "description": "ISO shares exercised this year.",
        +                    "type": "number"
        +                  },
        +                  "tmtFederal": {
        +                    "description": "Federal tentative minimum tax for the year in dollars.",
        +                    "type": "number"
        +                  },
        +                  "tmtState": {
        +                    "description": "State tentative minimum tax for the year in dollars (0 in states without AMT).",
        +                    "type": "number"
        +                  },
        +                  "year": {
        +                    "description": "Schedule year, 1-indexed (1 = current year).",
        +                    "type": "integer"
        +                  }
        +                },
        +                "required": [
        +                  "year",
        +                  "shares",
        +                  "bargain",
        +                  "regularFederal",
        +                  "regularState",
        +                  "tmtFederal",
        +                  "tmtState",
        +                  "amtOwedFederal",
        +                  "amtOwedState",
        +                  "creditRecovered",
        +                  "cashTax"
        +                ],
        +                "type": "object"
        +              },
        +              "type": "array"
        +            }
        +          },
        +          "required": [
        +            "label",
        +            "years",
        +            "totalTax",
        +            "baselineRegularTax",
        +            "exerciseTax",
        +            "creditEarned",
        +            "creditRecovered",
        +            "creditRemaining",
        +            "grossGain",
        +            "federalLTCG",
        +            "stateLTCG",
        +            "amtPremiumFV",
        +            "nfv"
        +          ],
        +          "type": "object"
        +        },
        +        "recommendedShares": {
        +          "description": "Optimal share count to exercise within the window.",
        +          "type": "integer"
        +        }
        +      },
        +      "required": [
        +        "recommendedShares",
        +        "recommendedExerciseTax",
        +        "recommendedNetValue",
        +        "fullExerciseShares",
        +        "fullExerciseTax",
        +        "fullExerciseNetValue",
        +        "holdYears",
        +        "futureFmvPerShare",
        +        "recommendedSchedule",
        +        "curve"
        +      ],
        +      "type": "object"
        +    },
        +    "effectiveHorizon": {
        +      "description": "Horizon actually used by the schedules: min(requested horizon, timing.maxHorizon).",
        +      "type": "integer"
        +    },
        +    "schedules": {
        +      "description": "The three candidate exercise schedules, each evaluated at the effective horizon. Compare nfv across them; optimized is the recommended plan.",
        +      "properties": {
        +        "evenSplit": {
        +          "description": "Exercise shares/horizon shares each year.",
        +          "properties": {
        +            "amtPremiumFV": {
        +              "description": "Future-valued AMT premium stream (exercise tax paid above the no-exercise baseline, compounded at cashReturnRate to the horizon) in dollars.",
        +              "type": "number"
        +            },
        +            "baselineRegularTax": {
        +              "description": "Tax owed with no exercise at all (regular federal + state on ordinary income, summed across the horizon) in dollars.",
        +              "type": "number"
        +            },
        +            "creditEarned": {
        +              "description": "Federal AMT credit generated across the horizon in dollars.",
        +              "type": "number"
        +            },
        +            "creditRecovered": {
        +              "description": "Federal AMT credit recovered across the horizon in dollars.",
        +              "type": "number"
        +            },
        +            "creditRemaining": {
        +              "description": "Federal AMT credit still unrecovered at the horizon in dollars.",
        +              "type": "number"
        +            },
        +            "exerciseTax": {
        +              "description": "totalTax minus baselineRegularTax: the marginal tax cost of exercising, in dollars.",
        +              "type": "number"
        +            },
        +            "federalLTCG": {
        +              "description": "Federal long-term capital gains tax (including NIIT) on grossGain in dollars.",
        +              "type": "number"
        +            },
        +            "grossGain": {
        +              "description": "shares x (projected FMV at horizon - strike): the LTCG-eligible gain in dollars.",
        +              "type": "number"
        +            },
        +            "label": {
        +              "description": "Which candidate plan this schedule represents.",
        +              "enum": [
        +                "lump_sum",
        +                "even_split",
        +                "optimized"
        +              ],
        +              "type": "string"
        +            },
        +            "nfv": {
        +              "description": "After-tax Net Final Value at the horizon in dollars: grossGain - federalLTCG - stateLTCG - amtPremiumFV. The headline number to report.",
        +              "type": "number"
        +            },
        +            "stateLTCG": {
        +              "description": "State long-term capital gains tax on grossGain in dollars.",
        +              "type": "number"
        +            },
        +            "totalTax": {
        +              "description": "Total cash tax paid across the horizon in dollars.",
        +              "type": "number"
        +            },
        +            "years": {
        +              "description": "Per-year detail, one entry per year of the effective horizon.",
        +              "items": {
        +                "description": "Exercise and tax detail for one calendar year of the schedule.",
        +                "properties": {
        +                  "amtOwedFederal": {
        +                    "description": "Federal AMT owed above regular tax this year in dollars.",
        +                    "type": "number"
        +                  },
        +                  "amtOwedState": {
        +                    "description": "State AMT owed above regular state tax this year in dollars.",
        +                    "type": "number"
        +                  },
        +                  "bargain": {
        +                    "description": "Bargain element recognized this year in dollars: shares x (projected FMV - strike).",
        +                    "type": "number"
        +                  },
        +                  "cashTax": {
        +                    "description": "Total cash tax paid this year in dollars: federal + state, net of credit recovery.",
        +                    "type": "number"
        +                  },
        +                  "creditRecovered": {
        +                    "description": "Federal AMT credit applied (recovered) this year in dollars.",
        +                    "type": "number"
        +                  },
        +                  "regularFederal": {
        +                    "description": "Regular federal income tax for the year in dollars (ordinary income only, before AMT).",
        +                    "type": "number"
        +                  },
        +                  "regularState": {
        +                    "description": "Regular state income tax for the year in dollars.",
        +                    "type": "number"
        +                  },
        +                  "shares": {
        +                    "description": "ISO shares exercised this year.",
        +                    "type": "number"
        +                  },
        +                  "tmtFederal": {
        +                    "description": "Federal tentative minimum tax for the year in dollars.",
        +                    "type": "number"
        +                  },
        +                  "tmtState": {
        +                    "description": "State tentative minimum tax for the year in dollars (0 in states without AMT).",
        +                    "type": "number"
        +                  },
        +                  "year": {
        +                    "description": "Schedule year, 1-indexed (1 = current year).",
        +                    "type": "integer"
        +                  }
        +                },
        +                "required": [
        +                  "year",
        +                  "shares",
        +                  "bargain",
        +                  "regularFederal",
        +                  "regularState",
        +                  "tmtFederal",
        +                  "tmtState",
        +                  "amtOwedFederal",
        +                  "amtOwedState",
        +                  "creditRecovered",
        +                  "cashTax"
        +                ],
        +                "type": "object"
        +              },
        +              "type": "array"
        +            }
        +          },
        +          "required": [
        +            "label",
        +            "years",
        +            "totalTax",
        +            "baselineRegularTax",
        +            "exerciseTax",
        +            "creditEarned",
        +            "creditRecovered",
        +            "creditRemaining",
        +            "grossGain",
        +            "federalLTCG",
        +            "stateLTCG",
        +            "amtPremiumFV",
        +            "nfv"
        +          ],
        +          "type": "object"
        +        },
        +        "lumpSum": {
        +          "description": "Exercise all shares in year 1.",
        +          "properties": {
        +            "amtPremiumFV": {
        +              "description": "Future-valued AMT premium stream (exercise tax paid above the no-exercise baseline, compounded at cashReturnRate to the horizon) in dollars.",
        +              "type": "number"
        +            },
        +            "baselineRegularTax": {
        +              "description": "Tax owed with no exercise at all (regular federal + state on ordinary income, summed across the horizon) in dollars.",
        +              "type": "number"
        +            },
        +            "creditEarned": {
        +              "description": "Federal AMT credit generated across the horizon in dollars.",
        +              "type": "number"
        +            },
        +            "creditRecovered": {
        +              "description": "Federal AMT credit recovered across the horizon in dollars.",
        +              "type": "number"
        +            },
        +            "creditRemaining": {
        +              "description": "Federal AMT credit still unrecovered at the horizon in dollars.",
        +              "type": "number"
        +            },
        +            "exerciseTax": {
        +              "description": "totalTax minus baselineRegularTax: the marginal tax cost of exercising, in dollars.",
        +              "type": "number"
        +            },
        +            "federalLTCG": {
        +              "description": "Federal long-term capital gains tax (including NIIT) on grossGain in dollars.",
        +              "type": "number"
        +            },
        +            "grossGain": {
        +              "description": "shares x (projected FMV at horizon - strike): the LTCG-eligible gain in dollars.",
        +              "type": "number"
        +            },
        +            "label": {
        +              "description": "Which candidate plan this schedule represents.",
        +              "enum": [
        +                "lump_sum",
        +                "even_split",
        +                "optimized"
        +              ],
        +              "type": "string"
        +            },
        +            "nfv": {
        +              "description": "After-tax Net Final Value at the horizon in dollars: grossGain - federalLTCG - stateLTCG - amtPremiumFV. The headline number to report.",
        +              "type": "number"
        +            },
        +            "stateLTCG": {
        +              "description": "State long-term capital gains tax on grossGain in dollars.",
        +              "type": "number"
        +            },
        +            "totalTax": {
        +              "description": "Total cash tax paid across the horizon in dollars.",
        +              "type": "number"
        +            },
        +            "years": {
        +              "description": "Per-year detail, one entry per year of the effective horizon.",
        +              "items": {
        +                "description": "Exercise and tax detail for one calendar year of the schedule.",
        +                "properties": {
        +                  "amtOwedFederal": {
        +                    "description": "Federal AMT owed above regular tax this year in dollars.",
        +                    "type": "number"
        +                  },
        +                  "amtOwedState": {
        +                    "description": "State AMT owed above regular state tax this year in dollars.",
        +                    "type": "number"
        +                  },
        +                  "bargain": {
        +                    "description": "Bargain element recognized this year in dollars: shares x (projected FMV - strike).",
        +                    "type": "number"
        +                  },
        +                  "cashTax": {
        +                    "description": "Total cash tax paid this year in dollars: federal + state, net of credit recovery.",
        +                    "type": "number"
        +                  },
        +                  "creditRecovered": {
        +                    "description": "Federal AMT credit applied (recovered) this year in dollars.",
        +                    "type": "number"
        +                  },
        +                  "regularFederal": {
        +                    "description": "Regular federal income tax for the year in dollars (ordinary income only, before AMT).",
        +                    "type": "number"
        +                  },
        +                  "regularState": {
        +                    "description": "Regular state income tax for the year in dollars.",
        +                    "type": "number"
        +                  },
        +                  "shares": {
        +                    "description": "ISO shares exercised this year.",
        +                    "type": "number"
        +                  },
        +                  "tmtFederal": {
        +                    "description": "Federal tentative minimum tax for the year in dollars.",
        +                    "type": "number"
        +                  },
        +                  "tmtState": {
        +                    "description": "State tentative minimum tax for the year in dollars (0 in states without AMT).",
        +                    "type": "number"
        +                  },
        +                  "year": {
        +                    "description": "Schedule year, 1-indexed (1 = current year).",
        +                    "type": "integer"
        +                  }
        +                },
        +                "required": [
        +                  "year",
        +                  "shares",
        +                  "bargain",
        +                  "regularFederal",
        +                  "regularState",
        +                  "tmtFederal",
        +                  "tmtState",
        +                  "amtOwedFederal",
        +                  "amtOwedState",
        +                  "creditRecovered",
        +                  "cashTax"
        +                ],
        +                "type": "object"
        +              },
        +              "type": "array"
        +            }
        +          },
        +          "required": [
        +            "label",
        +            "years",
        +            "totalTax",
        +            "baselineRegularTax",
        +            "exerciseTax",
        +            "creditEarned",
        +            "creditRecovered",
        +            "creditRemaining",
        +            "grossGain",
        +            "federalLTCG",
        +            "stateLTCG",
        +            "amtPremiumFV",
        +            "nfv"
        +          ],
        +          "type": "object"
        +        },
        +        "optimized": {
        +          "description": "The NFV-maximal per-year allocation found by the optimizer. The recommended plan.",
        +          "properties": {
        +            "amtPremiumFV": {
        +              "description": "Future-valued AMT premium stream (exercise tax paid above the no-exercise baseline, compounded at cashReturnRate to the horizon) in dollars.",
        +              "type": "number"
        +            },
        +            "baselineRegularTax": {
        +              "description": "Tax owed with no exercise at all (regular federal + state on ordinary income, summed across the horizon) in dollars.",
        +              "type": "number"
        +            },
        +            "creditEarned": {
        +              "description": "Federal AMT credit generated across the horizon in dollars.",
        +              "type": "number"
        +            },
        +            "creditRecovered": {
        +              "description": "Federal AMT credit recovered across the horizon in dollars.",
        +              "type": "number"
        +            },
        +            "creditRemaining": {
        +              "description": "Federal AMT credit still unrecovered at the horizon in dollars.",
        +              "type": "number"
        +            },
        +            "exerciseTax": {
        +              "description": "totalTax minus baselineRegularTax: the marginal tax cost of exercising, in dollars.",
        +              "type": "number"
        +            },
        +            "federalLTCG": {
        +              "description": "Federal long-term capital gains tax (including NIIT) on grossGain in dollars.",
        +              "type": "number"
        +            },
        +            "grossGain": {
        +              "description": "shares x (projected FMV at horizon - strike): the LTCG-eligible gain in dollars.",
        +              "type": "number"
        +            },
        +            "label": {
        +              "description": "Which candidate plan this schedule represents.",
        +              "enum": [
        +                "lump_sum",
        +                "even_split",
        +                "optimized"
        +              ],
        +              "type": "string"
        +            },
        +            "nfv": {
        +              "description": "After-tax Net Final Value at the horizon in dollars: grossGain - federalLTCG - stateLTCG - amtPremiumFV. The headline number to report.",
        +              "type": "number"
        +            },
        +            "stateLTCG": {
        +              "description": "State long-term capital gains tax on grossGain in dollars.",
        +              "type": "number"
        +            },
        +            "totalTax": {
        +              "description": "Total cash tax paid across the horizon in dollars.",
        +              "type": "number"
        +            },
        +            "years": {
        +              "description": "Per-year detail, one entry per year of the effective horizon.",
        +              "items": {
        +                "description": "Exercise and tax detail for one calendar year of the schedule.",
        +                "properties": {
        +                  "amtOwedFederal": {
        +                    "description": "Federal AMT owed above regular tax this year in dollars.",
        +                    "type": "number"
        +                  },
        +                  "amtOwedState": {
        +                    "description": "State AMT owed above regular state tax this year in dollars.",
        +                    "type": "number"
        +                  },
        +                  "bargain": {
        +                    "description": "Bargain element recognized this year in dollars: shares x (projected FMV - strike).",
        +                    "type": "number"
        +                  },
        +                  "cashTax": {
        +                    "description": "Total cash tax paid this year in dollars: federal + state, net of credit recovery.",
        +                    "type": "number"
        +                  },
        +                  "creditRecovered": {
        +                    "description": "Federal AMT credit applied (recovered) this year in dollars.",
        +                    "type": "number"
        +                  },
        +                  "regularFederal": {
        +                    "description": "Regular federal income tax for the year in dollars (ordinary income only, before AMT).",
        +                    "type": "number"
        +                  },
        +                  "regularState": {
        +                    "description": "Regular state income tax for the year in dollars.",
        +                    "type": "number"
        +                  },
        +                  "shares": {
        +                    "description": "ISO shares exercised this year.",
        +                    "type": "number"
        +                  },
        +                  "tmtFederal": {
        +                    "description": "Federal tentative minimum tax for the year in dollars.",
        +                    "type": "number"
        +                  },
        +                  "tmtState": {
        +                    "description": "State tentative minimum tax for the year in dollars (0 in states without AMT).",
        +                    "type": "number"
        +                  },
        +                  "year": {
        +                    "description": "Schedule year, 1-indexed (1 = current year).",
        +                    "type": "integer"
        +                  }
        +                },
        +                "required": [
        +                  "year",
        +                  "shares",
        +                  "bargain",
        +                  "regularFederal",
        +                  "regularState",
        +                  "tmtFederal",
        +                  "tmtState",
        +                  "amtOwedFederal",
        +                  "amtOwedState",
        +                  "creditRecovered",
        +                  "cashTax"
        +                ],
        +                "type": "object"
        +              },
        +              "type": "array"
        +            }
        +          },
        +          "required": [
        +            "label",
        +            "years",
        +            "totalTax",
        +            "baselineRegularTax",
        +            "exerciseTax",
        +            "creditEarned",
        +            "creditRecovered",
        +            "creditRemaining",
        +            "grossGain",
        +            "federalLTCG",
        +            "stateLTCG",
        +            "amtPremiumFV",
        +            "nfv"
        +          ],
        +          "type": "object"
        +        }
        +      },
        +      "required": [
        +        "lumpSum",
        +        "evenSplit",
        +        "optimized"
        +      ],
        +      "type": "object"
        +    },
        +    "stateHasAmt": {
        +      "description": "True when the user state levies its own AMT (CA, CO, CT, MN).",
        +      "type": "boolean"
        +    },
        +    "timing": {
        +      "description": "Timing constraints derived from grantDate and (when departed) terminationDate.",
        +      "properties": {
        +        "daysUntilWindowClose": {
        +          "description": "Days until the post-termination exercise window closes (can be negative when already past); null while still employed.",
        +          "type": [
        +            "number",
        +            "null"
        +          ]
        +        },
        +        "exerciseWindowClose": {
        +          "description": "Post-termination exercise deadline (terminationDate + 90 days) as an ISO 8601 date-time string; null while still employed.",
        +          "type": [
        +            "string",
        +            "null"
        +          ]
        +        },
        +        "grantExpiration": {
        +          "description": "Grant expiration date: grantDate + 10 years (IRC 422 maximum ISO term). ISO 8601 date-time string.",
        +          "type": "string"
        +        },
        +        "maxHorizon": {
        +          "description": "Maximum usable planning horizon in years (1..10), capped by grant expiration or the post-termination window.",
        +          "type": "integer"
        +        },
        +        "qdEligibleDate": {
        +          "description": "Earliest qualifying-disposition date measured from grant: grantDate + 2 years. ISO 8601 date-time string.",
        +          "type": "string"
        +        },
        +        "qdNotYetEligible": {
        +          "description": "True when grantDate + 2 years is still in the future (a sale today could not be a qualifying disposition).",
        +          "type": "boolean"
        +        },
        +        "windowClosed": {
        +          "description": "True when the user departed and the 90-day exercise deadline has already passed.",
        +          "type": "boolean"
        +        }
        +      },
        +      "required": [
        +        "grantExpiration",
        +        "qdEligibleDate",
        +        "exerciseWindowClose",
        +        "maxHorizon",
        +        "daysUntilWindowClose",
        +        "windowClosed",
        +        "qdNotYetEligible"
        +      ],
        +      "type": "object"
        +    }
        +  },
        +  "required": [
        +    "crossoverShares",
        +    "crossoverBargain",
        +    "alreadyInAmt",
        +    "schedules",
        +    "stateHasAmt",
        +    "bargainPerShare",
        +    "timing",
        +    "effectiveHorizon"
        +  ],
        +  "type": "object"
        +}
    • Changedconcentration_analyze3 fields changed
      • changedInput schema / properties / ticker / description
        Previous value: -"Optional public-stock symbol (e.g. \"NVDA\", \"AAPL\"). When set, the tool substitutes the ticker's trailing CAGR for any unsupplied expected-return / sale-price field instead of requiring the caller to invent one. ~90 symbols covered; unknown tickers fall through to \"required field\" errors so the model knows to ask the user."New value: +"Optional public-stock symbol (e.g. \"NVDA\", \"AAPL\"). When set, the tool substitutes a cached trailing return for any unsupplied expected-return / sale-price field AND a cached implied vol for any unsupplied volatility, instead of requiring the caller to invent either. Most large-cap public symbols are covered; unknown tickers fall through to \"required field\" errors so the model knows to ask the user."
      • changedInput schema / properties / volatility / description
        Previous value: -"Annualized volatility (sigma) of the stock as a decimal (0.72 = 72%). Pass the user-supplied volatility directly; the tool uses it both for hedge pricing (as implied vol) and for the 3y horizon drag, computed internally. The model MUST NOT compute drag itself — the correct formula is horizon-dependent and most models get it wrong. If the user does not supply a volatility number, ASK them; only when neither is supplied does hedge pricing fall back to sector_stats.annualVol × 1.20."New value: +"Annualized volatility (sigma) of the stock as a decimal (0.72 = 72%). Pass the user-supplied volatility directly; the tool uses it both for hedge pricing (as implied vol) and for the 3y horizon drag, computed internally. The model MUST NOT compute drag itself — the correct formula is horizon-dependent and most models get it wrong. If the user does not supply a volatility number AND no `ticker` resolves it from the cached implied-vol table, ASK them; only as a last fallback does hedge pricing use sector_stats.annualVol × 1.20."
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "description": "Single-stock concentration analysis. All dollar amounts are USD.",
        +  "properties": {
        +    "advisorBenchmarkLine": {
        +      "description": "One-line comparison of the user weight vs the common advisor 10% single-name guideline.",
        +      "type": "string"
        +    },
        +    "concentration": {
        +      "description": "Position value / total assets, 0..1.",
        +      "type": "number"
        +    },
        +    "daysUntilLongTerm": {
        +      "description": "Days until long-term treatment; 0 when already long-term.",
        +      "type": "number"
        +    },
        +    "hedging": {
        +      "description": "Black-Scholes cost of a 1-year 30%-OTM protective put covering the full position.",
        +      "properties": {
        +        "putPrice": {
        +          "description": "Put premium in dollars for the 1-year tenor.",
        +          "type": "number"
        +        },
        +        "riskFreeRate": {
        +          "description": "Annualized risk-free rate used in pricing, as a decimal.",
        +          "type": "number"
        +        },
        +        "sigma": {
        +          "description": "Annualized volatility used in pricing (explicit/ticker implied vol, else sector realized vol x 1.20).",
        +          "type": "number"
        +        },
        +        "strike": {
        +          "description": "Put strike in dollars (70% of position value).",
        +          "type": "number"
        +        }
        +      },
        +      "required": [
        +        "strike",
        +        "putPrice",
        +        "sigma",
        +        "riskFreeRate"
        +      ],
        +      "type": "object"
        +    },
        +    "isLongTermToday": {
        +      "description": "True when the position already qualifies for long-term capital gains treatment.",
        +      "type": "boolean"
        +    },
        +    "longTermDate": {
        +      "description": "Date the position turns long-term (acquisitionDate + 1 year). ISO 8601 date-time string.",
        +      "type": "string"
        +    },
        +    "lossExposure": {
        +      "description": "Dollar damage at 30/50/70% single-stock drawdowns.",
        +      "items": {
        +        "properties": {
        +          "dollarLoss": {
        +            "description": "Dollars lost at this drawdown.",
        +            "type": "number"
        +          },
        +          "drop": {
        +            "description": "Modeled drawdown as a fraction of position value (0.30, 0.50, 0.70).",
        +            "type": "number"
        +          },
        +          "newConcentration": {
        +            "description": "Portfolio concentration (0..1) after the drawdown.",
        +            "type": "number"
        +          }
        +        },
        +        "required": [
        +          "drop",
        +          "dollarLoss",
        +          "newConcentration"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "riskBand": {
        +      "description": "Qualitative concentration band for the position weight.",
        +      "enum": [
        +        "Low",
        +        "Moderate",
        +        "Concentrated",
        +        "Highly concentrated",
        +        "Extreme"
        +      ],
        +      "type": "string"
        +    },
        +    "schedule": {
        +      "description": "Sell-down plans over 1, 2, and 3 years; empty when the position is already at or below the target weight.",
        +      "items": {
        +        "properties": {
        +          "endOfHorizonWealth": {
        +            "description": "Total after-tax wealth in dollars at the end of the 3-year comparison horizon.",
        +            "type": "number"
        +          },
        +          "planKey": {
        +            "description": "Plan identifier.",
        +            "enum": [
        +              "lump_sum",
        +              "two_year",
        +              "three_year"
        +            ],
        +            "type": "string"
        +          },
        +          "planLabel": {
        +            "description": "Human-readable plan name, e.g. \"Sell over 2 years\".",
        +            "type": "string"
        +          },
        +          "savingsVsLumpSum": {
        +            "description": "Raw tax saved in dollars vs selling everything today; positive means this plan pays less tax.",
        +            "type": "number"
        +          },
        +          "taxBreakdown": {
        +            "description": "Plan-total tax slices, same-rate rows merged.",
        +            "items": {
        +              "description": "One tax slice: a dollar amount taxed at one rate.",
        +              "properties": {
        +                "amount": {
        +                  "description": "Dollars of gain in this slice.",
        +                  "type": "number"
        +                },
        +                "label": {
        +                  "description": "Tax line label, e.g. \"Federal LTCG\", \"NIIT\", \"California\".",
        +                  "type": "string"
        +                },
        +                "rate": {
        +                  "description": "Rate applied to this slice as a decimal (0.15 = 15%).",
        +                  "type": "number"
        +                },
        +                "tax": {
        +                  "description": "Tax in dollars: amount x rate.",
        +                  "type": "number"
        +                }
        +              },
        +              "required": [
        +                "label",
        +                "rate",
        +                "amount",
        +                "tax"
        +              ],
        +              "type": "object"
        +            },
        +            "type": "array"
        +          },
        +          "totalSale": {
        +            "description": "Total nominal sale dollars across the plan years.",
        +            "type": "number"
        +          },
        +          "totalTax": {
        +            "description": "Total tax in dollars across the plan years.",
        +            "type": "number"
        +          },
        +          "wealthByYear": {
        +            "description": "Total wealth in dollars at the end of each year, t = 0..3 (4 points). For charting.",
        +            "items": {
        +              "type": "number"
        +            },
        +            "type": "array"
        +          },
        +          "wealthVsLumpSum": {
        +            "description": "End-of-horizon wealth delta in dollars vs the sell-everything-today baseline; positive means this plan ends wealthier.",
        +            "type": "number"
        +          },
        +          "year1IsShortTerm": {
        +            "description": "True when the first sale year would be taxed at short-term rates.",
        +            "type": "boolean"
        +          },
        +          "yearlySales": {
        +            "description": "One entry per sale year: year (1-indexed), saleAmount, gainAmount, isLongTerm, federalTax, stateTax, totalTax in dollars, plus a per-slice breakdown.",
        +            "items": {
        +              "properties": {
        +                "breakdown": {
        +                  "description": "Per-rate tax slices for this sale.",
        +                  "items": {
        +                    "description": "One tax slice: a dollar amount taxed at one rate.",
        +                    "properties": {
        +                      "amount": {
        +                        "description": "Dollars of gain in this slice.",
        +                        "type": "number"
        +                      },
        +                      "label": {
        +                        "description": "Tax line label, e.g. \"Federal LTCG\", \"NIIT\", \"California\".",
        +                        "type": "string"
        +                      },
        +                      "rate": {
        +                        "description": "Rate applied to this slice as a decimal (0.15 = 15%).",
        +                        "type": "number"
        +                      },
        +                      "tax": {
        +                        "description": "Tax in dollars: amount x rate.",
        +                        "type": "number"
        +                      }
        +                    },
        +                    "required": [
        +                      "label",
        +                      "rate",
        +                      "amount",
        +                      "tax"
        +                    ],
        +                    "type": "object"
        +                  },
        +                  "type": "array"
        +                },
        +                "federalTax": {
        +                  "description": "Federal tax in dollars on this sale (including NIIT).",
        +                  "type": "number"
        +                },
        +                "gainAmount": {
        +                  "description": "Taxable gain in dollars within the sale.",
        +                  "type": "number"
        +                },
        +                "isLongTerm": {
        +                  "description": "True when this sale gets long-term capital gains treatment.",
        +                  "type": "boolean"
        +                },
        +                "saleAmount": {
        +                  "description": "Dollars sold this year.",
        +                  "type": "number"
        +                },
        +                "stateTax": {
        +                  "description": "State tax in dollars on this sale.",
        +                  "type": "number"
        +                },
        +                "totalTax": {
        +                  "description": "Total tax in dollars on this sale.",
        +                  "type": "number"
        +                },
        +                "year": {
        +                  "description": "Sale year, 1-indexed.",
        +                  "type": "number"
        +                }
        +              },
        +              "required": [
        +                "year",
        +                "saleAmount",
        +                "gainAmount",
        +                "isLongTerm",
        +                "federalTax",
        +                "stateTax",
        +                "totalTax",
        +                "breakdown"
        +              ],
        +              "type": "object"
        +            },
        +            "type": "array"
        +          }
        +        },
        +        "required": [
        +          "planKey",
        +          "planLabel",
        +          "yearlySales",
        +          "totalSale",
        +          "totalTax",
        +          "endOfHorizonWealth",
        +          "savingsVsLumpSum",
        +          "wealthVsLumpSum",
        +          "year1IsShortTerm",
        +          "taxBreakdown",
        +          "wealthByYear"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "sectorContextLine": {
        +      "description": "One-line volatility/drawdown context for the chosen sector.",
        +      "type": "string"
        +    },
        +    "waitForLtInsight": {
        +      "description": "Tax saved by waiting for long-term treatment before selling; null when already long-term or no sale is needed.",
        +      "properties": {
        +        "daysAway": {
        +          "description": "Days until that date.",
        +          "type": "number"
        +        },
        +        "delayedLumpSumTax": {
        +          "description": "Tax in dollars on the same sale executed after the long-term date.",
        +          "type": "number"
        +        },
        +        "immediateLumpSumTax": {
        +          "description": "Tax in dollars on the full sell-down executed today (short-term rates).",
        +          "type": "number"
        +        },
        +        "longTermDate": {
        +          "description": "Date the position turns long-term. ISO 8601 date-time string.",
        +          "type": "string"
        +        },
        +        "savings": {
        +          "description": "immediateLumpSumTax - delayedLumpSumTax in dollars (floored at 0).",
        +          "type": "number"
        +        }
        +      },
        +      "required": [
        +        "longTermDate",
        +        "daysAway",
        +        "immediateLumpSumTax",
        +        "delayedLumpSumTax",
        +        "savings"
        +      ],
        +      "type": [
        +        "object",
        +        "null"
        +      ]
        +    }
        +  },
        +  "required": [
        +    "concentration",
        +    "riskBand",
        +    "isLongTermToday",
        +    "longTermDate",
        +    "daysUntilLongTerm",
        +    "lossExposure",
        +    "waitForLtInsight",
        +    "schedule",
        +    "hedging",
        +    "sectorContextLine",
        +    "advisorBenchmarkLine"
        +  ],
        +  "type": "object"
        +}
    • Changedequity_funding_plan16 fields changed
      • addedInput schema / properties / cashInterestRate
        Added value: +{
        +  "description": "Annualized PRE-tax yield on cash held between each sale and the target date (money-market / short-term Treasury). The tool internally discounts this by the user's marginal federal + state ordinary rate before compounding, so the after-tax cash growth stays apples-to-apples with stock appreciation. Default 0 (interest ignored).",
        +  "type": "number"
        +}
      • addedInput schema / properties / defaultVolatility
        Added value: +{
        +  "description": "Annualized σ assumed for any stack that omits its own `volatility`. Drives the per-sale σ × √Δt shortfall calculation. Override per-stack on the stack object when one position is materially more or less volatile than the rest. Default 0.30.",
        +  "minimum": 0,
        +  "type": "number"
        +}
      • addedInput schema / properties / lots / items / properties / vestDate
        Added value: +{
        +  "description": "Optional future vest date for an unvested RSU tranche. The lot is excluded from sales whose date precedes `vestDate`. For unvested RSUs, set `vestDate` to the future vest date and pass `costBasisPerShare: 0`; the calc overrides basis with the projected FMV at vest. Same semantics as `stacks[].lots[].vestDate`.",
        +  "format": "date",
        +  "type": "string"
        +}
      • addedInput schema / properties / riskToleranceShortfall
        Added value: +{
        +  "description": "Max acceptable P(realized cash < target) under the lognormal price model, as a fraction (0.10 = 10%). The `recommended` plan is the wealth-maximal plan whose shortfall ≤ this value. Tighter values push the recommendation toward Lock-in-now; looser values let `recommended` accept more price exposure for higher expected wealth. Default 0.10.",
        +  "maximum": 1,
        +  "minimum": 0,
        +  "type": "number"
        +}
      • changedInput schema / properties / stacks / description
        Previous value: -"v1.7+ multi-stack input. Each stack is one equity position (one ticker) with its own currentPrice, growth assumption, and lot list. Use this when the user holds multiple tickers (e.g. current-employer RSUs + index fund + prior-employer holdings). For a single position, provide an array with one entry — OR omit `stacks` and use the legacy top-level `lots` + `currentPrice` fields."New value: +"Multi-stack input. Each stack is one equity position (one ticker) with its own current price, growth, optional volatility, and lot list. Use when the user holds multiple tickers (e.g. current-employer RSUs + ETF + prior-employer holdings); the optimizer searches sales across all stacks jointly so the schedule can prefer the lowest-tax inventory in each year."
      • changedInput schema / properties / stacks / items / properties / currentPrice / description
        Previous value: -"Current price for this stack's shares, USD."New value: +"$/share today for this stack. Anchors the projected-price compounding for every future candidate sale date in this stack."
      • changedInput schema / properties / stacks / items / properties / expectedAnnualGrowth / description
        Previous value: -"Optional per-stack growth decimal (e.g. 0.08 = 8%/yr). Defaults to 0."New value: +"Per-stack growth decimal (0.08 = 8%/yr). Projected sale price = currentPrice × (1 + expectedAnnualGrowth)^Δyears. Negative values model decline. Defaults to 0 (flat) unless `ticker` resolves it."
      • addedInput schema / properties / stacks / items / properties / lots / description
        Added value: +"Cost-basis cohorts within this stack (one per vest tranche / ESPP purchase / open-market buy)."
      • addedInput schema / properties / stacks / items / properties / lots / items / properties / acquisitionDate / description
        Added value: +"Acquisition date. Sales 366+ days later are long-term capital gains; earlier sales are short-term ordinary. Drives the LT-vs-ST classification at every candidate sale date."
      • addedInput schema / properties / stacks / items / properties / lots / items / properties / costBasisPerShare / description
        Added value: +"$/share basis. RSU = FMV at vest; ISO/NSO = exercise price; ESPP/open-market = purchase price."
      • addedInput schema / properties / stacks / items / properties / lots / items / properties / shares / description
        Added value: +"Whole shares in this lot."
      • addedInput schema / properties / stacks / items / properties / lots / items / properties / vestDate
        Added value: +{
        +  "description": "Optional future vest date for an unvested RSU tranche. The lot is excluded from sales whose date precedes `vestDate`. For unvested RSUs, set `vestDate` to the future vest date and pass `costBasisPerShare: 0`; the calc overrides basis with the projected FMV at vest.",
        +  "format": "date",
        +  "type": "string"
        +}
      • changedInput schema / properties / stacks / items / properties / ticker / description
        Previous value: -"Optional display label echoed in the schedule output."New value: +"Optional ticker label (e.g. \"NVDA\"). When set without `expectedAnnualGrowth`, growth is resolved from the trailing-CAGR table (~90 public-stock symbols covered). Echoed back in each SaleEntry for display."
      • addedInput schema / properties / stacks / items / properties / volatility
        Added value: +{
        +  "description": "Per-stack annualized σ used in the shortfall calculation (σ × √Δt per sale). Overrides `defaultVolatility` for THIS stack only. Useful when one stack is a single tech name (σ ≈ 0.40-0.60) and another is an ETF (σ ≈ 0.15-0.20). Omit to inherit `defaultVolatility`.",
        +  "minimum": 0,
        +  "type": "number"
        +}
      • addedInput schema / properties / today
        Added value: +{
        +  "description": "Reference \"now\" for year classification and projection compounding. Defaults to the server's current date; tests pass an explicit value for deterministic results.",
        +  "format": "date",
        +  "type": "string"
        +}
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "description": "Equity-funding plan comparison: four named plans plus the full risk/wealth frontier. All dollar amounts are USD.",
        +  "properties": {
        +    "appliedRiskTolerance": {
        +      "description": "Shortfall-probability tolerance actually applied (default 0.10 when not supplied).",
        +      "type": "number"
        +    },
        +    "balanced": {
        +      "description": "Bracket-aware spread across all candidate years: minimum tax.",
        +      "properties": {
        +        "lockInFraction": {
        +          "description": "Fraction (0..1) of the target locked in by an immediate sale, for hybrid candidates. Omitted on pure named plans.",
        +          "type": "number"
        +        },
        +        "plan": {
        +          "description": "The full sale schedule for this plan. Nested year/sale entries follow the shapes described here and may carry additional fields.",
        +          "properties": {
        +            "comparison": {
        +              "description": "This schedule vs the naive sell-everything-in-the-target-year alternative.",
        +              "properties": {
        +                "optimizedSavingsPct": {
        +                  "description": "Tax saved as a fraction of the naive plan tax.",
        +                  "type": "number"
        +                },
        +                "optimizedSavingsVsTargetYearSale": {
        +                  "description": "Tax saved in dollars by this schedule vs the naive plan.",
        +                  "type": "number"
        +                },
        +                "sellAllInTargetYearAfterTax": {
        +                  "description": "After-tax cash in dollars under that naive plan.",
        +                  "type": "number"
        +                },
        +                "sellAllInTargetYearTotalTax": {
        +                  "description": "Tax in dollars if all needed shares were sold in the target year.",
        +                  "type": "number"
        +                }
        +              },
        +              "required": [
        +                "sellAllInTargetYearTotalTax",
        +                "sellAllInTargetYearAfterTax",
        +                "optimizedSavingsVsTargetYearSale",
        +                "optimizedSavingsPct"
        +              ],
        +              "type": "object"
        +            },
        +            "feasible": {
        +              "description": "True when the schedule reaches the after-tax target by the target date.",
        +              "type": "boolean"
        +            },
        +            "remainingNetByStack": {
        +              "description": "Per-stack after-tax retained value in dollars, parallel to the input stacks array.",
        +              "items": {
        +                "type": "number"
        +              },
        +              "type": "array"
        +            },
        +            "remainingPositionAfterTax": {
        +              "description": "After-tax value in dollars of liquidating all retained shares at the target date (the cash backstop if scheduled sales come in light).",
        +              "type": "number"
        +            },
        +            "remainingPositionValue": {
        +              "description": "Market value in dollars of retained shares at the projected target-date price.",
        +              "type": "number"
        +            },
        +            "remainingShares": {
        +              "description": "Shares retained after all scheduled sales.",
        +              "type": "number"
        +            },
        +            "schedule": {
        +              "description": "Per-year sale schedule. Each entry: year, saleDateISO, sales (array of per-lot entries: stackIndex, ticker, lotIndex, shares, grossProceeds, gainAmount, isLongTerm, federalTax, stateTax, niit, netCash), yearGrossProceeds, yearTotalTax, yearNetCash, runningCumulativeNet.",
        +              "items": {
        +                "additionalProperties": true,
        +                "type": "object"
        +              },
        +              "type": "array"
        +            },
        +            "shortfall": {
        +              "description": "Present only when the target is not reachable from the available inventory.",
        +              "properties": {
        +                "gap": {
        +                  "description": "Dollars short of the target.",
        +                  "type": "number"
        +                },
        +                "maxAchievableAfterTax": {
        +                  "description": "Maximum after-tax cash in dollars achievable by the target date.",
        +                  "type": "number"
        +                }
        +              },
        +              "required": [
        +                "maxAchievableAfterTax",
        +                "gap"
        +              ],
        +              "type": "object"
        +            },
        +            "targetAfterTax": {
        +              "description": "Echo of the requested net cash target in dollars.",
        +              "type": "number"
        +            },
        +            "targetDateISO": {
        +              "description": "Echo of the target date as an ISO date string.",
        +              "type": "string"
        +            },
        +            "totalAfterTaxAchieved": {
        +              "description": "Net after-tax cash in dollars the schedule produces by the target date (including after-tax cash interest when cashInterestRate is set).",
        +              "type": "number"
        +            },
        +            "totalGrossProceeds": {
        +              "description": "Total gross sale proceeds in dollars.",
        +              "type": "number"
        +            },
        +            "totalSharesSold": {
        +              "description": "Total shares sold across the schedule.",
        +              "type": "number"
        +            },
        +            "totalTaxes": {
        +              "description": "Tax totals across all scheduled sales, in dollars.",
        +              "properties": {
        +                "federal": {
        +                  "description": "Federal capital gains / ordinary tax in dollars.",
        +                  "type": "number"
        +                },
        +                "niit": {
        +                  "description": "Net Investment Income Tax (3.8%) in dollars.",
        +                  "type": "number"
        +                },
        +                "state": {
        +                  "description": "State tax in dollars.",
        +                  "type": "number"
        +                },
        +                "total": {
        +                  "description": "Total tax in dollars.",
        +                  "type": "number"
        +                }
        +              },
        +              "required": [
        +                "federal",
        +                "state",
        +                "niit",
        +                "total"
        +              ],
        +              "type": "object"
        +            }
        +          },
        +          "required": [
        +            "feasible",
        +            "targetAfterTax",
        +            "targetDateISO",
        +            "totalAfterTaxAchieved",
        +            "totalSharesSold",
        +            "totalGrossProceeds",
        +            "totalTaxes",
        +            "schedule",
        +            "comparison",
        +            "remainingShares",
        +            "remainingPositionValue",
        +            "remainingPositionAfterTax",
        +            "remainingNetByStack"
        +          ],
        +          "type": "object"
        +        },
        +        "planKey": {
        +          "description": "Plan identifier. \"candidate\" entries appear only inside frontier.",
        +          "enum": [
        +            "recommended",
        +            "lock_in_now",
        +            "balanced",
        +            "hold_for_growth",
        +            "candidate"
        +          ],
        +          "type": "string"
        +        },
        +        "planLabel": {
        +          "description": "Human-readable plan name.",
        +          "type": "string"
        +        },
        +        "shortfallProbability": {
        +          "description": "Lognormal probability (0..1) that realized cash lands below the target. 0 means a deterministic hit (sell everything today).",
        +          "type": "number"
        +        },
        +        "totalTax": {
        +          "description": "Total tax paid across the plan in dollars.",
        +          "type": "number"
        +        },
        +        "wealthAtTarget": {
        +          "description": "Total wealth in dollars at the target date: net cash plus retained shares at the projected price. The metric the recommendation maximizes.",
        +          "type": "number"
        +        }
        +      },
        +      "required": [
        +        "planKey",
        +        "planLabel",
        +        "plan",
        +        "wealthAtTarget",
        +        "totalTax",
        +        "shortfallProbability"
        +      ],
        +      "type": "object"
        +    },
        +    "frontier": {
        +      "description": "All candidate plans from the hybrid lock-in sweep plus the named plans, sorted by shortfall probability.",
        +      "items": {
        +        "description": "One sale plan on the risk/wealth frontier.",
        +        "properties": {
        +          "lockInFraction": {
        +            "description": "Fraction (0..1) of the target locked in by an immediate sale, for hybrid candidates. Omitted on pure named plans.",
        +            "type": "number"
        +          },
        +          "plan": {
        +            "description": "The full sale schedule for this plan. Nested year/sale entries follow the shapes described here and may carry additional fields.",
        +            "properties": {
        +              "comparison": {
        +                "description": "This schedule vs the naive sell-everything-in-the-target-year alternative.",
        +                "properties": {
        +                  "optimizedSavingsPct": {
        +                    "description": "Tax saved as a fraction of the naive plan tax.",
        +                    "type": "number"
        +                  },
        +                  "optimizedSavingsVsTargetYearSale": {
        +                    "description": "Tax saved in dollars by this schedule vs the naive plan.",
        +                    "type": "number"
        +                  },
        +                  "sellAllInTargetYearAfterTax": {
        +                    "description": "After-tax cash in dollars under that naive plan.",
        +                    "type": "number"
        +                  },
        +                  "sellAllInTargetYearTotalTax": {
        +                    "description": "Tax in dollars if all needed shares were sold in the target year.",
        +                    "type": "number"
        +                  }
        +                },
        +                "required": [
        +                  "sellAllInTargetYearTotalTax",
        +                  "sellAllInTargetYearAfterTax",
        +                  "optimizedSavingsVsTargetYearSale",
        +                  "optimizedSavingsPct"
        +                ],
        +                "type": "object"
        +              },
        +              "feasible": {
        +                "description": "True when the schedule reaches the after-tax target by the target date.",
        +                "type": "boolean"
        +              },
        +              "remainingNetByStack": {
        +                "description": "Per-stack after-tax retained value in dollars, parallel to the input stacks array.",
        +                "items": {
        +                  "type": "number"
        +                },
        +                "type": "array"
        +              },
        +              "remainingPositionAfterTax": {
        +                "description": "After-tax value in dollars of liquidating all retained shares at the target date (the cash backstop if scheduled sales come in light).",
        +                "type": "number"
        +              },
        +              "remainingPositionValue": {
        +                "description": "Market value in dollars of retained shares at the projected target-date price.",
        +                "type": "number"
        +              },
        +              "remainingShares": {
        +                "description": "Shares retained after all scheduled sales.",
        +                "type": "number"
        +              },
        +              "schedule": {
        +                "description": "Per-year sale schedule. Each entry: year, saleDateISO, sales (array of per-lot entries: stackIndex, ticker, lotIndex, shares, grossProceeds, gainAmount, isLongTerm, federalTax, stateTax, niit, netCash), yearGrossProceeds, yearTotalTax, yearNetCash, runningCumulativeNet.",
        +                "items": {
        +                  "additionalProperties": true,
        +                  "type": "object"
        +                },
        +                "type": "array"
        +              },
        +              "shortfall": {
        +                "description": "Present only when the target is not reachable from the available inventory.",
        +                "properties": {
        +                  "gap": {
        +                    "description": "Dollars short of the target.",
        +                    "type": "number"
        +                  },
        +                  "maxAchievableAfterTax": {
        +                    "description": "Maximum after-tax cash in dollars achievable by the target date.",
        +                    "type": "number"
        +                  }
        +                },
        +                "required": [
        +                  "maxAchievableAfterTax",
        +                  "gap"
        +                ],
        +                "type": "object"
        +              },
        +              "targetAfterTax": {
        +                "description": "Echo of the requested net cash target in dollars.",
        +                "type": "number"
        +              },
        +              "targetDateISO": {
        +                "description": "Echo of the target date as an ISO date string.",
        +                "type": "string"
        +              },
        +              "totalAfterTaxAchieved": {
        +                "description": "Net after-tax cash in dollars the schedule produces by the target date (including after-tax cash interest when cashInterestRate is set).",
        +                "type": "number"
        +              },
        +              "totalGrossProceeds": {
        +                "description": "Total gross sale proceeds in dollars.",
        +                "type": "number"
        +              },
        +              "totalSharesSold": {
        +                "description": "Total shares sold across the schedule.",
        +                "type": "number"
        +              },
        +              "totalTaxes": {
        +                "description": "Tax totals across all scheduled sales, in dollars.",
        +                "properties": {
        +                  "federal": {
        +                    "description": "Federal capital gains / ordinary tax in dollars.",
        +                    "type": "number"
        +                  },
        +                  "niit": {
        +                    "description": "Net Investment Income Tax (3.8%) in dollars.",
        +                    "type": "number"
        +                  },
        +                  "state": {
        +                    "description": "State tax in dollars.",
        +                    "type": "number"
        +                  },
        +                  "total": {
        +                    "description": "Total tax in dollars.",
        +                    "type": "number"
        +                  }
        +                },
        +                "required": [
        +                  "federal",
        +                  "state",
        +                  "niit",
        +                  "total"
        +                ],
        +                "type": "object"
        +              }
        +            },
        +            "required": [
        +              "feasible",
        +              "targetAfterTax",
        +              "targetDateISO",
        +              "totalAfterTaxAchieved",
        +              "totalSharesSold",
        +              "totalGrossProceeds",
        +              "totalTaxes",
        +              "schedule",
        +              "comparison",
        +              "remainingShares",
        +              "remainingPositionValue",
        +              "remainingPositionAfterTax",
        +              "remainingNetByStack"
        +            ],
        +            "type": "object"
        +          },
        +          "planKey": {
        +            "description": "Plan identifier. \"candidate\" entries appear only inside frontier.",
        +            "enum": [
        +              "recommended",
        +              "lock_in_now",
        +              "balanced",
        +              "hold_for_growth",
        +              "candidate"
        +            ],
        +            "type": "string"
        +          },
        +          "planLabel": {
        +            "description": "Human-readable plan name.",
        +            "type": "string"
        +          },
        +          "shortfallProbability": {
        +            "description": "Lognormal probability (0..1) that realized cash lands below the target. 0 means a deterministic hit (sell everything today).",
        +            "type": "number"
        +          },
        +          "totalTax": {
        +            "description": "Total tax paid across the plan in dollars.",
        +            "type": "number"
        +          },
        +          "wealthAtTarget": {
        +            "description": "Total wealth in dollars at the target date: net cash plus retained shares at the projected price. The metric the recommendation maximizes.",
        +            "type": "number"
        +          }
        +        },
        +        "required": [
        +          "planKey",
        +          "planLabel",
        +          "plan",
        +          "wealthAtTarget",
        +          "totalTax",
        +          "shortfallProbability"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "holdForGrowth": {
        +      "description": "Sell only in the target year: maximum expected wealth, maximum price risk.",
        +      "properties": {
        +        "lockInFraction": {
        +          "description": "Fraction (0..1) of the target locked in by an immediate sale, for hybrid candidates. Omitted on pure named plans.",
        +          "type": "number"
        +        },
        +        "plan": {
        +          "description": "The full sale schedule for this plan. Nested year/sale entries follow the shapes described here and may carry additional fields.",
        +          "properties": {
        +            "comparison": {
        +              "description": "This schedule vs the naive sell-everything-in-the-target-year alternative.",
        +              "properties": {
        +                "optimizedSavingsPct": {
        +                  "description": "Tax saved as a fraction of the naive plan tax.",
        +                  "type": "number"
        +                },
        +                "optimizedSavingsVsTargetYearSale": {
        +                  "description": "Tax saved in dollars by this schedule vs the naive plan.",
        +                  "type": "number"
        +                },
        +                "sellAllInTargetYearAfterTax": {
        +                  "description": "After-tax cash in dollars under that naive plan.",
        +                  "type": "number"
        +                },
        +                "sellAllInTargetYearTotalTax": {
        +                  "description": "Tax in dollars if all needed shares were sold in the target year.",
        +                  "type": "number"
        +                }
        +              },
        +              "required": [
        +                "sellAllInTargetYearTotalTax",
        +                "sellAllInTargetYearAfterTax",
        +                "optimizedSavingsVsTargetYearSale",
        +                "optimizedSavingsPct"
        +              ],
        +              "type": "object"
        +            },
        +            "feasible": {
        +              "description": "True when the schedule reaches the after-tax target by the target date.",
        +              "type": "boolean"
        +            },
        +            "remainingNetByStack": {
        +              "description": "Per-stack after-tax retained value in dollars, parallel to the input stacks array.",
        +              "items": {
        +                "type": "number"
        +              },
        +              "type": "array"
        +            },
        +            "remainingPositionAfterTax": {
        +              "description": "After-tax value in dollars of liquidating all retained shares at the target date (the cash backstop if scheduled sales come in light).",
        +              "type": "number"
        +            },
        +            "remainingPositionValue": {
        +              "description": "Market value in dollars of retained shares at the projected target-date price.",
        +              "type": "number"
        +            },
        +            "remainingShares": {
        +              "description": "Shares retained after all scheduled sales.",
        +              "type": "number"
        +            },
        +            "schedule": {
        +              "description": "Per-year sale schedule. Each entry: year, saleDateISO, sales (array of per-lot entries: stackIndex, ticker, lotIndex, shares, grossProceeds, gainAmount, isLongTerm, federalTax, stateTax, niit, netCash), yearGrossProceeds, yearTotalTax, yearNetCash, runningCumulativeNet.",
        +              "items": {
        +                "additionalProperties": true,
        +                "type": "object"
        +              },
        +              "type": "array"
        +            },
        +            "shortfall": {
        +              "description": "Present only when the target is not reachable from the available inventory.",
        +              "properties": {
        +                "gap": {
        +                  "description": "Dollars short of the target.",
        +                  "type": "number"
        +                },
        +                "maxAchievableAfterTax": {
        +                  "description": "Maximum after-tax cash in dollars achievable by the target date.",
        +                  "type": "number"
        +                }
        +              },
        +              "required": [
        +                "maxAchievableAfterTax",
        +                "gap"
        +              ],
        +              "type": "object"
        +            },
        +            "targetAfterTax": {
        +              "description": "Echo of the requested net cash target in dollars.",
        +              "type": "number"
        +            },
        +            "targetDateISO": {
        +              "description": "Echo of the target date as an ISO date string.",
        +              "type": "string"
        +            },
        +            "totalAfterTaxAchieved": {
        +              "description": "Net after-tax cash in dollars the schedule produces by the target date (including after-tax cash interest when cashInterestRate is set).",
        +              "type": "number"
        +            },
        +            "totalGrossProceeds": {
        +              "description": "Total gross sale proceeds in dollars.",
        +              "type": "number"
        +            },
        +            "totalSharesSold": {
        +              "description": "Total shares sold across the schedule.",
        +              "type": "number"
        +            },
        +            "totalTaxes": {
        +              "description": "Tax totals across all scheduled sales, in dollars.",
        +              "properties": {
        +                "federal": {
        +                  "description": "Federal capital gains / ordinary tax in dollars.",
        +                  "type": "number"
        +                },
        +                "niit": {
        +                  "description": "Net Investment Income Tax (3.8%) in dollars.",
        +                  "type": "number"
        +                },
        +                "state": {
        +                  "description": "State tax in dollars.",
        +                  "type": "number"
        +                },
        +                "total": {
        +                  "description": "Total tax in dollars.",
        +                  "type": "number"
        +                }
        +              },
        +              "required": [
        +                "federal",
        +                "state",
        +                "niit",
        +                "total"
        +              ],
        +              "type": "object"
        +            }
        +          },
        +          "required": [
        +            "feasible",
        +            "targetAfterTax",
        +            "targetDateISO",
        +            "totalAfterTaxAchieved",
        +            "totalSharesSold",
        +            "totalGrossProceeds",
        +            "totalTaxes",
        +            "schedule",
        +            "comparison",
        +            "remainingShares",
        +            "remainingPositionValue",
        +            "remainingPositionAfterTax",
        +            "remainingNetByStack"
        +          ],
        +          "type": "object"
        +        },
        +        "planKey": {
        +          "description": "Plan identifier. \"candidate\" entries appear only inside frontier.",
        +          "enum": [
        +            "recommended",
        +            "lock_in_now",
        +            "balanced",
        +            "hold_for_growth",
        +            "candidate"
        +          ],
        +          "type": "string"
        +        },
        +        "planLabel": {
        +          "description": "Human-readable plan name.",
        +          "type": "string"
        +        },
        +        "shortfallProbability": {
        +          "description": "Lognormal probability (0..1) that realized cash lands below the target. 0 means a deterministic hit (sell everything today).",
        +          "type": "number"
        +        },
        +        "totalTax": {
        +          "description": "Total tax paid across the plan in dollars.",
        +          "type": "number"
        +        },
        +        "wealthAtTarget": {
        +          "description": "Total wealth in dollars at the target date: net cash plus retained shares at the projected price. The metric the recommendation maximizes.",
        +          "type": "number"
        +        }
        +      },
        +      "required": [
        +        "planKey",
        +        "planLabel",
        +        "plan",
        +        "wealthAtTarget",
        +        "totalTax",
        +        "shortfallProbability"
        +      ],
        +      "type": "object"
        +    },
        +    "lockInNow": {
        +      "description": "Sell everything needed in the current calendar year: minimum price risk, usually highest tax.",
        +      "properties": {
        +        "lockInFraction": {
        +          "description": "Fraction (0..1) of the target locked in by an immediate sale, for hybrid candidates. Omitted on pure named plans.",
        +          "type": "number"
        +        },
        +        "plan": {
        +          "description": "The full sale schedule for this plan. Nested year/sale entries follow the shapes described here and may carry additional fields.",
        +          "properties": {
        +            "comparison": {
        +              "description": "This schedule vs the naive sell-everything-in-the-target-year alternative.",
        +              "properties": {
        +                "optimizedSavingsPct": {
        +                  "description": "Tax saved as a fraction of the naive plan tax.",
        +                  "type": "number"
        +                },
        +                "optimizedSavingsVsTargetYearSale": {
        +                  "description": "Tax saved in dollars by this schedule vs the naive plan.",
        +                  "type": "number"
        +                },
        +                "sellAllInTargetYearAfterTax": {
        +                  "description": "After-tax cash in dollars under that naive plan.",
        +                  "type": "number"
        +                },
        +                "sellAllInTargetYearTotalTax": {
        +                  "description": "Tax in dollars if all needed shares were sold in the target year.",
        +                  "type": "number"
        +                }
        +              },
        +              "required": [
        +                "sellAllInTargetYearTotalTax",
        +                "sellAllInTargetYearAfterTax",
        +                "optimizedSavingsVsTargetYearSale",
        +                "optimizedSavingsPct"
        +              ],
        +              "type": "object"
        +            },
        +            "feasible": {
        +              "description": "True when the schedule reaches the after-tax target by the target date.",
        +              "type": "boolean"
        +            },
        +            "remainingNetByStack": {
        +              "description": "Per-stack after-tax retained value in dollars, parallel to the input stacks array.",
        +              "items": {
        +                "type": "number"
        +              },
        +              "type": "array"
        +            },
        +            "remainingPositionAfterTax": {
        +              "description": "After-tax value in dollars of liquidating all retained shares at the target date (the cash backstop if scheduled sales come in light).",
        +              "type": "number"
        +            },
        +            "remainingPositionValue": {
        +              "description": "Market value in dollars of retained shares at the projected target-date price.",
        +              "type": "number"
        +            },
        +            "remainingShares": {
        +              "description": "Shares retained after all scheduled sales.",
        +              "type": "number"
        +            },
        +            "schedule": {
        +              "description": "Per-year sale schedule. Each entry: year, saleDateISO, sales (array of per-lot entries: stackIndex, ticker, lotIndex, shares, grossProceeds, gainAmount, isLongTerm, federalTax, stateTax, niit, netCash), yearGrossProceeds, yearTotalTax, yearNetCash, runningCumulativeNet.",
        +              "items": {
        +                "additionalProperties": true,
        +                "type": "object"
        +              },
        +              "type": "array"
        +            },
        +            "shortfall": {
        +              "description": "Present only when the target is not reachable from the available inventory.",
        +              "properties": {
        +                "gap": {
        +                  "description": "Dollars short of the target.",
        +                  "type": "number"
        +                },
        +                "maxAchievableAfterTax": {
        +                  "description": "Maximum after-tax cash in dollars achievable by the target date.",
        +                  "type": "number"
        +                }
        +              },
        +              "required": [
        +                "maxAchievableAfterTax",
        +                "gap"
        +              ],
        +              "type": "object"
        +            },
        +            "targetAfterTax": {
        +              "description": "Echo of the requested net cash target in dollars.",
        +              "type": "number"
        +            },
        +            "targetDateISO": {
        +              "description": "Echo of the target date as an ISO date string.",
        +              "type": "string"
        +            },
        +            "totalAfterTaxAchieved": {
        +              "description": "Net after-tax cash in dollars the schedule produces by the target date (including after-tax cash interest when cashInterestRate is set).",
        +              "type": "number"
        +            },
        +            "totalGrossProceeds": {
        +              "description": "Total gross sale proceeds in dollars.",
        +              "type": "number"
        +            },
        +            "totalSharesSold": {
        +              "description": "Total shares sold across the schedule.",
        +              "type": "number"
        +            },
        +            "totalTaxes": {
        +              "description": "Tax totals across all scheduled sales, in dollars.",
        +              "properties": {
        +                "federal": {
        +                  "description": "Federal capital gains / ordinary tax in dollars.",
        +                  "type": "number"
        +                },
        +                "niit": {
        +                  "description": "Net Investment Income Tax (3.8%) in dollars.",
        +                  "type": "number"
        +                },
        +                "state": {
        +                  "description": "State tax in dollars.",
        +                  "type": "number"
        +                },
        +                "total": {
        +                  "description": "Total tax in dollars.",
        +                  "type": "number"
        +                }
        +              },
        +              "required": [
        +                "federal",
        +                "state",
        +                "niit",
        +                "total"
        +              ],
        +              "type": "object"
        +            }
        +          },
        +          "required": [
        +            "feasible",
        +            "targetAfterTax",
        +            "targetDateISO",
        +            "totalAfterTaxAchieved",
        +            "totalSharesSold",
        +            "totalGrossProceeds",
        +            "totalTaxes",
        +            "schedule",
        +            "comparison",
        +            "remainingShares",
        +            "remainingPositionValue",
        +            "remainingPositionAfterTax",
        +            "remainingNetByStack"
        +          ],
        +          "type": "object"
        +        },
        +        "planKey": {
        +          "description": "Plan identifier. \"candidate\" entries appear only inside frontier.",
        +          "enum": [
        +            "recommended",
        +            "lock_in_now",
        +            "balanced",
        +            "hold_for_growth",
        +            "candidate"
        +          ],
        +          "type": "string"
        +        },
        +        "planLabel": {
        +          "description": "Human-readable plan name.",
        +          "type": "string"
        +        },
        +        "shortfallProbability": {
        +          "description": "Lognormal probability (0..1) that realized cash lands below the target. 0 means a deterministic hit (sell everything today).",
        +          "type": "number"
        +        },
        +        "totalTax": {
        +          "description": "Total tax paid across the plan in dollars.",
        +          "type": "number"
        +        },
        +        "wealthAtTarget": {
        +          "description": "Total wealth in dollars at the target date: net cash plus retained shares at the projected price. The metric the recommendation maximizes.",
        +          "type": "number"
        +        }
        +      },
        +      "required": [
        +        "planKey",
        +        "planLabel",
        +        "plan",
        +        "wealthAtTarget",
        +        "totalTax",
        +        "shortfallProbability"
        +      ],
        +      "type": "object"
        +    },
        +    "recommended": {
        +      "description": "The wealth-maximal plan whose shortfall probability is at or below the applied risk tolerance. Present this plan first.",
        +      "properties": {
        +        "lockInFraction": {
        +          "description": "Fraction (0..1) of the target locked in by an immediate sale, for hybrid candidates. Omitted on pure named plans.",
        +          "type": "number"
        +        },
        +        "plan": {
        +          "description": "The full sale schedule for this plan. Nested year/sale entries follow the shapes described here and may carry additional fields.",
        +          "properties": {
        +            "comparison": {
        +              "description": "This schedule vs the naive sell-everything-in-the-target-year alternative.",
        +              "properties": {
        +                "optimizedSavingsPct": {
        +                  "description": "Tax saved as a fraction of the naive plan tax.",
        +                  "type": "number"
        +                },
        +                "optimizedSavingsVsTargetYearSale": {
        +                  "description": "Tax saved in dollars by this schedule vs the naive plan.",
        +                  "type": "number"
        +                },
        +                "sellAllInTargetYearAfterTax": {
        +                  "description": "After-tax cash in dollars under that naive plan.",
        +                  "type": "number"
        +                },
        +                "sellAllInTargetYearTotalTax": {
        +                  "description": "Tax in dollars if all needed shares were sold in the target year.",
        +                  "type": "number"
        +                }
        +              },
        +              "required": [
        +                "sellAllInTargetYearTotalTax",
        +                "sellAllInTargetYearAfterTax",
        +                "optimizedSavingsVsTargetYearSale",
        +                "optimizedSavingsPct"
        +              ],
        +              "type": "object"
        +            },
        +            "feasible": {
        +              "description": "True when the schedule reaches the after-tax target by the target date.",
        +              "type": "boolean"
        +            },
        +            "remainingNetByStack": {
        +              "description": "Per-stack after-tax retained value in dollars, parallel to the input stacks array.",
        +              "items": {
        +                "type": "number"
        +              },
        +              "type": "array"
        +            },
        +            "remainingPositionAfterTax": {
        +              "description": "After-tax value in dollars of liquidating all retained shares at the target date (the cash backstop if scheduled sales come in light).",
        +              "type": "number"
        +            },
        +            "remainingPositionValue": {
        +              "description": "Market value in dollars of retained shares at the projected target-date price.",
        +              "type": "number"
        +            },
        +            "remainingShares": {
        +              "description": "Shares retained after all scheduled sales.",
        +              "type": "number"
        +            },
        +            "schedule": {
        +              "description": "Per-year sale schedule. Each entry: year, saleDateISO, sales (array of per-lot entries: stackIndex, ticker, lotIndex, shares, grossProceeds, gainAmount, isLongTerm, federalTax, stateTax, niit, netCash), yearGrossProceeds, yearTotalTax, yearNetCash, runningCumulativeNet.",
        +              "items": {
        +                "additionalProperties": true,
        +                "type": "object"
        +              },
        +              "type": "array"
        +            },
        +            "shortfall": {
        +              "description": "Present only when the target is not reachable from the available inventory.",
        +              "properties": {
        +                "gap": {
        +                  "description": "Dollars short of the target.",
        +                  "type": "number"
        +                },
        +                "maxAchievableAfterTax": {
        +                  "description": "Maximum after-tax cash in dollars achievable by the target date.",
        +                  "type": "number"
        +                }
        +              },
        +              "required": [
        +                "maxAchievableAfterTax",
        +                "gap"
        +              ],
        +              "type": "object"
        +            },
        +            "targetAfterTax": {
        +              "description": "Echo of the requested net cash target in dollars.",
        +              "type": "number"
        +            },
        +            "targetDateISO": {
        +              "description": "Echo of the target date as an ISO date string.",
        +              "type": "string"
        +            },
        +            "totalAfterTaxAchieved": {
        +              "description": "Net after-tax cash in dollars the schedule produces by the target date (including after-tax cash interest when cashInterestRate is set).",
        +              "type": "number"
        +            },
        +            "totalGrossProceeds": {
        +              "description": "Total gross sale proceeds in dollars.",
        +              "type": "number"
        +            },
        +            "totalSharesSold": {
        +              "description": "Total shares sold across the schedule.",
        +              "type": "number"
        +            },
        +            "totalTaxes": {
        +              "description": "Tax totals across all scheduled sales, in dollars.",
        +              "properties": {
        +                "federal": {
        +                  "description": "Federal capital gains / ordinary tax in dollars.",
        +                  "type": "number"
        +                },
        +                "niit": {
        +                  "description": "Net Investment Income Tax (3.8%) in dollars.",
        +                  "type": "number"
        +                },
        +                "state": {
        +                  "description": "State tax in dollars.",
        +                  "type": "number"
        +                },
        +                "total": {
        +                  "description": "Total tax in dollars.",
        +                  "type": "number"
        +                }
        +              },
        +              "required": [
        +                "federal",
        +                "state",
        +                "niit",
        +                "total"
        +              ],
        +              "type": "object"
        +            }
        +          },
        +          "required": [
        +            "feasible",
        +            "targetAfterTax",
        +            "targetDateISO",
        +            "totalAfterTaxAchieved",
        +            "totalSharesSold",
        +            "totalGrossProceeds",
        +            "totalTaxes",
        +            "schedule",
        +            "comparison",
        +            "remainingShares",
        +            "remainingPositionValue",
        +            "remainingPositionAfterTax",
        +            "remainingNetByStack"
        +          ],
        +          "type": "object"
        +        },
        +        "planKey": {
        +          "description": "Plan identifier. \"candidate\" entries appear only inside frontier.",
        +          "enum": [
        +            "recommended",
        +            "lock_in_now",
        +            "balanced",
        +            "hold_for_growth",
        +            "candidate"
        +          ],
        +          "type": "string"
        +        },
        +        "planLabel": {
        +          "description": "Human-readable plan name.",
        +          "type": "string"
        +        },
        +        "shortfallProbability": {
        +          "description": "Lognormal probability (0..1) that realized cash lands below the target. 0 means a deterministic hit (sell everything today).",
        +          "type": "number"
        +        },
        +        "totalTax": {
        +          "description": "Total tax paid across the plan in dollars.",
        +          "type": "number"
        +        },
        +        "wealthAtTarget": {
        +          "description": "Total wealth in dollars at the target date: net cash plus retained shares at the projected price. The metric the recommendation maximizes.",
        +          "type": "number"
        +        }
        +      },
        +      "required": [
        +        "planKey",
        +        "planLabel",
        +        "plan",
        +        "wealthAtTarget",
        +        "totalTax",
        +        "shortfallProbability"
        +      ],
        +      "type": "object"
        +    },
        +    "targetAfterTax": {
        +      "description": "Echo of the requested net cash target in dollars.",
        +      "type": "number"
        +    },
        +    "targetDateISO": {
        +      "description": "Echo of the target date as an ISO date string.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "recommended",
        +    "lockInNow",
        +    "balanced",
        +    "holdForGrowth",
        +    "frontier",
        +    "targetAfterTax",
        +    "targetDateISO",
        +    "appliedRiskTolerance"
        +  ],
        +  "type": "object"
        +}
    • Changednso_calculate4 fields changed
      • changedInput schema / properties / holdFunding / description
        Previous value: -"How the strike cost and exercise tax are funded. 'sell-to-cover' sells enough shares to cover strike + tax (reduces sharesRetained). 'cash' pays from outside the position (full sharesRetained but requires the cashNeededAtExercise field)."New value: +"How the strike cost and exercise tax are funded. 'sell-to-cover' sells enough shares to cover strike + tax (reduces sharesRetained). 'cash' pays strike + tax from outside funds (full sharesRetained); no extra input is needed - the result reports the outside cash required as the output field cashNeededAtExercise."
      • changedInput schema / properties / ticker / description
        Previous value: -"Optional public-stock symbol (e.g. \"NVDA\", \"AAPL\"). When set, the tool substitutes the ticker's trailing CAGR for any unsupplied expected-return / sale-price field instead of requiring the caller to invent one. ~90 symbols covered; unknown tickers fall through to \"required field\" errors so the model knows to ask the user."New value: +"Optional public-stock symbol (e.g. \"NVDA\", \"AAPL\"). When set, the tool substitutes a cached trailing return for any unsupplied expected-return / sale-price field AND a cached implied vol for any unsupplied volatility, instead of requiring the caller to invent either. Most large-cap public symbols are covered; unknown tickers fall through to \"required field\" errors so the model knows to ask the user."
      • changedInput schema / properties / volatility / description
        Previous value: -"Annualized volatility (sigma) of the stock as a decimal (0.72 = 72%). Pass the user-supplied volatility directly; the tool computes the horizon-cumulative drag internally. The model MUST NOT compute drag itself — the correct formula is horizon-dependent and most models get it wrong. If the user does not supply a volatility number, ASK them."New value: +"Annualized volatility (sigma) of the stock as a decimal (0.72 = 72%). Pass the user-supplied volatility directly; the tool computes the horizon-cumulative drag internally. The model MUST NOT compute drag itself — the correct formula is horizon-dependent and most models get it wrong. If the user does not supply a volatility number AND no `ticker` resolves it from the cached implied-vol table, ASK them."
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "description": "NSO exercise sell-vs-hold result. All dollar amounts are USD.",
        +  "properties": {
        +    "bracketJump": {
        +      "description": "Marginal federal bracket change caused by the new ordinary income; null when the income stays within one bracket.",
        +      "properties": {
        +        "fromRate": {
        +          "description": "Marginal federal rate before the event, as a decimal (0.24 = 24%).",
        +          "type": "number"
        +        },
        +        "thresholdAtJump": {
        +          "description": "Taxable-income threshold in dollars where the bracket changes.",
        +          "type": "number"
        +        },
        +        "toRate": {
        +          "description": "Marginal federal rate after the event, as a decimal.",
        +          "type": "number"
        +        }
        +      },
        +      "required": [
        +        "fromRate",
        +        "toRate",
        +        "thresholdAtJump"
        +      ],
        +      "type": [
        +        "object",
        +        "null"
        +      ]
        +    },
        +    "exercise": {
        +      "description": "Tax bill at exercise on the bargain element (taxed as ordinary W-2 income).",
        +      "properties": {
        +        "additionalMedicare": {
        +          "description": "Additional Medicare (0.9%) tax in dollars.",
        +          "type": "number"
        +        },
        +        "bargainElement": {
        +          "description": "shares x (currentPrice - strike) in dollars, taxed as ordinary income at exercise.",
        +          "type": "number"
        +        },
        +        "federal": {
        +          "description": "Federal ordinary income tax on the bargain element in dollars.",
        +          "type": "number"
        +        },
        +        "medicare": {
        +          "description": "Medicare tax in dollars.",
        +          "type": "number"
        +        },
        +        "netCashSellAll": {
        +          "description": "bargainElement - total: net cash in dollars if every share is sold at exercise.",
        +          "type": "number"
        +        },
        +        "socialSecurity": {
        +          "description": "Social Security tax in dollars (0 when not employed or already past the wage base).",
        +          "type": "number"
        +        },
        +        "state": {
        +          "description": "State income tax on the bargain element in dollars.",
        +          "type": "number"
        +        },
        +        "total": {
        +          "description": "Total tax at exercise in dollars.",
        +          "type": "number"
        +        }
        +      },
        +      "required": [
        +        "bargainElement",
        +        "federal",
        +        "state",
        +        "socialSecurity",
        +        "medicare",
        +        "additionalMedicare",
        +        "total",
        +        "netCashSellAll"
        +      ],
        +      "type": "object"
        +    },
        +    "hold": {
        +      "description": "Exercise now and hold the shares holdYears for long-term capital gains treatment.",
        +      "properties": {
        +        "afterTaxProceedsAtSale": {
        +          "description": "After-tax sale proceeds in dollars at end of holdYears.",
        +          "type": "number"
        +        },
        +        "cashNeededAtExercise": {
        +          "description": "Outside cash required at exercise in dollars (strike + tax under cash funding; 0 under sell-to-cover).",
        +          "type": "number"
        +        },
        +        "costBasis": {
        +          "description": "Cost basis per share in dollars (the FMV at exercise).",
        +          "type": "number"
        +        },
        +        "effectiveSalePrice": {
        +          "description": "Projected sale price per share in dollars at end of holdYears, after the volatility haircut.",
        +          "type": "number"
        +        },
        +        "expectedGain": {
        +          "description": "Expected capital gain in dollars on the retained shares at sale.",
        +          "type": "number"
        +        },
        +        "funding": {
        +          "description": "How strike cost and exercise tax are funded (echo of holdFunding).",
        +          "enum": [
        +            "sell-to-cover",
        +            "cash"
        +          ],
        +          "type": "string"
        +        },
        +        "ltcgFederal": {
        +          "description": "Federal long-term capital gains tax (including NIIT) on the gain in dollars.",
        +          "type": "number"
        +        },
        +        "ltcgState": {
        +          "description": "State capital gains tax on the gain in dollars.",
        +          "type": "number"
        +        },
        +        "ltcgTotal": {
        +          "description": "Total capital gains tax at sale in dollars.",
        +          "type": "number"
        +        },
        +        "netAtYearN": {
        +          "description": "Net after-tax value of the hold strategy in dollars at end of holdYears (after subtracting forgone market growth).",
        +          "type": "number"
        +        },
        +        "sharesRetained": {
        +          "description": "Shares still held after funding the exercise.",
        +          "type": "number"
        +        },
        +        "sharesSoldToCover": {
        +          "description": "Shares sold at exercise to cover strike + tax (sell-to-cover only; 0 in cash mode).",
        +          "type": "number"
        +        },
        +        "strikeCost": {
        +          "description": "Total strike cost in dollars: shares x strike.",
        +          "type": "number"
        +        },
        +        "y0OutflowForgoneNet": {
        +          "description": "After-tax market growth forgone in dollars by spending cash at exercise: y0OutflowGain - y0OutflowLtcgTotal.",
        +          "type": "number"
        +        },
        +        "y0OutflowGain": {
        +          "description": "Opportunity-cost gain in dollars the year-0 cash outflow would have earned at the market rate (cash funding only; 0 for sell-to-cover).",
        +          "type": "number"
        +        },
        +        "y0OutflowLtcgFederal": {
        +          "description": "Federal capital gains tax in dollars on the forgone market gain (cash funding only).",
        +          "type": "number"
        +        },
        +        "y0OutflowLtcgState": {
        +          "description": "State capital gains tax in dollars on the forgone market gain (cash funding only).",
        +          "type": "number"
        +        },
        +        "y0OutflowLtcgTotal": {
        +          "description": "Total capital gains tax in dollars on the forgone market gain (cash funding only).",
        +          "type": "number"
        +        }
        +      },
        +      "required": [
        +        "funding",
        +        "costBasis",
        +        "strikeCost",
        +        "cashNeededAtExercise",
        +        "sharesSoldToCover",
        +        "sharesRetained",
        +        "effectiveSalePrice",
        +        "expectedGain",
        +        "ltcgFederal",
        +        "ltcgState",
        +        "ltcgTotal",
        +        "afterTaxProceedsAtSale",
        +        "y0OutflowGain",
        +        "y0OutflowLtcgFederal",
        +        "y0OutflowLtcgState",
        +        "y0OutflowLtcgTotal",
        +        "y0OutflowForgoneNet",
        +        "netAtYearN"
        +      ],
        +      "type": "object"
        +    },
        +    "holdMinusCashless": {
        +      "description": "hold.netAtYearN - sellNowInvest.netAtYearN in dollars. Positive favors holding the shares; negative favors selling at exercise and reinvesting.",
        +      "type": "number"
        +    },
        +    "sellNowInvest": {
        +      "description": "Counterfactual: sell every share at exercise and reinvest the net cash at expectedMarketReturn for holdYears.",
        +      "properties": {
        +        "ltcgFederal": {
        +          "description": "Federal capital gains tax (including NIIT) in dollars on the market gain at the horizon.",
        +          "type": "number"
        +        },
        +        "ltcgState": {
        +          "description": "State capital gains tax in dollars on the market gain.",
        +          "type": "number"
        +        },
        +        "ltcgTotal": {
        +          "description": "Total capital gains tax in dollars on the market gain.",
        +          "type": "number"
        +        },
        +        "marketGain": {
        +          "description": "Market growth in dollars on the reinvested cash over holdYears.",
        +          "type": "number"
        +        },
        +        "netAtYearN": {
        +          "description": "Net after-tax value of sell-now-and-invest in dollars at end of holdYears.",
        +          "type": "number"
        +        },
        +        "netCashAtY0": {
        +          "description": "Net cash in dollars after exercise tax, available to reinvest.",
        +          "type": "number"
        +        }
        +      },
        +      "required": [
        +        "netCashAtY0",
        +        "marketGain",
        +        "ltcgFederal",
        +        "ltcgState",
        +        "ltcgTotal",
        +        "netAtYearN"
        +      ],
        +      "type": "object"
        +    }
        +  },
        +  "required": [
        +    "exercise",
        +    "bracketJump",
        +    "hold",
        +    "sellNowInvest",
        +    "holdMinusCashless"
        +  ],
        +  "type": "object"
        +}
    • Changedprotective_put_price3 fields changed
      • addedInput schema / properties / ticker
        Added value: +{
        +  "description": "Optional public-stock symbol (e.g. \"NVDA\"). When set without an explicit `volatility`, the tool substitutes the ticker's cached implied vol. Unknown tickers fall through to the sector default. Echoed to `tickerLabel` in the response.",
        +  "type": "string"
        +}
      • changedInput schema / properties / volatility / description
        Previous value: -"Annualized implied volatility (sigma) of the stock. Defaults to a sector-typical IV when omitted. The model SHOULD NOT invent this. Either pass an explicit value the user gave you, or omit it and let the sector default apply."New value: +"Annualized implied volatility (sigma) of the stock. Resolution order: (1) explicit `volatility` if passed; (2) cached implied vol if `ticker` is covered; (3) sector-typical IV as last fallback. The model SHOULD NOT invent this. Either pass an explicit value the user gave you, set a covered `ticker`, or omit and let the sector default apply."
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "description": "Protective put and zero-cost collar pricing. All dollar amounts are USD.",
        +  "properties": {
        +    "barePut": {
        +      "description": "Bare protective put: pay premium for a hard floor.",
        +      "properties": {
        +        "annualCost": {
        +          "description": "Premium annualized, in dollars per year.",
        +          "type": "number"
        +        },
        +        "annualCostPct": {
        +          "description": "Annualized premium as a fraction of position value.",
        +          "type": "number"
        +        },
        +        "badYearDropPct": {
        +          "description": "Bad-year drawdown as a fraction of position value (always >= 0).",
        +          "type": "number"
        +        },
        +        "badYearPrice": {
        +          "description": "Position value in dollars at the 10th-percentile (1-in-10 bad year) outcome under real-world drift.",
        +          "type": "number"
        +        },
        +        "coveredLossAtBadYear": {
        +          "description": "Dollars the put pays at the bad-year price; 0 when the bad-year drop never reaches the protection floor.",
        +          "type": "number"
        +        },
        +        "expectedProfit": {
        +          "description": "Expected position profit in dollars over the tenor under real-world drift.",
        +          "type": "number"
        +        },
        +        "maxLoss": {
        +          "description": "Worst-case loss in dollars with the put in place: position - strike + premium.",
        +          "type": "number"
        +        },
        +        "premium": {
        +          "description": "Put premium in dollars for the full tenor.",
        +          "type": "number"
        +        },
        +        "premiumToCoveredRatio": {
        +          "description": "Premium per dollar of bad-year coverage. null (serialized from Infinity) when the put covers nothing at the bad-year price; above ~0.40 the floor is set too deep.",
        +          "type": [
        +            "number",
        +            "null"
        +          ]
        +        },
        +        "premiumToExpectedProfitRatio": {
        +          "description": "Fraction of typical-period expected profit consumed by the premium. null (serialized from Infinity) when expected profit is zero or negative; above ~0.50 the hedge eats most of the upside.",
        +          "type": [
        +            "number",
        +            "null"
        +          ]
        +        },
        +        "strike": {
        +          "description": "Put strike in dollars: (1 - protectionLevel) x position value.",
        +          "type": "number"
        +        }
        +      },
        +      "required": [
        +        "strike",
        +        "premium",
        +        "annualCost",
        +        "annualCostPct",
        +        "maxLoss",
        +        "badYearPrice",
        +        "badYearDropPct",
        +        "coveredLossAtBadYear",
        +        "premiumToCoveredRatio",
        +        "expectedProfit",
        +        "premiumToExpectedProfitRatio"
        +      ],
        +      "type": "object"
        +    },
        +    "collar": {
        +      "description": "Put financed by a short call: lower or zero net premium in exchange for capped upside.",
        +      "properties": {
        +        "annualCost": {
        +          "description": "Net premium annualized, in dollars per year.",
        +          "type": "number"
        +        },
        +        "annualCostPct": {
        +          "description": "Annualized net premium as a fraction of position value.",
        +          "type": "number"
        +        },
        +        "callStrike": {
        +          "description": "Short call strike in dollars (the upside cap level).",
        +          "type": "number"
        +        },
        +        "capProbability": {
        +          "description": "Real-world probability (0..1) the stock finishes above the call strike at expiration, i.e. the upside cap binds.",
        +          "type": "number"
        +        },
        +        "isZeroCost": {
        +          "description": "True when the solved call strike makes the collar effectively zero net premium.",
        +          "type": "boolean"
        +        },
        +        "maxLoss": {
        +          "description": "Worst-case loss in dollars with the collar in place.",
        +          "type": "number"
        +        },
        +        "netPremium": {
        +          "description": "Net premium in dollars: put premium - call premium, floored at 0.",
        +          "type": "number"
        +        },
        +        "putStrike": {
        +          "description": "Long put strike in dollars (same floor as the bare put).",
        +          "type": "number"
        +        },
        +        "upsideCap": {
        +          "description": "Maximum upside in dollars before the short call caps gains: callStrike - position value.",
        +          "type": "number"
        +        },
        +        "upsideCapPct": {
        +          "description": "Maximum upside as a fraction of position value.",
        +          "type": "number"
        +        }
        +      },
        +      "required": [
        +        "putStrike",
        +        "callStrike",
        +        "netPremium",
        +        "annualCost",
        +        "annualCostPct",
        +        "maxLoss",
        +        "upsideCap",
        +        "upsideCapPct",
        +        "isZeroCost",
        +        "capProbability"
        +      ],
        +      "type": "object"
        +    },
        +    "inputs": {
        +      "description": "Echo of the resolved inputs actually priced: positionValue, sector, volatility (the sigma used after ticker/sector resolution), protectionLevel, tenorYears, plus expectedReturn and tickerLabel when supplied.",
        +      "properties": {
        +        "expectedReturn": {
        +          "description": "Caller-supplied annual expected return used for probability metrics. Omitted when not supplied.",
        +          "type": "number"
        +        },
        +        "positionValue": {
        +          "description": "Position value priced, in dollars.",
        +          "type": "number"
        +        },
        +        "protectionLevel": {
        +          "description": "Protection level as a fraction below spot (0.10 = 10% OTM put).",
        +          "type": "number"
        +        },
        +        "sector": {
        +          "description": "Sector tag used for defaults.",
        +          "type": "string"
        +        },
        +        "tenorYears": {
        +          "description": "Option tenor in years.",
        +          "type": "number"
        +        },
        +        "tickerLabel": {
        +          "description": "Display label echoed from the request (ticker or tickerLabel). Omitted when not supplied.",
        +          "type": "string"
        +        },
        +        "volatility": {
        +          "description": "Annualized sigma actually used in pricing, as a decimal.",
        +          "type": "number"
        +        }
        +      },
        +      "required": [
        +        "positionValue",
        +        "sector",
        +        "volatility",
        +        "protectionLevel",
        +        "tenorYears"
        +      ],
        +      "type": "object"
        +    },
        +    "payoffRange": {
        +      "description": "Price-move range covered by payoffTable, extended at least 15% beyond each collar arm and at least +/-50%.",
        +      "properties": {
        +        "lowerPct": {
        +          "description": "Lower bound of the modeled price move, as a fraction of spot (negative).",
        +          "type": "number"
        +        },
        +        "upperPct": {
        +          "description": "Upper bound of the modeled price move, as a fraction of spot.",
        +          "type": "number"
        +        }
        +      },
        +      "required": [
        +        "lowerPct",
        +        "upperPct"
        +      ],
        +      "type": "object"
        +    },
        +    "payoffTable": {
        +      "description": "Terminal P&L in dollars at each 10%-step drawdown across payoffRange, for the bare put, the collar, and the unhedged position.",
        +      "items": {
        +        "properties": {
        +          "barePutPnl": {
        +            "description": "Position + put P&L in dollars at this move.",
        +            "type": "number"
        +          },
        +          "collarPnl": {
        +            "description": "Position + collar P&L in dollars at this move.",
        +            "type": "number"
        +          },
        +          "drawdownPct": {
        +            "description": "Price move as a fraction of spot (-0.30 = down 30%, 0.2 = up 20%).",
        +            "type": "number"
        +          },
        +          "unhedgedPnl": {
        +            "description": "Unhedged position P&L in dollars at this move.",
        +            "type": "number"
        +          }
        +        },
        +        "required": [
        +          "drawdownPct",
        +          "barePutPnl",
        +          "collarPnl",
        +          "unhedgedPnl"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "realWorldDrift": {
        +      "description": "Annual real-world drift used for the probability metrics: expectedReturn when supplied, else the sector long-run return. Does not affect premium math.",
        +      "type": "number"
        +    },
        +    "recommended": {
        +      "description": "Suggested structure: collar unless its cap binds too often (>20% probability); protective-put when the put is reasonably priced; none when neither is clean.",
        +      "enum": [
        +        "collar",
        +        "protective-put",
        +        "none"
        +      ],
        +      "type": "string"
        +    },
        +    "riskFreeRate": {
        +      "description": "Annualized risk-free rate used in Black-Scholes, looked up for the tenor, as a decimal.",
        +      "type": "number"
        +    }
        +  },
        +  "required": [
        +    "inputs",
        +    "riskFreeRate",
        +    "realWorldDrift",
        +    "barePut",
        +    "collar",
        +    "payoffTable",
        +    "payoffRange",
        +    "recommended"
        +  ],
        +  "type": "object"
        +}
    • Changedqsbs_check1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "description": "Section 1202 QSBS qualification result. All dollar amounts are USD.",
        +  "properties": {
        +    "applicableCap": {
        +      "description": "max(perIssuerCap, tenXBasisCap): the exclusion cap actually applied, in dollars.",
        +      "type": "number"
        +    },
        +    "era": {
        +      "description": "Acquisition-era classification that sets the exclusion schedule (50% pre-2009, 75% 2009-2010, 100% at 5y pre-OBBBA, tiered 50/75/100% at 3/4/5y under OBBBA).",
        +      "enum": [
        +        "pre-2009",
        +        "pre-2010",
        +        "pre-obbba",
        +        "obbba"
        +      ],
        +      "type": "string"
        +    },
        +    "excludableGain": {
        +      "description": "Portion of expectedGain excludable from federal tax in dollars.",
        +      "type": "number"
        +    },
        +    "exclusionPercent": {
        +      "description": "Fraction of the capped gain excludable from federal tax, per the era and holding-period tier.",
        +      "enum": [
        +        0,
        +        0.5,
        +        0.75,
        +        1
        +      ],
        +      "type": "number"
        +    },
        +    "federalTaxSaved": {
        +      "description": "Federal LTCG tax (including NIIT) avoided on the excluded gain, in dollars.",
        +      "type": "number"
        +    },
        +    "holdingYears": {
        +      "description": "Calendar-aware years between acquisitionDate and saleDate.",
        +      "type": "number"
        +    },
        +    "perIssuerCap": {
        +      "description": "The $10M statutory per-issuer cap in dollars.",
        +      "type": "number"
        +    },
        +    "stateConforms": {
        +      "description": "Whether the user state conforms to the federal 1202 exclusion.",
        +      "enum": [
        +        "full",
        +        "partial",
        +        "none"
        +      ],
        +      "type": "string"
        +    },
        +    "stateNote": {
        +      "description": "Per-state conformity explanation. May be omitted.",
        +      "type": "string"
        +    },
        +    "taxableGain": {
        +      "description": "Portion of expectedGain still federally taxable in dollars (overage above the cap plus any non-excluded fraction).",
        +      "type": "number"
        +    },
        +    "tenXBasisCap": {
        +      "description": "10 x adjustedBasis cap in dollars.",
        +      "type": "number"
        +    },
        +    "tests": {
        +      "description": "The eight statutory tests with per-test status, so an agent can show exactly which gate failed.",
        +      "items": {
        +        "properties": {
        +          "detail": {
        +            "description": "One-line explanation of the test outcome.",
        +            "type": "string"
        +          },
        +          "id": {
        +            "description": "Stable test identifier.",
        +            "type": "string"
        +          },
        +          "label": {
        +            "description": "Human-readable test name.",
        +            "type": "string"
        +          },
        +          "status": {
        +            "description": "\"wait\" means the test will pass with more holding time.",
        +            "enum": [
        +              "pass",
        +              "fail",
        +              "unsure",
        +              "wait"
        +            ],
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "id",
        +          "label",
        +          "status",
        +          "detail"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "verdict": {
        +      "description": "Overall verdict. \"partial\"/\"caveats\" mean some tests came back unsure; \"too-soon\" means the holding period has not reached an exclusion tier yet.",
        +      "enum": [
        +        "qualifies",
        +        "partial",
        +        "too-soon",
        +        "caveats",
        +        "disqualified"
        +      ],
        +      "type": "string"
        +    },
        +    "yearsUntilFullExclusion": {
        +      "description": "Additional years to hold before reaching the 100% exclusion tier; 0 when already reached.",
        +      "type": "number"
        +    }
        +  },
        +  "required": [
        +    "verdict",
        +    "exclusionPercent",
        +    "perIssuerCap",
        +    "tenXBasisCap",
        +    "applicableCap",
        +    "excludableGain",
        +    "taxableGain",
        +    "federalTaxSaved",
        +    "stateConforms",
        +    "holdingYears",
        +    "yearsUntilFullExclusion",
        +    "era",
        +    "tests"
        +  ],
        +  "type": "object"
        +}
    • Changedrsu_sell_vs_hold3 fields changed
      • changedInput schema / properties / ticker / description
        Previous value: -"Optional public-stock symbol (e.g. \"NVDA\", \"AAPL\"). When set, the tool substitutes the ticker's trailing CAGR for any unsupplied expected-return / sale-price field instead of requiring the caller to invent one. ~90 symbols covered; unknown tickers fall through to \"required field\" errors so the model knows to ask the user."New value: +"Optional public-stock symbol (e.g. \"NVDA\", \"AAPL\"). When set, the tool substitutes a cached trailing return for any unsupplied expected-return / sale-price field AND a cached implied vol for any unsupplied volatility, instead of requiring the caller to invent either. Most large-cap public symbols are covered; unknown tickers fall through to \"required field\" errors so the model knows to ask the user."
      • changedInput schema / properties / volatility / description
        Previous value: -"Annualized volatility (sigma) of the stock as a decimal (0.72 = 72%). Pass the user-supplied volatility directly; the tool computes the horizon-cumulative drag internally. The model MUST NOT compute drag itself — the correct formula is horizon-dependent and most models get it wrong. If the user does not supply a volatility number, ASK them."New value: +"Annualized volatility (sigma) of the stock as a decimal (0.72 = 72%). Pass the user-supplied volatility directly; the tool computes the horizon-cumulative drag internally. The model MUST NOT compute drag itself — the correct formula is horizon-dependent and most models get it wrong. If the user does not supply a volatility number AND no `ticker` resolves it from the cached implied-vol table, ASK them."
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "description": "RSU sell-at-vest vs hold result. All dollar amounts are USD.",
        +  "properties": {
        +    "bracketJump": {
        +      "description": "Marginal federal bracket change caused by the new ordinary income; null when the income stays within one bracket.",
        +      "properties": {
        +        "fromRate": {
        +          "description": "Marginal federal rate before the event, as a decimal (0.24 = 24%).",
        +          "type": "number"
        +        },
        +        "thresholdAtJump": {
        +          "description": "Taxable-income threshold in dollars where the bracket changes.",
        +          "type": "number"
        +        },
        +        "toRate": {
        +          "description": "Marginal federal rate after the event, as a decimal.",
        +          "type": "number"
        +        }
        +      },
        +      "required": [
        +        "fromRate",
        +        "toRate",
        +        "thresholdAtJump"
        +      ],
        +      "type": [
        +        "object",
        +        "null"
        +      ]
        +    },
        +    "hold": {
        +      "description": "Keep the after-tax shares for holdYears, then sell.",
        +      "properties": {
        +        "capGainFederal": {
        +          "description": "Federal capital gains tax in dollars on the gain: LTCG (including NIIT) when isLongTerm, else the marginal ordinary rate.",
        +          "type": "number"
        +        },
        +        "capGainState": {
        +          "description": "State capital gains tax in dollars on the gain.",
        +          "type": "number"
        +        },
        +        "capGainTotal": {
        +          "description": "Total capital gains tax in dollars at sale.",
        +          "type": "number"
        +        },
        +        "costBasis": {
        +          "description": "Cost basis per share in dollars (FMV at vest).",
        +          "type": "number"
        +        },
        +        "effectiveSalePrice": {
        +          "description": "Projected sale price per share in dollars at end of holdYears, after the volatility haircut.",
        +          "type": "number"
        +        },
        +        "expectedGain": {
        +          "description": "Expected capital gain in dollars on the retained shares: (effectiveSalePrice - costBasis) x sharesRetained.",
        +          "type": "number"
        +        },
        +        "isLongTerm": {
        +          "description": "True when holdYears >= 1, so appreciation gets long-term capital gains treatment.",
        +          "type": "boolean"
        +        },
        +        "netAtYearN": {
        +          "description": "Net after-tax value of holding in dollars at end of holdYears: sale proceeds - capGainTotal.",
        +          "type": "number"
        +        },
        +        "sharesRetained": {
        +          "description": "Shares kept after the sell-to-cover dollar-equivalent of the vest tax.",
        +          "type": "number"
        +        }
        +      },
        +      "required": [
        +        "costBasis",
        +        "effectiveSalePrice",
        +        "sharesRetained",
        +        "expectedGain",
        +        "capGainFederal",
        +        "capGainState",
        +        "capGainTotal",
        +        "isLongTerm",
        +        "netAtYearN"
        +      ],
        +      "type": "object"
        +    },
        +    "holdMinusSell": {
        +      "description": "hold.netAtYearN - sellNowInvest.netAtYearN in dollars. Positive favors holding the vested shares; negative favors selling at vest and reinvesting.",
        +      "type": "number"
        +    },
        +    "sellNowInvest": {
        +      "description": "Counterfactual: sell every share at vest and reinvest the net cash at expectedMarketReturn for holdYears.",
        +      "properties": {
        +        "capGainFederal": {
        +          "description": "Federal capital gains tax in dollars on the market gain: LTCG (including NIIT) when isLongTerm, else the marginal ordinary rate.",
        +          "type": "number"
        +        },
        +        "capGainState": {
        +          "description": "State capital gains tax in dollars on the market gain.",
        +          "type": "number"
        +        },
        +        "capGainTotal": {
        +          "description": "Total capital gains tax in dollars on the market gain.",
        +          "type": "number"
        +        },
        +        "isLongTerm": {
        +          "description": "True when holdYears >= 1.",
        +          "type": "boolean"
        +        },
        +        "marketGain": {
        +          "description": "Market growth in dollars on the reinvested cash over holdYears.",
        +          "type": "number"
        +        },
        +        "netAtYearN": {
        +          "description": "Net after-tax value of sell-at-vest-and-invest in dollars at end of holdYears.",
        +          "type": "number"
        +        },
        +        "netCashAtY0": {
        +          "description": "Net cash in dollars at vest available to reinvest (equals vest.netCashAtVest).",
        +          "type": "number"
        +        }
        +      },
        +      "required": [
        +        "netCashAtY0",
        +        "marketGain",
        +        "capGainFederal",
        +        "capGainState",
        +        "capGainTotal",
        +        "isLongTerm",
        +        "netAtYearN"
        +      ],
        +      "type": "object"
        +    },
        +    "vest": {
        +      "description": "Tax bill at vest on the full vest value (taxed as ordinary W-2 income).",
        +      "properties": {
        +        "additionalMedicare": {
        +          "description": "Additional Medicare (0.9%) tax in dollars.",
        +          "type": "number"
        +        },
        +        "federal": {
        +          "description": "True federal ordinary income tax on the vest value in dollars (marginal bracket, not the withholding).",
        +          "type": "number"
        +        },
        +        "federalWithheldAtVest": {
        +          "description": "Mandatory federal supplemental withholding in dollars (22% on the first $1M of supplemental wages, 37% above). When less than vest.federal, the difference is owed at tax time.",
        +          "type": "number"
        +        },
        +        "medicare": {
        +          "description": "Medicare tax in dollars.",
        +          "type": "number"
        +        },
        +        "netCashAtVest": {
        +          "description": "vestValue - total: net cash in dollars if every share is sold at vest.",
        +          "type": "number"
        +        },
        +        "socialSecurity": {
        +          "description": "Social Security tax in dollars (0 when not employed or already past the wage base).",
        +          "type": "number"
        +        },
        +        "state": {
        +          "description": "State income tax on the vest value in dollars.",
        +          "type": "number"
        +        },
        +        "total": {
        +          "description": "Total tax at vest in dollars.",
        +          "type": "number"
        +        },
        +        "vestValue": {
        +          "description": "shares x currentPrice in dollars, taxed as ordinary income at vest.",
        +          "type": "number"
        +        }
        +      },
        +      "required": [
        +        "vestValue",
        +        "federal",
        +        "state",
        +        "socialSecurity",
        +        "medicare",
        +        "additionalMedicare",
        +        "total",
        +        "netCashAtVest",
        +        "federalWithheldAtVest"
        +      ],
        +      "type": "object"
        +    }
        +  },
        +  "required": [
        +    "vest",
        +    "bracketJump",
        +    "hold",
        +    "sellNowInvest",
        +    "holdMinusSell"
        +  ],
        +  "type": "object"
        +}
  6. 2 tool updatesv1.7.0
    • Changedamt_iso_optimize1 field changed
      • changedInput schema / properties / cashReturnRate / description
        Previous value: -"Annual after-tax return on idle cash (decimal), used to time-value the cash-tax stream. 0.05 = 5% (~short-Treasury yield). Ask the user (e.g. \"what after-tax yield should I use for idle cash, e.g. ~5% for short-term Treasury?\"). At 0 the math collapses to a nominal sum."New value: +"Annual after-tax return on idle cash (decimal), used to time-value the cash-tax stream. 0.05 = 5% (~short-Treasury yield). Required. The model MUST NOT invent this value; ask the user (e.g. \"what after-tax yield should I use for idle cash, e.g. ~5% for short-term Treasury?\"). At 0 the math collapses to a nominal sum."
    • Addedequity_funding_plan
  7. 1 tool updatev1.3.6
    • Changedamt_iso_optimize1 field changed
      • changedInput schema / properties / cashReturnRate / description
        Previous value: -"Annual after-tax return on idle cash (decimal), used to time-value the cash-tax stream. 0.05 = 5% (~short-Treasury yield). Required. The model MUST NOT invent this value; ask the user (e.g. \"what after-tax yield should I use for idle cash, e.g. ~5% for short-term Treasury?\"). At 0 the math collapses to a nominal sum."New value: +"Annual after-tax return on idle cash (decimal), used to time-value the cash-tax stream. 0.05 = 5% (~short-Treasury yield). Ask the user (e.g. \"what after-tax yield should I use for idle cash, e.g. ~5% for short-term Treasury?\"). At 0 the math collapses to a nominal sum."
  8. 4 tool updatesv1.3.4
    • Changedamt_iso_optimize4 fields changed
      • changedInput schema / properties / cashReturnRate / description
        Previous value: -"Annual after-tax return on idle cash (decimal), used to time-value the cash-tax stream. 0.05 = 5% (~short-Treasury yield). At 0 the math collapses to a nominal sum."New value: +"Annual after-tax return on idle cash (decimal), used to time-value the cash-tax stream. 0.05 = 5% (~short-Treasury yield). Required. The model MUST NOT invent this value; ask the user (e.g. \"what after-tax yield should I use for idle cash, e.g. ~5% for short-term Treasury?\"). At 0 the math collapses to a nominal sum."
      • addedInput schema / properties / volatility
        Added value: +{
        +  "description": "Annualized volatility (sigma) of the stock as a decimal (0.72 = 72%). Pass the user-supplied volatility directly; the tool computes the horizon-cumulative drag internally. The model MUST NOT compute drag itself — the correct formula is horizon-dependent and most models get it wrong. If the user does not supply a volatility number, ASK them.",
        +  "minimum": 0,
        +  "type": "number"
        +}
      • removedInput schema / properties / volatilityDrag
        Removed value: -{
        -  "description": "Multiplicative haircut on the terminal-FMV growth path (0..0.99), capturing the half-variance correction in compounded returns. 0 = no drag, 0.20 = 20% haircut at horizon.",
        -  "maximum": 0.99,
        -  "minimum": 0,
        -  "type": "number"
        -}
      • changedInput schema / required
        Previous value: -[
        -  "shares",
        -  "strike",
        -  "fmv",
        -  "volatilityDrag",
        -  "filingStatus",
        -  "ordinaryIncome",
        -  "stateCode",
        -  "carryforwardCredit",
        -  "horizon",
        -  "cashReturnRate",
        -  "grantDate",
        -  "hasLeftCompany",
        -  "terminationDate"
        -]New value: +[
        +  "shares",
        +  "strike",
        +  "fmv",
        +  "filingStatus",
        +  "ordinaryIncome",
        +  "stateCode",
        +  "carryforwardCredit",
        +  "horizon",
        +  "cashReturnRate",
        +  "grantDate",
        +  "hasLeftCompany",
        +  "terminationDate"
        +]
    • Changedconcentration_analyze3 fields changed
      • changedInput schema / properties / volatility / description
        Previous value: -"Annualized implied volatility (sigma) of the stock. Optional. When omitted, falls back to sector_stats.annualVol × 1.20 (the IV-over-RV multiplier)."New value: +"Annualized volatility (sigma) of the stock as a decimal (0.72 = 72%). Pass the user-supplied volatility directly; the tool uses it both for hedge pricing (as implied vol) and for the 3y horizon drag, computed internally. The model MUST NOT compute drag itself — the correct formula is horizon-dependent and most models get it wrong. If the user does not supply a volatility number, ASK them; only when neither is supplied does hedge pricing fall back to sector_stats.annualVol × 1.20."
      • removedInput schema / properties / volatilityDrag
        Removed value: -{
        -  "description": "Multiplicative haircut on the expected stock-price path at the 3-year analysis horizon (0..0.99), capturing the half-variance correction in compounded returns.",
        -  "maximum": 0.99,
        -  "minimum": 0,
        -  "type": "number"
        -}
      • changedInput schema / required
        Previous value: -[
        -  "positionValue",
        -  "costBasis",
        -  "acquisitionDate",
        -  "sector",
        -  "stateCode",
        -  "filingStatus",
        -  "ordinaryIncome",
        -  "totalAssets",
        -  "volatilityDrag"
        -]New value: +[
        +  "positionValue",
        +  "costBasis",
        +  "acquisitionDate",
        +  "sector",
        +  "stateCode",
        +  "filingStatus",
        +  "ordinaryIncome",
        +  "totalAssets"
        +]
    • Changednso_calculate3 fields changed
      • removedInput schema / properties / haircut
        Removed value: -{
        -  "description": "Multiplicative haircut on expectedSalePrice (0..1) capturing volatility drag at the hold horizon. 0.20 = 20% haircut.",
        -  "maximum": 1,
        -  "minimum": 0,
        -  "type": "number"
        -}
      • addedInput schema / properties / volatility
        Added value: +{
        +  "description": "Annualized volatility (sigma) of the stock as a decimal (0.72 = 72%). Pass the user-supplied volatility directly; the tool computes the horizon-cumulative drag internally. The model MUST NOT compute drag itself — the correct formula is horizon-dependent and most models get it wrong. If the user does not supply a volatility number, ASK them.",
        +  "minimum": 0,
        +  "type": "number"
        +}
      • changedInput schema / required
        Previous value: -[
        -  "shares",
        -  "strike",
        -  "currentPrice",
        -  "ordinaryIncome",
        -  "filingStatus",
        -  "stateCode",
        -  "stillEmployed",
        -  "holdYears",
        -  "haircut",
        -  "holdFunding"
        -]New value: +[
        +  "shares",
        +  "strike",
        +  "currentPrice",
        +  "ordinaryIncome",
        +  "filingStatus",
        +  "stateCode",
        +  "stillEmployed",
        +  "holdYears",
        +  "holdFunding"
        +]
    • Changedrsu_sell_vs_hold3 fields changed
      • removedInput schema / properties / haircut
        Removed value: -{
        -  "description": "Multiplicative haircut on expectedSalePrice (0..1) capturing volatility drag at the hold horizon.",
        -  "maximum": 1,
        -  "minimum": 0,
        -  "type": "number"
        -}
      • addedInput schema / properties / volatility
        Added value: +{
        +  "description": "Annualized volatility (sigma) of the stock as a decimal (0.72 = 72%). Pass the user-supplied volatility directly; the tool computes the horizon-cumulative drag internally. The model MUST NOT compute drag itself — the correct formula is horizon-dependent and most models get it wrong. If the user does not supply a volatility number, ASK them.",
        +  "minimum": 0,
        +  "type": "number"
        +}
      • changedInput schema / required
        Previous value: -[
        -  "shares",
        -  "currentPrice",
        -  "ordinaryIncome",
        -  "filingStatus",
        -  "stateCode",
        -  "stillEmployed",
        -  "holdYears",
        -  "haircut"
        -]New value: +[
        +  "shares",
        +  "currentPrice",
        +  "ordinaryIncome",
        +  "filingStatus",
        +  "stateCode",
        +  "stillEmployed",
        +  "holdYears"
        +]
  9. 6 tool updatesv1.2.2
    • Changedamt_iso_optimize13 fields changed
      • addedInput schema / properties / carryforwardCredit / description
        Added value: +"Existing federal AMT credit (Minimum Tax Credit, Form 8801) carryforward from prior tax years, USD. Recoverable in future years where regular federal tax exceeds tentative minimum tax."
      • addedInput schema / properties / cashReturnRate / description
        Added value: +"Annual after-tax return on idle cash (decimal), used to time-value the cash-tax stream. 0.05 = 5% (~short-Treasury yield). At 0 the math collapses to a nominal sum."
      • addedInput schema / properties / filingStatus / description
        Added value: +"Federal filing status. Drives the ordinary-bracket walk, the AMT exemption tier ($90,100 single / $140,200 MFJ for 2026), and the AMT exemption phaseout start ($500,000 single / $1,000,000 MFJ)."
      • addedInput schema / properties / fmv / description
        Added value: +"Current fair market value per share, USD. Anchors year-1 of the growth path; future years compound from here using expectedGrowth and volatilityDrag."
      • addedInput schema / properties / grantDate / description
        Added value: +"ISO grant date (YYYY-MM-DD). Drives the 10-year statutory grant expiration (IRC §422) and the 2-year qualifying-disposition threshold from grant."
      • addedInput schema / properties / hasLeftCompany / description
        Added value: +"True if the user has separated from the company. Activates the 90-day post-termination ISO exercise window measured from terminationDate."
      • addedInput schema / properties / horizon / description
        Added value: +"Planning horizon in years (1..10). The optimizer searches all feasible per-year share allocations across this many years."
      • addedInput schema / properties / ordinaryIncome / description
        Added value: +"Annual W-2 ordinary income before this exercise, USD. Baseline for the bracket walk and the AMT exemption phaseout."
      • addedInput schema / properties / shares / description
        Added value: +"Total Incentive Stock Option (ISO) shares available to exercise across the planning horizon."
      • addedInput schema / properties / stateCode / description
        Added value: +"Two-letter US state code (e.g. CA, NY, TX). Drives state ordinary brackets, state long-term capital gains (LTCG) treatment, and state AMT (CA, CO, CT, MN)."
      • addedInput schema / properties / strike / description
        Added value: +"Strike price per share, USD."
      • addedInput schema / properties / terminationDate / description
        Added value: +"Separation date (YYYY-MM-DD) when hasLeftCompany=true; null when still employed. Together with hasLeftCompany, drives the 90-day exercise window deadline."
      • addedInput schema / properties / volatilityDrag / description
        Added value: +"Multiplicative haircut on the terminal-FMV growth path (0..0.99), capturing the half-variance correction in compounded returns. 0 = no drag, 0.20 = 20% haircut at horizon."
    • Changedconcentration_analyze14 fields changed
      • addedInput schema / properties / acquisitionDate / description
        Added value: +"Earliest acquisition date in the lot (YYYY-MM-DD). Drives the 1-year LTCG threshold and the long-term-vs-short-term tax routing."
      • addedInput schema / properties / costBasis / description
        Added value: +"Total cost basis of the position, USD (sum of strikes paid + ordinary-income inclusions on RSU vest / NSO exercise / disqualified ISO)."
      • addedInput schema / properties / filingStatus / description
        Added value: +"Federal filing status. Drives LTCG brackets and the NIIT MAGI threshold."
      • addedInput schema / properties / hedgeChoice / description
        Added value: +"Optional hedge specification. When provided, adds a hedged scenario to the sell-down-vs-hold comparison and computes the post-tax NFV of the hedged hold. Omit to compare only sell-down vs. hold."
      • addedInput schema / properties / hedgeChoice / properties / kind / description
        Added value: +"Hedge instrument: 'put' (bare protective put — pay premium for downside protection) or 'collar' (put financed by a short call — caps upside in exchange for lower or zero net premium)."
      • addedInput schema / properties / hedgeChoice / properties / protectionLevel / description
        Added value: +"Put strike chosen as (1 − this fraction) × spot. 0.10 = 10% OTM put. Range 0.05..0.50."
      • addedInput schema / properties / hedgeChoice / properties / tenorYears / description
        Added value: +"Option tenor in years. 1 = 12-month; 0.25 = ~90-day."
      • addedInput schema / properties / hedgeChoice / properties / upsideCapPct / description
        Added value: +"For collars only: optional explicit upside cap as fraction above spot (e.g. 0.20 = 20% cap). Omit to let the tool solve for the cap that makes the collar zero-net-premium."
      • addedInput schema / properties / ordinaryIncome / description
        Added value: +"Annual W-2 ordinary income before any sales, USD. Baseline for LTCG bracket determination."
      • addedInput schema / properties / positionValue / description
        Added value: +"Current market value of the concentrated single-stock position, USD."
      • addedInput schema / properties / sector / description
        Added value: +"Sector tag. Drives the default volatility used in the hedge-cost computation when no explicit volatility is provided. See lib/markets/sector-stats.ts for the per-sector annualVol table; this tool applies IV_OVER_RV_MULTIPLIER (1.20) to the realized vol to approximate implied vol."
      • addedInput schema / properties / stateCode / description
        Added value: +"Two-letter US state code. Drives state LTCG and ordinary brackets."
      • addedInput schema / properties / volatility / description
        Added value: +"Annualized implied volatility (sigma) of the stock. Optional. When omitted, falls back to sector_stats.annualVol × 1.20 (the IV-over-RV multiplier)."
      • addedInput schema / properties / volatilityDrag / description
        Added value: +"Multiplicative haircut on the expected stock-price path at the 3-year analysis horizon (0..0.99), capturing the half-variance correction in compounded returns."
    • Changednso_calculate10 fields changed
      • addedInput schema / properties / currentPrice / description
        Added value: +"Current fair market value per share, USD. The bargain element at exercise is shares × (currentPrice − strike)."
      • addedInput schema / properties / filingStatus / description
        Added value: +"Federal filing status. Drives ordinary brackets and LTCG brackets used at the hold horizon."
      • addedInput schema / properties / haircut / description
        Added value: +"Multiplicative haircut on expectedSalePrice (0..1) capturing volatility drag at the hold horizon. 0.20 = 20% haircut."
      • addedInput schema / properties / holdFunding / description
        Added value: +"How the strike cost and exercise tax are funded. 'sell-to-cover' sells enough shares to cover strike + tax (reduces sharesRetained). 'cash' pays from outside the position (full sharesRetained but requires the cashNeededAtExercise field)."
      • addedInput schema / properties / holdYears / description
        Added value: +"Years to hold after exercise (minimum 1). At ≥1 year, the appreciation since exercise is LTCG; sub-1-year holds are out of scope."
      • addedInput schema / properties / ordinaryIncome / description
        Added value: +"Annual W-2 ordinary income before this exercise, USD. Baseline for the bracket walk on the bargain element."
      • addedInput schema / properties / shares / description
        Added value: +"Non-qualified Stock Option (NSO) shares to exercise."
      • addedInput schema / properties / stateCode / description
        Added value: +"Two-letter US state code. Drives state ordinary and LTCG treatment."
      • addedInput schema / properties / stillEmployed / description
        Added value: +"True if still employed at exercise. FICA (Social Security + Medicare + Additional Medicare) applies only when true."
      • addedInput schema / properties / strike / description
        Added value: +"Strike price per share, USD."
    • Changedprotective_put_price6 fields changed
      • addedInput schema / properties / expectedReturn / description
        Added value: +"Annual expected stock return (decimal). Drives risk-neutral drift in the cap-hit / floor-hit probability metrics. Does not affect premium math. Default 0."
      • addedInput schema / properties / positionValue / description
        Added value: +"Market value of the underlying single-stock position, USD. Premium and max-loss scale linearly with this."
      • addedInput schema / properties / protectionLevel / description
        Added value: +"Put strike as (1 − this fraction) × spot. 0.10 = 10% OTM put. Range 0.05..0.50."
      • addedInput schema / properties / sector / description
        Added value: +"Sector tag. Drives the default volatility when no explicit `volatility` is supplied. Lookup table is in lib/markets/sector-stats.ts."
      • addedInput schema / properties / tenorYears / description
        Added value: +"Option tenor in years. 1 = 12-month; 0.25 = ~90-day."
      • addedInput schema / properties / tickerLabel / description
        Added value: +"Optional display string echoed back in the result. Not used in pricing."
    • Changedqsbs_check12 fields changed
      • addedInput schema / properties / acquisitionDate / description
        Added value: +"Date the QSBS shares were acquired (YYYY-MM-DD). Drives the holding-period test and the era classification (50% pre-2009, 75% 2009-2010, 100% 2010-2025-07-04, OBBBA tiered after 2025-07-05)."
      • addedInput schema / properties / acquisitionMethod / description
        Added value: +"§1202 Test 2: How the user obtained the shares. 'original-issuance' (direct from the company) qualifies. 'gift-or-inheritance' tacks the original holder's basis and clock. 'secondary' (bought on a secondary market) does NOT qualify. 'unsure' triggers a partial verdict."
      • addedInput schema / properties / activeBusiness / description
        Added value: +"§1202 Test 5: Did the corporation use ≥80% of its assets in the active conduct of a qualified trade throughout the holding period? 'yes' qualifies. 'no' fails. 'unsure' returns a partial verdict (user should confirm with their CFO)."
      • addedInput schema / properties / adjustedBasis / description
        Added value: +"Adjusted basis of the QSBS shares, USD. Used in the 10× basis cap: the per-issuer exclusion cap is max($10M, 10 × adjustedBasis)."
      • addedInput schema / properties / assetCategory / description
        Added value: +"§1202 Test 3: Aggregate gross assets of the issuing corporation at the time of issuance. 'under-50m' qualifies pre-OBBBA. '50m-to-75m' qualifies ONLY under OBBBA 2026+ (post-2025-07-05). 'over-75m' never qualifies. 'unsure' returns a partial verdict."
      • addedInput schema / properties / entityType / description
        Added value: +"§1202 Test 1: Type of issuer at the time of acquisition. Only 'us-c-corp' qualifies. S-corps, LLCs, partnerships, and foreign entities fail."
      • addedInput schema / properties / expectedGain / description
        Added value: +"Expected total gain on sale, USD. Compared against the per-issuer exclusion cap to compute excludableGain and taxableGain."
      • addedInput schema / properties / filingStatus / description
        Added value: +"Federal filing status. Drives the LTCG bracket on any non-excluded gain and the NIIT MAGI threshold."
      • addedInput schema / properties / industry / description
        Added value: +"§1202 Test 4: Industry classification of the corporation. Qualified-trade-or-business industries qualify (tech-software, manufacturing, biotech-research, retail-wholesale, hospitality, etc.). Specified service trades or businesses (law, engineering, architecture, accounting-actuarial, consulting, finance, farming, extraction, health-services, performing-arts) generally do NOT qualify."
      • addedInput schema / properties / ordinaryIncome / description
        Added value: +"Annual W-2 ordinary income, USD. Baseline for the federal LTCG bracket on any taxable gain."
      • addedInput schema / properties / saleDate / description
        Added value: +"Planned or actual sale date (YYYY-MM-DD). Together with acquisitionDate determines holdingYears."
      • addedInput schema / properties / stateCode / description
        Added value: +"Two-letter US state code. Drives the state-conformity verdict: CA/AL/PA/MS do not conform (full state tax owed); HI/MA partial; NJ 2026-01-01 conformity switch; most others fully conform."
    • Changedrsu_sell_vs_hold8 fields changed
      • addedInput schema / properties / currentPrice / description
        Added value: +"Fair market value per share at vest, USD. Also the cost basis on retained shares."
      • addedInput schema / properties / filingStatus / description
        Added value: +"Federal filing status."
      • addedInput schema / properties / haircut / description
        Added value: +"Multiplicative haircut on expectedSalePrice (0..1) capturing volatility drag at the hold horizon."
      • addedInput schema / properties / holdYears / description
        Added value: +"Years to hold after vest (0.25..5). Below 1 year triggers the short-term capital gains cliff (ordinary rates on appreciation)."
      • addedInput schema / properties / ordinaryIncome / description
        Added value: +"Annual W-2 ordinary income before this vest, USD. Baseline for the bracket walk on the vest amount."
      • addedInput schema / properties / shares / description
        Added value: +"Restricted Stock Unit (RSU) shares vesting in this tranche."
      • addedInput schema / properties / stateCode / description
        Added value: +"Two-letter US state code."
      • addedInput schema / properties / stillEmployed / description
        Added value: +"True if still employed at vest. Drives FICA applicability and whether the 22% supplemental withholding rule applies."
  10. 6 tool updatesv1.2.0
    • First observedamt_iso_optimize
    • First observedconcentration_analyze
    • First observednso_calculate
    • First observedprotective_put_price
    • First observedqsbs_check
    • First observedrsu_sell_vs_hold

TDQS

A4.9/5.0
Disambiguation5/5

Each tool targets a distinct equity-compensation scenario or planning question: ISO/AMT, NSO, RSU vest, RSU lots, concentration, hedging, QSBS, and funding goals. Cross-references in descriptions direct agents to the right tool, leaving no ambiguous overlap.

Naming Consistency4/5

Most tools follow a noun_verb pattern (amt_iso_optimize, nso_calculate, concentration_analyze, qsbs_check, rsu_lot_optimize), but a few deviate: rsu_sell_vs_hold is a phrase, protective_put_price is ambiguous (could be read as a noun), and equity_funding_plan is a noun-noun rather than verb-final. All are snake_case and readable, but the inconsistency costs a point.

Tool Count5/5

Eight tools is appropriate for the server's stated scope (ISO/NSO/RSU/QSBS tax, concentration, hedging, funding). Each tool addresses a distinct need, and no tool feels redundant or missing.

Completeness5/5

The surface covers the full lifecycle of equity compensation planning: exercise/vest tax treatment (ISO/NSO/RSU), post-vest lot optimization, concentration risk, hedge pricing, QSBS qualification, and goal-based funding. The only noted limitation is integrated multi-position analysis, which is explicitly outsourced to a beta, so for the single-position scope it is complete.

Maintenance

ActivityActive
ResponsivenessResponsive

Related MCP Connectors

Related MCP Servers

  • A
    license
    B
    quality
    C
    maintenance
    39 tax tools for US individual taxpayers — federal/state tax calculations, credits, deductions, retirement strategies, audit risk, and tax planning. All calculations run locally, no data leaves the machine. Supports TY2024 and TY2025 (One Big Beautiful Bill Act).
    43
    454
    12
    MIT
  • A
    license
    A
    quality
    A
    maintenance
    63 deterministic quant computation tools for autonomous financial agents. Options pricing, derivatives, risk metrics, portfolio optimization, statistics, crypto/DeFi, macro/FX, time value of money. 1,000 free calls/day, no signup required.
    74
    11
    MIT
  • F
    license
    Not graded
    quality
    C
    maintenance
    Tax-aware retirement planning for Canada and the US. CPP/OAS and Social Security timing, RRSP/TFSA/401k/IRA projections, Monte Carlo simulation, withdrawal order optimization, and historical backtesting against 150 years of market data.
    2
    -
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables AI assistants to analyze single-company concentration risk for tech workers with RSU compensation, providing risk scores, wealth-at-risk, and action items.
    74
    Apache 2.0

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/AlvisoOculus/optionsahoy-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server