optionsahoy-mcp
Server Details
Equity comp tax/trade optimizer: ISO/AMT exercise, NSO, RSU, QSBS, concentration, hedging. 50-state.
- Status
- Healthy
- Last Tested
- Transport
- Streamable HTTP
- URL
- Repository
- AlvisoOculus/optionsahoy-mcp
- GitHub Stars
- 5
- Server Listing
- OptionsAhoy: Stock Equity and Tax Optimizer
Available Tools
8 toolsamt_iso_optimizeARead-onlyIdempotentInspect
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. Computes the multi-year 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. 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. Also returns crossoverShares, crossoverBargain, alreadyInAmt, timing, stateHasAmt, bargainPerShare, and effectiveHorizon; 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. Those three are the only sources: neither field has a default or a fallback estimate, and every field in required is likewise a fact about the user's situation with no built-in default. A call that neither supplies nor resolves growth or volatility 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 https://optionsahoy.com/beta?src=mcp_multi.
| Name | Required | Description | Default |
|---|---|---|---|
| fmv | Yes | 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. | |
| shares | Yes | Total Incentive Stock Option (ISO) shares available to exercise across the planning horizon. Must come from the user. | |
| strike | Yes | Strike price per share, USD. Must come from the user. | |
| ticker | No | 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 the implied vol as of the last close for any unsupplied volatility. Growth and vol come from different sources, so some symbols resolve only one, and a vol that is not current resolves as nothing. A field the ticker cannot resolve falls through to a "required field" error naming that field: pass it 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 growth. | |
| horizon | Yes | 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. | |
| grantDate | Yes | ISO grant date (YYYY-MM-DD). Drives the 10-year statutory grant expiration (IRC §422) and the 2-year qualifying-disposition threshold from grant. | |
| stateCode | Yes | 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). | |
| volatility | No | 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 as of the last market close; if neither supplies it, ask the user rather than estimating one. | |
| filingStatus | Yes | 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). | |
| cashReturnRate | No | 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. | |
| expectedGrowth | No | 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. | |
| hasLeftCompany | Yes | True if the user has separated from the company. Activates the 90-day post-termination ISO exercise window measured from terminationDate. | |
| ordinaryIncome | Yes | 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. | |
| volatilityDrag | No | 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. | |
| terminationDate | No | 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. | |
| carryforwardCredit | No | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
| timing | Yes | Timing constraints derived from grantDate and (when departed) terminationDate. |
| schedules | Yes | 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. |
| stateHasAmt | Yes | True when the user state levies its own AMT (CA, CO, CT, MN). |
| alreadyInAmt | Yes | True when the user owes AMT even with zero exercise (regular tax below tentative minimum tax at baseline income). |
| bargainPerShare | Yes | Year-1 bargain element per share in dollars: max(0, fmv - strike). |
| crossoverShares | Yes | Maximum whole shares exercisable in year 1 before federal AMT exceeds regular tax (the AMT crossover). |
| crossoverBargain | Yes | Bargain element in dollars at the crossover share count: crossoverShares x (fmv - strike). |
| effectiveHorizon | Yes | Horizon actually used by the schedules: min(requested horizon, timing.maxHorizon). |
| departedRecommendation | No | 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. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations (readOnlyHint, idempotentHint, destructiveHint, openWorldHint=false): discloses pure deterministic computation, no network access, no PII retention, compiled-in 50-state tax tables. Most valuable, it discloses a genuine limitation — departedRecommendation is scanned not exhaustively searched and can land a few shares off optimum — plus the provenance caveat that syntactically valid numbers pass without check. No contradiction with annotations; the description reinforces, rather than repeats, the read-only/idempotent safety profile.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but front-loaded with the core use cases and routing before the NFV mechanics. Nearly every sentence earns its place given the 16-param financial complexity, but there is measurable redundancy: the 'market' sentinel rule appears three times (description, ticker param, expectedGrowth param) and the verification link twice. Tightening would help, but the prioritization of routing and limitations is sound.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given 16 parameters, 2 enums, multiple resolution modes, and AMT/ISO tax complexity, this is exceptionally complete. It names the headline output (schedules.optimized.nfv), the baseline comparison plans, and the auxiliary returns, which is appropriate even with an output schema present. It covers resolution rules, the post-termination window, grant expiration, and the multi-position independence caveat — nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 cross-parameter semantics the schema alone can't convey: the three-source resolution rule for growth/volatility (explicit value, covered ticker, or 'market' sentinel), the fact neither field has a default, the volatilityDrag-OR-volatility precedence rule, and the 'number from any other source is accepted as-is' caveat. The one omission: the description explains expectedGrowth resolution but the interplay with volatilityDrag is only implied.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a precise verb+resource: computes the multi-year ISO exercise schedule that maximizes after-tax Net Final Value (NFV). It defines NFV explicitly and names three sibling tools with the exact condition that selects each (nso_calculate for NSOs, rsu_sell_vs_hold for RSUs at vest, qsbs_check for §1202), which unambiguously differentiates it from the sibling set shown.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Opens with an exhaustive when-to-use list of user intents (AMT bomb, phantom income, early exercise, multi-year schedule) and explicitly routes away from siblings. It also gives a concrete example call, tells the agent to ask rather than assume when values are absent, and specifies the only three valid sources for growth/volatility.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
concentration_analyzeARead-onlyIdempotentInspect
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. Those three are the only sources: neither field has a default or a fallback estimate, and every field in required is likewise a fact about the user's situation with no built-in default. A call that neither supplies nor resolves growth or volatility 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 https://optionsahoy.com/beta?src=mcp_multi.
| Name | Required | Description | Default |
|---|---|---|---|
| sector | Yes | Sector tag. Drives the default volatility used in the hedge-cost computation when no explicit volatility is provided (a sector-typical implied volatility). | |
| ticker | No | 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 the implied vol as of the last close for any unsupplied volatility. Growth and vol come from different sources, so some symbols resolve only one, and a vol that is not current resolves as nothing. A field the ticker cannot resolve falls through to a "required field" error naming that field: pass it 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 growth. | |
| costBasis | Yes | Total 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. | |
| stateCode | Yes | Two-letter US state code. Drives state LTCG and ordinary brackets. | |
| volatility | No | Annualized 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 as of the last market close; 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. | |
| hedgeChoice | No | 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`. | |
| totalAssets | Yes | Total 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. | |
| filingStatus | Yes | Federal filing status. Drives LTCG brackets and the NIIT MAGI threshold. | |
| positionValue | Yes | Current market value of the concentrated single-stock position, USD. Must come from the user. | |
| ordinaryIncome | Yes | Annual 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. | |
| volatilityDrag | No | Alternative 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. | |
| acquisitionDate | Yes | Earliest acquisition date in the lot (YYYY-MM-DD). Drives the 1-year LTCG threshold and the long-term-vs-short-term tax routing. | |
| expectedMarketReturn | No | Annual 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. | |
| expectedPositionReturn | No | Annual 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
| Name | Required | Description |
|---|---|---|
| hedging | Yes | 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). |
| riskBand | Yes | Qualitative concentration band for the position weight. |
| schedule | Yes | Sell-down plans over 1, 2, and 3 years; empty when the position is already at or below the target weight. |
| longTermDate | Yes | Date the position turns long-term (acquisitionDate + 1 year). ISO 8601 date-time string. |
| lossExposure | Yes | Dollar damage at 30/50/70% single-stock drawdowns. |
| concentration | Yes | Position value / total assets, 0..1. |
| isLongTermToday | Yes | True when the position already qualifies for long-term capital gains treatment. |
| waitForLtInsight | Yes | Tax saved by waiting for long-term treatment before selling; null when already long-term or no sale is needed. |
| daysUntilLongTerm | Yes | Days until long-term treatment; 0 when already long-term. |
| sectorContextLine | Yes | One-line volatility/drawdown context for the chosen sector. |
| advisorBenchmarkLine | Yes | One-line comparison of the user weight vs the common advisor 10% single-name guideline. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, idempotentHint=true, destructiveHint=false, so the safety profile is known. The description goes far beyond that by disclosing input resolution logic (growth/vol must come from user, ticker, or 'market' sentinel; no defaults), the acceptance of any syntactically valid number without provenance check, the independence of multiple tool results, and the specific tax tables used with a verification link. It fully explains what the tool does under the hood and its limitations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long, but every clause earns its place given the tool's complexity (14 params, nested hedgeChoice, resolution logic). It is front-loaded with usage context and example, then details input constraints. It could be tightened in places (e.g., the long paragraph on input sources), but it remains efficiently organized and free of fluff.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool this complex, the description is exhaustive: it lists the full return-object structure, provides a concrete example call, clarifies the resolution fallback path, names the independent verification URL, and even points to the beta for multi-position optimization. With a rich output schema and 100% schema coverage, nothing an agent needs to invoke it correctly is left unspecified.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is complete (100%), but the description adds crucial meaning beyond the schema: it explains how totalAssets frames risk relative to the whole portfolio, how sector drives default volatility, how ticker resolution works for growth vs. vol, the distinction between volatility and volatilityDrag, and which parameters must come from the user and cannot be inferred. This transforms the schema from a bare list into a behavioral contract.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with explicit use-case triggers ('when someone asks how risky a large single-stock position is...'), then states the core function: 'Single-stock concentration risk analysis on an existing position.' It immediately distinguishes itself from siblings by naming protective_put_price for standalone hedge pricing and amt_iso_optimize/nso_calculate/rsu_sell_vs_hold for tax math, making its unique role unmistakable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides crystal-clear when-to-use conditions ('Use this when...') and explicit alternatives for other scenarios ('For standalone hedge pricing use protective_put_price; for the tax math... route to amt_iso_optimize / nso_calculate / rsu_sell_vs_hold first'). Even tells users to ask for missing values (tenorYears, protectionLevel) rather than assume. No ambiguity about when to call this tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
equity_funding_planARead-onlyIdempotentInspect
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 https://optionsahoy.com/beta?src=mcp_multi.
| Name | Required | Description | Default |
|---|---|---|---|
| lots | No | Legacy 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). | |
| stacks | No | 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. | |
| stateCode | Yes | Two-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). | |
| targetDate | Yes | Date 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. | |
| currentPrice | No | Legacy single-stack current share price, USD. Pair with legacy `lots` (omit `stacks`). This value must come from the user. | |
| filingStatus | Yes | Federal filing status. Drives LTCG brackets, NIIT threshold ($200K single / $250K MFJ MAGI), and state bracket lookups. | |
| ordinaryIncome | Yes | Annual 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. | |
| targetAfterTax | Yes | Net 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. | |
| cashInterestRate | No | 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). | |
| defaultVolatility | No | 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. | |
| expectedAnnualGrowth | No | Legacy 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. | |
| riskToleranceShortfall | No | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
| balanced | Yes | Bracket-aware spread across all candidate years: minimum tax. |
| frontier | Yes | All candidate plans from the hybrid lock-in sweep plus the named plans, sorted by shortfall probability. |
| lockInNow | Yes | Sell everything needed in the current calendar year: minimum price risk, usually highest tax. |
| recommended | Yes | The 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. |
| holdForGrowth | Yes | Sell only in the target year: maximum expected wealth, maximum price risk. |
| targetDateISO | Yes | Echo of the target date as an ISO date string. |
| targetAfterTax | Yes | Echo of the requested net cash target in dollars. |
| appliedRiskTolerance | Yes | Shortfall-probability tolerance actually applied (default 0.10 when not supplied). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already mark the tool read-only and idempotent; the description adds behavioral guarantees beyond that: determinism ('no randomness and no model inference'), explicit error behavior for missing required fields ('returns an error naming the field rather than an estimated result'), and no provenance checks on numeric inputs. This is valuable context that annotations do not provide.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured and front-loaded, with the use case, output plans, alternatives, example, and parameter semantics arranged logically. However, it is somewhat verbose: 'Use this when' is repeated, and the beta-link sentence at the end is not needed for tool selection. Still, for a 12-parameter tool the density of useful detail justifies the length.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex tool with 12 parameters, the description covers the purpose, when to use it, alternatives, out-of-scope items, a concrete example, error behavior, and parameter semantics. It intentionally defers the output shape to the outputSchema, which is appropriate since one exists. No major gaps remain.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage of parameters is 100%, so the baseline is 3. The description adds meaning beyond the schema by explaining how `expectedAnnualGrowth` can be a decimal, the string 'market', or resolved from a ticker, and by clarifying that omitting growth is a hard error. It also explains the `stacks` vs legacy `lots` distinction and that required fields have no built-in defaults.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
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 names the tool's core outputs: four named plans (`lockInNow`, `balanced`, `holdForGrowth`, `recommended`) plus a `frontier` sweep. It explicitly differentiates from siblings by directing upstream tax math to `rsu_sell_vs_hold` / `nso_calculate` / `amt_iso_optimize` and QSBS to `qsbs_check`.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit 'Use this when' statements appear twice, defining the intended scenario and giving concrete exclusions ('Out of scope: FICA, AMT, QSBS routing'). It also names sibling alternatives to call first, so the agent knows when this tool is the right choice versus others.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
nso_calculateARead-onlyIdempotentInspect
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 exercise, hold, sellNowInvest (the counterfactual: sell at exercise and reinvest at expectedMarketReturn), holdMinusCashless (the dollar delta between those two), and bracketJump; see outputSchema for the full shape. NSO hold is always long-term (sub-1-year is out of scope), so there is no separate long-term flag. 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. Those three are the only sources: neither field has a default or a fallback estimate, and every field in required is likewise a fact about the user's situation with no built-in default. A call that neither supplies nor resolves growth or volatility 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 https://optionsahoy.com/beta?src=mcp_multi.
| Name | Required | Description | Default |
|---|---|---|---|
| shares | Yes | Non-qualified Stock Option (NSO) shares to exercise. Must come from the user. | |
| strike | Yes | Strike price per share, USD. Must come from the user. | |
| ticker | No | 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 the implied vol as of the last close for any unsupplied volatility. Growth and vol come from different sources, so some symbols resolve only one, and a vol that is not current resolves as nothing. A field the ticker cannot resolve falls through to a "required field" error naming that field: pass it 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 growth. | |
| haircut | No | Alternative 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. | |
| holdYears | Yes | Years 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. | |
| stateCode | Yes | Two-letter US state code. Drives state ordinary and LTCG treatment. | |
| volatility | No | 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 as of the last market close; if neither supplies it, ask the user rather than estimating one. | |
| holdFunding | Yes | 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. | |
| currentPrice | Yes | Current fair market value per share, USD. The bargain element at exercise is shares × (currentPrice − strike). Must come from the user. | |
| filingStatus | Yes | Federal filing status. Drives ordinary brackets and LTCG brackets used at the hold horizon. | |
| stillEmployed | Yes | True if still employed at exercise. FICA (Social Security + Medicare + Additional Medicare) applies only when true. | |
| ordinaryIncome | Yes | Annual 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. | |
| expectedSalePrice | No | Projected $/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. | |
| expectedMarketReturn | No | Annual after-tax-proceeds reinvestment rate. Defaults to SPY trailing CAGR for holdYears if omitted; the string "market" names that same default explicitly. |
Output Schema
| Name | Required | Description |
|---|---|---|
| hold | Yes | Exercise now and hold the shares holdYears for long-term capital gains treatment. |
| exercise | Yes | Tax bill at exercise on the bargain element (taxed as ordinary W-2 income). |
| bracketJump | Yes | Marginal federal bracket change caused by the new ordinary income; null when the income stays within one bracket. |
| sellNowInvest | Yes | Counterfactual: sell every share at exercise and reinvest the net cash at expectedMarketReturn for holdYears. |
| holdMinusCashless | Yes | hold.netAtYearN - sellNowInvest.netAtYearN in dollars. Positive favors holding the shares; negative favors selling at exercise and reinvesting. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, idempotentHint=true, destructiveHint=false, and the description goes well beyond them: it explains the tool is deterministic and offline, that tax tables are compiled in, that hold is always long-term, that it returns required-field errors for missing growth/volatility, that it has no defaults, and that results are independent single-position calculations. This is rich, accurate behavioral disclosure with no contradiction.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but well-structured: purpose and outputs first, usage and alternatives next, then detailed parameter semantics and caveats. Every sentence carries important information, and the density is justified by the tool's complexity (14 params, many edge cases). While not concise in the strict sense, it is efficiently organized and front-loaded, so the essential purpose appears immediately.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the high parameter count (14), complex tax calculations, and a provided output schema, the description is exceptionally complete. It covers all input sources (user, ticker, 'market'), error behavior, no-default policies, funding methods, and even points to verification links and multi-tool integration. Nothing an agent needs to call this correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Although schema coverage is 100%, the description adds substantial meaning beyond the schema. It explains the 'market' sentinel for expectedSalePrice, that volatility must be the raw sigma not a drag, that haircut is an alternative and horizon-dependent, that holdYears changes the answer and should be asked for if absent, and the cash vs sell-to-cover implications including the cashNeededAtExercise output. These clarifications materially improve parameter understanding.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource ('After-tax payout on a non-qualified stock option (NSO) exercise') and clearly distinguishes from siblings: 'Use for NSOs; for ISOs use `amt_iso_optimize`, for RSUs use `rsu_sell_vs_hold`.' It also lists the key outputs (exercise, hold, sellNowInvest, holdMinusCashless, bracketJump), so an agent knows exactly what this tool does and how it differs.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states when to use it ('Use this when someone asks whether to exercise and hold or exercise and sell non-qualified stock options...') and when not to, naming the alternative tools for ISOs and RSUs. Also covers edge cases like 'sub-1-year is out of scope' and warns against inventing inputs. The guidance is unambiguous and actionable.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
protective_put_priceARead-onlyIdempotentInspect
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. For concentration-vs-hedge tax-cost comparison, use concentration_analyze with a hedgeChoice. Parameter interactions: volatility omitted resolves from ticker, else 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 (when a chain resolves it defaults to that stock's trailing annualized return, else to the sector's long-run return; never 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 and deterministic. With a ticker and no explicit volatility, each leg prices at its own strike's implied volatility off that stock's live chain (pricingMode "chain-skew"); otherwise all legs price at one sigma ("flat"), understating out-of-the-money protection. 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). 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 https://optionsahoy.com/beta?src=mcp_multi.
| Name | Required | Description | Default |
|---|---|---|---|
| sector | Yes | Sector tag. Drives the default volatility when no explicit `volatility` is supplied (a sector-typical implied volatility). | |
| ticker | No | Optional public-stock symbol (e.g. "NVDA"). When set without an explicit `volatility`, each leg prices at its own strike's chain-implied vol, else at its at-the-money vol as of the last close, else the sector default. Echoed to `tickerLabel` in the response. | |
| tenorYears | Yes | Option 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. | |
| volatility | No | Annualized implied volatility (sigma) of the stock. Resolution order: (1) explicit `volatility` if passed; (2) the `ticker`'s implied vol as of the last close; (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. | |
| tickerLabel | No | Optional display string echoed back in the result. Not used in pricing. | |
| positionValue | Yes | Market value of the underlying single-stock position, USD. Premium and max-loss scale linearly with this. Must come from the user. | |
| expectedReturn | No | 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. | |
| protectionLevel | Yes | Put 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. | |
| spreadRiskLevel | No | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
| collar | Yes | Put financed by a short call: lower or zero net premium in exchange for capped upside. |
| inputs | Yes | Echo of the resolved inputs actually priced: positionValue, sector, volatility (the sigma priced), volatilitySource (where that sigma came from) and pricingMode (how the legs were priced), protectionLevel, tenorYears, plus expectedReturn, spreadRiskLevel, and tickerLabel when supplied. |
| barePut | Yes | Bare protective put: pay premium for a hard floor. |
| putSpread | Yes | 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. |
| payoffRange | Yes | Price-move range covered by payoffTable, extended at least 15% beyond each collar arm and at least +/-50%. |
| payoffTable | Yes | 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. |
| recommended | Yes | 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. |
| riskFreeRate | Yes | Annualized risk-free rate used in option pricing, looked up for the tenor, as a decimal. |
| realWorldDrift | Yes | Annual real-world drift used for the probability metrics: expectedReturn when supplied, else the sector long-run return. Does not affect premium math. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/destructive/idempotent hints, but the description adds substantial relevant behavior: deterministic closed-form pricing with no randomness, chain-skew vs flat volatility resolution, the effect of omitted vs explicit `volatility`, how `tenorYears` affects probabilities, how `expectedReturn` only affects probability metrics, and full response-structure disclosure. It also warns that arbitrary numeric inputs are accepted without provenance checks.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but well-organized with clear subsections (parameter interactions, pricing modes, return keys, example call, required-field semantics) and front-loads the most important usage guidance. Almost every sentence adds unique information, but the sheer length and the mention of the beta integration make it slightly less concise than ideal.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (three pricing structures, nine parameters, nontrivial parameter interactions), the description is remarkably complete: it explains when to use it, how to select parameters, what each output block contains, potential limitations, and how it relates to other tools. The presence of an output schema reduces the burden of explaining return values, but the description still provides useful key-level detail.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Even though the schema already documents all 9 parameters at 100% coverage, the description adds significant meaning beyond it: parameter interaction rules ('volatility omitted resolves from ticker, else sector IV'), how `protectionLevel` maps to strike, how `spreadRiskLevel` targets the probability of breaching the short strike, and the outcome of omitting or supplying `upsideCapPct` in collars. It also provides a complete example invocation.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: 'closed-form pricing of a protective put, a zero-cost collar, and a put spread.' It also lists concrete user phrasings that should trigger this tool and explicitly distinguishes it from `concentration_analyze`, making it easy for an agent to select correctly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It opens with explicit use-case language ('Use this when someone asks how much it costs to hedge...'), and explicitly names the alternative for a different use case ('For concentration-vs-hedge tax-cost comparison, use `concentration_analyze`'). It also gives a concrete example call that demonstrates valid usage.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
qsbs_checkARead-onlyIdempotentInspect
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 https://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.
| Name | Required | Description | Default |
|---|---|---|---|
| industry | Yes | §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. | |
| saleDate | Yes | Planned or actual sale date (YYYY-MM-DD). Together with acquisitionDate determines holdingYears. | |
| stateCode | Yes | 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. | |
| entityType | Yes | §1202 Test 1: Type of issuer at the time of acquisition. Only 'us-c-corp' qualifies. S-corps, LLCs, partnerships, and foreign entities fail. | |
| expectedGain | Yes | Expected total gain on sale, USD. Compared against the per-issuer exclusion cap to compute excludableGain and taxableGain. Must come from the user. | |
| filingStatus | Yes | Federal filing status. Drives the LTCG bracket on any non-excluded gain and the NIIT MAGI threshold. | |
| adjustedBasis | Yes | Adjusted 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. | |
| assetCategory | Yes | §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. | |
| activeBusiness | Yes | §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). | |
| ordinaryIncome | Yes | Annual 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. | |
| acquisitionDate | Yes | 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). | |
| acquisitionMethod | Yes | §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
| Name | Required | Description |
|---|---|---|
| era | Yes | 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). |
| tests | Yes | The six statutory tests with per-test status, identifying any gate that failed. |
| verdict | Yes | 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. |
| stateNote | No | Per-state conformity explanation. May be omitted. |
| taxableGain | Yes | Portion of expectedGain still federally taxable in dollars (overage above the cap plus any non-excluded fraction). |
| holdingYears | Yes | Calendar-aware years between acquisitionDate and saleDate. |
| perIssuerCap | Yes | Statutory per-issuer cap in dollars: $10M pre-OBBBA, $15M for stock acquired after July 4, 2025. |
| tenXBasisCap | Yes | 10 x adjustedBasis cap in dollars. |
| applicableCap | Yes | max(perIssuerCap, tenXBasisCap): the exclusion cap actually applied, in dollars. |
| stateConforms | Yes | Whether the user state conforms to the federal 1202 exclusion. |
| excludableGain | Yes | Portion of expectedGain excludable from federal tax in dollars. |
| federalTaxSaved | Yes | Federal LTCG tax (including NIIT) avoided on the excluded gain, in dollars. |
| exclusionPercent | Yes | Fraction of the capped gain excludable from federal tax, per the era and holding-period tier. |
| cappedOverageNote | No | 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. |
| yearsUntilFullExclusion | Yes | Additional years to hold before reaching the 100% exclusion tier; 0 when already reached. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent, but the description adds substantial behavioral detail: 'Pure stateless check: no filing, reporting, or IRS lookup happens', 'The math runs inside the tool with no randomness and no model inference', error behavior on missing required fields, and the meaning of 'unsure' values. This goes beyond the annotations 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with use-case guidance and organized into coherent paragraphs, but it is very long and contains redundancy ('Use this when...' followed by 'Use this tool for §1202 / QSBS qualification') and promotional content (beta link). While thoroughness is warranted for a complex statutory tool, it is not as concise as it could be.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers all six statutory tests, era date ranges, per-issuer cap formula, return object keys, example call, state conformity rules, and error behavior. With a complete output schema and detailed annotations, the description still adds significant context, making it fully complete for tool selection and invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% but the description adds crucial interaction semantics: entityType='other' short-circuits to disqualified, acquisitionMethod='secondary' likewise, assetCategory='over-75m' fails immediately; gift/inheritance tacks holding period from original holder; acquisitionDate drives era classification with specific date ranges; and the per-issuer cap formula (max($10M, 10 × adjustedBasis)) is explained. This meaningfully supplements the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'Use this when someone asks whether stock qualifies for the qualified small business stock (QSBS) / Section 1202 gain exclusion' and identifies itself as a 'Section 1202 Qualified Small Business Stock (QSBS) qualification check.' It also distinguishes from sibling tools by directing AMT timing questions to amt_iso_optimize.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states when to use ('Use this when someone asks...') and names a specific alternative ('For AMT timing on the ISO exercise that produced the QSBS holding, use amt_iso_optimize first'). It also explains parameter short-circuits (e.g., entityType='other' immediately disqualifies), giving clear usage context.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
rsu_lot_optimizeARead-onlyIdempotentInspect
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 https://optionsahoy.com/beta?src=mcp_multi.
| Name | Required | Description | Default |
|---|---|---|---|
| lots | Yes | The 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. | |
| stateCode | Yes | Two-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). | |
| currentPrice | Yes | Current share price, USD. Every sale, on every date, is priced at this value (flat-price assumption). This value must come from the user. | |
| filingStatus | Yes | Federal filing status. Drives LTCG brackets, the NIIT threshold, and state bracket lookups. | |
| horizonYears | Yes | Tax 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. | |
| divestFraction | Yes | Fraction 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. | |
| ordinaryIncome | Yes | Total 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
| Name | Required | Description |
|---|---|---|
| schedule | Yes | The sell plan, grouped by tax year. |
| totalTax | Yes | Total plan tax across all years (federal LTCG + NIIT + state, net of in-plan loss carryforward), in dollars. |
| totalGross | Yes | Gross proceeds from the divested shares, in dollars. |
| attribution | Yes | Telescoping attribution of the total saving vs a FIFO-all-today sale. lotSelection + spreadingDeferral = total. |
| totalShares | Yes | Total shares across all input lots. |
| horizonCards | Yes | The same divest target under a 1-year ("all now"), 2-year, and 3-year plan, for the trade-off strip. |
| sharesToSell | Yes | Shares the plan divests (round(divestFraction x totalShares), floored at 1). |
| totalAfterTax | Yes | After-tax proceeds from the divested shares, in dollars. |
| deferralCallouts | Yes | Per-lot short-term-to-long-term deferral opportunities. |
| keptUnrealizedGain | Yes | Unrealized gain still carried by the shares NOT sold, in dollars (deferred, not eliminated). |
| headlineDeltaVsFifo | Yes | Dollars saved versus selling oldest-first (FIFO) on the SAME schedule. Pure lot-selection benefit; >= 0 by construction. |
| headlineAfterTaxKept | Yes | After-tax proceeds under the plan, in dollars: the headline "you keep $X" figure. |
| carryforwardRemaining | Yes | Capital loss remaining at the end of the plan horizon, in dollars (reported, not modeled into future years). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint/idempotentHint/destructiveHint, and the description adds substantial behavioral context beyond them: the flat-price pricing assumption, no growth model, no randomness or model inference, explicit error behavior for missing required fields, acceptance of unverified numbers, and the independence of multi-tool calculations. This far exceeds the transparency burden and does not contradict any annotation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but well-structured: trigger, levers, assumptions, outputs, sibling differentiation, out-of-scope, example, and validation behavior. It includes some redundancy (e.g., 'no randomness and no model inference') and a beta link that is tangential, but every major section earns its place for a tool of this complexity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description is exceptionally complete for a complex 7-parameter tool: it covers the algorithm's three levers, the flat-price assumption, the return schedule structure, FIFO comparison, horizon trade-off, per-lot callouts, missing-field error behavior, alternatives, out-of-scope topics, and includes a full example. Since an output schema exists, return-value documentation is doubly covered.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema has 100% parameter descriptions, so the baseline is 3. The description adds no new per-parameter meaning; it restates the flat-price assumption and the decimal-fraction instruction already present in the schema, and the parameter names and schema descriptions already carry the semantics clearly.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: it 'chooses WHICH lots and WHICH sale dates minimize computed total tax to divest that many shares', clearly naming the three optimization levers. It also explicitly distinguishes itself from siblings by stating 'This tool owns WHICH LOTS and WHICH DATES' and pointing to concentration_analyze, rsu_sell_vs_hold, and equity_funding_plan for adjacent questions.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description opens with 'Use this when someone asks which vested RSU lots to sell first, in which years' and then gives explicit alternatives: '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'. It also lists out-of-scope topics, leaving no ambiguity.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
rsu_sell_vs_holdARead-onlyIdempotentInspect
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. Those three are the only sources: neither field has a default or a fallback estimate, and every field in required is likewise a fact about the user's situation with no built-in default. A call that neither supplies nor resolves growth or volatility 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 https://optionsahoy.com/beta?src=mcp_multi.
| Name | Required | Description | Default |
|---|---|---|---|
| shares | Yes | Restricted Stock Unit (RSU) shares vesting in this tranche. Must come from the user. | |
| ticker | No | 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 the implied vol as of the last close for any unsupplied volatility. Growth and vol come from different sources, so some symbols resolve only one, and a vol that is not current resolves as nothing. A field the ticker cannot resolve falls through to a "required field" error naming that field: pass it 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 growth. | |
| haircut | No | Alternative 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. | |
| holdYears | Yes | Years 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. | |
| stateCode | Yes | Two-letter US state code. | |
| volatility | No | 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 as of the last market close; if neither supplies it, ask the user rather than estimating one. | |
| currentPrice | Yes | Fair market value per share at vest, USD. Also the cost basis on retained shares. Must come from the user. | |
| filingStatus | Yes | Federal filing status. | |
| stillEmployed | Yes | True if still employed at vest. Drives FICA applicability and whether the 22% supplemental withholding rule applies. | |
| ordinaryIncome | Yes | Annual 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. | |
| expectedSalePrice | No | Projected $/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. | |
| expectedMarketReturn | No | Annual after-tax-proceeds reinvestment rate. Defaults to SPY trailing CAGR for holdYears if omitted; the string "market" names that same default explicitly. |
Output Schema
| Name | Required | Description |
|---|---|---|
| hold | Yes | Keep the after-tax shares for holdYears, then sell. |
| vest | Yes | Tax bill at vest on the full vest value (taxed as ordinary W-2 income). |
| bracketJump | Yes | Marginal federal bracket change caused by the new ordinary income; null when the income stays within one bracket. |
| holdMinusSell | Yes | hold.netAtYearN - sellNowInvest.netAtYearN in dollars. Positive favors holding the vested shares; negative favors selling at vest and reinvesting. |
| sellNowInvest | Yes | Counterfactual: sell every share at vest and reinvest the net cash at expectedMarketReturn for holdYears. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare readOnlyHint=true, openWorldHint=false, idempotentHint=true, destructiveHint=false. The description reinforces this with 'Deterministic and offline; tax tables compiled in.' It adds context on error behavior (required-field errors when growth/volatility are unresolved), the lack of defaults, and the independence of multi-tool results. 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Though long, the description is front-loaded with the primary use case and routing, and every sentence carries actionable information—ranging from output field names, an example call, input resolution rules, and verification links. The length is proportionate to the tool's 12-parameter complexity and the nuanced conditions (e.g., required vs optional, sourcing rules). No filler re-states schema content.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The output schema exists, and the description references it for the full shape. The description covers all critical aspects: triggers, modeling boundaries (federal/state/FICA/tax), alternatives, input resolution strategies, error conditions, and cross-tool integration notes. Nothing an agent needs to invoke the tool correctly is left implicit.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, but the description adds substantial value beyond each field's schema definition: it explains the interplay between `volatility` and `haircut`, the meaning of the `market` sentinel for expected sale price and market return, the fallthrough logic with `ticker`, the user-driven nature of `holdYears`, and the definition of `ordinaryIncome` as after-deductions. This significantly improves an agent's ability to pass correct values.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with explicit trigger phrases ('Use this when someone asks whether to sell RSUs at vest or hold them...'), names the specific comparison (after-tax sell-at-vest vs hold-to-LTCG), and lists the key output fields. It distinguishes from siblings by naming `amt_iso_optimize` and `nso_calculate` as alternatives for other stock types, leaving no ambiguity about scope.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly states when to use this tool: 'Use for RSUs at vest; for ISO/AMT use `amt_iso_optimize`, for NSO use `nso_calculate`.' It also describes the input contract, including what must be supplied or resolved, and what happens if not, giving the agent clear decision rules for invocation.
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 tool update
- Changed
protective_put_price6 fields changed- changed
Input schema / properties / ticker / descriptionPrevious value: -"Optional public-stock symbol (e.g. \"NVDA\"). When set without an explicit `volatility`, the tool substitutes the ticker's implied vol as of the last close, else the sector default. Echoed to `tickerLabel` in the response."New value: +"Optional public-stock symbol (e.g. \"NVDA\"). When set without an explicit `volatility`, each leg prices at its own strike's chain-implied vol, else at its at-the-money vol as of the last close, else the sector default. Echoed to `tickerLabel` in the response." - changed
Output schema / properties / inputs / descriptionPrevious value: -"Echo of the resolved inputs actually priced: positionValue, sector, volatility (the sigma priced) and volatilitySource (where that sigma came from), protectionLevel, tenorYears, plus expectedReturn, spreadRiskLevel, and tickerLabel when supplied."New value: +"Echo of the resolved inputs actually priced: positionValue, sector, volatility (the sigma priced), volatilitySource (where that sigma came from) and pricingMode (how the legs were priced), protectionLevel, tenorYears, plus expectedReturn, spreadRiskLevel, and tickerLabel when supplied." - added
Output schema / properties / inputs / properties / pricingModeAdded value: +{ + "description": "How the legs were priced. \"chain-skew\": each leg is priced at the implied volatility of its own strike, read off the live chain, so the floor put carries the market's downside skew and the put spread's short leg carries its own. \"flat\": every leg is priced at the single `volatility` above, which understates what out-of-the-money protection costs and overstates the rebate the spread's short leg earns - the quote is an estimate of this structure's cost, not a strike-aware one. Reached whenever no live chain applies: an explicit `volatility`, no `ticker`, or a chain that could not be fetched or was not current.", + "enum": [ + "chain-skew", + "flat" + ], + "type": "string" +} - changed
Output schema / properties / inputs / properties / volatilitySource / descriptionPrevious value: -"Which source produced the sigma actually priced: \"explicit\" (caller-supplied), \"ticker\" (the stock's own implied vol as of the last close), or \"sector-default\" (feed unavailable or ticker uncovered; a sector-typical estimate - tell the user the price is not stock-specific)."New value: +"Which source produced the sigma actually priced: \"explicit\" (caller-supplied), \"chain\" (interpolated from the stock's live option chain at the strike being priced), \"ticker\" (the stock's published at-the-money implied vol as of the last close), or \"sector-default\" (feed unavailable or ticker uncovered; a sector-typical estimate - tell the user the price is not stock-specific)." - changed
Output schema / properties / inputs / properties / volatilitySource / enumPrevious value: -[ - "explicit", - "ticker", - "sector-default" -]New value: +[ + "explicit", + "ticker", + "sector-default", + "chain" +] - changed
Output schema / properties / inputs / requiredPrevious value: -[ - "positionValue", - "sector", - "volatility", - "volatilitySource", - "protectionLevel", - "tenorYears" -]New value: +[ + "positionValue", + "sector", + "volatility", + "volatilitySource", + "pricingMode", + "protectionLevel", + "tenorYears" +]
1 tool update
- Changed
protective_put_price3 fields changed- changed
Output schema / properties / inputs / descriptionPrevious 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."New value: +"Echo of the resolved inputs actually priced: positionValue, sector, volatility (the sigma priced) and volatilitySource (where that sigma came from), protectionLevel, tenorYears, plus expectedReturn, spreadRiskLevel, and tickerLabel when supplied." - added
Output schema / properties / inputs / properties / volatilitySourceAdded value: +{ + "description": "Which source produced the sigma actually priced: \"explicit\" (caller-supplied), \"ticker\" (the stock's own implied vol as of the last close), or \"sector-default\" (feed unavailable or ticker uncovered; a sector-typical estimate - tell the user the price is not stock-specific).", + "enum": [ + "explicit", + "ticker", + "sector-default" + ], + "type": "string" +} - changed
Output schema / properties / inputs / requiredPrevious value: -[ - "positionValue", - "sector", - "volatility", - "protectionLevel", - "tenorYears" -]New value: +[ + "positionValue", + "sector", + "volatility", + "volatilitySource", + "protectionLevel", + "tenorYears" +]
5 tool updates
- Changed
amt_iso_optimize2 fields changed- changed
Input schema / properties / ticker / descriptionPrevious 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."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 the implied vol as of the last close for any unsupplied volatility. Growth and vol come from different sources, so some symbols resolve only one, and a vol that is not current resolves as nothing. A field the ticker cannot resolve falls through to a \"required field\" error naming that field: pass it 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 growth." - changed
Input schema / properties / volatility / descriptionPrevious 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."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 as of the last market close; if neither supplies it, ask the user rather than estimating one."
- Changed
concentration_analyze2 fields changed- changed
Input schema / properties / ticker / descriptionPrevious 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."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 the implied vol as of the last close for any unsupplied volatility. Growth and vol come from different sources, so some symbols resolve only one, and a vol that is not current resolves as nothing. A field the ticker cannot resolve falls through to a \"required field\" error naming that field: pass it 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 growth." - changed
Input schema / properties / volatility / descriptionPrevious value: -"Annualized 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."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 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 as of the last market close; 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."
- Changed
nso_calculate2 fields changed- changed
Input schema / properties / ticker / descriptionPrevious 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."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 the implied vol as of the last close for any unsupplied volatility. Growth and vol come from different sources, so some symbols resolve only one, and a vol that is not current resolves as nothing. A field the ticker cannot resolve falls through to a \"required field\" error naming that field: pass it 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 growth." - changed
Input schema / properties / volatility / descriptionPrevious 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."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 as of the last market close; if neither supplies it, ask the user rather than estimating one."
- Changed
protective_put_price2 fields changed- changed
Input schema / properties / ticker / descriptionPrevious value: -"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."New value: +"Optional public-stock symbol (e.g. \"NVDA\"). When set without an explicit `volatility`, the tool substitutes the ticker's implied vol as of the last close, else the sector default. Echoed to `tickerLabel` in the response." - changed
Input schema / properties / volatility / descriptionPrevious 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. An explicit value must come from the user; otherwise set a covered `ticker`, or omit and let the sector default apply."New value: +"Annualized implied volatility (sigma) of the stock. Resolution order: (1) explicit `volatility` if passed; (2) the `ticker`'s implied vol as of the last close; (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."
- Changed
rsu_sell_vs_hold2 fields changed- changed
Input schema / properties / ticker / descriptionPrevious 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."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 the implied vol as of the last close for any unsupplied volatility. Growth and vol come from different sources, so some symbols resolve only one, and a vol that is not current resolves as nothing. A field the ticker cannot resolve falls through to a \"required field\" error naming that field: pass it 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 growth." - changed
Input schema / properties / volatility / descriptionPrevious 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."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 as of the last market close; if neither supplies it, ask the user rather than estimating one."
1 tool update
- Changed
equity_funding_plan1 field changed- removed
Input schema / anyOfRemoved value: -[ - { - "required": [ - "stacks" - ] - }, - { - "required": [ - "lots", - "currentPrice" - ] - } -]
1 tool update
- Changed
equity_funding_plan2 fields changed- changed
Input schema / properties / stacks / items / properties / currentPrice / descriptionPrevious value: -"$/share today for this stack. Anchors the projected-price compounding for every future candidate sale date in this stack."New value: +"$/share today for this stack. Anchors the projected-price compounding for every future candidate sale date in this stack. Must come from the user." - changed
Input schema / properties / targetDate / descriptionPrevious value: -"Date 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."New value: +"Date 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."
1 tool update
- Changed
qsbs_check1 field changed- changed
Input schema / properties / industry / descriptionPrevious 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."New value: +"§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."
8 tool updates
- Changed
amt_iso_optimize17 fields changed- changed
Input schema / properties / carryforwardCredit / descriptionPrevious 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." - changed
Input schema / properties / cashReturnRate / descriptionPrevious 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." - changed
Input schema / properties / expectedGrowth / descriptionPrevious 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."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." - changed
Input schema / properties / fmv / descriptionPrevious 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." - changed
Input schema / properties / horizon / descriptionPrevious 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." - changed
Input schema / properties / ordinaryIncome / descriptionPrevious 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." - changed
Input schema / properties / shares / descriptionPrevious 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." - changed
Input schema / properties / strike / descriptionPrevious value: -"Strike price per share, USD."New value: +"Strike price per share, USD. Must come from the user." - changed
Input schema / properties / volatility / descriptionPrevious 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." - changed
Input schema / properties / volatilityDrag / descriptionPrevious 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." - changed
Output schema / properties / departedRecommendation / descriptionPrevious 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." - changed
Output schema / properties / departedRecommendation / properties / recommendedSchedule / properties / nfv / descriptionPrevious 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." - changed
Output schema / properties / departedRecommendation / properties / recommendedShares / descriptionPrevious value: -"Optimal share count to exercise within the window."New value: +"Share count to exercise within the window, the best found by the scan." - changed
Output schema / properties / schedules / descriptionPrevious 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." - changed
Output schema / properties / schedules / properties / evenSplit / properties / nfv / descriptionPrevious 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." - changed
Output schema / properties / schedules / properties / lumpSum / properties / nfv / descriptionPrevious 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." - changed
Output schema / properties / schedules / properties / optimized / properties / nfv / descriptionPrevious 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."
- Changed
concentration_analyze9 fields changed- changed
Input schema / properties / costBasis / descriptionPrevious value: -"Total cost basis of the position, USD (sum of strikes paid + ordinary-income inclusions on RSU vest / NSO exercise / disqualified ISO)."New value: +"Total 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." - changed
Input schema / properties / expectedPositionReturn / descriptionPrevious value: -"Annual 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."New value: +"Annual 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." - changed
Input schema / properties / hedgeChoice / properties / protectionLevel / descriptionPrevious value: -"Put strike chosen as (1 − this fraction) × spot. 0.10 = 10% OTM put. Range 0.05..0.50."New value: +"Put strike chosen 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." - changed
Input schema / properties / hedgeChoice / properties / tenorYears / descriptionPrevious value: -"Option tenor in years. 1 = 12-month; 0.25 = ~90-day."New value: +"Option 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." - changed
Input schema / properties / ordinaryIncome / descriptionPrevious value: -"Annual W-2 ordinary income before any sales, USD. Baseline for LTCG bracket determination."New value: +"Annual 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." - changed
Input schema / properties / positionValue / descriptionPrevious value: -"Current market value of the concentrated single-stock position, USD."New value: +"Current market value of the concentrated single-stock position, USD. Must come from the user." - changed
Input schema / properties / totalAssets / descriptionPrevious value: -"Total investable portfolio in dollars (concentrated position + everything else). User-supplied; never inferred. If the user did not state it, ASK."New value: +"Total 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." - changed
Input schema / properties / volatility / descriptionPrevious 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."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 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." - changed
Input schema / properties / volatilityDrag / descriptionPrevious value: -"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."New value: +"Alternative 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."
- Changed
equity_funding_plan4 fields changed- changed
Input schema / properties / currentPrice / descriptionPrevious value: -"Legacy single-stack current share price, USD. Pair with legacy `lots` (omit `stacks`). The model SHOULD NOT invent this; pass the user's current price."New value: +"Legacy single-stack current share price, USD. Pair with legacy `lots` (omit `stacks`). This value must come from the user." - changed
Input schema / properties / ordinaryIncome / descriptionPrevious value: -"Annual W-2 ordinary income, USD. Used as the baseline for the federal LTCG bracket walk in each candidate year and for NIIT threshold tests."New value: +"Annual 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." - changed
Input schema / properties / targetAfterTax / descriptionPrevious value: -"Net 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."New value: +"Net 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." - changed
Output schema / properties / recommended / descriptionPrevious value: -"The wealth-maximal plan whose shortfall probability is at or below the applied risk tolerance. Present this plan first."New value: +"The 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."
- Changed
nso_calculate8 fields changed- changed
Input schema / properties / currentPrice / descriptionPrevious value: -"Current fair market value per share, USD. The bargain element at exercise is shares × (currentPrice − strike)."New value: +"Current fair market value per share, USD. The bargain element at exercise is shares × (currentPrice − strike). Must come from the user." - changed
Input schema / properties / expectedSalePrice / descriptionPrevious value: -"Projected $/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."New value: +"Projected $/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." - changed
Input schema / properties / haircut / descriptionPrevious value: -"Alternative to `volatility`: the multiplicative volatility-drag haircut on expectedSalePrice already computed for the hold. Supply this OR `volatility` (if both are given, haircut wins). Most callers should pass `volatility` and let the tool compute the haircut; the model MUST NOT compute it itself."New value: +"Alternative 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." - changed
Input schema / properties / holdYears / descriptionPrevious 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."New 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. 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." - changed
Input schema / properties / ordinaryIncome / descriptionPrevious value: -"Annual W-2 ordinary income before this exercise, USD. Baseline for the bracket walk on the bargain element."New value: +"Annual 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." - changed
Input schema / properties / shares / descriptionPrevious value: -"Non-qualified Stock Option (NSO) shares to exercise."New value: +"Non-qualified Stock Option (NSO) shares to exercise. Must come from the user." - changed
Input schema / properties / strike / descriptionPrevious value: -"Strike price per share, USD."New value: +"Strike price per share, USD. Must come from the user." - changed
Input schema / properties / volatility / descriptionPrevious 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."
- Changed
protective_put_price5 fields changed- changed
Input schema / properties / positionValue / descriptionPrevious value: -"Market value of the underlying single-stock position, USD. Premium and max-loss scale linearly with this."New value: +"Market value of the underlying single-stock position, USD. Premium and max-loss scale linearly with this. Must come from the user." - changed
Input schema / properties / protectionLevel / descriptionPrevious value: -"Put strike as (1 − this fraction) × spot. 0.10 = 10% OTM put. Range 0.05..0.50."New value: +"Put 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." - changed
Input schema / properties / tenorYears / descriptionPrevious value: -"Option tenor in years. 1 = 12-month; 0.25 = ~90-day."New value: +"Option 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." - changed
Input schema / properties / volatility / descriptionPrevious 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."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. An explicit value must come from the user; otherwise set a covered `ticker`, or omit and let the sector default apply." - changed
Output schema / properties / putSpread / properties / available / descriptionPrevious value: -"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."New value: +"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, the numeric fields of this block are null and unavailableReason carries the explanation in their place."
- Changed
qsbs_check4 fields changed- changed
Input schema / properties / adjustedBasis / descriptionPrevious value: -"Adjusted basis of the QSBS shares, USD. Used in the 10× basis cap: the per-issuer exclusion cap is max($10M, 10 × adjustedBasis)."New value: +"Adjusted 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." - changed
Input schema / properties / expectedGain / descriptionPrevious value: -"Expected total gain on sale, USD. Compared against the per-issuer exclusion cap to compute excludableGain and taxableGain."New value: +"Expected total gain on sale, USD. Compared against the per-issuer exclusion cap to compute excludableGain and taxableGain. Must come from the user." - changed
Input schema / properties / ordinaryIncome / descriptionPrevious value: -"Annual W-2 ordinary income, USD. Baseline for the federal LTCG bracket on any taxable gain."New value: +"Annual 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." - changed
Output schema / properties / tests / descriptionPrevious value: -"The six 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, identifying any gate that failed."
- Changed
rsu_lot_optimize5 fields changed- changed
Input schema / properties / currentPrice / descriptionPrevious value: -"Current share price, USD. Every sale, on every date, is priced at this value (flat-price assumption). Pass the user's price; the model must not invent it."New value: +"Current share price, USD. Every sale, on every date, is priced at this value (flat-price assumption). This value must come from the user." - changed
Input schema / properties / divestFraction / descriptionPrevious value: -"Fraction 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."New value: +"Fraction 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." - changed
Input schema / properties / horizonYears / descriptionPrevious value: -"Tax 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."New value: +"Tax 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." - changed
Input schema / properties / lots / descriptionPrevious value: -"Vested RSU lots you still hold (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."New value: +"The 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." - changed
Input schema / properties / ordinaryIncome / descriptionPrevious value: -"Total household ordinary income for the year, USD (W-2 + interest + other). Sets the federal LTCG bracket floor, the short-term ordinary rate, and the net investment income tax (NIIT) threshold test. Assumed constant across plan years."New value: +"Total 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."
- Changed
rsu_sell_vs_hold7 fields changed- changed
Input schema / properties / currentPrice / descriptionPrevious value: -"Fair market value per share at vest, USD. Also the cost basis on retained shares."New value: +"Fair market value per share at vest, USD. Also the cost basis on retained shares. Must come from the user." - changed
Input schema / properties / expectedSalePrice / descriptionPrevious value: -"Projected $/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."New value: +"Projected $/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." - changed
Input schema / properties / haircut / descriptionPrevious value: -"Alternative to `volatility`: the multiplicative volatility-drag haircut on expectedSalePrice already computed for the hold. Supply this OR `volatility` (if both are given, haircut wins). Most callers should pass `volatility` and let the tool compute the haircut; the model MUST NOT compute it itself."New value: +"Alternative 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." - changed
Input schema / properties / holdYears / descriptionPrevious value: -"Years to hold after vest (0.25..5). Below 1 year triggers the short-term capital gains cliff (ordinary rates on appreciation)."New value: +"Years 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." - changed
Input schema / properties / ordinaryIncome / descriptionPrevious value: -"Annual W-2 ordinary income before this vest, USD. Baseline for the bracket walk on the vest amount."New value: +"Annual 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." - changed
Input schema / properties / shares / descriptionPrevious value: -"Restricted Stock Unit (RSU) shares vesting in this tranche."New value: +"Restricted Stock Unit (RSU) shares vesting in this tranche. Must come from the user." - changed
Input schema / properties / volatility / descriptionPrevious 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."
5 tool updates
- Changed
amt_iso_optimize3 fields changed- changed
Input schema / properties / expectedGrowth / descriptionPrevious 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." - changed
Input schema / properties / expectedGrowth / typePrevious value: -"number"New value: +[ + "number", + "string" +] - changed
Input schema / properties / ticker / descriptionPrevious 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."
- Changed
concentration_analyze5 fields changed- changed
Input schema / properties / expectedMarketReturn / descriptionPrevious value: -"Annual after-tax-proceeds reinvestment rate. Defaults to SPY trailing CAGR for the 3-year horizon if omitted."New value: +"Annual 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." - changed
Input schema / properties / expectedMarketReturn / typePrevious value: -"number"New value: +[ + "number", + "string" +] - changed
Input schema / properties / expectedPositionReturn / descriptionPrevious value: -"Annual expected return on the concentrated stock as a decimal (0.10 = 10%). Required unless `ticker` resolves it from trailing CAGR."New value: +"Annual 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." - changed
Input schema / properties / expectedPositionReturn / typePrevious value: -"number"New value: +[ + "number", + "string" +] - changed
Input schema / properties / ticker / descriptionPrevious 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."
- Changed
equity_funding_plan4 fields changed- changed
Input schema / properties / expectedAnnualGrowth / descriptionPrevious value: -"Legacy single-stack annual growth decimal. Optional; defaults to 0. Each future year's projected price is `currentPrice × (1 + expectedAnnualGrowth)^Δyears`. Negative values model decline."New value: +"Legacy 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." - changed
Input schema / properties / expectedAnnualGrowth / typePrevious value: -"number"New value: +[ + "number", + "string" +] - changed
Input schema / properties / stacks / items / properties / expectedAnnualGrowth / descriptionPrevious 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."New value: +"Per-stack growth decimal (0.08 = 8%/yr), or the string \"market\" for the S&P 500 trailing average. Projected sale price = currentPrice × (1 + expectedAnnualGrowth)^Δyears. Negative values model decline; pass 0 for a deliberately flat-price plan. Required unless `ticker` resolves it; omitting it is an error, not a flat default." - changed
Input schema / properties / stacks / items / properties / expectedAnnualGrowth / typePrevious value: -"number"New value: +[ + "number", + "string" +]
- Changed
nso_calculate5 fields changed- changed
Input schema / properties / expectedMarketReturn / descriptionPrevious value: -"Annual after-tax-proceeds reinvestment rate. Defaults to SPY trailing CAGR for holdYears if omitted."New value: +"Annual after-tax-proceeds reinvestment rate. Defaults to SPY trailing CAGR for holdYears if omitted; the string \"market\" names that same default explicitly." - changed
Input schema / properties / expectedMarketReturn / typePrevious value: -"number"New value: +[ + "number", + "string" +] - changed
Input schema / properties / expectedSalePrice / descriptionPrevious value: -"Projected $/share at end of holdYears. Required unless `ticker` resolves it from currentPrice × (1 + trailing CAGR)^holdYears."New value: +"Projected $/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." - changed
Input schema / properties / expectedSalePrice / typePrevious value: -"number"New value: +[ + "number", + "string" +] - changed
Input schema / properties / ticker / descriptionPrevious 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."
- Changed
rsu_sell_vs_hold5 fields changed- changed
Input schema / properties / expectedMarketReturn / descriptionPrevious value: -"Annual after-tax-proceeds reinvestment rate. Defaults to SPY trailing CAGR for holdYears if omitted."New value: +"Annual after-tax-proceeds reinvestment rate. Defaults to SPY trailing CAGR for holdYears if omitted; the string \"market\" names that same default explicitly." - changed
Input schema / properties / expectedMarketReturn / typePrevious value: -"number"New value: +[ + "number", + "string" +] - changed
Input schema / properties / expectedSalePrice / descriptionPrevious value: -"Projected $/share at end of holdYears. Required unless `ticker` resolves it from currentPrice × (1 + trailing CAGR)^holdYears."New value: +"Projected $/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." - changed
Input schema / properties / expectedSalePrice / typePrevious value: -"number"New value: +[ + "number", + "string" +] - changed
Input schema / properties / ticker / descriptionPrevious 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."
1 tool update
- Changed
rsu_lot_optimize2 fields changed- changed
Input schema / properties / lots / descriptionPrevious value: -"Vested RSU lots you still hold (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."New value: +"Vested RSU lots you still hold (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." - added
Input schema / properties / lots / maxItemsAdded value: +20
1 tool update
- Added
rsu_lot_optimize
5 tool updates
- Changed
amt_iso_optimize1 field changed- changed
Input schema / properties / ticker / descriptionPrevious 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."
- Changed
concentration_analyze1 field changed- changed
Input schema / properties / ticker / descriptionPrevious 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."
- Changed
equity_funding_plan1 field changed- changed
Input schema / properties / stacks / items / properties / ticker / descriptionPrevious 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."New value: +"Optional ticker label (e.g. \"NVDA\"). When set without `expectedAnnualGrowth`, growth is resolved from the cached trailing-CAGR snapshot when the symbol is covered there (see the covered-tickers resource for the current set). Echoed back in each SaleEntry for display."
- Changed
nso_calculate1 field changed- changed
Input schema / properties / ticker / descriptionPrevious 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."
- Changed
rsu_sell_vs_hold1 field changed- changed
Input schema / properties / ticker / descriptionPrevious 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."
6 tool updates
- Changed
amt_iso_optimize4 fields changed- changed
Input schema / properties / carryforwardCredit / descriptionPrevious 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." - added
Input schema / properties / volatility / maximumAdded value: +5 - added
Input schema / properties / volatilityDragAdded 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" +} - changed
Input schema / requiredPrevious value: -[ - "shares", - "strike", - "fmv", - "filingStatus", - "ordinaryIncome", - "stateCode", - "carryforwardCredit", - "horizon", - "grantDate", - "hasLeftCompany" -]New value: +[ + "shares", + "strike", + "fmv", + "filingStatus", + "ordinaryIncome", + "stateCode", + "horizon", + "grantDate", + "hasLeftCompany" +]
- Changed
concentration_analyze1 field changed- added
Input schema / properties / volatility / maximumAdded value: +5
- Changed
equity_funding_plan2 fields changed- added
Input schema / properties / defaultVolatility / maximumAdded value: +5 - added
Input schema / properties / stacks / items / properties / volatility / maximumAdded value: +5
- Changed
nso_calculate2 fields changed- added
Input schema / properties / haircutAdded value: +{ + "description": "Alternative to `volatility`: the multiplicative volatility-drag haircut on expectedSalePrice already computed for the hold. Supply this OR `volatility` (if both are given, haircut wins). Most callers should pass `volatility` and let the tool compute the haircut; the model MUST NOT compute it itself.", + "maximum": 1, + "minimum": 0, + "type": "number" +} - added
Input schema / properties / volatility / maximumAdded value: +5
- Changed
protective_put_price1 field changed- added
Input schema / properties / volatility / maximumAdded value: +5
- Changed
rsu_sell_vs_hold2 fields changed- added
Input schema / properties / haircutAdded value: +{ + "description": "Alternative to `volatility`: the multiplicative volatility-drag haircut on expectedSalePrice already computed for the hold. Supply this OR `volatility` (if both are given, haircut wins). Most callers should pass `volatility` and let the tool compute the haircut; the model MUST NOT compute it itself.", + "maximum": 1, + "minimum": 0, + "type": "number" +} - added
Input schema / properties / volatility / maximumAdded value: +5
Frequently Asked Questions
Claiming proves that you control a remote MCP connector. It does not move, proxy, or interrupt the server.
Open the connector listing, choose Claim ownership, and sign in to Glama.
Complete one verification method:
GitHub identity — fastest for official registry listings. For a namespace such as
io.github.alice/server, link the matching GitHub user, then choose Claim with GitHub. An organization namespace such asio.github.acme/serveralso needs that organization to have installed the Glama AI GitHub App and approved its permissions, because GitHub discloses organization membership only to apps it has installed. Use HTTP or DNS when it has not.HTTP challenge — works when you can deploy a public file. Generate a token, publish the exact JSON Glama shows at
/.well-known/glama.jsonon the same origin as the connector, then choose Check HTTP challenge.DNS challenge — works when you control DNS but cannot change the server. Generate a token, create the exact TXT record Glama shows, wait for it to propagate, then choose Check DNS challenge.
After verification, Glama sends a confirmation email and gives you access to listing details, thumbnails, health checks, and analytics. Keep the HTTP file or DNS record in place: Glama periodically checks it and ownership remains verified while the token is discoverable.
The HTTP ownership file has this structure:
{
"$schema": "https://glama.ai/mcp/schemas/connector.json",
"claim": "glama_claim_..."
}Claim tokens are opaque, stable, and bound to the signed-in Glama account. They contain no email address or other personal information. If Glama can no longer discover a verified HTTP or DNS token, it starts a seven-day grace period before removing claim-based access. Restore the same token during that period to keep ownership verified. Never publish an email address, Glama session token, GitHub token, or connector credential as ownership proof.
If verification fails, confirm that you copied the current token exactly. The HTTP file must be public, return valid JSON with a successful HTTP response, and stay on the connector's origin. DNS changes may need more time to propagate. A claim cannot transfer to a different origin or hostname: if the connector target changes, Glama starts the grace period and the new target must be claimed separately after the previous claim is released.
For a connector linked to the official MCP Registry, registry updates continue to replace its name, description, and URL by default. After claiming, open Manage connector and enable Use Glama listing details as the source of truth if edits made on Glama should be preserved. Categories and thumbnails are always managed on Glama; registry linkage and technical connection settings continue to sync.
Control your server's listing on Glama, including description and metadata
Access analytics and receive server usage reports
Get monitoring and health status updates for your server
Feature your server to boost visibility and reach more users
To improve your MCP server's ranking:
Claim ownership of the server listing
Complete the server profile with an accurate description and thumbnail
Provide a test profile so Glama can connect to and evaluate the server
Keep tool definitions clear and complete to earn a high Tool Definition Quality Score (TDQS)
Route real usage through the Glama Gateway; more recorded successful server uses also improve the ranking
For users:
Full audit trail – every tool call is logged with inputs and outputs for compliance and debugging
Granular tool control – enable or disable individual tools per connector to limit what your AI agents can do
Centralized credential management – store and rotate API keys and OAuth tokens in one place
Change alerts – get notified when a connector changes its schema, adds or removes tools, or updates tool definitions, so nothing breaks silently
For server owners:
Proven adoption – public usage metrics on your listing show real-world traction and build trust with prospective users
Tool-level analytics – see which tools are being used most, helping you prioritize development and documentation
Direct user feedback – users can report issues and suggest improvements through the listing, giving you a channel you would not have otherwise
The connector status is unhealthy when Glama is unable to successfully connect to the server. This can happen for several reasons:
The server is experiencing an outage
The URL of the server is wrong
Credentials required to access the server are missing or invalid
If you are the owner of this MCP connector and would like to make modifications to the listing, including providing test credentials for accessing the server, please contact support@glama.ai.
Discussions
No comments yet. Be the first to start the discussion!
Related MCP Connectors
Concentration risk analysis for tech workers with RSU comp — global (US/IN/CA/UK/EU/AU).
Deterministic US financial planning: retirement Monte Carlo, Roth conversion, RMD, tax, IRMAA, SS
Crypto cost-basis lots (FIFO/LIFO/HIFO/specific-ID), Form 8949 rows, 1099-DA diff. 5 of 8 free.
81Deterministic MLP tax engine with IRS citations. 6 tools: basis, §751, estate, projections.
Related MCP Servers
- AlicenseBqualityCmaintenance39 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).4345412MIT

cinderfi-mcpofficial
FlicenseNot gradedqualityCmaintenanceTax-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-- AlicenseNot gradedqualityCmaintenanceEnables AI assistants to analyze single-company concentration risk for tech workers with RSU compensation, providing risk scores, wealth-at-risk, and action items.74Apache 2.0
- AlicenseNot gradedqualityDmaintenanceEnables tax-loss harvesting and tax-aware portfolio rebalancing via LLM agents, allowing users to identify selling opportunities and substitute holdings based on return correlation.AGPL 3.0
Glama MCP Gateway
Add one secure layer between your agents and this server.
TDQS
Most tools are cleanly scoped to distinct instruments and decisions (ISO/AMT, NSO, RSU vest, lot selection, hedge pricing, QSBS, concentration, cash funding), and the descriptions actively cross-reference the correct tool for each scenario. The main ambiguity is among the divestment-oriented tools (rsu_lot_optimize, equity_funding_plan, concentration_analyze), though the descriptions explain their different triggers well.
Names mostly follow a readable `{domain}_{action}` snake_case pattern, e.g., amt_iso_optimize, nso_calculate, qsbs_check. The verbs vary across optimize/calculate/check/analyze/plan/sell, and equity_funding_plan and protective_put_price read more as noun phrases, so the convention is recognizable but not perfectly uniform.
With 8 tools, the set is well-scoped and each tool covers a meaningful, non-redundant computation in the equity-compensation planning domain. There are no filler tools or extreme over- nor under-provisioning.
The core single-position workflows are covered: ISO/AMT optimization, NSO exercise, RSU vest decisions, lot selection, hedging, QSBS qualification, concentration, and goal-based funding. Missing pieces are mostly edge-domain items like ESPP, disqualifying dispositions, and integrated multi-position scenarios, which are explicitly noted as out of scope or beta.