Skip to main content
Glama

Server Details

Read-only access to your own crypto portfolio across 11+ exchanges and 20+ chains: holdings, full history, FIFO cost basis and P&L, fees and income. Ask Claude or ChatGPT about your portfolio; the connector can never trade, move funds or act as a login.

Ownership verified
Status
Healthy
OAuth
Works in Glama
Last Tested
Transport
Streamable HTTP · MCP 2025-11-25
URL
Repository
khapham7165/whereitgoes-mcp
GitHub Stars
1
Server Listing
WhereItGoes MCP server

TDQS

A4.4/5.0

Scored across 15 tools

Disambiguation4/5

Most tools target distinct analytical questions, and descriptions actively disambiguate by cross-referencing siblings (e.g. get_changes says 'for profit use get_performance, for money in and out use get_money_flows'). However, the read side has a dense cluster of overlapping financial-reporting tools (get_changes, get_movements, get_trading_activity, get_performance, get_money_flows, get_income, get_fees) that an agent could easily misselect without parsing long descriptions.

Naming Consistency3/5

The dominant pattern is get_* for reads, with list_profiles and two write tools breaking it. The bigger inconsistency is semantic: set_target_allocation and propose_alert_change behave identically (both return an approval link and do not apply), yet one is named 'set' and the other 'propose', which is misleading.

Tool Count5/5

15 tools is well within a healthy range for a portfolio analytics server, and each earns its place by covering a distinct analytical dimension (performance, risk, fees, income, flows, movements, alerts, targets). No obvious redundancy or padding.

Completeness4/5

The read surface is thorough (portfolio, performance, risk, fees, income, flows, movements, trading, connections, alerts, targets) and both write domains (alerts, target allocation) have propose-based lifecycle coverage. Gaps are deliberate but real: no way to add exchanges/wallets, no sync trigger, no notification inbox, and partial fee/gas coverage.

Available Tools

15 tools
get_alertsConfigured price and value alertsA
Read-onlyIdempotent
Inspect

List the alerts the user has set up: for each, its kind (asset_price, asset_pct, portfolio_value, portfolio_pct, holding_value, holding_weight), subject, above or below threshold, a readable label, whether it is enabled, once or recurring, delivery channels, cooldown and when it last fired. An account with none returns an empty list. This is the configuration, not a notification inbox. Use for "what alerts do I have set", "did my BTC alert fire", and before propose_alert_change, whose update and delete take the id from here. To create, edit or delete an alert use propose_alert_change. For current prices use get_portfolio.

NameRequiredDescriptionDefault
profileNoWhich portfolio to read, for accounts with more than one: its id, or its name (exact, case-insensitive, e.g. "Trading"). Omit to read this connection's default portfolio; every answer names the portfolio it read in `profile`. An unknown or shared name returns profile_not_found or profile_ambiguous listing the real ones, and list_profiles shows them up front.

Output Schema

NameRequiredDescription
errorNoPresent ONLY when the call was refused instead of answered: upgrade_required (Pro plan needed), not_found, profile_not_found, profile_ambiguous, invalid_arguments, write_not_permitted. No data fields come with it; read `message` to the user rather than retrying the same call.
alertsNo
messageNoPlain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user.
profileNoThe portfolio these figures belong to (absent on list_profiles).
next_stepNoWhat the user should do about data_status.
source_urlNoLink to the page in the WhereItGoes app that shows this data.
data_statusNoPresent only when the portfolio has no data yet. nothing_connected: no exchange and no wallet is connected, so empty lists and zeros mean "no data", NOT a $0 portfolio, and no data fields come with it (except on get_connections, get_alerts, get_target_allocation and list_profiles, which also answer). Send the user to next_step. syncing: a source is connected but its first sync is still running, so figures are partial and not final.
upgrade_urlNoWhere the user can upgrade, on an upgrade_required refusal.
stock_assetsNoPresent when the answer mentions Binance Stocks shares (EQ_<ticker>): they are US stocks, not crypto.

TDQS

A4.4/5.0
Behavior4/5

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

Annotations already cover the safety profile (readOnly, idempotent, non-destructive, closed-world), so the description's job is narrower and it delivers: the empty-list case for accounts with no alerts, and the key scoping caveat that this is configuration state rather than a delivery/notification log. It does not add anything about the `profile` resolution behavior (that lives entirely in the schema), which keeps it short of a 5.

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

Conciseness4/5

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

Front-loaded with the verb and scope, then the usage routing, with zero filler sentences. The parenthetical enumeration of the six alert kinds is long but functions as scope definition rather than padding; still, it is the one stretch that costs density.

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

Completeness5/5

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

Output schema exists, yet the description still gives the agent enough to decide and act: what comes back, the empty case, the id handoff to propose_alert_change, and where to go for the adjacent price and mutation needs. Nothing needed 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.

Parameters3/5

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

The single `profile` parameter is fully documented in the schema (100% coverage), including name-vs-id resolution and the profile_not_found/profile_ambiguous failure modes, but the description never references it or mentions multi-portfolio behavior at all. With schema coverage this high, the baseline 3 applies — the schema carries the parameter burden.

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

Purpose5/5

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

States a specific verb and resource ("List the alerts the user has set up") and then enumerates exactly what each record contains (kind, subject, threshold direction, enabled, recurrence, channels, cooldown, last fired). It also explicitly separates itself from the notification inbox and from propose_alert_change, so an agent can distinguish it from every nearby tool.

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

Usage Guidelines5/5

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

Gives concrete trigger phrases ("what alerts do I have set", "did my BTC alert fire") and an explicit ordering rule: call this before propose_alert_change because that tool's update/delete need the `id` from here. It also routes two adjacent needs away: mutations go to propose_alert_change, current prices go to get_portfolio.

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

get_changesWhat changed: ranked notable eventsA
Read-onlyIdempotent
Inspect

Detect what changed over period and rank it, most significant first: the biggest USD flows, allocation shifts, drawdowns, newly seen sources and reconciliation to-dos, each USD-valued, with countsByType counting everything detected before the list was capped. Built for short windows such as last_7d or last_30d; omitting period scans all time. Use for "what changed", "anything unusual", "notable moves this week". It reports events, not totals: for profit over the same period use get_performance, for money in and out use get_money_flows.

NameRequiredDescriptionDefault
periodNoTime window ending now: last_24h, last_7d, last_30d, last_90d, ytd (since 1 January UTC) or all. Omit for all time.
profileNoWhich portfolio to read, for accounts with more than one: its id, or its name (exact, case-insensitive, e.g. "Trading"). Omit to read this connection's default portfolio; every answer names the portfolio it read in `profile`. An unknown or shared name returns profile_not_found or profile_ambiguous listing the real ones, and list_profiles shows them up front.

Output Schema

NameRequiredDescription
errorNoPresent ONLY when the call was refused instead of answered: upgrade_required (Pro plan needed), not_found, profile_not_found, profile_ambiguous, invalid_arguments, write_not_permitted. No data fields come with it; read `message` to the user rather than retrying the same call.
notesNo
eventsNoRanked, most significant first.
windowNoThe time window these figures cover.
messageNoPlain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user.
profileNoThe portfolio these figures belong to (absent on list_profiles).
next_stepNoWhat the user should do about data_status.
truncatedNoPresent only when a list was longer than the 200-row cap and was cut: list field name -> {total, kept, keep, from, to}. A `<field>_note` string beside that list says the same in words. Never present a cut list as complete.
source_urlNoLink to the page in the WhereItGoes app that shows this data.
data_statusNoPresent only when the portfolio has no data yet. nothing_connected: no exchange and no wallet is connected, so empty lists and zeros mean "no data", NOT a $0 portfolio, and no data fields come with it (except on get_connections, get_alerts, get_target_allocation and list_profiles, which also answer). Send the user to next_step. syncing: a source is connected but its first sync is still running, so figures are partial and not final.
events_noteNoPresent only when `events` was cut; says how many rows were kept and which.
upgrade_urlNoWhere the user can upgrade, on an upgrade_required refusal.
countsByTypeNoEvents detected per type before the list was capped.
stock_assetsNoPresent when the answer mentions Binance Stocks shares (EQ_<ticker>): they are US stocks, not crypto.

TDQS

A4.5/5.0
Behavior4/5

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

Annotations already declare readOnly, idempotent, non-destructive and closed-world, so the safety profile is covered. The description adds real behavioral detail beyond them: results are ranked by significance, the list is capped, and `countsByType` reports everything detected before the cap — a truncation semantic an agent would otherwise miss. It stops short of describing error/empty behavior or the cap size itself.

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

Conciseness5/5

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

Front-loaded with the action and ranking rule, then scope guidance, then disambiguation from siblings — a natural decision order. Dense but every clause carries information; no filler and no repetition of the title.

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

Completeness5/5

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

An output schema exists, so return shape needn't be spelled out, and annotations cover safety. The description still supplies the one thing structured fields can't: how results are ordered and truncated, and which sibling to use instead. Nothing needed 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.

Parameters3/5

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

Schema coverage is 100% and both parameters are fully documented in the schema, including the omission-means-all-time rule and profile resolution errors. The description's only parameter-adjacent addition is repeating that omitting `period` scans all time, which the schema already states. Baseline 3 applies since the schema does the work.

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

Purpose5/5

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

States a specific verb and resource ('Detect what changed over `period` and rank it, most significant first') with the exact event categories returned and the USD-valued nature of each. It explicitly separates itself from get_performance and get_money_flows, so an agent can pick it without opening any schema.

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

Usage Guidelines5/5

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

Gives the intended scope ('Built for short windows such as last_7d or last_30d'), sample user phrasings to match ('what changed', 'anything unusual', 'notable moves this week'), and names two alternatives with the condition that selects each. Both when-to-use and when-to-use-something-else are covered.

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

get_connectionsConnected exchanges, tracked wallets and sync statusA
Read-onlyIdempotent
Inspect

List where the portfolio's data comes from, in one of three views. exchanges: every exchange and data source the app supports, with configured true on the ones the user connected (count only those) and a masked key hint, never the key. wallets: the watch-only on-chain addresses tracked, with label, wallet family, chains scanned and whether enabled, plus the EVM chains this app can read (balanceOnly where history is unavailable). sync_status: whether a sync of this portfolio is running or queued right now, and its queue position. No view takes a window. They hold no balances (get_portfolio holdings carry a per-venue split), do not test live connectivity, and cannot start a sync, which runs automatically and from the WhereItGoes app; for when the last sync finished use get_portfolio (recent_change, lastSyncAt). Use for "which exchanges am I connected to", "is my Base address tracked", "is it still syncing". When nothing is connected yet, the answer says so (data_status "nothing_connected") with the app links to add a wallet address or an exchange API key; pass those to the user.

NameRequiredDescriptionDefault
viewYesWhich view to read: exchanges (supported and connected exchanges); wallets (tracked on-chain addresses); sync_status (is a sync running or queued now). Each view takes only the arguments the description lists for it.
profileNoWhich portfolio to read, for accounts with more than one: its id, or its name (exact, case-insensitive, e.g. "Trading"). Omit to read this connection's default portfolio; every answer names the portfolio it read in `profile`. An unknown or shared name returns profile_not_found or profile_ambiguous listing the real ones, and list_profiles shows them up front.

Output Schema

NameRequiredDescription
viewNoWhich view answered; its data is under the key of the same name.
errorNoPresent ONLY when the call was refused instead of answered: upgrade_required (Pro plan needed), not_found, profile_not_found, profile_ambiguous, invalid_arguments, write_not_permitted. No data fields come with it; read `message` to the user rather than retrying the same call.
messageNoPlain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user.
profileNoThe portfolio these figures belong to (absent on list_profiles).
walletsNoPresent when view = "wallets".
exchangesNoPresent when view = "exchanges".
next_stepNoWhat the user should do about data_status.
source_urlNoLink to the page in the WhereItGoes app that shows this data.
data_statusNoPresent only when the portfolio has no data yet. nothing_connected: no exchange and no wallet is connected, so empty lists and zeros mean "no data", NOT a $0 portfolio, and no data fields come with it (except on get_connections, get_alerts, get_target_allocation and list_profiles, which also answer). Send the user to next_step. syncing: a source is connected but its first sync is still running, so figures are partial and not final.
sync_statusNoPresent when view = "sync_status".
upgrade_urlNoWhere the user can upgrade, on an upgrade_required refusal.
stock_assetsNoPresent when the answer mentions Binance Stocks shares (EQ_<ticker>): they are US stocks, not crypto.

TDQS

A4.8/5.0
Behavior5/5

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

Beyond the read-only annotations, it discloses that keys are masked and never returned, that no balances are held, that sync cannot be started and runs automatically, and that empty connection states return data_status 'nothing_connected' with app links. These are substantive behavioral traits an agent could not infer from the annotations alone.

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

Conciseness4/5

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

The opening sentence front-loads the core purpose, and the rest is dense, view-by-view detail rather than padding. It is long but most sentences carry operational value; a tighter grouping of the view definitions could improve scanability.

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

Completeness5/5

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

With an output schema present, return formatting need not be explained, and the description still covers all three views, constraints, the empty state, and alternatives. Nothing an agent needs in order to choose a view is missing.

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

Parameters4/5

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

Schema coverage is 100%, so the baseline is 3, but the description adds real meaning per view: what exchanges/wallets/sync_status contain, that configured is counted only when true, and that no view takes a window. The profile parameter is left to the schema, which documents it fully.

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

Purpose5/5

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

It states a specific verb and resource ('List where the portfolio's data comes from') and immediately enumerates the three views, making the scope unambiguous. It is clearly distinguishable from data-bearing siblings like get_portfolio and get_trading_activity.

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

Usage Guidelines5/5

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

It gives explicit user-intent examples ('which exchanges am I connected to', 'is my Base address tracked', 'is it still syncing') and routes the agent elsewhere for last-sync timing via get_portfolio. It also states what the tool cannot do (start a sync, test connectivity), closing off wrong-use paths.

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

get_feesFees paid, by venue, asset and typeA
Read-onlyIdempotent
Inspect

Total the fees paid over period (omit for all time), each valued in USD on the day it was paid: trading, on-chain gas, withdrawal and fiat-spread fees, as totalUsd plus breakdowns by venue (with the effective trading-fee rate in basis points), by asset and by type, each largest first, and a series per bucket (month by default, or day or week), oldest first. Use for "what am I paying in fees", "which exchange costs me the most". Coverage is partial by nature (gas on EVM token transfers and contract calls, some chains, and swap slippage are not tracked), and partial plus notes say what was left out. For fees as one line of overall profit use get_performance; for single fee-bearing events use get_movements.

NameRequiredDescriptionDefault
bucketNoGranularity of the fee series. Defaults to month.
periodNoTime window ending now: last_24h, last_7d, last_30d, last_90d, ytd (since 1 January UTC) or all. Omit for all time.
profileNoWhich portfolio to read, for accounts with more than one: its id, or its name (exact, case-insensitive, e.g. "Trading"). Omit to read this connection's default portfolio; every answer names the portfolio it read in `profile`. An unknown or shared name returns profile_not_found or profile_ambiguous listing the real ones, and list_profiles shows them up front.

Output Schema

NameRequiredDescription
errorNoPresent ONLY when the call was refused instead of answered: upgrade_required (Pro plan needed), not_found, profile_not_found, profile_ambiguous, invalid_arguments, write_not_permitted. No data fields come with it; read `message` to the user rather than retrying the same call.
notesNoWhat the coverage leaves out.
bucketNo
byTypeNoLargest first.
pointsNoOldest bucket first.
windowNoThe time window these figures cover.
byAssetNoLargest first.
byVenueNoLargest first.
messageNoPlain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user.
partialNotrue when some fees could not be valued or are not tracked.
profileNoThe portfolio these figures belong to (absent on list_profiles).
feeCountNoFee-bearing events counted.
totalUsdNoAll fees in the window, USD, each valued on the day paid.
avgFeeUsdNo
next_stepNoWhat the user should do about data_status.
truncatedNoPresent only when a list was longer than the 200-row cap and was cut: list field name -> {total, kept, keep, from, to}. A `<field>_note` string beside that list says the same in words. Never present a cut list as complete.
source_urlNoLink to the page in the WhereItGoes app that shows this data.
data_statusNoPresent only when the portfolio has no data yet. nothing_connected: no exchange and no wallet is connected, so empty lists and zeros mean "no data", NOT a $0 portfolio, and no data fields come with it (except on get_connections, get_alerts, get_target_allocation and list_profiles, which also answer). Send the user to next_step. syncing: a source is connected but its first sync is still running, so figures are partial and not final.
points_noteNoPresent only when `points` was cut; says how many rows were kept and which.
upgrade_urlNoWhere the user can upgrade, on an upgrade_required refusal.
stock_assetsNoPresent when the answer mentions Binance Stocks shares (EQ_<ticker>): they are US stocks, not crypto.
unpricedAssetsNo

TDQS

A4.6/5.0
Behavior5/5

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

Annotations already cover the safety profile (read-only, idempotent, non-destructive), and the description adds substantial context beyond them: USD valuation on the day paid, sort orders (largest first vs oldest first), partial coverage caveats, and the `partial`/`notes` fields signaling omissions. This is exactly the extra behavioral context the annotations cannot carry.

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

Conciseness4/5

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

Front-loaded with the verb and resource, and every clause carries information (breakdowns, sort orders, caveats, alternatives). The first sentence is a dense run-on that takes effort to parse, which keeps it from a 5, but there is no throwaway content.

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

Completeness5/5

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

An output schema exists, so return values need not be explained. The description still covers coverage limitations, default behavior, and sibling alternatives, leaving nothing an agent needs in order to call this correctly.

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

Parameters3/5

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

Schema coverage is 100% and the schema already documents defaults (bucket=month, period omit=all time) and the profile fallback/error behavior. The description restates the defaults and mentions `bucket`/`period` in passing but adds no syntax or edge-case meaning beyond the schema, so the baseline 3 applies.

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

Purpose5/5

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

States a specific verb and resource ('Total the fees paid') and enumerates the exact breakdowns produced (venue, asset, type, series). It also names the closest sibling tools (get_performance, get_movements) with the conditions that separate them, so an agent can distinguish it without reading schemas.

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

Usage Guidelines5/5

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

Explicit 'Use for' examples map to real agent intents, and two alternatives are given with their selecting conditions: get_performance for fees as one line of overall profit, get_movements for single fee-bearing events. No inference required.

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

get_incomeIncome received: staking, lending, rewards, airdropsA
Read-onlyIdempotent
Inspect

Total the income the portfolio received (yield, staking, lending, rewards, airdrops) over period (omit for all time), each payment valued in USD on the day it arrived, in one of two views. by_category: totals per category (staking, lending, defi, rewards, airdrop, other), topSources largest first, and a monthly series. by_asset: per bucket (week by default, or day or month), oldest first, the USD each asset paid, with topAssets largest first; bucket applies to this view only and by_category refuses it. Futures P&L is excluded because it is a trading result, and unpricedAssets lists what could not be valued. A series longer than 200 buckets keeps the most recent 200 and says so in truncated, so use week or month for long periods. Use for "how much did staking pay me this year", "which coin has paid me the most". For the individual payments use get_movements with kind INCOME; for income as one line of overall profit use get_performance.

NameRequiredDescriptionDefault
viewYesWhich view to read: by_category (totals per income category); by_asset (series of income per asset). Each view takes only the arguments the description lists for it.
bucketNoby_asset only. Granularity of the series. Defaults to week.
periodNoTime window ending now: last_24h, last_7d, last_30d, last_90d, ytd (since 1 January UTC) or all. Omit for all time.
profileNoWhich portfolio to read, for accounts with more than one: its id, or its name (exact, case-insensitive, e.g. "Trading"). Omit to read this connection's default portfolio; every answer names the portfolio it read in `profile`. An unknown or shared name returns profile_not_found or profile_ambiguous listing the real ones, and list_profiles shows them up front.

Output Schema

NameRequiredDescription
viewNoWhich view answered; its data is under the key of the same name.
errorNoPresent ONLY when the call was refused instead of answered: upgrade_required (Pro plan needed), not_found, profile_not_found, profile_ambiguous, invalid_arguments, write_not_permitted. No data fields come with it; read `message` to the user rather than retrying the same call.
messageNoPlain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user.
profileNoThe portfolio these figures belong to (absent on list_profiles).
by_assetNoPresent when view = "by_asset".
next_stepNoWhat the user should do about data_status.
source_urlNoLink to the page in the WhereItGoes app that shows this data.
by_categoryNoPresent when view = "by_category".
data_statusNoPresent only when the portfolio has no data yet. nothing_connected: no exchange and no wallet is connected, so empty lists and zeros mean "no data", NOT a $0 portfolio, and no data fields come with it (except on get_connections, get_alerts, get_target_allocation and list_profiles, which also answer). Send the user to next_step. syncing: a source is connected but its first sync is still running, so figures are partial and not final.
upgrade_urlNoWhere the user can upgrade, on an upgrade_required refusal.
stock_assetsNoPresent when the answer mentions Binance Stocks shares (EQ_<ticker>): they are US stocks, not crypto.

TDQS

A4.8/5.0
Behavior5/5

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

Annotations already cover the safety profile (readOnly, idempotent, non-destructive), and the description layers on substantive behavior beyond them: USD valuation at payment date, futures P&L exclusion, the `unpricedAssets` list, and the 200-bucket truncation with a `truncated` flag. Profile resolution failure modes (profile_not_found / profile_ambiguous) are also disclosed.

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

Conciseness4/5

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

Front-loaded with the core purpose, then the view breakdown, then exclusions, then alternatives — a sensible ordering with no filler sentences. It is dense and long for a single block, but each clause carries operational meaning rather than repetition.

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

Completeness5/5

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

With an output schema present, the description needn't explain return shapes, yet it still flags the fields that matter for interpretation (totals, topSources, topAssets, truncated, unpricedAssets) and the ordering of each series. An agent has everything required to pick a view and read the result correctly.

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

Parameters4/5

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

Schema coverage is 100%, so the baseline would be 3; the description goes further by articulating cross-parameter constraints the schema cannot express — `bucket` applies only to by_asset and by_category rejects it, and omitting `period` means all time. It does not restate the enum values or add per-value semantics for the categories.

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

Purpose5/5

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

States a specific verb and resource ('total the income the portfolio received') and enumerates exactly what counts as income (yield, staking, lending, rewards, airdrops). It then breaks the output into the two named views, letting an agent distinguish it immediately from get_movements and get_performance.

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

Usage Guidelines5/5

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

Gives explicit routing: use this for aggregate questions ('how much did staking pay me this year'), use get_movements with kind INCOME for individual payments, and get_performance for income as one line of overall profit. It also states the when-not for `bucket` (by_category refuses it) and advises week/month for long periods.

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

get_money_flowsMoney in and out: USD flows, deposits, transfers, fiatA
Read-onlyIdempotent
Inspect

Summarise money entering and leaving the portfolio, in one of four views. capital_flow: deposits and withdrawals per bucket (month by default, or week) over period (omit for all time), in USD split by asset and valued on the day they moved, oldest bucket first; moves between the user's own accounts, Earn shuffles and cancelled withdrawals are excluded, so this is capital genuinely entering or leaving. deposits: exchange deposits, newest first, with what is settled about each one's cost basis (manual_cost, transfer_id when matched to the user's own withdrawal, reconciliation classification); exchange deposits only, spam hidden. transfers: withdrawals matched to the deposit that landed on another of the user's own accounts, most recently matched first, both legs in full. fiat: per local fiat currency (e.g. VND, KRW), all-time fiat spent buying crypto and received selling it, net invested and volume-weighted rates, in that currency, not USD. period and bucket apply to capital_flow only; other views refuse them. Lists return at most 200 rows. Use for "how much did I deposit each month", "which deposits need a cost basis", "did my BTC arrive in my wallet". For single events in native units use get_movements.

NameRequiredDescriptionDefault
viewYesWhich view to read: capital_flow (USD deposited and withdrawn per bucket); deposits (exchange deposits and their cost-basis status); transfers (matched moves between own accounts); fiat (fiat in and out per local currency). Each view takes only the arguments the description lists for it.
bucketNocapital_flow only. Granularity of the series. Defaults to month.
periodNoTime window ending now: last_24h, last_7d, last_30d, last_90d, ytd (since 1 January UTC) or all. Omit for all time.
profileNoWhich portfolio to read, for accounts with more than one: its id, or its name (exact, case-insensitive, e.g. "Trading"). Omit to read this connection's default portfolio; every answer names the portfolio it read in `profile`. An unknown or shared name returns profile_not_found or profile_ambiguous listing the real ones, and list_profiles shows them up front.

Output Schema

NameRequiredDescription
fiatNoPresent when view = "fiat".
viewNoWhich view answered; its data is under the key of the same name.
errorNoPresent ONLY when the call was refused instead of answered: upgrade_required (Pro plan needed), not_found, profile_not_found, profile_ambiguous, invalid_arguments, write_not_permitted. No data fields come with it; read `message` to the user rather than retrying the same call.
messageNoPlain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user.
profileNoThe portfolio these figures belong to (absent on list_profiles).
depositsNoPresent when view = "deposits".
next_stepNoWhat the user should do about data_status.
transfersNoPresent when view = "transfers".
source_urlNoLink to the page in the WhereItGoes app that shows this data.
data_statusNoPresent only when the portfolio has no data yet. nothing_connected: no exchange and no wallet is connected, so empty lists and zeros mean "no data", NOT a $0 portfolio, and no data fields come with it (except on get_connections, get_alerts, get_target_allocation and list_profiles, which also answer). Send the user to next_step. syncing: a source is connected but its first sync is still running, so figures are partial and not final.
upgrade_urlNoWhere the user can upgrade, on an upgrade_required refusal.
capital_flowNoPresent when view = "capital_flow".
stock_assetsNoPresent when the answer mentions Binance Stocks shares (EQ_<ticker>): they are US stocks, not crypto.

TDQS

A4.8/5.0
Behavior5/5

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

Annotations already cover the read-only/idempotent profile, but the description adds substantive behavior: the exclusion rules for internal moves, Earn shuffles and cancelled withdrawals, spam hiding on deposits, a 200-row cap, and ordering per view (oldest bucket first, newest first, most recently matched first). That is exactly the context annotations cannot convey.

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

Conciseness4/5

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

Front-loaded with the one-line purpose, then organized view-by-view, with the fallback to get_movements last. The view paragraphs are dense run-on clauses, but nearly every clause carries operational meaning.

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

Completeness5/5

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

With an output schema present, the description need not explain return shapes, and it doesn't waste space doing so. It covers view selection, per-view parameters, exclusions, ordering, row caps, and the sibling escape hatch – everything needed to call it correctly.

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

Parameters4/5

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

Schema coverage is 100%, so the baseline is 3; the description still adds value by tying each parameter to a view ('period and bucket apply to capital_flow only') and restating the all-time default for an omitted period. It does not add much on profile beyond what the schema already says.

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

Purpose5/5

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

Opens with a specific verb and resource ('Summarise money entering and leaving the portfolio') and then enumerates all four views with their exact scope. It distinguishes itself from the closest sibling by naming get_movements for single events in native units.

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

Usage Guidelines5/5

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

Gives concrete routing examples ('how much did I deposit each month', 'which deposits need a cost basis', 'did my BTC arrive in my wallet') and an explicit alternative for a different need. It also states the constraint that period and bucket apply only to capital_flow and are refused by other views.

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

get_movementsSearch the money-movement ledgerA
Read-onlyIdempotent
Inspect

Search the raw money-movement ledger, newest first: deposits, withdrawals, internal transfers, income and trades, each with venue, source, status, its amount in NATIVE token units and value_usd (its USD value on the day; null if unpriced). Filters combine with AND: kind picks the event type; asset, venue and source take comma-separated lists; status must be copied from countsByStatus (venue-native, case-sensitive); q is a substring search over asset, transaction id, status and address fragments; period bounds the time (omit for all time). limit (default 100, max 200) caps the rows returned while total still counts every match; there is no paging, so narrow the filters to reach older rows. The countsBySource / countsByVenue / countsByStatus facets list the filter values this account actually has. Use to find individual events, e.g. "my BTC withdrawals from Binance this year". For dollar totals of money in and out use get_money_flows; for income totals use get_income.

NameRequiredDescriptionDefault
qNoCase-insensitive substring search over asset, transaction id, status and stored details such as an address fragment.
kindNoINFLOW = deposit, OUTFLOW = withdrawal, TRANSFER = between the user's own accounts, INCOME = yield, staking or airdrop, TRADE = a buy or sell. Omit for every kind.
assetNoAsset ticker, e.g. "BTC"; comma-separate for several ("BTC,ETH"). Case-insensitive.
limitNoHow many of the newest matching rows to return, 1-200. Defaults to 100.
venueNoVenue name as it appears in countsByVenue, e.g. "binance"; comma-separate for several.
periodNoTime window ending now: last_24h, last_7d, last_30d, last_90d, ytd (since 1 January UTC) or all. Omit for all time.
sourceNoSource label as it appears in countsBySource, e.g. "deposit" or "staking"; comma-separate for several.
statusNoStatus exactly as it appears in countsByStatus, e.g. "SUCCESS" or "2".
profileNoWhich portfolio to read, for accounts with more than one: its id, or its name (exact, case-insensitive, e.g. "Trading"). Omit to read this connection's default portfolio; every answer names the portfolio it read in `profile`. An unknown or shared name returns profile_not_found or profile_ambiguous listing the real ones, and list_profiles shows them up front.

Output Schema

NameRequiredDescription
errorNoPresent ONLY when the call was refused instead of answered: upgrade_required (Pro plan needed), not_found, profile_not_found, profile_ambiguous, invalid_arguments, write_not_permitted. No data fields come with it; read `message` to the user rather than retrying the same call.
itemsNoNewest first, at most `limit` rows.
totalNoEvery row matching the filters, before `limit`.
messageNoPlain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user.
profileNoThe portfolio these figures belong to (absent on list_profiles).
next_stepNoWhat the user should do about data_status.
truncatedNoPresent only when a list was longer than the 200-row cap and was cut: list field name -> {total, kept, keep, from, to}. A `<field>_note` string beside that list says the same in words. Never present a cut list as complete.
hiddenSpamNoRows hidden because they were marked spam.
items_noteNoPresent only when `items` was cut; says how many rows were kept and which.
source_urlNoLink to the page in the WhereItGoes app that shows this data.
data_statusNoPresent only when the portfolio has no data yet. nothing_connected: no exchange and no wallet is connected, so empty lists and zeros mean "no data", NOT a $0 portfolio, and no data fields come with it (except on get_connections, get_alerts, get_target_allocation and list_profiles, which also answer). Send the user to next_step. syncing: a source is connected but its first sync is still running, so figures are partial and not final.
upgrade_urlNoWhere the user can upgrade, on an upgrade_required refusal.
stock_assetsNoPresent when the answer mentions Binance Stocks shares (EQ_<ticker>): they are US stocks, not crypto.
countsByVenueNo
countsBySourceNo
countsByStatusNo

TDQS

A4.4/5.0
Behavior3/5

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

Annotations already declare readOnlyHint, idempotentHint and non-destructive, so the safety profile needs no restating. The description still adds real behavior beyond them: ordering (newest first), the limit/total interaction with no paging, AND-combination of filters, null value_usd when unpriced, and that facets reflect only values this account actually has. A 3 reflects solid added context on an already well-annotated read tool.

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

Conciseness4/5

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

Front-loaded with purpose and the key scoping rule (newest first, no paging), and nearly every clause carries operational information. It is a single dense block, though — the filter semantics would scan better as separate sentences or a short list.

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

Completeness5/5

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

For a 9-parameter read tool with an output schema and full annotation coverage, the description supplies everything an agent needs to call it correctly: filter behavior, ordering, limit semantics, missing-pagination workaround, and the sibling routing. Return-value detail is rightly left to the output schema.

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

Parameters4/5

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

Schema coverage is 100%, so the baseline is 3, but the description adds cross-parameter semantics the schema cannot express: how filters combine, that comma-separated lists are accepted for asset/venue/source, and that status must be copied verbatim from countsByStatus because it is venue-native and case-sensitive. It also reiterates limit's default and max, which the schema already covers.

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

Purpose5/5

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

Opens with a specific verb and resource ('Search the raw money-movement ledger') and enumerates exactly what the ledger contains (deposits, withdrawals, transfers, income, trades) with per-row fields. It explicitly distinguishes itself from get_money_flows and get_income by use case, so an agent can route without inspecting siblings.

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

Usage Guidelines5/5

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

Gives an explicit when-to-use with a concrete example ('my BTC withdrawals from Binance this year') and names the two alternatives plus the condition that selects them ('for dollar totals... use get_money_flows; for income totals use get_income'). It also states the operational constraint that there is no paging, so older rows require narrower filters.

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

get_performanceProfit and loss, daily value history, results per pairA
Read-onlyIdempotent
Inspect

Measure how the portfolio performed, in one of three views. pnl: TRUE profit and loss over period with the user's own deposits and withdrawals stripped out: realized vs unrealized, net flows, income, fees, top gainers and losers, and a plain-English explanation. history: the daily series over period, oldest first: total value, net capital in, cost basis and cash in and out per UTC day. realized_by_pair: all-time realized FIFO profit ranked by trading pair, best first. Arguments apply per view, and a call that sets one its view does not take is refused: period (pnl, history; omit for all time); slim (history: true drops the per-asset breakdown, recommended unless composition is needed); limit and quote (realized_by_pair: the limit best AND limit worst pairs quoted in quote, defaults 20 and USDT, never summed across quote currencies). All values are USD end-of-day marks, not live quotes: never report pnl.endValue as the current value (get_portfolio has it). A history longer than 200 days is sampled evenly, always keeping the first and the latest day, and says so in truncated; pick a shorter period for every day. For buy and sell totals use get_trading_activity; for single trades use get_movements.

NameRequiredDescriptionDefault
slimNohistory only. true returns the daily totals alone; false or omitted adds a USD value and a quantity for every asset on every day.
viewYesWhich view to read: pnl (true profit and loss over period); history (daily value series over period); realized_by_pair (all-time realized profit per trading pair). Each view takes only the arguments the description lists for it.
limitNorealized_by_pair only. How many best AND how many worst pairs to return, 1-100; up to twice this many rows come back. Defaults to 20.
quoteNorealized_by_pair only. Quote currency whose pairs to rank, e.g. "USDT" for BTCUSDT. Defaults to USDT.
periodNoTime window ending now: last_24h, last_7d, last_30d, last_90d, ytd (since 1 January UTC) or all. Omit for all time.
profileNoWhich portfolio to read, for accounts with more than one: its id, or its name (exact, case-insensitive, e.g. "Trading"). Omit to read this connection's default portfolio; every answer names the portfolio it read in `profile`. An unknown or shared name returns profile_not_found or profile_ambiguous listing the real ones, and list_profiles shows them up front.

Output Schema

NameRequiredDescription
pnlNoPresent when view = "pnl".
viewNoWhich view answered; its data is under the key of the same name.
errorNoPresent ONLY when the call was refused instead of answered: upgrade_required (Pro plan needed), not_found, profile_not_found, profile_ambiguous, invalid_arguments, write_not_permitted. No data fields come with it; read `message` to the user rather than retrying the same call.
historyNoPresent when view = "history".
messageNoPlain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user.
profileNoThe portfolio these figures belong to (absent on list_profiles).
next_stepNoWhat the user should do about data_status.
source_urlNoLink to the page in the WhereItGoes app that shows this data.
data_statusNoPresent only when the portfolio has no data yet. nothing_connected: no exchange and no wallet is connected, so empty lists and zeros mean "no data", NOT a $0 portfolio, and no data fields come with it (except on get_connections, get_alerts, get_target_allocation and list_profiles, which also answer). Send the user to next_step. syncing: a source is connected but its first sync is still running, so figures are partial and not final.
upgrade_urlNoWhere the user can upgrade, on an upgrade_required refusal.
stock_assetsNoPresent when the answer mentions Binance Stocks shares (EQ_<ticker>): they are US stocks, not crypto.
realized_by_pairNoPresent when view = "realized_by_pair".

TDQS

A5/5.0
Behavior5/5

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

Beyond the read-only and idempotent annotations, the description discloses important behavioral traits: values are USD end-of-day marks rather than live quotes, pnl.endValue must not be reported as current value, history over 200 days is sampled evenly and flagged via `truncated`, and invalid per-view arguments are refused. It also explains profile lookup failure modes (`profile_not_found`, `profile_ambiguous`), adding substantial context beyond the annotations.

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

Conciseness5/5

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

The description is dense but front-loaded: it states the tool's purpose and views first, then per-view arguments and behavioral caveats. Every sentence carries actionable information for a complex three-view tool, with no filler or repetition.

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

Completeness5/5

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

For a read-only analytics tool with an output schema and full annotation coverage, the description supplies everything an agent needs: per-view semantics, sampling/truncation behavior, quote-currency defaults, profile selection, and sibling-tool routing. Return-value details are appropriately left to the output schema.

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

Parameters5/5

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

Even though schema coverage is 100%, the description adds meaningful semantics beyond the schema: it maps each argument to its applicable view, clarifies that `limit` returns best AND worst pairs, that `quote` rankings are never summed across currencies, that `slim` is recommended unless per-asset composition is needed, and that `period` should be omitted for all time. This is substantially more useful than the schema's field-level descriptions alone.

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

Purpose5/5

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

The description opens with a specific verb and resource ('Measure how the portfolio performed') and immediately decomposes the three supported views (pnl, history, realized_by_pair), including what each returns. It also distinguishes the tool from siblings such as get_portfolio and get_trading_activity, so an agent can identify its scope without opening the schema.

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

Usage Guidelines5/5

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

The description explicitly routes the agent to alternatives: get_trading_activity for buy and sell totals, get_movements for single trades, and get_portfolio for current value. It further states that arguments apply per view and that a call setting an argument its view does not take is refused, which gives clear when-to-use and when-not-to-use guidance.

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

get_portfolioPortfolio overview, holdings and recent changeA
Read-onlyIdempotent
Inspect

Read the portfolio as it stands now, in one of three views. overview: a whole-portfolio summary (live total value, allocation by asset, class, venue and chain, concentration, stablecoin and self-custody shares, 30-day true P&L, drift from the target allocation, what BTC and ETH did, and on a Pro plan risk, a 0-100 health score and ranked findings, which a free plan gets empty and named in gated); start here for open questions such as "how am I doing". holdings: every asset held with quantity, USD price and value, weight, 24h change, FIFO average cost, unrealized and realized P&L and a per-venue split, largest first, plus totalUsdt; use for "what do I hold", "my net worth". recent_change: 24h, 7d and 30d change in % and USD, lifetime net capital and cash in and out, and lastSyncAt. No view takes a window: holdings come from the last computed snapshot (asOf), recent_change is null until daily history exists, and overview's P&L is an end-of-day 30-day figure. For profit over another period use get_performance; for drawdown and volatility use get_risk. The answer is nested under the key named after the view. A new account may have nothing connected: the answer is then data_status "nothing_connected" with a link to add a wallet address or exchange in the app, not a $0 portfolio.

NameRequiredDescriptionDefault
viewYesWhich view to read: overview (whole-portfolio summary, start here); holdings (every asset held, largest first); recent_change (24h, 7d and 30d change and last sync). Each view takes only the arguments the description lists for it.
profileNoWhich portfolio to read, for accounts with more than one: its id, or its name (exact, case-insensitive, e.g. "Trading"). Omit to read this connection's default portfolio; every answer names the portfolio it read in `profile`. An unknown or shared name returns profile_not_found or profile_ambiguous listing the real ones, and list_profiles shows them up front.

Output Schema

NameRequiredDescription
viewNoWhich view answered; its data is under the key of the same name.
errorNoPresent ONLY when the call was refused instead of answered: upgrade_required (Pro plan needed), not_found, profile_not_found, profile_ambiguous, invalid_arguments, write_not_permitted. No data fields come with it; read `message` to the user rather than retrying the same call.
messageNoPlain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user.
profileNoThe portfolio these figures belong to (absent on list_profiles).
holdingsNoPresent when view = "holdings".
overviewNoPresent when view = "overview".
next_stepNoWhat the user should do about data_status.
source_urlNoLink to the page in the WhereItGoes app that shows this data.
data_statusNoPresent only when the portfolio has no data yet. nothing_connected: no exchange and no wallet is connected, so empty lists and zeros mean "no data", NOT a $0 portfolio, and no data fields come with it (except on get_connections, get_alerts, get_target_allocation and list_profiles, which also answer). Send the user to next_step. syncing: a source is connected but its first sync is still running, so figures are partial and not final.
upgrade_urlNoWhere the user can upgrade, on an upgrade_required refusal.
stock_assetsNoPresent when the answer mentions Binance Stocks shares (EQ_<ticker>): they are US stocks, not crypto.
recent_changeNoPresent when view = "recent_change".

TDQS

A4.8/5.0
Behavior5/5

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

Goes well beyond the readOnly/idempotent annotations: it discloses that no view accepts a time window, that holdings come from the last computed snapshot with `asOf`, that recent_change is null until daily history exists, that overview P&L is an end-of-day 30-day figure, that Pro-plan sections come back empty and named in `gated`, and that a fresh account returns data_status "nothing_connected" rather than a $0 portfolio.

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

Conciseness4/5

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

Front-loaded with the core action and the three view choices before caveats and alternatives. The per-view field enumerations are dense (e.g. the overview run-on list), but each carries selection-relevant information, so little is wasted.

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

Completeness5/5

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

For a multi-view read tool with an output schema, this covers everything an agent needs: when to pick each view, the snapshot/staleness semantics, plan gating, the nesting key, and the empty-account case with its remedy. No meaningful gaps remain.

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

Parameters4/5

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

Schema coverage is 100%, so the schema already documents both parameters, but the description adds substantive meaning by detailing what each `view` value actually yields and by clarifying the no-window constraint. Profile semantics are left mostly to the schema's already thorough description.

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

Purpose5/5

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

States a specific verb and resource ("Read the portfolio as it stands now") and enumerates all three views with the exact content each returns. It is clearly distinguishable from siblings like get_performance and get_risk, which are named as the tools for other time windows and risk metrics.

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

Usage Guidelines5/5

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

Gives explicit routing cues: "start here for open questions such as 'how am I doing'" for overview, "use for 'what do I hold', 'my net worth'" for holdings, and explicit exclusions — "For profit over another period use get_performance; for drawdown and volatility use get_risk." It also points to list_profiles for discovering profile names.

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

get_riskRisk metrics and portfolio health score (Pro)A
Read-onlyIdempotent
Inspect

Assess portfolio risk, in one of two views; both need a Pro plan, and a free account gets an upgrade_required answer, not an error. metrics: measured risk over period (omit for full history): max drawdown in % and USD with peak and trough days, annualized volatility and return, Sharpe, and return, beta and correlation against benchmark (BTC by default, or ETH). Drawdown is flow-honest, so a withdrawal is not counted as a loss, and a metric is null when the window holds too few daily returns. score: a 0-100 structural health rating with a band (resilient, solid, fragile, concentrated), pillar sub-scores (diversification, custody, liquidity, quality, volatility, hygiene, target drift) and what moved since the last reading; it scores structure, never returns, over a fixed 30 days, so it takes no period or benchmark and refuses them. A pillar that cannot be measured yet is listed in excluded, not scored zero. Use metrics for "what was my worst drawdown", "do I just track bitcoin"; score for "how healthy is my portfolio". For profit use get_performance.

NameRequiredDescriptionDefault
viewYesWhich view to read: metrics (drawdown, volatility, Sharpe, beta over period); score (0-100 health score, fixed 30 days). Each view takes only the arguments the description lists for it.
periodNoTime window ending now: last_24h, last_7d, last_30d, last_90d, ytd (since 1 January UTC) or all. Omit for all time.
profileNoWhich portfolio to read, for accounts with more than one: its id, or its name (exact, case-insensitive, e.g. "Trading"). Omit to read this connection's default portfolio; every answer names the portfolio it read in `profile`. An unknown or shared name returns profile_not_found or profile_ambiguous listing the real ones, and list_profiles shows them up front.
benchmarkNometrics only. Asset to measure beta, correlation and excess return against. Defaults to BTC.

Output Schema

NameRequiredDescription
viewNoWhich view answered; its data is under the key of the same name.
errorNoPresent ONLY when the call was refused instead of answered: upgrade_required (Pro plan needed), not_found, profile_not_found, profile_ambiguous, invalid_arguments, write_not_permitted. No data fields come with it; read `message` to the user rather than retrying the same call.
scoreNoPresent when view = "score".
messageNoPlain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user.
metricsNoPresent when view = "metrics".
profileNoThe portfolio these figures belong to (absent on list_profiles).
next_stepNoWhat the user should do about data_status.
source_urlNoLink to the page in the WhereItGoes app that shows this data.
data_statusNoPresent only when the portfolio has no data yet. nothing_connected: no exchange and no wallet is connected, so empty lists and zeros mean "no data", NOT a $0 portfolio, and no data fields come with it (except on get_connections, get_alerts, get_target_allocation and list_profiles, which also answer). Send the user to next_step. syncing: a source is connected but its first sync is still running, so figures are partial and not final.
upgrade_urlNoWhere the user can upgrade, on an upgrade_required refusal.
stock_assetsNoPresent when the answer mentions Binance Stocks shares (EQ_<ticker>): they are US stocks, not crypto.

TDQS

A4.8/5.0
Behavior5/5

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

Annotations only cover read-only safety; the description adds substantially more: the Pro entitlement and that failure surfaces as `upgrade_required` rather than an error, flow-honest drawdown treatment (withdrawals not counted as losses), null metrics on sparse windows, and score's fixed 30-day window with unmeasurable pillars listed in `excluded` rather than zeroed. These are the non-obvious semantics that would otherwise cause misreads.

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

Conciseness4/5

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

Front-loaded with the two-view split and no filler sentences; every clause carries information. It is dense and semicolon-chained rather than visually structured, which costs a little at the margins, but nothing is redundant.

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

Completeness5/5

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

An output schema exists, so return formatting is not the description's burden; what remains — entitlement, view-specific argument scoping, default portfolio resolution path, and the null/excluded edge cases — is all covered. Nothing an agent needs to invoke this correctly on the first try is missing.

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

Parameters4/5

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

Schema coverage is 100%, so the baseline is 3, but the description adds meaning beyond the schema: `period` omitted means full history, score ignores/refuses `period` and `benchmark`, and `benchmark` defaults to BTC. It leaves `profile` resolution to the schema, which already documents the name/id and not-found behavior.

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

Purpose5/5

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

Opens with a specific verb+resource ('Assess portfolio risk') and immediately decomposes the two views with their exact contents (drawdown/volatility/Sharpe/beta vs. 0-100 health score with pillars). It explicitly names the sibling it is not (get_performance for profit), so an agent can route correctly without opening a schema.

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

Usage Guidelines5/5

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

Gives explicit routing rules: 'Use metrics for "what was my worst drawdown", "do I just track bitcoin"; score for "how healthy is my portfolio"', plus 'For profit use get_performance'. It also states the gating condition (Pro plan required, free accounts get upgrade_required) and that score refuses period/benchmark — both are when/when-not instructions.

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

get_target_allocationTarget allocation planA
Read-onlyIdempotent
Inspect

Read the user's target allocation: the plan they MEANT to hold, as set.rows of {subject, targetPct, bandPct} largest first, or set: null if they never set one. set.dim names the axis: "asset" means each subject is a ticker (BTC), "sector" means each is one of our sector names (Layer 1, DeFi, Stablecoins). bandPct is the tolerance in percentage POINTS, so a 40% target with a 5-point band is on plan anywhere from 35% to 45%. Use for "what is my target allocation", "what was my plan", and before set_target_allocation so an edit starts from the real plan. This is the INTENTION only: for what they actually hold against it, read the drift in get_portfolio (overview), which scores the two together.

NameRequiredDescriptionDefault
profileNoWhich portfolio to read, for accounts with more than one: its id, or its name (exact, case-insensitive, e.g. "Trading"). Omit to read this connection's default portfolio; every answer names the portfolio it read in `profile`. An unknown or shared name returns profile_not_found or profile_ambiguous listing the real ones, and list_profiles shows them up front.

Output Schema

NameRequiredDescription
setNonull when no target allocation is set.
errorNoPresent ONLY when the call was refused instead of answered: upgrade_required (Pro plan needed), not_found, profile_not_found, profile_ambiguous, invalid_arguments, write_not_permitted. No data fields come with it; read `message` to the user rather than retrying the same call.
messageNoPlain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user.
profileNoThe portfolio these figures belong to (absent on list_profiles).
next_stepNoWhat the user should do about data_status.
source_urlNoLink to the page in the WhereItGoes app that shows this data.
data_statusNoPresent only when the portfolio has no data yet. nothing_connected: no exchange and no wallet is connected, so empty lists and zeros mean "no data", NOT a $0 portfolio, and no data fields come with it (except on get_connections, get_alerts, get_target_allocation and list_profiles, which also answer). Send the user to next_step. syncing: a source is connected but its first sync is still running, so figures are partial and not final.
upgrade_urlNoWhere the user can upgrade, on an upgrade_required refusal.
stock_assetsNoPresent when the answer mentions Binance Stocks shares (EQ_<ticker>): they are US stocks, not crypto.

TDQS

A4.6/5.0
Behavior5/5

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

Annotations already cover read-only, idempotent, non-destructive behavior, yet the description adds materially: the null-result case when no plan exists, the rows/dim structure, and the key semantic warning that this is intention only. That last point prevents a plausible misuse (treating targets as holdings), which annotations cannot convey.

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

Conciseness4/5

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

Front-loads the core definition, then the return shape, then usage. The band example is long but earns its place by clarifying 'percentage points' ambiguity. Slightly dense, but nearly every clause carries operational meaning.

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

Completeness5/5

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

Given an output schema exists, the description needn't enumerate returns, yet explaining set.rows/set.dim/bandPct semantics and the null state is genuinely useful for interpretation. Combined with the intention-vs-actual distinction, an agent has everything needed to call and correctly use this tool.

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

Parameters3/5

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

There is one optional 'profile' parameter with 100% schema description coverage, including default behavior and error cases. The description adds no parameter guidance beyond the schema, so the baseline of 3 is appropriate despite the overall richness.

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

Purpose5/5

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

States a specific verb and resource ('Read the user's target allocation') and immediately defines what the resource is: intended plan vs actual holdings. It explicitly contrasts with get_portfolio's drift and set_target_allocation, so an agent can distinguish it from every sibling without reading schemas.

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

Usage Guidelines5/5

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

Provides concrete trigger phrases ('what is my target allocation', 'what was my plan') and explicitly says to call it before set_target_allocation so edits start from the real plan. It also names the alternative for actual-vs-target comparison (drift in get_portfolio).

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

get_trading_activityLifetime trade totals per asset and daily activity calendarA
Read-onlyIdempotent
Inspect

Read trading activity over the whole history (no window), in one of two views. asset_totals: for every asset traded, one row per asset and quote currency with quantity bought and sold, amount spent and received in that quote, trade count and first and last trade time, largest buy volume first; these are raw sums, not profit, and there is no asset filter, so pick the row you need. calendar: per UTC day with any activity, counts of trades, deposits, withdrawals and income events, trade volume in USD and realized and unrealized P&L, oldest first, plus all-time totals and the active years; past 200 active days the most recent 200 are kept and truncated says so, while totals always cover everything. Use for "how much ETH have I bought in total", "my busiest days". For realized profit per pair use get_performance; for the individual trades use get_movements with kind TRADE.

NameRequiredDescriptionDefault
viewYesWhich view to read: asset_totals (lifetime buy and sell sums per asset); calendar (per-day activity counts and P&L). Each view takes only the arguments the description lists for it.
profileNoWhich portfolio to read, for accounts with more than one: its id, or its name (exact, case-insensitive, e.g. "Trading"). Omit to read this connection's default portfolio; every answer names the portfolio it read in `profile`. An unknown or shared name returns profile_not_found or profile_ambiguous listing the real ones, and list_profiles shows them up front.

Output Schema

NameRequiredDescription
viewNoWhich view answered; its data is under the key of the same name.
errorNoPresent ONLY when the call was refused instead of answered: upgrade_required (Pro plan needed), not_found, profile_not_found, profile_ambiguous, invalid_arguments, write_not_permitted. No data fields come with it; read `message` to the user rather than retrying the same call.
messageNoPlain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user.
profileNoThe portfolio these figures belong to (absent on list_profiles).
calendarNoPresent when view = "calendar".
next_stepNoWhat the user should do about data_status.
source_urlNoLink to the page in the WhereItGoes app that shows this data.
data_statusNoPresent only when the portfolio has no data yet. nothing_connected: no exchange and no wallet is connected, so empty lists and zeros mean "no data", NOT a $0 portfolio, and no data fields come with it (except on get_connections, get_alerts, get_target_allocation and list_profiles, which also answer). Send the user to next_step. syncing: a source is connected but its first sync is still running, so figures are partial and not final.
upgrade_urlNoWhere the user can upgrade, on an upgrade_required refusal.
asset_totalsNoPresent when view = "asset_totals".
stock_assetsNoPresent when the answer mentions Binance Stocks shares (EQ_<ticker>): they are US stocks, not crypto.

TDQS

A4.8/5.0
Behavior5/5

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

Beyond the readOnly/idempotent annotations it discloses behavior that materially affects interpretation: values are raw sums not profit, there is no asset filter so the caller must pick the row, calendar is capped at the 200 most recent active days with a `truncated` flag while `totals` always cover everything. Those are exactly the traps an agent would otherwise fall into.

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

Conciseness4/5

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

The scope constraint and the two views are front-loaded and every clause carries information. The view paragraphs are dense run-on sentences, which costs readability even though little is redundant.

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

Completeness5/5

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

An output schema exists, yet the description still supplies the interpretive context that matters (raw sums, ordering, truncation) so the agent knows how to read results. Nothing needed to call this tool correctly is missing.

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

Parameters4/5

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

Schema coverage is 100%, so the baseline is 3, but the description goes further by spelling out what each view value actually yields (columns, sort order, truncation semantics) rather than just restating the enum. It adds nothing for the `profile` parameter, which is left entirely to the schema, keeping it just short of a 5.

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

Purpose5/5

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

States a specific verb (read trading activity) with an explicit scope (whole history, no window) and enumerates the two views and their row shapes. An agent can immediately tell this apart from get_performance and get_movements.

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

Usage Guidelines5/5

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

Gives concrete example questions ("how much ETH have I bought in total", "my busiest days") plus explicit routing: get_performance for realized profit per pair, get_movements with kind TRADE for individual trades. Both the when and the when-not are covered.

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

list_profilesPortfolios on this accountA
Read-onlyIdempotent
Inspect

List the portfolios on this account (id, name, and how many exchanges and wallets each holds), plus which one is active in the app. Call this FIRST when the user names a portfolio ("what is in my trading account") so you can pass its name or id as the profile argument of another tool. Takes no arguments; an account with one portfolio needs none of this, because every tool reads the connection's default portfolio when profile is omitted.

NameRequiredDescriptionDefault

No parameters

Output Schema

NameRequiredDescription
errorNoPresent ONLY when the call was refused instead of answered: upgrade_required (Pro plan needed), not_found, profile_not_found, profile_ambiguous, invalid_arguments, write_not_permitted. No data fields come with it; read `message` to the user rather than retrying the same call.
itemsNo
activeNoId of the portfolio active in the app.
messageNoPlain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user.
profileNoThe portfolio these figures belong to (absent on list_profiles).
next_stepNoWhat the user should do about data_status.
source_urlNoLink to the page in the WhereItGoes app that shows this data.
data_statusNoPresent only when the portfolio has no data yet. nothing_connected: no exchange and no wallet is connected, so empty lists and zeros mean "no data", NOT a $0 portfolio, and no data fields come with it (except on get_connections, get_alerts, get_target_allocation and list_profiles, which also answer). Send the user to next_step. syncing: a source is connected but its first sync is still running, so figures are partial and not final.
upgrade_urlNoWhere the user can upgrade, on an upgrade_required refusal.
stock_assetsNoPresent when the answer mentions Binance Stocks shares (EQ_<ticker>): they are US stocks, not crypto.

TDQS

A4.7/5.0
Behavior4/5

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

Annotations already declare readOnlyHint, idempotentHint, non-destructive, and closed-world behavior, so the description does not need to repeat safety traits. It adds meaningful behavioral context beyond annotations: it takes no arguments, returns active-portfolio status, and explains default-portfolio behavior when `profile` is omitted.

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

Conciseness5/5

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

The description is front-loaded with the core purpose, then usage guidance, then the no-argument/default-portfolio caveat. Every sentence earns its place by helping the agent decide whether and when to call this tool.

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

Completeness5/5

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

Given zero required parameters, read-only annotations, and an output schema, the description is complete enough for correct invocation. It explains what the tool returns at a high level, when to call it, and how its output feeds the `profile` argument of other tools.

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

Parameters4/5

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

The tool has zero parameters and 100% schema description coverage, so parameter semantics are inherently simple. The description correctly notes that it takes no arguments and explains the downstream `profile` usage pattern that makes this tool relevant.

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

Purpose5/5

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

The description states a specific verb and resource: list portfolios on the account, including id, name, exchange count, wallet count, and the active portfolio. It distinguishes itself from siblings like get_portfolio by making clear this is the enumeration step used before calling other portfolio-scoped tools.

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

Usage Guidelines5/5

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

It gives explicit usage guidance: call this FIRST when the user names a portfolio, then pass the returned name or id as the `profile` argument of another tool. It also states when the call is unnecessary: accounts with one portfolio need none of this because tools default to the connection's default portfolio when `profile` is omitted.

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

propose_alert_changePropose creating, editing or deleting an alert (user approves)A
Destructive
Inspect

Propose a change to the user's price and value alerts: create a new one, edit one, or delete one, picked by action. Use this when the user asks to be told when something happens ("alert me if BTC drops below 60k", "email me when my portfolio passes 100k") or to change or remove an alert; to see which alerts exist, and their ids, call get_alerts instead, and always call it before an update or delete. This does NOT take effect when you call it: it returns a link the user has to open and approve in their own browser, so tell them to open it rather than saying it is done. Alerts are a Pro feature: an account without Pro gets upgrade_required, and a connection the user did not grant "propose changes" gets write_not_permitted; read either out and stop. Arguments by action. create: kind, op, threshold, plus subject for the asset kinds, optional repeat, cooldownSec, channels, note. update: alertId plus only the fields to change (op, threshold, enabled, repeat, cooldownSec, channels, note); kind and subject cannot be edited, so delete and re-create instead. delete: alertId only. An argument the action does not take is refused, not ignored.

NameRequiredDescriptionDefault
opNoFire when the watched value goes above or below the threshold.
kindNocreate only. What the alert watches: asset_price (one asset's USD price), asset_pct (one asset's 24h % change), portfolio_value (total USD value), portfolio_pct (portfolio 24h % change), holding_value (USD value of the user's position in one asset), holding_weight (one asset's share of the portfolio, %).
noteNoA short note shown with the alert, up to 200 characters. On update, an empty string clears it.
actionYescreate a new alert, update an existing one (by alertId), or delete one (by alertId).
repeatNoonce (default): fire one time, then pause. recurring: fire every time the level is crossed again, no more often than cooldownSec.
alertIdNoupdate and delete only: the `id` of the alert, exactly as get_alerts returns it.
enabledNoupdate only: false pauses the alert, true turns it back on.
profileNoWhich portfolio to read, for accounts with more than one: its id, or its name (exact, case-insensitive, e.g. "Trading"). Omit to read this connection's default portfolio; every answer names the portfolio it read in `profile`. An unknown or shared name returns profile_not_found or profile_ambiguous listing the real ones, and list_profiles shows them up front.
subjectNocreate only, and required for asset_price, asset_pct, holding_value and holding_weight: the ticker, e.g. BTC. Omit for the portfolio kinds.
channelsNoWhere it is delivered. ["inapp"] (default) shows it in the app's notifications; ["inapp", "email"] also emails the user (the app's "Also email me" option).
thresholdNoThe trigger level, in the kind's unit: USD for asset_price, portfolio_value and holding_value (greater than 0); percent for asset_pct and portfolio_pct (signed, so -10 means a 10% drop); percent of the portfolio, 0 to 100, for holding_weight.
cooldownSecNorecurring alerts: minimum seconds between two notifications, 60 to 604800. Omit for the default (1 hour, or 6 hours for the % and allocation kinds).

Output Schema

NameRequiredDescription
errorNoPresent ONLY when the call was refused instead of answered: upgrade_required (Pro plan needed), not_found, profile_not_found, profile_ambiguous, invalid_arguments, write_not_permitted. No data fields come with it; read `message` to the user rather than retrying the same call.
titleNoWhat the change does.
statusNoAlways this: nothing has been applied yet.
messageNoPlain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user.
previewNoCourtesy diff; the authoritative one is on the confirm page.
profileNoThe portfolio these figures belong to (absent on list_profiles).
next_stepNoWhat the user should do about data_status.
expires_atNoWhen the link stops working, epoch ms.
source_urlNoLink to the page in the WhereItGoes app that shows this data.
confirm_urlNoThe link the user must open and approve; tell them to open it.
data_statusNoPresent only when the portfolio has no data yet. nothing_connected: no exchange and no wallet is connected, so empty lists and zeros mean "no data", NOT a $0 portfolio, and no data fields come with it (except on get_connections, get_alerts, get_target_allocation and list_profiles, which also answer). Send the user to next_step. syncing: a source is connected but its first sync is still running, so figures are partial and not final.
upgrade_urlNoWhere the user can upgrade, on an upgrade_required refusal.
stock_assetsNoPresent when the answer mentions Binance Stocks shares (EQ_<ticker>): they are US stocks, not crypto.

TDQS

A4.6/5.0
Behavior4/5

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

Annotations already declare destructiveHint=true and idempotentHint=false, and the description reinforces this by clarifying the call itself is non-applying: it returns a link the user must approve in their browser. It also discloses the Pro-gating errors (upgrade_required, write_not_permitted) and that non-applicable arguments are refused rather than ignored. It does not cover rate limits or what happens if the user rejects the proposal, which keeps it just short of a 5.

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

Conciseness4/5

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

The critical routing and approval behavior is front-loaded in the first two sentences, with errors and per-action argument lists following in a predictable order. It is long for a description, and the argument enumeration partially repeats what the 100%-covered schema already says, but every sentence carries operational weight.

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

Completeness5/5

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

Covers everything an agent needs to avoid misusing a 12-parameter, non-idempotent mutation tool: the approval-link flow, prerequisite listing call, permission errors, and action-specific argument rules. An output schema exists, so the description is not obligated to explain return values, and it still mentions the returned link and error codes where relevant.

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

Parameters4/5

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

Schema coverage is already 100%, so the baseline is 3, but the description goes beyond it with action-conditional parameter sets (create needs kind/op/threshold/subject; update takes only the fields to change; delete takes alertId alone) and the immutability rule that kind and subject cannot be edited. It does not restate the numeric bounds or enum meanings, which the schema already handles well.

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

Purpose5/5

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

States a specific verb (propose a change) and resource (price and value alerts), and enumerates the three sub-operations selected by `action`. It explicitly contrasts with get_alerts, so an agent can distinguish it from the only sibling that touches alerts.

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

Usage Guidelines5/5

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

Gives concrete trigger examples ('alert me if BTC drops below 60k'), names the alternative tool for listing (get_alerts), and imposes an ordering rule: always call get_alerts before an update or delete. That is explicit when-to-use and when-not guidance rather than implied intent.

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

set_target_allocationPropose a target allocation change (user approves)A
Destructive
Inspect

Propose a change to the user's target allocation - the plan they mean to hold. This REPLACES the whole plan: send every target you want to keep, not just the ones you are changing, because any subject you omit is deleted. Send an empty rows to delete the plan entirely. dim must be "asset" (subjects are tickers) or "sector" (subjects are our sector names - call get_target_allocation first if you are unsure which axis their plan uses). This does NOT take effect when you call it: it returns a link the user has to open and approve themselves, so tell them to open it. Read get_target_allocation first so you are editing their real plan rather than guessing at it.

NameRequiredDescriptionDefault
dimYesWhich axis the plan is on. Every subject must belong to this axis.
rowsYesThe COMPLETE plan. Anything omitted is removed.
profileNoWhich portfolio to read, for accounts with more than one: its id, or its name (exact, case-insensitive, e.g. "Trading"). Omit to read this connection's default portfolio; every answer names the portfolio it read in `profile`. An unknown or shared name returns profile_not_found or profile_ambiguous listing the real ones, and list_profiles shows them up front.

Output Schema

NameRequiredDescription
errorNoPresent ONLY when the call was refused instead of answered: upgrade_required (Pro plan needed), not_found, profile_not_found, profile_ambiguous, invalid_arguments, write_not_permitted. No data fields come with it; read `message` to the user rather than retrying the same call.
titleNoWhat the change does.
statusNoAlways this: nothing has been applied yet.
messageNoPlain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user.
previewNoCourtesy diff; the authoritative one is on the confirm page.
profileNoThe portfolio these figures belong to (absent on list_profiles).
next_stepNoWhat the user should do about data_status.
expires_atNoWhen the link stops working, epoch ms.
source_urlNoLink to the page in the WhereItGoes app that shows this data.
confirm_urlNoThe link the user must open and approve; tell them to open it.
data_statusNoPresent only when the portfolio has no data yet. nothing_connected: no exchange and no wallet is connected, so empty lists and zeros mean "no data", NOT a $0 portfolio, and no data fields come with it (except on get_connections, get_alerts, get_target_allocation and list_profiles, which also answer). Send the user to next_step. syncing: a source is connected but its first sync is still running, so figures are partial and not final.
upgrade_urlNoWhere the user can upgrade, on an upgrade_required refusal.
stock_assetsNoPresent when the answer mentions Binance Stocks shares (EQ_<ticker>): they are US stocks, not crypto.

TDQS

A4.8/5.0
Behavior5/5

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

Annotations already flag destructiveHint=true, but the description supplies the context annotations cannot: the operation REPLACES the entire plan, omitted subjects are deleted, an empty rows deletes the plan outright, and nothing takes effect until the user approves a returned link. This is exactly the extra behavioral detail the annotations leave out.

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

Conciseness4/5

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

The destructive replace warning is front-loaded and every sentence carries operational weight. There is mild redundancy in telling the agent to read get_target_allocation first twice (once for dim, once for the overall plan), which keeps it just short of a 5.

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

Completeness5/5

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

Given an output schema and rich annotations, the description covers the remaining gaps an agent needs: destruction semantics, the empty-rows edge case, dim axis expectations, and the approval-link flow. Nothing required 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.

Parameters4/5

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

Schema coverage is 100%, so the baseline is 3; the description earns an extra point by restating the replacement semantics of rows in operational terms and explaining that dim must match the subjects ('tickers' vs 'our sector names') with a fallback instruction. The profile parameter is left to the schema, which is acceptable given its thorough inline description.

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

Purpose5/5

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

The first sentence states a specific verb and resource ('Propose a change to the user's target allocation') and immediately clarifies scope ('the plan they mean to hold'), which separates it cleanly from the sibling read tool get_target_allocation. An agent can identify what the tool does without opening the schema.

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

Usage Guidelines5/5

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

The description explicitly routes the agent: 'Read get_target_allocation first so you are editing their real plan rather than guessing at it' and 'call get_target_allocation first if you are unsure which axis their plan uses.' It also states the follow-up obligation ('tell them to open it'), so the full use condition and next step are prescribed.

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.

  1. 1 tool update
    • Changedget_portfolio12 fields changed
      • changedOutput schema / properties / holdings / properties / assets / items / properties / position / anyOf
        Previous value: -[
        -  {
        -    "additionalProperties": true,
        -    "properties": {
        -      "apyPct": {
        -        "description": "Supply APY percent where the protocol publishes one; null = unknown.",
        -        "type": [
        -          "number",
        -          "null"
        -        ]
        -      },
        -      "chain": {
        -        "description": "Chain the receipt sits on.",
        -        "type": "string"
        -      },
        -      "contract": {
        -        "description": "The receipt token's contract address.",
        -        "type": "string"
        -      },
        -      "expiresAt": {
        -        "description": "Pendle maturity, epoch ms; past it the position is matured (redeemable).",
        -        "type": [
        -          "number",
        -          "null"
        -        ]
        -      },
        -      "folded": {
        -        "description": "true when \"Merge related tokens\" counted it inside its underlying's row (aUSDC inside USDC).",
        -        "type": "boolean"
        -      },
        -      "kind": {
        -        "description": "Receipt kind: aave-atoken, comet, erc4626, lst, pendle-pt/yt/lp/sy, stargate-lp, curve-lp, curve-gauge.",
        -        "type": "string"
        -      },
        -      "proofSource": {
        -        "description": "Which official registry proved the token.",
        -        "type": "string"
        -      },
        -      "protocol": {
        -        "description": "Protocol key, e.g. \"aave-v3\", \"morpho\", \"pendle\".",
        -        "type": "string"
        -      },
        -      "protocolLabel": {
        -        "description": "Protocol as a person reads it, e.g. \"Aave v3\".",
        -        "type": "string"
        -      },
        -      "qty": {
        -        "description": "Receipt quantity, native units.",
        -        "type": "string"
        -      },
        -      "receipt": {
        -        "description": "The receipt token's own ticker, e.g. \"aArbWETH\". Not what to call it.",
        -        "type": "string"
        -      },
        -      "summary": {
        -        "description": "How to say it: \"Aave v3 WETH supply on Arbitrum\".",
        -        "type": "string"
        -      },
        -      "underlying": {
        -        "description": "What the position is a claim on, e.g. \"WETH\"; Curve pools list their coins joined with \"/\".",
        -        "type": "string"
        -      },
        -      "underlyingQty": {
        -        "description": "Quantity of the underlying the receipt is worth (qty x protocol rate), in underlyingUnit.",
        -        "type": [
        -          "string",
        -          "null"
        -        ]
        -      },
        -      "underlyingUnit": {
        -        "description": "Unit of underlyingQty.",
        -        "type": "string"
        -      },
        -      "valueUsdt": {
        -        "description": "USD value of this position.",
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "kind",
        -      "protocol",
        -      "protocolLabel",
        -      "receipt",
        -      "contract",
        -      "underlying",
        -      "chain",
        -      "qty",
        -      "underlyingQty",
        -      "underlyingUnit",
        -      "valueUsdt",
        -      "apyPct",
        -      "proofSource",
        -      "folded"
        -    ],
        -    "type": "object"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "additionalProperties": true,
        +    "properties": {
        +      "apyPct": {
        +        "description": "Supply APY percent where the protocol publishes one; null = unknown.",
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      },
        +      "chain": {
        +        "description": "Chain the receipt sits on.",
        +        "type": "string"
        +      },
        +      "contract": {
        +        "description": "The receipt token's contract address.",
        +        "type": "string"
        +      },
        +      "expiresAt": {
        +        "description": "Pendle maturity, epoch ms; past it the position is matured (redeemable).",
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      },
        +      "folded": {
        +        "description": "true when \"Merge related tokens\" counted it inside its underlying's row (aUSDC inside USDC).",
        +        "type": "boolean"
        +      },
        +      "kind": {
        +        "description": "Receipt kind: aave-atoken, comet, erc4626, lst, pendle-pt/yt/lp/sy, stargate-lp, curve-lp, curve-gauge, univ2-lp.",
        +        "type": "string"
        +      },
        +      "proofSource": {
        +        "description": "Which official registry proved the token.",
        +        "type": "string"
        +      },
        +      "protocol": {
        +        "description": "Protocol key, e.g. \"aave-v3\", \"morpho\", \"pendle\".",
        +        "type": "string"
        +      },
        +      "protocolLabel": {
        +        "description": "Protocol as a person reads it, e.g. \"Aave v3\".",
        +        "type": "string"
        +      },
        +      "qty": {
        +        "description": "Receipt quantity, native units.",
        +        "type": "string"
        +      },
        +      "receipt": {
        +        "description": "The receipt token's own ticker, e.g. \"aArbWETH\". Not what to call it.",
        +        "type": "string"
        +      },
        +      "summary": {
        +        "description": "How to say it: \"Aave v3 WETH supply on Arbitrum\".",
        +        "type": "string"
        +      },
        +      "underlying": {
        +        "description": "What the position is a claim on, e.g. \"WETH\"; Curve pools and Uniswap v2 pairs list their coins joined with \"/\".",
        +        "type": "string"
        +      },
        +      "underlyingQty": {
        +        "description": "Quantity of the underlying the receipt is worth (qty x protocol rate), in underlyingUnit.",
        +        "type": [
        +          "string",
        +          "null"
        +        ]
        +      },
        +      "underlyingUnit": {
        +        "description": "Unit of underlyingQty.",
        +        "type": "string"
        +      },
        +      "valueUsdt": {
        +        "description": "USD value of this position.",
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "kind",
        +      "protocol",
        +      "protocolLabel",
        +      "receipt",
        +      "contract",
        +      "underlying",
        +      "chain",
        +      "qty",
        +      "underlyingQty",
        +      "underlyingUnit",
        +      "valueUsdt",
        +      "apyPct",
        +      "proofSource",
        +      "folded"
        +    ],
        +    "type": "object"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • changedOutput schema / properties / holdings / properties / assets / items / properties / positions / items / properties / kind / description
        Previous value: -"Receipt kind: aave-atoken, comet, erc4626, lst, pendle-pt/yt/lp/sy, stargate-lp, curve-lp, curve-gauge."New value: +"Receipt kind: aave-atoken, comet, erc4626, lst, pendle-pt/yt/lp/sy, stargate-lp, curve-lp, curve-gauge, univ2-lp."
      • changedOutput schema / properties / holdings / properties / assets / items / properties / positions / items / properties / underlying / description
        Previous value: -"What the position is a claim on, e.g. \"WETH\"; Curve pools list their coins joined with \"/\"."New value: +"What the position is a claim on, e.g. \"WETH\"; Curve pools and Uniswap v2 pairs list their coins joined with \"/\"."
      • addedOutput schema / properties / holdings / properties / assets / items / properties / userTrusted
        Added value: +{
        +  "additionalProperties": true,
        +  "description": "Present when the USER counted this token from the unverified list: valued at an estimate, not a verified price. Say so when you cite it.",
        +  "properties": {
        +    "chainId": {
        +      "type": "string"
        +    },
        +    "contract": {
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "chainId",
        +    "contract"
        +  ],
        +  "type": "object"
        +}
      • changedOutput schema / properties / holdings / properties / assets / items / properties / weightPct / description
        Previous value: -"Share of totalUsdt, percent."New value: +"Share of grossUsdt (the holdings), percent."
      • removedOutput schema / properties / holdings / properties / excludedDebt
        Removed value: -{
        -  "additionalProperties": true,
        -  "description": "Borrowings (Aave debt tokens) found on-chain and NOT counted in totalUsdt or any row. Present only when there are some.",
        -  "properties": {
        -    "count": {
        -      "description": "Proven debt tokens left out.",
        -      "type": "number"
        -    },
        -    "protocols": {
        -      "description": "Protocols borrowed from.",
        -      "items": {
        -        "type": "string"
        -      },
        -      "type": "array"
        -    },
        -    "valueUsdt": {
        -      "description": "What they are worth, USD, as a POSITIVE amount owed.",
        -      "type": "string"
        -    }
        -  },
        -  "required": [
        -    "count",
        -    "valueUsdt",
        -    "protocols"
        -  ],
        -  "type": "object"
        -}
      • addedOutput schema / properties / holdings / properties / grossUsdt
        Added value: +{
        +  "description": "What the holdings rows are worth, USD (sum of assets[].valueUsdt).",
        +  "type": "string"
        +}
      • addedOutput schema / properties / holdings / properties / liabilities
        Added value: +{
        +  "description": "Proven DeFi borrowings (Aave-family debt tokens), largest first. Never rows of assets; subtracted from totalUsdt.",
        +  "items": {
        +    "additionalProperties": true,
        +    "properties": {
        +      "chain": {
        +        "type": "string"
        +      },
        +      "kind": {
        +        "const": "aave-debt",
        +        "type": "string"
        +      },
        +      "protocol": {
        +        "type": "string"
        +      },
        +      "protocolLabel": {
        +        "type": "string"
        +      },
        +      "qty": {
        +        "description": "Amount owed in the underlying, interest included.",
        +        "type": "string"
        +      },
        +      "receipt": {
        +        "description": "The debt token ticker, e.g. \"variableDebtBasUSDC\".",
        +        "type": "string"
        +      },
        +      "summary": {
        +        "description": "The sentence to say, e.g. \"Aave v3 USDC borrowed on Base\".",
        +        "type": "string"
        +      },
        +      "underlying": {
        +        "description": "What is owed, e.g. \"USDC\".",
        +        "type": "string"
        +      },
        +      "valueUsdt": {
        +        "description": "Amount owed, USD, POSITIVE.",
        +        "type": "string"
        +      },
        +      "venue": {
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "kind",
        +      "protocol",
        +      "protocolLabel",
        +      "receipt",
        +      "underlying",
        +      "chain",
        +      "venue",
        +      "qty",
        +      "valueUsdt"
        +    ],
        +    "type": "object"
        +  },
        +  "type": "array"
        +}
      • addedOutput schema / properties / holdings / properties / liabilitiesUsdt
        Added value: +{
        +  "description": "What is owed against them, USD, a POSITIVE amount (sum of liabilities[].valueUsdt).",
        +  "type": "string"
        +}
      • changedOutput schema / properties / holdings / properties / totalUsdt / description
        Previous value: -"Whole portfolio value in USD, decimal string with 2 decimals."New value: +"NET WORTH in USD: grossUsdt minus liabilitiesUsdt, decimal string with 2 decimals."
      • addedOutput schema / properties / holdings / properties / unverified
        Added value: +{
        +  "description": "UNVERIFIED wallet tokens the trust checks do not count: never holdings, never in totalUsdt. Tokens with no market are only counted in unverifiedSummary.",
        +  "items": {
        +    "additionalProperties": true,
        +    "properties": {
        +      "chain": {
        +        "type": "string"
        +      },
        +      "choice": {
        +        "description": "The user's call: count (now also a row of assets, userTrusted), hide, or null.",
        +        "type": [
        +          "string",
        +          "null"
        +        ]
        +      },
        +      "contract": {
        +        "type": "string"
        +      },
        +      "estimatedValueUsdt": {
        +        "description": "Best-effort USD ESTIMATE, only for a token with a real DEX pool deep enough to sell it. NOT in any total.",
        +        "type": [
        +          "string",
        +          "null"
        +        ]
        +      },
        +      "liquidityUsd": {
        +        "description": "Reserve of the deepest qualifying pool, USD.",
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      },
        +      "market": {
        +        "description": "liquid | no-market | unchecked.",
        +        "type": "string"
        +      },
        +      "priceSource": {
        +        "type": [
        +          "string",
        +          "null"
        +        ]
        +      },
        +      "qty": {
        +        "description": "Quantity held, native units.",
        +        "type": "string"
        +      },
        +      "reason": {
        +        "description": "Why it is not counted: unlisted, thin-market, no-evidence, unpriced, receipt-unpriced.",
        +        "type": "string"
        +      },
        +      "symbol": {
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "symbol",
        +      "chain",
        +      "contract",
        +      "qty",
        +      "estimatedValueUsdt",
        +      "liquidityUsd",
        +      "market",
        +      "reason",
        +      "priceSource",
        +      "choice"
        +    ],
        +    "type": "object"
        +  },
        +  "type": "array"
        +}
      • addedOutput schema / properties / holdings / properties / unverifiedSummary
        Added value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "countedCount": {
        +      "type": "number"
        +    },
        +    "countedUsdt": {
        +      "description": "USD of the ones the user counted (already inside totalUsdt).",
        +      "type": "string"
        +    },
        +    "estimatedTotalUsdt": {
        +      "description": "ABOUT figure: totalUsdt plus liquidUsdt. Say \"about\"; it is not the verified total.",
        +      "type": "string"
        +    },
        +    "hiddenCount": {
        +      "type": "number"
        +    },
        +    "liquidCount": {
        +      "type": "number"
        +    },
        +    "liquidUsdt": {
        +      "description": "Estimated USD of the liquid, undecided unverified tokens.",
        +      "type": "string"
        +    },
        +    "noMarketCount": {
        +      "type": "number"
        +    },
        +    "uncheckedCount": {
        +      "type": "number"
        +    }
        +  },
        +  "required": [
        +    "liquidCount",
        +    "liquidUsdt",
        +    "noMarketCount",
        +    "uncheckedCount",
        +    "countedCount",
        +    "countedUsdt",
        +    "hiddenCount",
        +    "estimatedTotalUsdt"
        +  ],
        +  "type": "object"
        +}
  2. 15 tool updates
    • Changedget_alerts3 fields changed
      • addedOutput schema / properties / data_status
        Added value: +{
        +  "description": "Present only when the portfolio has no data yet. nothing_connected: no exchange and no wallet is connected, so empty lists and zeros mean \"no data\", NOT a $0 portfolio, and no data fields come with it (except on get_connections, get_alerts, get_target_allocation and list_profiles, which also answer). Send the user to next_step. syncing: a source is connected but its first sync is still running, so figures are partial and not final.",
        +  "enum": [
        +    "nothing_connected",
        +    "syncing"
        +  ],
        +  "type": "string"
        +}
      • changedOutput schema / properties / message / description
        Previous value: -"Plain-language explanation that accompanies `error` or a proposed change."New value: +"Plain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user."
      • addedOutput schema / properties / next_step
        Added value: +{
        +  "additionalProperties": true,
        +  "description": "What the user should do about data_status.",
        +  "properties": {
        +    "action": {
        +      "enum": [
        +        "connect_a_source",
        +        "wait_for_first_sync"
        +      ],
        +      "type": "string"
        +    },
        +    "add_exchange_url": {
        +      "description": "App page to connect an exchange with a read-only API key.",
        +      "type": "string"
        +    },
        +    "add_wallet_url": {
        +      "description": "App page to add a wallet address to watch.",
        +      "type": "string"
        +    },
        +    "settings_url": {
        +      "type": "string"
        +    },
        +    "sync_status_url": {
        +      "description": "App page showing sync progress.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "action"
        +  ],
        +  "type": "object"
        +}
    • Changedget_changes3 fields changed
      • addedOutput schema / properties / data_status
        Added value: +{
        +  "description": "Present only when the portfolio has no data yet. nothing_connected: no exchange and no wallet is connected, so empty lists and zeros mean \"no data\", NOT a $0 portfolio, and no data fields come with it (except on get_connections, get_alerts, get_target_allocation and list_profiles, which also answer). Send the user to next_step. syncing: a source is connected but its first sync is still running, so figures are partial and not final.",
        +  "enum": [
        +    "nothing_connected",
        +    "syncing"
        +  ],
        +  "type": "string"
        +}
      • changedOutput schema / properties / message / description
        Previous value: -"Plain-language explanation that accompanies `error` or a proposed change."New value: +"Plain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user."
      • addedOutput schema / properties / next_step
        Added value: +{
        +  "additionalProperties": true,
        +  "description": "What the user should do about data_status.",
        +  "properties": {
        +    "action": {
        +      "enum": [
        +        "connect_a_source",
        +        "wait_for_first_sync"
        +      ],
        +      "type": "string"
        +    },
        +    "add_exchange_url": {
        +      "description": "App page to connect an exchange with a read-only API key.",
        +      "type": "string"
        +    },
        +    "add_wallet_url": {
        +      "description": "App page to add a wallet address to watch.",
        +      "type": "string"
        +    },
        +    "settings_url": {
        +      "type": "string"
        +    },
        +    "sync_status_url": {
        +      "description": "App page showing sync progress.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "action"
        +  ],
        +  "type": "object"
        +}
    • Changedget_connections3 fields changed
      • addedOutput schema / properties / data_status
        Added value: +{
        +  "description": "Present only when the portfolio has no data yet. nothing_connected: no exchange and no wallet is connected, so empty lists and zeros mean \"no data\", NOT a $0 portfolio, and no data fields come with it (except on get_connections, get_alerts, get_target_allocation and list_profiles, which also answer). Send the user to next_step. syncing: a source is connected but its first sync is still running, so figures are partial and not final.",
        +  "enum": [
        +    "nothing_connected",
        +    "syncing"
        +  ],
        +  "type": "string"
        +}
      • changedOutput schema / properties / message / description
        Previous value: -"Plain-language explanation that accompanies `error` or a proposed change."New value: +"Plain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user."
      • addedOutput schema / properties / next_step
        Added value: +{
        +  "additionalProperties": true,
        +  "description": "What the user should do about data_status.",
        +  "properties": {
        +    "action": {
        +      "enum": [
        +        "connect_a_source",
        +        "wait_for_first_sync"
        +      ],
        +      "type": "string"
        +    },
        +    "add_exchange_url": {
        +      "description": "App page to connect an exchange with a read-only API key.",
        +      "type": "string"
        +    },
        +    "add_wallet_url": {
        +      "description": "App page to add a wallet address to watch.",
        +      "type": "string"
        +    },
        +    "settings_url": {
        +      "type": "string"
        +    },
        +    "sync_status_url": {
        +      "description": "App page showing sync progress.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "action"
        +  ],
        +  "type": "object"
        +}
    • Changedget_fees3 fields changed
      • addedOutput schema / properties / data_status
        Added value: +{
        +  "description": "Present only when the portfolio has no data yet. nothing_connected: no exchange and no wallet is connected, so empty lists and zeros mean \"no data\", NOT a $0 portfolio, and no data fields come with it (except on get_connections, get_alerts, get_target_allocation and list_profiles, which also answer). Send the user to next_step. syncing: a source is connected but its first sync is still running, so figures are partial and not final.",
        +  "enum": [
        +    "nothing_connected",
        +    "syncing"
        +  ],
        +  "type": "string"
        +}
      • changedOutput schema / properties / message / description
        Previous value: -"Plain-language explanation that accompanies `error` or a proposed change."New value: +"Plain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user."
      • addedOutput schema / properties / next_step
        Added value: +{
        +  "additionalProperties": true,
        +  "description": "What the user should do about data_status.",
        +  "properties": {
        +    "action": {
        +      "enum": [
        +        "connect_a_source",
        +        "wait_for_first_sync"
        +      ],
        +      "type": "string"
        +    },
        +    "add_exchange_url": {
        +      "description": "App page to connect an exchange with a read-only API key.",
        +      "type": "string"
        +    },
        +    "add_wallet_url": {
        +      "description": "App page to add a wallet address to watch.",
        +      "type": "string"
        +    },
        +    "settings_url": {
        +      "type": "string"
        +    },
        +    "sync_status_url": {
        +      "description": "App page showing sync progress.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "action"
        +  ],
        +  "type": "object"
        +}
    • Changedget_income3 fields changed
      • addedOutput schema / properties / data_status
        Added value: +{
        +  "description": "Present only when the portfolio has no data yet. nothing_connected: no exchange and no wallet is connected, so empty lists and zeros mean \"no data\", NOT a $0 portfolio, and no data fields come with it (except on get_connections, get_alerts, get_target_allocation and list_profiles, which also answer). Send the user to next_step. syncing: a source is connected but its first sync is still running, so figures are partial and not final.",
        +  "enum": [
        +    "nothing_connected",
        +    "syncing"
        +  ],
        +  "type": "string"
        +}
      • changedOutput schema / properties / message / description
        Previous value: -"Plain-language explanation that accompanies `error` or a proposed change."New value: +"Plain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user."
      • addedOutput schema / properties / next_step
        Added value: +{
        +  "additionalProperties": true,
        +  "description": "What the user should do about data_status.",
        +  "properties": {
        +    "action": {
        +      "enum": [
        +        "connect_a_source",
        +        "wait_for_first_sync"
        +      ],
        +      "type": "string"
        +    },
        +    "add_exchange_url": {
        +      "description": "App page to connect an exchange with a read-only API key.",
        +      "type": "string"
        +    },
        +    "add_wallet_url": {
        +      "description": "App page to add a wallet address to watch.",
        +      "type": "string"
        +    },
        +    "settings_url": {
        +      "type": "string"
        +    },
        +    "sync_status_url": {
        +      "description": "App page showing sync progress.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "action"
        +  ],
        +  "type": "object"
        +}
    • Changedget_money_flows3 fields changed
      • addedOutput schema / properties / data_status
        Added value: +{
        +  "description": "Present only when the portfolio has no data yet. nothing_connected: no exchange and no wallet is connected, so empty lists and zeros mean \"no data\", NOT a $0 portfolio, and no data fields come with it (except on get_connections, get_alerts, get_target_allocation and list_profiles, which also answer). Send the user to next_step. syncing: a source is connected but its first sync is still running, so figures are partial and not final.",
        +  "enum": [
        +    "nothing_connected",
        +    "syncing"
        +  ],
        +  "type": "string"
        +}
      • changedOutput schema / properties / message / description
        Previous value: -"Plain-language explanation that accompanies `error` or a proposed change."New value: +"Plain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user."
      • addedOutput schema / properties / next_step
        Added value: +{
        +  "additionalProperties": true,
        +  "description": "What the user should do about data_status.",
        +  "properties": {
        +    "action": {
        +      "enum": [
        +        "connect_a_source",
        +        "wait_for_first_sync"
        +      ],
        +      "type": "string"
        +    },
        +    "add_exchange_url": {
        +      "description": "App page to connect an exchange with a read-only API key.",
        +      "type": "string"
        +    },
        +    "add_wallet_url": {
        +      "description": "App page to add a wallet address to watch.",
        +      "type": "string"
        +    },
        +    "settings_url": {
        +      "type": "string"
        +    },
        +    "sync_status_url": {
        +      "description": "App page showing sync progress.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "action"
        +  ],
        +  "type": "object"
        +}
    • Changedget_movements3 fields changed
      • addedOutput schema / properties / data_status
        Added value: +{
        +  "description": "Present only when the portfolio has no data yet. nothing_connected: no exchange and no wallet is connected, so empty lists and zeros mean \"no data\", NOT a $0 portfolio, and no data fields come with it (except on get_connections, get_alerts, get_target_allocation and list_profiles, which also answer). Send the user to next_step. syncing: a source is connected but its first sync is still running, so figures are partial and not final.",
        +  "enum": [
        +    "nothing_connected",
        +    "syncing"
        +  ],
        +  "type": "string"
        +}
      • changedOutput schema / properties / message / description
        Previous value: -"Plain-language explanation that accompanies `error` or a proposed change."New value: +"Plain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user."
      • addedOutput schema / properties / next_step
        Added value: +{
        +  "additionalProperties": true,
        +  "description": "What the user should do about data_status.",
        +  "properties": {
        +    "action": {
        +      "enum": [
        +        "connect_a_source",
        +        "wait_for_first_sync"
        +      ],
        +      "type": "string"
        +    },
        +    "add_exchange_url": {
        +      "description": "App page to connect an exchange with a read-only API key.",
        +      "type": "string"
        +    },
        +    "add_wallet_url": {
        +      "description": "App page to add a wallet address to watch.",
        +      "type": "string"
        +    },
        +    "settings_url": {
        +      "type": "string"
        +    },
        +    "sync_status_url": {
        +      "description": "App page showing sync progress.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "action"
        +  ],
        +  "type": "object"
        +}
    • Changedget_performance3 fields changed
      • addedOutput schema / properties / data_status
        Added value: +{
        +  "description": "Present only when the portfolio has no data yet. nothing_connected: no exchange and no wallet is connected, so empty lists and zeros mean \"no data\", NOT a $0 portfolio, and no data fields come with it (except on get_connections, get_alerts, get_target_allocation and list_profiles, which also answer). Send the user to next_step. syncing: a source is connected but its first sync is still running, so figures are partial and not final.",
        +  "enum": [
        +    "nothing_connected",
        +    "syncing"
        +  ],
        +  "type": "string"
        +}
      • changedOutput schema / properties / message / description
        Previous value: -"Plain-language explanation that accompanies `error` or a proposed change."New value: +"Plain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user."
      • addedOutput schema / properties / next_step
        Added value: +{
        +  "additionalProperties": true,
        +  "description": "What the user should do about data_status.",
        +  "properties": {
        +    "action": {
        +      "enum": [
        +        "connect_a_source",
        +        "wait_for_first_sync"
        +      ],
        +      "type": "string"
        +    },
        +    "add_exchange_url": {
        +      "description": "App page to connect an exchange with a read-only API key.",
        +      "type": "string"
        +    },
        +    "add_wallet_url": {
        +      "description": "App page to add a wallet address to watch.",
        +      "type": "string"
        +    },
        +    "settings_url": {
        +      "type": "string"
        +    },
        +    "sync_status_url": {
        +      "description": "App page showing sync progress.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "action"
        +  ],
        +  "type": "object"
        +}
    • Changedget_portfolio6 fields changed
      • addedOutput schema / properties / data_status
        Added value: +{
        +  "description": "Present only when the portfolio has no data yet. nothing_connected: no exchange and no wallet is connected, so empty lists and zeros mean \"no data\", NOT a $0 portfolio, and no data fields come with it (except on get_connections, get_alerts, get_target_allocation and list_profiles, which also answer). Send the user to next_step. syncing: a source is connected but its first sync is still running, so figures are partial and not final.",
        +  "enum": [
        +    "nothing_connected",
        +    "syncing"
        +  ],
        +  "type": "string"
        +}
      • addedOutput schema / properties / holdings / properties / assets / items / properties / position
        Added value: +{
        +  "anyOf": [
        +    {
        +      "additionalProperties": true,
        +      "properties": {
        +        "apyPct": {
        +          "description": "Supply APY percent where the protocol publishes one; null = unknown.",
        +          "type": [
        +            "number",
        +            "null"
        +          ]
        +        },
        +        "chain": {
        +          "description": "Chain the receipt sits on.",
        +          "type": "string"
        +        },
        +        "contract": {
        +          "description": "The receipt token's contract address.",
        +          "type": "string"
        +        },
        +        "expiresAt": {
        +          "description": "Pendle maturity, epoch ms; past it the position is matured (redeemable).",
        +          "type": [
        +            "number",
        +            "null"
        +          ]
        +        },
        +        "folded": {
        +          "description": "true when \"Merge related tokens\" counted it inside its underlying's row (aUSDC inside USDC).",
        +          "type": "boolean"
        +        },
        +        "kind": {
        +          "description": "Receipt kind: aave-atoken, comet, erc4626, lst, pendle-pt/yt/lp/sy, stargate-lp, curve-lp, curve-gauge.",
        +          "type": "string"
        +        },
        +        "proofSource": {
        +          "description": "Which official registry proved the token.",
        +          "type": "string"
        +        },
        +        "protocol": {
        +          "description": "Protocol key, e.g. \"aave-v3\", \"morpho\", \"pendle\".",
        +          "type": "string"
        +        },
        +        "protocolLabel": {
        +          "description": "Protocol as a person reads it, e.g. \"Aave v3\".",
        +          "type": "string"
        +        },
        +        "qty": {
        +          "description": "Receipt quantity, native units.",
        +          "type": "string"
        +        },
        +        "receipt": {
        +          "description": "The receipt token's own ticker, e.g. \"aArbWETH\". Not what to call it.",
        +          "type": "string"
        +        },
        +        "summary": {
        +          "description": "How to say it: \"Aave v3 WETH supply on Arbitrum\".",
        +          "type": "string"
        +        },
        +        "underlying": {
        +          "description": "What the position is a claim on, e.g. \"WETH\"; Curve pools list their coins joined with \"/\".",
        +          "type": "string"
        +        },
        +        "underlyingQty": {
        +          "description": "Quantity of the underlying the receipt is worth (qty x protocol rate), in underlyingUnit.",
        +          "type": [
        +            "string",
        +            "null"
        +          ]
        +        },
        +        "underlyingUnit": {
        +          "description": "Unit of underlyingQty.",
        +          "type": "string"
        +        },
        +        "valueUsdt": {
        +          "description": "USD value of this position.",
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "kind",
        +        "protocol",
        +        "protocolLabel",
        +        "receipt",
        +        "contract",
        +        "underlying",
        +        "chain",
        +        "qty",
        +        "underlyingQty",
        +        "underlyingUnit",
        +        "valueUsdt",
        +        "apyPct",
        +        "proofSource",
        +        "folded"
        +      ],
        +      "type": "object"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "Present when the row IS a DeFi position (a receipt token such as aArbWETH): name it by position.summary, never by the receipt ticker."
        +}
      • addedOutput schema / properties / holdings / properties / assets / items / properties / positions
        Added value: +{
        +  "description": "Every proven DeFi receipt under this row, including ones folded into it (USDC carrying aUSDC).",
        +  "items": {
        +    "additionalProperties": true,
        +    "properties": {
        +      "apyPct": {
        +        "description": "Supply APY percent where the protocol publishes one; null = unknown.",
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      },
        +      "chain": {
        +        "description": "Chain the receipt sits on.",
        +        "type": "string"
        +      },
        +      "contract": {
        +        "description": "The receipt token's contract address.",
        +        "type": "string"
        +      },
        +      "expiresAt": {
        +        "description": "Pendle maturity, epoch ms; past it the position is matured (redeemable).",
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      },
        +      "folded": {
        +        "description": "true when \"Merge related tokens\" counted it inside its underlying's row (aUSDC inside USDC).",
        +        "type": "boolean"
        +      },
        +      "kind": {
        +        "description": "Receipt kind: aave-atoken, comet, erc4626, lst, pendle-pt/yt/lp/sy, stargate-lp, curve-lp, curve-gauge.",
        +        "type": "string"
        +      },
        +      "proofSource": {
        +        "description": "Which official registry proved the token.",
        +        "type": "string"
        +      },
        +      "protocol": {
        +        "description": "Protocol key, e.g. \"aave-v3\", \"morpho\", \"pendle\".",
        +        "type": "string"
        +      },
        +      "protocolLabel": {
        +        "description": "Protocol as a person reads it, e.g. \"Aave v3\".",
        +        "type": "string"
        +      },
        +      "qty": {
        +        "description": "Receipt quantity, native units.",
        +        "type": "string"
        +      },
        +      "receipt": {
        +        "description": "The receipt token's own ticker, e.g. \"aArbWETH\". Not what to call it.",
        +        "type": "string"
        +      },
        +      "summary": {
        +        "description": "How to say it: \"Aave v3 WETH supply on Arbitrum\".",
        +        "type": "string"
        +      },
        +      "underlying": {
        +        "description": "What the position is a claim on, e.g. \"WETH\"; Curve pools list their coins joined with \"/\".",
        +        "type": "string"
        +      },
        +      "underlyingQty": {
        +        "description": "Quantity of the underlying the receipt is worth (qty x protocol rate), in underlyingUnit.",
        +        "type": [
        +          "string",
        +          "null"
        +        ]
        +      },
        +      "underlyingUnit": {
        +        "description": "Unit of underlyingQty.",
        +        "type": "string"
        +      },
        +      "valueUsdt": {
        +        "description": "USD value of this position.",
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "kind",
        +      "protocol",
        +      "protocolLabel",
        +      "receipt",
        +      "contract",
        +      "underlying",
        +      "chain",
        +      "qty",
        +      "underlyingQty",
        +      "underlyingUnit",
        +      "valueUsdt",
        +      "apyPct",
        +      "proofSource",
        +      "folded"
        +    ],
        +    "type": "object"
        +  },
        +  "type": "array"
        +}
      • addedOutput schema / properties / holdings / properties / excludedDebt
        Added value: +{
        +  "additionalProperties": true,
        +  "description": "Borrowings (Aave debt tokens) found on-chain and NOT counted in totalUsdt or any row. Present only when there are some.",
        +  "properties": {
        +    "count": {
        +      "description": "Proven debt tokens left out.",
        +      "type": "number"
        +    },
        +    "protocols": {
        +      "description": "Protocols borrowed from.",
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "valueUsdt": {
        +      "description": "What they are worth, USD, as a POSITIVE amount owed.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "count",
        +    "valueUsdt",
        +    "protocols"
        +  ],
        +  "type": "object"
        +}
      • changedOutput schema / properties / message / description
        Previous value: -"Plain-language explanation that accompanies `error` or a proposed change."New value: +"Plain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user."
      • addedOutput schema / properties / next_step
        Added value: +{
        +  "additionalProperties": true,
        +  "description": "What the user should do about data_status.",
        +  "properties": {
        +    "action": {
        +      "enum": [
        +        "connect_a_source",
        +        "wait_for_first_sync"
        +      ],
        +      "type": "string"
        +    },
        +    "add_exchange_url": {
        +      "description": "App page to connect an exchange with a read-only API key.",
        +      "type": "string"
        +    },
        +    "add_wallet_url": {
        +      "description": "App page to add a wallet address to watch.",
        +      "type": "string"
        +    },
        +    "settings_url": {
        +      "type": "string"
        +    },
        +    "sync_status_url": {
        +      "description": "App page showing sync progress.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "action"
        +  ],
        +  "type": "object"
        +}
    • Changedget_risk3 fields changed
      • addedOutput schema / properties / data_status
        Added value: +{
        +  "description": "Present only when the portfolio has no data yet. nothing_connected: no exchange and no wallet is connected, so empty lists and zeros mean \"no data\", NOT a $0 portfolio, and no data fields come with it (except on get_connections, get_alerts, get_target_allocation and list_profiles, which also answer). Send the user to next_step. syncing: a source is connected but its first sync is still running, so figures are partial and not final.",
        +  "enum": [
        +    "nothing_connected",
        +    "syncing"
        +  ],
        +  "type": "string"
        +}
      • changedOutput schema / properties / message / description
        Previous value: -"Plain-language explanation that accompanies `error` or a proposed change."New value: +"Plain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user."
      • addedOutput schema / properties / next_step
        Added value: +{
        +  "additionalProperties": true,
        +  "description": "What the user should do about data_status.",
        +  "properties": {
        +    "action": {
        +      "enum": [
        +        "connect_a_source",
        +        "wait_for_first_sync"
        +      ],
        +      "type": "string"
        +    },
        +    "add_exchange_url": {
        +      "description": "App page to connect an exchange with a read-only API key.",
        +      "type": "string"
        +    },
        +    "add_wallet_url": {
        +      "description": "App page to add a wallet address to watch.",
        +      "type": "string"
        +    },
        +    "settings_url": {
        +      "type": "string"
        +    },
        +    "sync_status_url": {
        +      "description": "App page showing sync progress.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "action"
        +  ],
        +  "type": "object"
        +}
    • Changedget_target_allocation3 fields changed
      • addedOutput schema / properties / data_status
        Added value: +{
        +  "description": "Present only when the portfolio has no data yet. nothing_connected: no exchange and no wallet is connected, so empty lists and zeros mean \"no data\", NOT a $0 portfolio, and no data fields come with it (except on get_connections, get_alerts, get_target_allocation and list_profiles, which also answer). Send the user to next_step. syncing: a source is connected but its first sync is still running, so figures are partial and not final.",
        +  "enum": [
        +    "nothing_connected",
        +    "syncing"
        +  ],
        +  "type": "string"
        +}
      • changedOutput schema / properties / message / description
        Previous value: -"Plain-language explanation that accompanies `error` or a proposed change."New value: +"Plain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user."
      • addedOutput schema / properties / next_step
        Added value: +{
        +  "additionalProperties": true,
        +  "description": "What the user should do about data_status.",
        +  "properties": {
        +    "action": {
        +      "enum": [
        +        "connect_a_source",
        +        "wait_for_first_sync"
        +      ],
        +      "type": "string"
        +    },
        +    "add_exchange_url": {
        +      "description": "App page to connect an exchange with a read-only API key.",
        +      "type": "string"
        +    },
        +    "add_wallet_url": {
        +      "description": "App page to add a wallet address to watch.",
        +      "type": "string"
        +    },
        +    "settings_url": {
        +      "type": "string"
        +    },
        +    "sync_status_url": {
        +      "description": "App page showing sync progress.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "action"
        +  ],
        +  "type": "object"
        +}
    • Changedget_trading_activity3 fields changed
      • addedOutput schema / properties / data_status
        Added value: +{
        +  "description": "Present only when the portfolio has no data yet. nothing_connected: no exchange and no wallet is connected, so empty lists and zeros mean \"no data\", NOT a $0 portfolio, and no data fields come with it (except on get_connections, get_alerts, get_target_allocation and list_profiles, which also answer). Send the user to next_step. syncing: a source is connected but its first sync is still running, so figures are partial and not final.",
        +  "enum": [
        +    "nothing_connected",
        +    "syncing"
        +  ],
        +  "type": "string"
        +}
      • changedOutput schema / properties / message / description
        Previous value: -"Plain-language explanation that accompanies `error` or a proposed change."New value: +"Plain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user."
      • addedOutput schema / properties / next_step
        Added value: +{
        +  "additionalProperties": true,
        +  "description": "What the user should do about data_status.",
        +  "properties": {
        +    "action": {
        +      "enum": [
        +        "connect_a_source",
        +        "wait_for_first_sync"
        +      ],
        +      "type": "string"
        +    },
        +    "add_exchange_url": {
        +      "description": "App page to connect an exchange with a read-only API key.",
        +      "type": "string"
        +    },
        +    "add_wallet_url": {
        +      "description": "App page to add a wallet address to watch.",
        +      "type": "string"
        +    },
        +    "settings_url": {
        +      "type": "string"
        +    },
        +    "sync_status_url": {
        +      "description": "App page showing sync progress.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "action"
        +  ],
        +  "type": "object"
        +}
    • Changedlist_profiles3 fields changed
      • addedOutput schema / properties / data_status
        Added value: +{
        +  "description": "Present only when the portfolio has no data yet. nothing_connected: no exchange and no wallet is connected, so empty lists and zeros mean \"no data\", NOT a $0 portfolio, and no data fields come with it (except on get_connections, get_alerts, get_target_allocation and list_profiles, which also answer). Send the user to next_step. syncing: a source is connected but its first sync is still running, so figures are partial and not final.",
        +  "enum": [
        +    "nothing_connected",
        +    "syncing"
        +  ],
        +  "type": "string"
        +}
      • changedOutput schema / properties / message / description
        Previous value: -"Plain-language explanation that accompanies `error` or a proposed change."New value: +"Plain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user."
      • addedOutput schema / properties / next_step
        Added value: +{
        +  "additionalProperties": true,
        +  "description": "What the user should do about data_status.",
        +  "properties": {
        +    "action": {
        +      "enum": [
        +        "connect_a_source",
        +        "wait_for_first_sync"
        +      ],
        +      "type": "string"
        +    },
        +    "add_exchange_url": {
        +      "description": "App page to connect an exchange with a read-only API key.",
        +      "type": "string"
        +    },
        +    "add_wallet_url": {
        +      "description": "App page to add a wallet address to watch.",
        +      "type": "string"
        +    },
        +    "settings_url": {
        +      "type": "string"
        +    },
        +    "sync_status_url": {
        +      "description": "App page showing sync progress.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "action"
        +  ],
        +  "type": "object"
        +}
    • Changedpropose_alert_change3 fields changed
      • addedOutput schema / properties / data_status
        Added value: +{
        +  "description": "Present only when the portfolio has no data yet. nothing_connected: no exchange and no wallet is connected, so empty lists and zeros mean \"no data\", NOT a $0 portfolio, and no data fields come with it (except on get_connections, get_alerts, get_target_allocation and list_profiles, which also answer). Send the user to next_step. syncing: a source is connected but its first sync is still running, so figures are partial and not final.",
        +  "enum": [
        +    "nothing_connected",
        +    "syncing"
        +  ],
        +  "type": "string"
        +}
      • changedOutput schema / properties / message / description
        Previous value: -"Plain-language explanation that accompanies `error` or a proposed change."New value: +"Plain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user."
      • addedOutput schema / properties / next_step
        Added value: +{
        +  "additionalProperties": true,
        +  "description": "What the user should do about data_status.",
        +  "properties": {
        +    "action": {
        +      "enum": [
        +        "connect_a_source",
        +        "wait_for_first_sync"
        +      ],
        +      "type": "string"
        +    },
        +    "add_exchange_url": {
        +      "description": "App page to connect an exchange with a read-only API key.",
        +      "type": "string"
        +    },
        +    "add_wallet_url": {
        +      "description": "App page to add a wallet address to watch.",
        +      "type": "string"
        +    },
        +    "settings_url": {
        +      "type": "string"
        +    },
        +    "sync_status_url": {
        +      "description": "App page showing sync progress.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "action"
        +  ],
        +  "type": "object"
        +}
    • Changedset_target_allocation3 fields changed
      • addedOutput schema / properties / data_status
        Added value: +{
        +  "description": "Present only when the portfolio has no data yet. nothing_connected: no exchange and no wallet is connected, so empty lists and zeros mean \"no data\", NOT a $0 portfolio, and no data fields come with it (except on get_connections, get_alerts, get_target_allocation and list_profiles, which also answer). Send the user to next_step. syncing: a source is connected but its first sync is still running, so figures are partial and not final.",
        +  "enum": [
        +    "nothing_connected",
        +    "syncing"
        +  ],
        +  "type": "string"
        +}
      • changedOutput schema / properties / message / description
        Previous value: -"Plain-language explanation that accompanies `error` or a proposed change."New value: +"Plain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user."
      • addedOutput schema / properties / next_step
        Added value: +{
        +  "additionalProperties": true,
        +  "description": "What the user should do about data_status.",
        +  "properties": {
        +    "action": {
        +      "enum": [
        +        "connect_a_source",
        +        "wait_for_first_sync"
        +      ],
        +      "type": "string"
        +    },
        +    "add_exchange_url": {
        +      "description": "App page to connect an exchange with a read-only API key.",
        +      "type": "string"
        +    },
        +    "add_wallet_url": {
        +      "description": "App page to add a wallet address to watch.",
        +      "type": "string"
        +    },
        +    "settings_url": {
        +      "type": "string"
        +    },
        +    "sync_status_url": {
        +      "description": "App page showing sync progress.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "action"
        +  ],
        +  "type": "object"
        +}
  3. 33 tool updates
    • Removedget_activity_calendar
    • Changedget_alerts1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "$schema": "http://json-schema.org/draft-07/schema#",
        +  "additionalProperties": true,
        +  "properties": {
        +    "alerts": {
        +      "items": {
        +        "additionalProperties": true,
        +        "properties": {
        +          "channels": {
        +            "items": {
        +              "type": "string"
        +            },
        +            "type": "array"
        +          },
        +          "cooldownSec": {
        +            "type": "number"
        +          },
        +          "enabled": {
        +            "type": "boolean"
        +          },
        +          "id": {
        +            "type": "string"
        +          },
        +          "kind": {
        +            "description": "asset_price, asset_pct, portfolio_value, portfolio_pct, holding_value or holding_weight.",
        +            "type": "string"
        +          },
        +          "label": {
        +            "description": "Readable summary of the rule.",
        +            "type": "string"
        +          },
        +          "lastFiredAt": {
        +            "description": "Last trigger, epoch ms.",
        +            "type": [
        +              "number",
        +              "null"
        +            ]
        +          },
        +          "op": {
        +            "description": "above or below.",
        +            "type": "string"
        +          },
        +          "repeat": {
        +            "description": "once or recurring.",
        +            "type": "string"
        +          },
        +          "subject": {
        +            "description": "Asset the alert watches; null for whole-portfolio kinds.",
        +            "type": [
        +              "string",
        +              "null"
        +            ]
        +          },
        +          "threshold": {
        +            "description": "Trigger level, in the kind's unit (USD or percent).",
        +            "type": "number"
        +          }
        +        },
        +        "required": [
        +          "id",
        +          "kind",
        +          "subject",
        +          "op",
        +          "threshold",
        +          "label",
        +          "enabled",
        +          "repeat",
        +          "cooldownSec",
        +          "channels",
        +          "lastFiredAt"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "error": {
        +      "description": "Present ONLY when the call was refused instead of answered: upgrade_required (Pro plan needed), not_found, profile_not_found, profile_ambiguous, invalid_arguments, write_not_permitted. No data fields come with it; read `message` to the user rather than retrying the same call.",
        +      "type": "string"
        +    },
        +    "message": {
        +      "description": "Plain-language explanation that accompanies `error` or a proposed change.",
        +      "type": "string"
        +    },
        +    "profile": {
        +      "additionalProperties": true,
        +      "description": "The portfolio these figures belong to (absent on list_profiles).",
        +      "properties": {
        +        "id": {
        +          "type": "string"
        +        },
        +        "name": {
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "id",
        +        "name"
        +      ],
        +      "type": "object"
        +    },
        +    "source_url": {
        +      "description": "Link to the page in the WhereItGoes app that shows this data.",
        +      "type": "string"
        +    },
        +    "stock_assets": {
        +      "additionalProperties": true,
        +      "description": "Present when the answer mentions Binance Stocks shares (EQ_<ticker>): they are US stocks, not crypto.",
        +      "properties": {
        +        "assets": {
        +          "items": {
        +            "additionalProperties": true,
        +            "properties": {
        +              "asset": {
        +                "type": "string"
        +              },
        +              "ticker": {
        +                "type": "string"
        +              }
        +            },
        +            "required": [
        +              "asset",
        +              "ticker"
        +            ],
        +            "type": "object"
        +          },
        +          "type": "array"
        +        },
        +        "note": {
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "note",
        +        "assets"
        +      ],
        +      "type": "object"
        +    },
        +    "upgrade_url": {
        +      "description": "Where the user can upgrade, on an upgrade_required refusal.",
        +      "type": "string"
        +    }
        +  },
        +  "type": "object"
        +}
    • Removedget_asset_trade_stats
    • Removedget_capital_flow
    • Changedget_changes2 fields changed
      • changedInput schema / properties / period / description
        Previous value: -"Time window ending now: last_24h (today / since yesterday), last_7d, last_30d, last_90d, ytd (since 1 January UTC), or all. Omit for all time."New value: +"Time window ending now: last_24h, last_7d, last_30d, last_90d, ytd (since 1 January UTC) or all. Omit for all time."
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "$schema": "http://json-schema.org/draft-07/schema#",
        +  "additionalProperties": true,
        +  "properties": {
        +    "countsByType": {
        +      "additionalProperties": {
        +        "type": "number"
        +      },
        +      "description": "Events detected per type before the list was capped.",
        +      "type": "object"
        +    },
        +    "error": {
        +      "description": "Present ONLY when the call was refused instead of answered: upgrade_required (Pro plan needed), not_found, profile_not_found, profile_ambiguous, invalid_arguments, write_not_permitted. No data fields come with it; read `message` to the user rather than retrying the same call.",
        +      "type": "string"
        +    },
        +    "events": {
        +      "description": "Ranked, most significant first.",
        +      "items": {
        +        "additionalProperties": true,
        +        "properties": {
        +          "amountUsd": {
        +            "description": "USD size, when the event is a valued flow.",
        +            "type": "string"
        +          },
        +          "asset": {
        +            "type": "string"
        +          },
        +          "day": {
        +            "description": "UTC day, YYYY-MM-DD.",
        +            "type": "string"
        +          },
        +          "detail": {
        +            "type": "string"
        +          },
        +          "magnitudePct": {
        +            "type": "string"
        +          },
        +          "severity": {
        +            "enum": [
        +              "info",
        +              "warn"
        +            ],
        +            "type": "string"
        +          },
        +          "title": {
        +            "type": "string"
        +          },
        +          "type": {
        +            "enum": [
        +              "flow",
        +              "allocation",
        +              "drawdown",
        +              "new_source",
        +              "reconciliation"
        +            ],
        +            "type": "string"
        +          },
        +          "venue": {
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "type",
        +          "severity",
        +          "day",
        +          "title",
        +          "detail"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "events_note": {
        +      "description": "Present only when `events` was cut; says how many rows were kept and which.",
        +      "type": "string"
        +    },
        +    "message": {
        +      "description": "Plain-language explanation that accompanies `error` or a proposed change.",
        +      "type": "string"
        +    },
        +    "notes": {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "profile": {
        +      "additionalProperties": true,
        +      "description": "The portfolio these figures belong to (absent on list_profiles).",
        +      "properties": {
        +        "id": {
        +          "type": "string"
        +        },
        +        "name": {
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "id",
        +        "name"
        +      ],
        +      "type": "object"
        +    },
        +    "source_url": {
        +      "description": "Link to the page in the WhereItGoes app that shows this data.",
        +      "type": "string"
        +    },
        +    "stock_assets": {
        +      "additionalProperties": true,
        +      "description": "Present when the answer mentions Binance Stocks shares (EQ_<ticker>): they are US stocks, not crypto.",
        +      "properties": {
        +        "assets": {
        +          "items": {
        +            "additionalProperties": true,
        +            "properties": {
        +              "asset": {
        +                "type": "string"
        +              },
        +              "ticker": {
        +                "type": "string"
        +              }
        +            },
        +            "required": [
        +              "asset",
        +              "ticker"
        +            ],
        +            "type": "object"
        +          },
        +          "type": "array"
        +        },
        +        "note": {
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "note",
        +        "assets"
        +      ],
        +      "type": "object"
        +    },
        +    "truncated": {
        +      "additionalProperties": {
        +        "additionalProperties": true,
        +        "properties": {
        +          "from": {
        +            "description": "Day or bucket of the first kept row, for a dated series.",
        +            "type": "string"
        +          },
        +          "keep": {
        +            "description": "Which rows survived the cut.",
        +            "enum": [
        +              "first",
        +              "most_recent",
        +              "evenly_spaced",
        +              "best_and_worst"
        +            ],
        +            "type": "string"
        +          },
        +          "kept": {
        +            "description": "Rows returned.",
        +            "type": "number"
        +          },
        +          "to": {
        +            "description": "Day or bucket of the last kept row, for a dated series.",
        +            "type": "string"
        +          },
        +          "total": {
        +            "description": "Rows the full answer had.",
        +            "type": "number"
        +          }
        +        },
        +        "required": [
        +          "total",
        +          "kept",
        +          "keep"
        +        ],
        +        "type": "object"
        +      },
        +      "description": "Present only when a list was longer than the 200-row cap and was cut: list field name -> {total, kept, keep, from, to}. A `<field>_note` string beside that list says the same in words. Never present a cut list as complete.",
        +      "type": "object"
        +    },
        +    "upgrade_url": {
        +      "description": "Where the user can upgrade, on an upgrade_required refusal.",
        +      "type": "string"
        +    },
        +    "window": {
        +      "additionalProperties": true,
        +      "description": "The time window these figures cover.",
        +      "properties": {
        +        "from": {
        +          "description": "Window start, epoch ms; null = from the first record.",
        +          "type": [
        +            "number",
        +            "null"
        +          ]
        +        },
        +        "fromDay": {
        +          "description": "First UTC day covered, YYYY-MM-DD.",
        +          "type": [
        +            "string",
        +            "null"
        +          ]
        +        },
        +        "to": {
        +          "description": "Window end, epoch ms; null = now.",
        +          "type": [
        +            "number",
        +            "null"
        +          ]
        +        },
        +        "toDay": {
        +          "description": "Last UTC day covered, YYYY-MM-DD.",
        +          "type": [
        +            "string",
        +            "null"
        +          ]
        +        }
        +      },
        +      "required": [
        +        "from",
        +        "to",
        +        "fromDay",
        +        "toDay"
        +      ],
        +      "type": "object"
        +    }
        +  },
        +  "type": "object"
        +}
    • Addedget_connections
    • Removedget_dashboard
    • Removedget_deposits
    • Changedget_fees3 fields changed
      • changedInput schema / properties / bucket / description
        Previous value: -"Granularity of the fee time series. Defaults to month."New value: +"Granularity of the fee series. Defaults to month."
      • changedInput schema / properties / period / description
        Previous value: -"Time window ending now: last_24h (today / since yesterday), last_7d, last_30d, last_90d, ytd (since 1 January UTC), or all. Omit for all time."New value: +"Time window ending now: last_24h, last_7d, last_30d, last_90d, ytd (since 1 January UTC) or all. Omit for all time."
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "$schema": "http://json-schema.org/draft-07/schema#",
        +  "additionalProperties": true,
        +  "properties": {
        +    "avgFeeUsd": {
        +      "type": "string"
        +    },
        +    "bucket": {
        +      "enum": [
        +        "day",
        +        "week",
        +        "month"
        +      ],
        +      "type": "string"
        +    },
        +    "byAsset": {
        +      "description": "Largest first.",
        +      "items": {
        +        "additionalProperties": true,
        +        "properties": {
        +          "asset": {
        +            "type": "string"
        +          },
        +          "count": {
        +            "type": "number"
        +          },
        +          "qty": {
        +            "description": "Native units.",
        +            "type": "string"
        +          },
        +          "usd": {
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "asset",
        +          "usd",
        +          "qty",
        +          "count"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "byType": {
        +      "description": "Largest first.",
        +      "items": {
        +        "additionalProperties": true,
        +        "properties": {
        +          "count": {
        +            "type": "number"
        +          },
        +          "type": {
        +            "enum": [
        +              "trading",
        +              "network",
        +              "withdrawal",
        +              "fiat",
        +              "other"
        +            ],
        +            "type": "string"
        +          },
        +          "usd": {
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "type",
        +          "usd",
        +          "count"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "byVenue": {
        +      "description": "Largest first.",
        +      "items": {
        +        "additionalProperties": true,
        +        "properties": {
        +          "count": {
        +            "type": "number"
        +          },
        +          "rateBps": {
        +            "description": "Trading fees / traded volume, basis points.",
        +            "type": [
        +              "number",
        +              "null"
        +            ]
        +          },
        +          "tradedVolumeUsd": {
        +            "type": "string"
        +          },
        +          "usd": {
        +            "type": "string"
        +          },
        +          "venue": {
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "venue",
        +          "usd",
        +          "count",
        +          "tradedVolumeUsd",
        +          "rateBps"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "error": {
        +      "description": "Present ONLY when the call was refused instead of answered: upgrade_required (Pro plan needed), not_found, profile_not_found, profile_ambiguous, invalid_arguments, write_not_permitted. No data fields come with it; read `message` to the user rather than retrying the same call.",
        +      "type": "string"
        +    },
        +    "feeCount": {
        +      "description": "Fee-bearing events counted.",
        +      "type": "number"
        +    },
        +    "message": {
        +      "description": "Plain-language explanation that accompanies `error` or a proposed change.",
        +      "type": "string"
        +    },
        +    "notes": {
        +      "description": "What the coverage leaves out.",
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "partial": {
        +      "description": "true when some fees could not be valued or are not tracked.",
        +      "type": "boolean"
        +    },
        +    "points": {
        +      "description": "Oldest bucket first.",
        +      "items": {
        +        "additionalProperties": true,
        +        "properties": {
        +          "byType": {
        +            "additionalProperties": {
        +              "type": "string"
        +            },
        +            "description": "fee type -> USD in the bucket.",
        +            "type": "object"
        +          },
        +          "rateBps": {
        +            "type": [
        +              "number",
        +              "null"
        +            ]
        +          },
        +          "t": {
        +            "type": "string"
        +          },
        +          "tradedVolumeUsd": {
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "t",
        +          "byType",
        +          "tradedVolumeUsd",
        +          "rateBps"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "points_note": {
        +      "description": "Present only when `points` was cut; says how many rows were kept and which.",
        +      "type": "string"
        +    },
        +    "profile": {
        +      "additionalProperties": true,
        +      "description": "The portfolio these figures belong to (absent on list_profiles).",
        +      "properties": {
        +        "id": {
        +          "type": "string"
        +        },
        +        "name": {
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "id",
        +        "name"
        +      ],
        +      "type": "object"
        +    },
        +    "source_url": {
        +      "description": "Link to the page in the WhereItGoes app that shows this data.",
        +      "type": "string"
        +    },
        +    "stock_assets": {
        +      "additionalProperties": true,
        +      "description": "Present when the answer mentions Binance Stocks shares (EQ_<ticker>): they are US stocks, not crypto.",
        +      "properties": {
        +        "assets": {
        +          "items": {
        +            "additionalProperties": true,
        +            "properties": {
        +              "asset": {
        +                "type": "string"
        +              },
        +              "ticker": {
        +                "type": "string"
        +              }
        +            },
        +            "required": [
        +              "asset",
        +              "ticker"
        +            ],
        +            "type": "object"
        +          },
        +          "type": "array"
        +        },
        +        "note": {
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "note",
        +        "assets"
        +      ],
        +      "type": "object"
        +    },
        +    "totalUsd": {
        +      "description": "All fees in the window, USD, each valued on the day paid.",
        +      "type": "string"
        +    },
        +    "truncated": {
        +      "additionalProperties": {
        +        "additionalProperties": true,
        +        "properties": {
        +          "from": {
        +            "description": "Day or bucket of the first kept row, for a dated series.",
        +            "type": "string"
        +          },
        +          "keep": {
        +            "description": "Which rows survived the cut.",
        +            "enum": [
        +              "first",
        +              "most_recent",
        +              "evenly_spaced",
        +              "best_and_worst"
        +            ],
        +            "type": "string"
        +          },
        +          "kept": {
        +            "description": "Rows returned.",
        +            "type": "number"
        +          },
        +          "to": {
        +            "description": "Day or bucket of the last kept row, for a dated series.",
        +            "type": "string"
        +          },
        +          "total": {
        +            "description": "Rows the full answer had.",
        +            "type": "number"
        +          }
        +        },
        +        "required": [
        +          "total",
        +          "kept",
        +          "keep"
        +        ],
        +        "type": "object"
        +      },
        +      "description": "Present only when a list was longer than the 200-row cap and was cut: list field name -> {total, kept, keep, from, to}. A `<field>_note` string beside that list says the same in words. Never present a cut list as complete.",
        +      "type": "object"
        +    },
        +    "unpricedAssets": {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "upgrade_url": {
        +      "description": "Where the user can upgrade, on an upgrade_required refusal.",
        +      "type": "string"
        +    },
        +    "window": {
        +      "additionalProperties": true,
        +      "description": "The time window these figures cover.",
        +      "properties": {
        +        "from": {
        +          "description": "Window start, epoch ms; null = from the first record.",
        +          "type": [
        +            "number",
        +            "null"
        +          ]
        +        },
        +        "fromDay": {
        +          "description": "First UTC day covered, YYYY-MM-DD.",
        +          "type": [
        +            "string",
        +            "null"
        +          ]
        +        },
        +        "to": {
        +          "description": "Window end, epoch ms; null = now.",
        +          "type": [
        +            "number",
        +            "null"
        +          ]
        +        },
        +        "toDay": {
        +          "description": "Last UTC day covered, YYYY-MM-DD.",
        +          "type": [
        +            "string",
        +            "null"
        +          ]
        +        }
        +      },
        +      "required": [
        +        "from",
        +        "to",
        +        "fromDay",
        +        "toDay"
        +      ],
        +      "type": "object"
        +    }
        +  },
        +  "type": "object"
        +}
    • Removedget_fiat_stats
    • Addedget_income
    • Removedget_income_by_source
    • Removedget_income_timeseries
    • Addedget_money_flows
    • Changedget_movements5 fields changed
      • changedInput schema / properties / kind / description
        Previous value: -"INFLOW = deposit, OUTFLOW = withdrawal, TRANSFER = a move between the user's own accounts, INCOME = yield, staking or airdrop, TRADE = a buy or sell. Omit for every kind."New value: +"INFLOW = deposit, OUTFLOW = withdrawal, TRANSFER = between the user's own accounts, INCOME = yield, staking or airdrop, TRADE = a buy or sell. Omit for every kind."
      • changedInput schema / properties / period / description
        Previous value: -"Time window ending now: last_24h (today / since yesterday), last_7d, last_30d, last_90d, ytd (since 1 January UTC), or all. Omit for all time."New value: +"Time window ending now: last_24h, last_7d, last_30d, last_90d, ytd (since 1 January UTC) or all. Omit for all time."
      • changedInput schema / properties / q / description
        Previous value: -"Free-text substring search (case-insensitive) over asset, transaction id, status and stored details such as an address fragment."New value: +"Case-insensitive substring search over asset, transaction id, status and stored details such as an address fragment."
      • changedInput schema / properties / status / description
        Previous value: -"Status exactly as it appears in countsByStatus. Venue-native and case-sensitive (\"SUCCESS\", \"2\"), so copy it from the facet rather than guessing."New value: +"Status exactly as it appears in countsByStatus, e.g. \"SUCCESS\" or \"2\"."
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "$schema": "http://json-schema.org/draft-07/schema#",
        +  "additionalProperties": true,
        +  "properties": {
        +    "countsBySource": {
        +      "items": {
        +        "additionalProperties": true,
        +        "properties": {
        +          "n": {
        +            "type": "number"
        +          },
        +          "source": {
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "source",
        +          "n"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "countsByStatus": {
        +      "items": {
        +        "additionalProperties": true,
        +        "properties": {
        +          "n": {
        +            "type": "number"
        +          },
        +          "source": {
        +            "type": "string"
        +          },
        +          "status": {
        +            "type": "string"
        +          },
        +          "venue": {
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "venue",
        +          "source",
        +          "status",
        +          "n"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "countsByVenue": {
        +      "items": {
        +        "additionalProperties": true,
        +        "properties": {
        +          "display": {
        +            "type": "string"
        +          },
        +          "n": {
        +            "type": "number"
        +          },
        +          "venue": {
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "venue",
        +          "n",
        +          "display"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "error": {
        +      "description": "Present ONLY when the call was refused instead of answered: upgrade_required (Pro plan needed), not_found, profile_not_found, profile_ambiguous, invalid_arguments, write_not_permitted. No data fields come with it; read `message` to the user rather than retrying the same call.",
        +      "type": "string"
        +    },
        +    "hiddenSpam": {
        +      "description": "Rows hidden because they were marked spam.",
        +      "type": "number"
        +    },
        +    "items": {
        +      "description": "Newest first, at most `limit` rows.",
        +      "items": {
        +        "additionalProperties": true,
        +        "properties": {
        +          "base_amount": {
        +            "description": "Amount of base_asset, NATIVE token units as a decimal string (not USD).",
        +            "type": [
        +              "string",
        +              "null"
        +            ]
        +          },
        +          "base_asset": {
        +            "description": "Asset moved.",
        +            "type": [
        +              "string",
        +              "null"
        +            ]
        +          },
        +          "executed_at": {
        +            "description": "When it happened, epoch ms.",
        +            "type": "number"
        +          },
        +          "fee": {
        +            "description": "Fee charged, native units of fee_asset.",
        +            "type": [
        +              "string",
        +              "null"
        +            ]
        +          },
        +          "fee_asset": {
        +            "description": "Asset the fee was charged in.",
        +            "type": [
        +              "string",
        +              "null"
        +            ]
        +          },
        +          "id": {
        +            "description": "Stable row id, e.g. \"cex:123\" or \"wallet:456\".",
        +            "type": "string"
        +          },
        +          "kind": {
        +            "description": "INFLOW (deposit), OUTFLOW (withdrawal), TRANSFER (between own accounts), INCOME, or TRADE.",
        +            "type": "string"
        +          },
        +          "quote_amount": {
        +            "description": "Second leg amount, native units.",
        +            "type": [
        +              "string",
        +              "null"
        +            ]
        +          },
        +          "quote_asset": {
        +            "description": "Second leg asset, for trades.",
        +            "type": [
        +              "string",
        +              "null"
        +            ]
        +          },
        +          "source": {
        +            "description": "Venue-native source label, e.g. \"deposit\", \"withdraw\", \"staking\".",
        +            "type": "string"
        +          },
        +          "status": {
        +            "description": "Venue-native status, case-sensitive.",
        +            "type": [
        +              "string",
        +              "null"
        +            ]
        +          },
        +          "value_usd": {
        +            "description": "USD value on the day it happened; null when unpriced.",
        +            "type": [
        +              "string",
        +              "null"
        +            ]
        +          },
        +          "venue": {
        +            "description": "Exchange or wallet venue the row came from.",
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "id",
        +          "venue",
        +          "source",
        +          "kind",
        +          "base_asset",
        +          "base_amount",
        +          "quote_asset",
        +          "quote_amount",
        +          "fee",
        +          "fee_asset",
        +          "status",
        +          "executed_at",
        +          "value_usd"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "items_note": {
        +      "description": "Present only when `items` was cut; says how many rows were kept and which.",
        +      "type": "string"
        +    },
        +    "message": {
        +      "description": "Plain-language explanation that accompanies `error` or a proposed change.",
        +      "type": "string"
        +    },
        +    "profile": {
        +      "additionalProperties": true,
        +      "description": "The portfolio these figures belong to (absent on list_profiles).",
        +      "properties": {
        +        "id": {
        +          "type": "string"
        +        },
        +        "name": {
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "id",
        +        "name"
        +      ],
        +      "type": "object"
        +    },
        +    "source_url": {
        +      "description": "Link to the page in the WhereItGoes app that shows this data.",
        +      "type": "string"
        +    },
        +    "stock_assets": {
        +      "additionalProperties": true,
        +      "description": "Present when the answer mentions Binance Stocks shares (EQ_<ticker>): they are US stocks, not crypto.",
        +      "properties": {
        +        "assets": {
        +          "items": {
        +            "additionalProperties": true,
        +            "properties": {
        +              "asset": {
        +                "type": "string"
        +              },
        +              "ticker": {
        +                "type": "string"
        +              }
        +            },
        +            "required": [
        +              "asset",
        +              "ticker"
        +            ],
        +            "type": "object"
        +          },
        +          "type": "array"
        +        },
        +        "note": {
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "note",
        +        "assets"
        +      ],
        +      "type": "object"
        +    },
        +    "total": {
        +      "description": "Every row matching the filters, before `limit`.",
        +      "type": "number"
        +    },
        +    "truncated": {
        +      "additionalProperties": {
        +        "additionalProperties": true,
        +        "properties": {
        +          "from": {
        +            "description": "Day or bucket of the first kept row, for a dated series.",
        +            "type": "string"
        +          },
        +          "keep": {
        +            "description": "Which rows survived the cut.",
        +            "enum": [
        +              "first",
        +              "most_recent",
        +              "evenly_spaced",
        +              "best_and_worst"
        +            ],
        +            "type": "string"
        +          },
        +          "kept": {
        +            "description": "Rows returned.",
        +            "type": "number"
        +          },
        +          "to": {
        +            "description": "Day or bucket of the last kept row, for a dated series.",
        +            "type": "string"
        +          },
        +          "total": {
        +            "description": "Rows the full answer had.",
        +            "type": "number"
        +          }
        +        },
        +        "required": [
        +          "total",
        +          "kept",
        +          "keep"
        +        ],
        +        "type": "object"
        +      },
        +      "description": "Present only when a list was longer than the 200-row cap and was cut: list field name -> {total, kept, keep, from, to}. A `<field>_note` string beside that list says the same in words. Never present a cut list as complete.",
        +      "type": "object"
        +    },
        +    "upgrade_url": {
        +      "description": "Where the user can upgrade, on an upgrade_required refusal.",
        +      "type": "string"
        +    }
        +  },
        +  "type": "object"
        +}
    • Addedget_performance
    • Removedget_pnl_by_symbol
    • Removedget_pnl_overview
    • Changedget_portfolio3 fields changed
      • addedInput schema / properties / view
        Added value: +{
        +  "description": "Which view to read: overview (whole-portfolio summary, start here); holdings (every asset held, largest first); recent_change (24h, 7d and 30d change and last sync). Each view takes only the arguments the description lists for it.",
        +  "enum": [
        +    "overview",
        +    "holdings",
        +    "recent_change"
        +  ],
        +  "type": "string"
        +}
      • addedInput schema / required
        Added value: +[
        +  "view"
        +]
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "$schema": "http://json-schema.org/draft-07/schema#",
        +  "additionalProperties": true,
        +  "properties": {
        +    "error": {
        +      "description": "Present ONLY when the call was refused instead of answered: upgrade_required (Pro plan needed), not_found, profile_not_found, profile_ambiguous, invalid_arguments, write_not_permitted. No data fields come with it; read `message` to the user rather than retrying the same call.",
        +      "type": "string"
        +    },
        +    "holdings": {
        +      "additionalProperties": true,
        +      "description": "Present when view = \"holdings\".",
        +      "properties": {
        +        "asOf": {
        +          "description": "When the holdings snapshot was computed, epoch ms (not a live quote).",
        +          "type": [
        +            "number",
        +            "null"
        +          ]
        +        },
        +        "assets": {
        +          "description": "One row per asset, largest USD value first.",
        +          "items": {
        +            "additionalProperties": true,
        +            "properties": {
        +              "asset": {
        +                "description": "Asset key, e.g. \"BTC\".",
        +                "type": "string"
        +              },
        +              "byVenue": {
        +                "description": "Where the asset is held.",
        +                "items": {
        +                  "additionalProperties": true,
        +                  "properties": {
        +                    "displayName": {
        +                      "type": "string"
        +                    },
        +                    "qty": {
        +                      "description": "Quantity at this venue, native units.",
        +                      "type": "string"
        +                    },
        +                    "type": {
        +                      "type": "string"
        +                    },
        +                    "valueUsdt": {
        +                      "description": "USD value at this venue.",
        +                      "type": [
        +                        "string",
        +                        "null"
        +                      ]
        +                    },
        +                    "venue": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "venue",
        +                    "displayName",
        +                    "type",
        +                    "qty",
        +                    "valueUsdt"
        +                  ],
        +                  "type": "object"
        +                },
        +                "type": "array"
        +              },
        +              "fifoAvgCost": {
        +                "description": "FIFO average cost per unit, USD.",
        +                "type": [
        +                  "string",
        +                  "null"
        +                ]
        +              },
        +              "priceChangePct24h": {
        +                "description": "Signed 24h price change, percent.",
        +                "type": [
        +                  "string",
        +                  "null"
        +                ]
        +              },
        +              "priceUsdt": {
        +                "description": "USD price used for the valuation.",
        +                "type": [
        +                  "string",
        +                  "null"
        +                ]
        +              },
        +              "realizedPnlUsdt": {
        +                "description": "Realized FIFO P&L to date, USD.",
        +                "type": [
        +                  "string",
        +                  "null"
        +                ]
        +              },
        +              "sector": {
        +                "type": [
        +                  "string",
        +                  "null"
        +                ]
        +              },
        +              "total": {
        +                "description": "Quantity held across every venue, native units.",
        +                "type": "string"
        +              },
        +              "trades": {
        +                "description": "Trades recorded for the asset.",
        +                "type": "number"
        +              },
        +              "unknownCostQty": {
        +                "description": "Quantity with no known cost basis (excluded from avg cost and unrealized).",
        +                "type": [
        +                  "string",
        +                  "null"
        +                ]
        +              },
        +              "unrealizedAbs": {
        +                "description": "Unrealized P&L on the open quantity, USD.",
        +                "type": [
        +                  "string",
        +                  "null"
        +                ]
        +              },
        +              "unrealizedPct": {
        +                "description": "Unrealized P&L, percent.",
        +                "type": [
        +                  "string",
        +                  "null"
        +                ]
        +              },
        +              "valueChange24hUsdt": {
        +                "description": "USD value change over 24h.",
        +                "type": [
        +                  "string",
        +                  "null"
        +                ]
        +              },
        +              "valueUsdt": {
        +                "description": "USD value; null when unpriced.",
        +                "type": [
        +                  "string",
        +                  "null"
        +                ]
        +              },
        +              "weightPct": {
        +                "description": "Share of totalUsdt, percent.",
        +                "type": [
        +                  "string",
        +                  "null"
        +                ]
        +              }
        +            },
        +            "required": [
        +              "asset",
        +              "total",
        +              "priceUsdt",
        +              "priceChangePct24h",
        +              "valueUsdt",
        +              "valueChange24hUsdt",
        +              "weightPct",
        +              "fifoAvgCost",
        +              "unknownCostQty",
        +              "unrealizedAbs",
        +              "unrealizedPct",
        +              "realizedPnlUsdt",
        +              "trades",
        +              "byVenue"
        +            ],
        +            "type": "object"
        +          },
        +          "type": "array"
        +        },
        +        "assets_note": {
        +          "description": "Present only when `assets` was cut; says how many rows were kept and which.",
        +          "type": "string"
        +        },
        +        "regrouping": {
        +          "description": "true while a recompute for a changed grouping setting is in flight.",
        +          "type": "boolean"
        +        },
        +        "totalUsdt": {
        +          "description": "Whole portfolio value in USD, decimal string with 2 decimals.",
        +          "type": "string"
        +        },
        +        "truncated": {
        +          "additionalProperties": {
        +            "additionalProperties": true,
        +            "properties": {
        +              "from": {
        +                "description": "Day or bucket of the first kept row, for a dated series.",
        +                "type": "string"
        +              },
        +              "keep": {
        +                "description": "Which rows survived the cut.",
        +                "enum": [
        +                  "first",
        +                  "most_recent",
        +                  "evenly_spaced",
        +                  "best_and_worst"
        +                ],
        +                "type": "string"
        +              },
        +              "kept": {
        +                "description": "Rows returned.",
        +                "type": "number"
        +              },
        +              "to": {
        +                "description": "Day or bucket of the last kept row, for a dated series.",
        +                "type": "string"
        +              },
        +              "total": {
        +                "description": "Rows the full answer had.",
        +                "type": "number"
        +              }
        +            },
        +            "required": [
        +              "total",
        +              "kept",
        +              "keep"
        +            ],
        +            "type": "object"
        +          },
        +          "description": "Present only when a list was longer than the 200-row cap and was cut: list field name -> {total, kept, keep, from, to}. A `<field>_note` string beside that list says the same in words. Never present a cut list as complete.",
        +          "type": "object"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    "message": {
        +      "description": "Plain-language explanation that accompanies `error` or a proposed change.",
        +      "type": "string"
        +    },
        +    "overview": {
        +      "additionalProperties": true,
        +      "description": "Present when view = \"overview\".",
        +      "properties": {
        +        "allocation": {
        +          "additionalProperties": true,
        +          "properties": {
        +            "byAsset": {
        +              "description": "Largest first, at most 12; the rest are counted in `truncated.assets`.",
        +              "items": {
        +                "additionalProperties": true,
        +                "properties": {
        +                  "asset": {
        +                    "type": "string"
        +                  },
        +                  "name": {
        +                    "type": "string"
        +                  },
        +                  "pct": {
        +                    "description": "Share of total value, percent.",
        +                    "type": "number"
        +                  },
        +                  "priceChangePct24h": {
        +                    "description": "24h price change, percent.",
        +                    "type": [
        +                      "number",
        +                      "null"
        +                    ]
        +                  },
        +                  "valueUsd": {
        +                    "description": "USD value.",
        +                    "type": "number"
        +                  }
        +                },
        +                "required": [
        +                  "asset",
        +                  "name",
        +                  "pct",
        +                  "valueUsd",
        +                  "priceChangePct24h"
        +                ],
        +                "type": "object"
        +              },
        +              "type": "array"
        +            },
        +            "byChain": {
        +              "items": {
        +                "additionalProperties": true,
        +                "properties": {
        +                  "name": {
        +                    "type": "string"
        +                  },
        +                  "pct": {
        +                    "type": "number"
        +                  },
        +                  "valueUsd": {
        +                    "type": "number"
        +                  }
        +                },
        +                "required": [
        +                  "name",
        +                  "pct",
        +                  "valueUsd"
        +                ],
        +                "type": "object"
        +              },
        +              "type": "array"
        +            },
        +            "byClass": {
        +              "items": {
        +                "additionalProperties": true,
        +                "properties": {
        +                  "name": {
        +                    "type": "string"
        +                  },
        +                  "pct": {
        +                    "type": "number"
        +                  },
        +                  "valueUsd": {
        +                    "type": "number"
        +                  }
        +                },
        +                "required": [
        +                  "name",
        +                  "pct",
        +                  "valueUsd"
        +                ],
        +                "type": "object"
        +              },
        +              "type": "array"
        +            },
        +            "byVenue": {
        +              "items": {
        +                "additionalProperties": true,
        +                "properties": {
        +                  "name": {
        +                    "type": "string"
        +                  },
        +                  "pct": {
        +                    "type": "number"
        +                  },
        +                  "valueUsd": {
        +                    "type": "number"
        +                  }
        +                },
        +                "required": [
        +                  "name",
        +                  "pct",
        +                  "valueUsd"
        +                ],
        +                "type": "object"
        +              },
        +              "type": "array"
        +            },
        +            "truncated": {
        +              "additionalProperties": true,
        +              "description": "How many assets, venues and chains were left out of the lists above.",
        +              "properties": {
        +                "assets": {
        +                  "type": "number"
        +                },
        +                "chains": {
        +                  "type": "number"
        +                },
        +                "venues": {
        +                  "type": "number"
        +                }
        +              },
        +              "required": [
        +                "assets",
        +                "venues",
        +                "chains"
        +              ],
        +              "type": "object"
        +            }
        +          },
        +          "required": [
        +            "byAsset",
        +            "byClass",
        +            "byVenue",
        +            "byChain",
        +            "truncated"
        +          ],
        +          "type": "object"
        +        },
        +        "asOf": {
        +          "description": "When this overview was assembled, epoch ms.",
        +          "type": "number"
        +        },
        +        "candidates": {
        +          "description": "Ranked findings, most severe first. Pro only: empty on a free plan.",
        +          "items": {
        +            "additionalProperties": true,
        +            "properties": {
        +              "ruleId": {
        +                "type": "string"
        +              },
        +              "severity": {
        +                "type": "string"
        +              },
        +              "statement": {
        +                "type": "string"
        +              }
        +            },
        +            "required": [
        +              "ruleId",
        +              "severity",
        +              "statement"
        +            ],
        +            "type": "object"
        +          },
        +          "type": "array"
        +        },
        +        "concentration": {
        +          "additionalProperties": true,
        +          "properties": {
        +            "hhi": {
        +              "description": "Herfindahl index of the weights, 0-10000.",
        +              "type": "number"
        +            },
        +            "top1Pct": {
        +              "type": "number"
        +            },
        +            "top3Pct": {
        +              "type": "number"
        +            },
        +            "topName": {
        +              "type": [
        +                "string",
        +                "null"
        +              ]
        +            }
        +          },
        +          "required": [
        +            "hhi",
        +            "top1Pct",
        +            "top3Pct",
        +            "topName"
        +          ],
        +          "type": "object"
        +        },
        +        "degraded": {
        +          "description": "Blocks that failed to compute this time.",
        +          "items": {
        +            "type": "string"
        +          },
        +          "type": "array"
        +        },
        +        "flags": {
        +          "additionalProperties": true,
        +          "properties": {
        +            "cexPct": {
        +              "description": "Share held on exchanges, percent.",
        +              "type": "number"
        +            },
        +            "dustCount": {
        +              "type": "number"
        +            },
        +            "selfCustodyPct": {
        +              "description": "Share held in self-custody wallets, percent.",
        +              "type": "number"
        +            },
        +            "stablecoinPct": {
        +              "type": "number"
        +            },
        +            "stockPct": {
        +              "type": "number"
        +            },
        +            "unpricedCount": {
        +              "type": "number"
        +            },
        +            "volatilePct": {
        +              "type": "number"
        +            }
        +          },
        +          "required": [
        +            "stablecoinPct",
        +            "volatilePct",
        +            "stockPct",
        +            "cexPct",
        +            "selfCustodyPct",
        +            "dustCount",
        +            "unpricedCount"
        +          ],
        +          "type": "object"
        +        },
        +        "gated": {
        +          "description": "Blocks withheld because the account is not on a Pro plan.",
        +          "items": {
        +            "type": "string"
        +          },
        +          "type": "array"
        +        },
        +        "market": {
        +          "anyOf": [
        +            {
        +              "additionalProperties": true,
        +              "properties": {
        +                "windowDays": {
        +                  "type": "number"
        +                }
        +              },
        +              "required": [
        +                "windowDays"
        +              ],
        +              "type": "object"
        +            },
        +            {
        +              "type": "null"
        +            }
        +          ],
        +          "description": "What BTC and ETH did over the same window, plus the fear and greed reading."
        +        },
        +        "performance": {
        +          "anyOf": [
        +            {
        +              "additionalProperties": true,
        +              "properties": {
        +                "feesUsd": {
        +                  "type": [
        +                    "number",
        +                    "null"
        +                  ]
        +                },
        +                "incomeUsd": {
        +                  "type": [
        +                    "number",
        +                    "null"
        +                  ]
        +                },
        +                "netFlowsUsd": {
        +                  "description": "Deposits minus withdrawals over the 30 days, USD.",
        +                  "type": [
        +                    "number",
        +                    "null"
        +                  ]
        +                },
        +                "realizedUsd": {
        +                  "type": [
        +                    "number",
        +                    "null"
        +                  ]
        +                },
        +                "topGainers": {
        +                  "items": {
        +                    "additionalProperties": true,
        +                    "properties": {
        +                      "asset": {
        +                        "type": "string"
        +                      },
        +                      "pnlUsd": {
        +                        "type": "number"
        +                      }
        +                    },
        +                    "required": [
        +                      "asset",
        +                      "pnlUsd"
        +                    ],
        +                    "type": "object"
        +                  },
        +                  "type": "array"
        +                },
        +                "topLosers": {
        +                  "items": {
        +                    "additionalProperties": true,
        +                    "properties": {
        +                      "asset": {
        +                        "type": "string"
        +                      },
        +                      "pnlUsd": {
        +                        "type": "number"
        +                      }
        +                    },
        +                    "required": [
        +                      "asset",
        +                      "pnlUsd"
        +                    ],
        +                    "type": "object"
        +                  },
        +                  "type": "array"
        +                },
        +                "truePnlPct": {
        +                  "description": "Same as a percent.",
        +                  "type": [
        +                    "number",
        +                    "null"
        +                  ]
        +                },
        +                "truePnlUsd": {
        +                  "description": "Profit over the 30 days with deposits and withdrawals stripped out, USD.",
        +                  "type": [
        +                    "number",
        +                    "null"
        +                  ]
        +                },
        +                "unrealizedUsd": {
        +                  "type": [
        +                    "number",
        +                    "null"
        +                  ]
        +                },
        +                "valuedThroughDay": {
        +                  "description": "UTC day the figures are valued through (end-of-day, not live).",
        +                  "type": [
        +                    "string",
        +                    "null"
        +                  ]
        +                }
        +              },
        +              "required": [
        +                "valuedThroughDay",
        +                "truePnlUsd",
        +                "truePnlPct",
        +                "netFlowsUsd",
        +                "realizedUsd",
        +                "unrealizedUsd",
        +                "incomeUsd",
        +                "feesUsd",
        +                "topGainers",
        +                "topLosers"
        +              ],
        +              "type": "object"
        +            },
        +            {
        +              "type": "null"
        +            }
        +          ]
        +        },
        +        "period": {
        +          "additionalProperties": true,
        +          "description": "The fixed 30-day window the performance and market blocks cover.",
        +          "properties": {
        +            "from": {
        +              "type": [
        +                "number",
        +                "null"
        +              ]
        +            },
        +            "label": {
        +              "type": "string"
        +            },
        +            "to": {
        +              "type": [
        +                "number",
        +                "null"
        +              ]
        +            }
        +          },
        +          "required": [
        +            "label",
        +            "from",
        +            "to"
        +          ],
        +          "type": "object"
        +        },
        +        "risk": {
        +          "anyOf": [
        +            {
        +              "additionalProperties": true,
        +              "properties": {
        +                "annualizedVolPct": {
        +                  "type": [
        +                    "number",
        +                    "null"
        +                  ]
        +                },
        +                "maxDrawdownPct": {
        +                  "type": [
        +                    "number",
        +                    "null"
        +                  ]
        +                },
        +                "sharpe": {
        +                  "type": [
        +                    "number",
        +                    "null"
        +                  ]
        +                }
        +              },
        +              "required": [
        +                "annualizedVolPct",
        +                "maxDrawdownPct",
        +                "sharpe"
        +              ],
        +              "type": "object"
        +            },
        +            {
        +              "type": "null"
        +            }
        +          ],
        +          "description": "Pro only: null on a free plan (see `gated`)."
        +        },
        +        "score": {
        +          "anyOf": [
        +            {
        +              "additionalProperties": true,
        +              "properties": {
        +                "band": {
        +                  "type": "string"
        +                },
        +                "score": {
        +                  "description": "0-100.",
        +                  "type": "number"
        +                }
        +              },
        +              "required": [
        +                "score",
        +                "band"
        +              ],
        +              "type": "object"
        +            },
        +            {
        +              "type": "null"
        +            }
        +          ],
        +          "description": "Pro only: null on a free plan (see `gated`)."
        +        },
        +        "targets": {
        +          "anyOf": [
        +            {
        +              "additionalProperties": true,
        +              "properties": {
        +                "dim": {
        +                  "type": "string"
        +                },
        +                "rows": {
        +                  "items": {
        +                    "additionalProperties": true,
        +                    "properties": {
        +                      "actualPct": {
        +                        "type": "number"
        +                      },
        +                      "bandPct": {
        +                        "type": "number"
        +                      },
        +                      "deltaUsd": {
        +                        "description": "Signed USD to reach target: + sell this much, - buy this much.",
        +                        "type": "number"
        +                      },
        +                      "driftPct": {
        +                        "description": "Signed percentage points: + overweight, - underweight.",
        +                        "type": "number"
        +                      },
        +                      "inBand": {
        +                        "type": "boolean"
        +                      },
        +                      "subject": {
        +                        "type": "string"
        +                      },
        +                      "targetPct": {
        +                        "type": "number"
        +                      }
        +                    },
        +                    "required": [
        +                      "subject",
        +                      "targetPct",
        +                      "actualPct",
        +                      "driftPct",
        +                      "bandPct",
        +                      "inBand",
        +                      "deltaUsd"
        +                    ],
        +                    "type": "object"
        +                  },
        +                  "type": "array"
        +                }
        +              },
        +              "required": [
        +                "dim",
        +                "rows"
        +              ],
        +              "type": "object"
        +            },
        +            {
        +              "type": "null"
        +            }
        +          ],
        +          "description": "Drift from the target allocation; null when no target is set."
        +        },
        +        "totals": {
        +          "additionalProperties": true,
        +          "properties": {
        +            "assetCount": {
        +              "description": "Number of priced holdings.",
        +              "type": "number"
        +            },
        +            "valueUsd": {
        +              "description": "Live total portfolio value, USD.",
        +              "type": "number"
        +            }
        +          },
        +          "required": [
        +            "valueUsd",
        +            "assetCount"
        +          ],
        +          "type": "object"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    "profile": {
        +      "additionalProperties": true,
        +      "description": "The portfolio these figures belong to (absent on list_profiles).",
        +      "properties": {
        +        "id": {
        +          "type": "string"
        +        },
        +        "name": {
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "id",
        +        "name"
        +      ],
        +      "type": "object"
        +    },
        +    "recent_change": {
        +      "additionalProperties": true,
        +      "description": "Present when view = \"recent_change\".",
        +      "properties": {
        +        "cashDeployedUsdt": {
        +          "description": "Lifetime fiat cash put in, USD.",
        +          "type": [
        +            "string",
        +            "null"
        +          ]
        +        },
        +        "cashWithdrawnUsdt": {
        +          "description": "Lifetime fiat cash taken out, USD.",
        +          "type": [
        +            "string",
        +            "null"
        +          ]
        +        },
        +        "change24hPct": {
        +          "description": "Portfolio value change over 24h, percent; null until daily history exists.",
        +          "type": [
        +            "string",
        +            "null"
        +          ]
        +        },
        +        "change24hUsdt": {
        +          "description": "Same in USD.",
        +          "type": [
        +            "string",
        +            "null"
        +          ]
        +        },
        +        "change30dPct": {
        +          "type": [
        +            "string",
        +            "null"
        +          ]
        +        },
        +        "change30dUsdt": {
        +          "type": [
        +            "string",
        +            "null"
        +          ]
        +        },
        +        "change7dPct": {
        +          "type": [
        +            "string",
        +            "null"
        +          ]
        +        },
        +        "change7dUsdt": {
        +          "type": [
        +            "string",
        +            "null"
        +          ]
        +        },
        +        "costBasisUsdt": {
        +          "description": "Cost basis of current holdings, USD.",
        +          "type": [
        +            "string",
        +            "null"
        +          ]
        +        },
        +        "lastSyncAt": {
        +          "description": "When the last sync finished, epoch ms.",
        +          "type": [
        +            "number",
        +            "null"
        +          ]
        +        },
        +        "lastSyncMode": {
        +          "type": [
        +            "string",
        +            "null"
        +          ]
        +        },
        +        "netCapitalUsdt": {
        +          "description": "Lifetime net capital put in, USD.",
        +          "type": [
        +            "string",
        +            "null"
        +          ]
        +        },
        +        "totalMovements": {
        +          "description": "Deposits, withdrawals, transfers and income on record.",
        +          "type": "number"
        +        },
        +        "totalTrades": {
        +          "description": "Trades on record.",
        +          "type": "number"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    "source_url": {
        +      "description": "Link to the page in the WhereItGoes app that shows this data.",
        +      "type": "string"
        +    },
        +    "stock_assets": {
        +      "additionalProperties": true,
        +      "description": "Present when the answer mentions Binance Stocks shares (EQ_<ticker>): they are US stocks, not crypto.",
        +      "properties": {
        +        "assets": {
        +          "items": {
        +            "additionalProperties": true,
        +            "properties": {
        +              "asset": {
        +                "type": "string"
        +              },
        +              "ticker": {
        +                "type": "string"
        +              }
        +            },
        +            "required": [
        +              "asset",
        +              "ticker"
        +            ],
        +            "type": "object"
        +          },
        +          "type": "array"
        +        },
        +        "note": {
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "note",
        +        "assets"
        +      ],
        +      "type": "object"
        +    },
        +    "upgrade_url": {
        +      "description": "Where the user can upgrade, on an upgrade_required refusal.",
        +      "type": "string"
        +    },
        +    "view": {
        +      "description": "Which view answered; its data is under the key of the same name.",
        +      "enum": [
        +        "overview",
        +        "holdings",
        +        "recent_change"
        +      ],
        +      "type": "string"
        +    }
        +  },
        +  "type": "object"
        +}
    • Removedget_portfolio_history
    • Removedget_portfolio_score
    • Removedget_portfolio_snapshot
    • Addedget_risk
    • Removedget_risk_metrics
    • Removedget_sync_status
    • Changedget_target_allocation1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "$schema": "http://json-schema.org/draft-07/schema#",
        +  "additionalProperties": true,
        +  "properties": {
        +    "error": {
        +      "description": "Present ONLY when the call was refused instead of answered: upgrade_required (Pro plan needed), not_found, profile_not_found, profile_ambiguous, invalid_arguments, write_not_permitted. No data fields come with it; read `message` to the user rather than retrying the same call.",
        +      "type": "string"
        +    },
        +    "message": {
        +      "description": "Plain-language explanation that accompanies `error` or a proposed change.",
        +      "type": "string"
        +    },
        +    "profile": {
        +      "additionalProperties": true,
        +      "description": "The portfolio these figures belong to (absent on list_profiles).",
        +      "properties": {
        +        "id": {
        +          "type": "string"
        +        },
        +        "name": {
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "id",
        +        "name"
        +      ],
        +      "type": "object"
        +    },
        +    "set": {
        +      "anyOf": [
        +        {
        +          "additionalProperties": true,
        +          "properties": {
        +            "dim": {
        +              "description": "Axis of the plan: \"asset\" (subjects are tickers), \"sector\" (subjects are sector names) or \"class\".",
        +              "type": "string"
        +            },
        +            "rows": {
        +              "description": "Largest target first.",
        +              "items": {
        +                "additionalProperties": true,
        +                "properties": {
        +                  "bandPct": {
        +                    "description": "Tolerance in percentage POINTS either side.",
        +                    "type": "number"
        +                  },
        +                  "subject": {
        +                    "type": "string"
        +                  },
        +                  "targetPct": {
        +                    "description": "Target share, percent.",
        +                    "type": "number"
        +                  }
        +                },
        +                "required": [
        +                  "subject",
        +                  "targetPct",
        +                  "bandPct"
        +                ],
        +                "type": "object"
        +              },
        +              "type": "array"
        +            },
        +            "updatedAt": {
        +              "description": "Epoch ms.",
        +              "type": "number"
        +            }
        +          },
        +          "required": [
        +            "dim",
        +            "rows",
        +            "updatedAt"
        +          ],
        +          "type": "object"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "description": "null when no target allocation is set."
        +    },
        +    "source_url": {
        +      "description": "Link to the page in the WhereItGoes app that shows this data.",
        +      "type": "string"
        +    },
        +    "stock_assets": {
        +      "additionalProperties": true,
        +      "description": "Present when the answer mentions Binance Stocks shares (EQ_<ticker>): they are US stocks, not crypto.",
        +      "properties": {
        +        "assets": {
        +          "items": {
        +            "additionalProperties": true,
        +            "properties": {
        +              "asset": {
        +                "type": "string"
        +              },
        +              "ticker": {
        +                "type": "string"
        +              }
        +            },
        +            "required": [
        +              "asset",
        +              "ticker"
        +            ],
        +            "type": "object"
        +          },
        +          "type": "array"
        +        },
        +        "note": {
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "note",
        +        "assets"
        +      ],
        +      "type": "object"
        +    },
        +    "upgrade_url": {
        +      "description": "Where the user can upgrade, on an upgrade_required refusal.",
        +      "type": "string"
        +    }
        +  },
        +  "type": "object"
        +}
    • Addedget_trading_activity
    • Removedget_transfers
    • Removedget_venues
    • Removedget_wallets
    • Changedlist_profiles1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "$schema": "http://json-schema.org/draft-07/schema#",
        +  "additionalProperties": true,
        +  "properties": {
        +    "active": {
        +      "description": "Id of the portfolio active in the app.",
        +      "type": [
        +        "string",
        +        "null"
        +      ]
        +    },
        +    "error": {
        +      "description": "Present ONLY when the call was refused instead of answered: upgrade_required (Pro plan needed), not_found, profile_not_found, profile_ambiguous, invalid_arguments, write_not_permitted. No data fields come with it; read `message` to the user rather than retrying the same call.",
        +      "type": "string"
        +    },
        +    "items": {
        +      "items": {
        +        "additionalProperties": true,
        +        "properties": {
        +          "exchangeCount": {
        +            "type": "number"
        +          },
        +          "id": {
        +            "description": "Pass as the `profile` argument.",
        +            "type": "string"
        +          },
        +          "name": {
        +            "description": "Pass as the `profile` argument.",
        +            "type": "string"
        +          },
        +          "walletCount": {
        +            "type": "number"
        +          }
        +        },
        +        "required": [
        +          "id",
        +          "name",
        +          "exchangeCount",
        +          "walletCount"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "message": {
        +      "description": "Plain-language explanation that accompanies `error` or a proposed change.",
        +      "type": "string"
        +    },
        +    "profile": {
        +      "additionalProperties": true,
        +      "description": "The portfolio these figures belong to (absent on list_profiles).",
        +      "properties": {
        +        "id": {
        +          "type": "string"
        +        },
        +        "name": {
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "id",
        +        "name"
        +      ],
        +      "type": "object"
        +    },
        +    "source_url": {
        +      "description": "Link to the page in the WhereItGoes app that shows this data.",
        +      "type": "string"
        +    },
        +    "stock_assets": {
        +      "additionalProperties": true,
        +      "description": "Present when the answer mentions Binance Stocks shares (EQ_<ticker>): they are US stocks, not crypto.",
        +      "properties": {
        +        "assets": {
        +          "items": {
        +            "additionalProperties": true,
        +            "properties": {
        +              "asset": {
        +                "type": "string"
        +              },
        +              "ticker": {
        +                "type": "string"
        +              }
        +            },
        +            "required": [
        +              "asset",
        +              "ticker"
        +            ],
        +            "type": "object"
        +          },
        +          "type": "array"
        +        },
        +        "note": {
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "note",
        +        "assets"
        +      ],
        +      "type": "object"
        +    },
        +    "upgrade_url": {
        +      "description": "Where the user can upgrade, on an upgrade_required refusal.",
        +      "type": "string"
        +    }
        +  },
        +  "type": "object"
        +}
    • Addedpropose_alert_change
    • Changedset_target_allocation1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "$schema": "http://json-schema.org/draft-07/schema#",
        +  "additionalProperties": true,
        +  "properties": {
        +    "confirm_url": {
        +      "description": "The link the user must open and approve; tell them to open it.",
        +      "type": "string"
        +    },
        +    "error": {
        +      "description": "Present ONLY when the call was refused instead of answered: upgrade_required (Pro plan needed), not_found, profile_not_found, profile_ambiguous, invalid_arguments, write_not_permitted. No data fields come with it; read `message` to the user rather than retrying the same call.",
        +      "type": "string"
        +    },
        +    "expires_at": {
        +      "description": "When the link stops working, epoch ms.",
        +      "type": "number"
        +    },
        +    "message": {
        +      "description": "Plain-language explanation that accompanies `error` or a proposed change.",
        +      "type": "string"
        +    },
        +    "preview": {
        +      "description": "Courtesy diff; the authoritative one is on the confirm page.",
        +      "items": {
        +        "additionalProperties": true,
        +        "properties": {
        +          "after": {
        +            "type": [
        +              "string",
        +              "null"
        +            ]
        +          },
        +          "before": {
        +            "type": [
        +              "string",
        +              "null"
        +            ]
        +          },
        +          "kind": {
        +            "enum": [
        +              "add",
        +              "remove",
        +              "change",
        +              "same"
        +            ],
        +            "type": "string"
        +          },
        +          "label": {
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "label",
        +          "before",
        +          "after",
        +          "kind"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "profile": {
        +      "additionalProperties": true,
        +      "description": "The portfolio these figures belong to (absent on list_profiles).",
        +      "properties": {
        +        "id": {
        +          "type": "string"
        +        },
        +        "name": {
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "id",
        +        "name"
        +      ],
        +      "type": "object"
        +    },
        +    "source_url": {
        +      "description": "Link to the page in the WhereItGoes app that shows this data.",
        +      "type": "string"
        +    },
        +    "status": {
        +      "const": "confirmation_required",
        +      "description": "Always this: nothing has been applied yet.",
        +      "type": "string"
        +    },
        +    "stock_assets": {
        +      "additionalProperties": true,
        +      "description": "Present when the answer mentions Binance Stocks shares (EQ_<ticker>): they are US stocks, not crypto.",
        +      "properties": {
        +        "assets": {
        +          "items": {
        +            "additionalProperties": true,
        +            "properties": {
        +              "asset": {
        +                "type": "string"
        +              },
        +              "ticker": {
        +                "type": "string"
        +              }
        +            },
        +            "required": [
        +              "asset",
        +              "ticker"
        +            ],
        +            "type": "object"
        +          },
        +          "type": "array"
        +        },
        +        "note": {
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "note",
        +        "assets"
        +      ],
        +      "type": "object"
        +    },
        +    "title": {
        +      "description": "What the change does.",
        +      "type": "string"
        +    },
        +    "upgrade_url": {
        +      "description": "Where the user can upgrade, on an upgrade_required refusal.",
        +      "type": "string"
        +    }
        +  },
        +  "type": "object"
        +}
  4. 25 tool updates
    • Changedget_activity_calendar1 field changed
      • changedInput schema / properties / profile / description
        Previous value: -"Which portfolio to read, by name or id, for accounts that have more than one. Omit to use this connection's default. Call list_profiles first if the user names a portfolio you have not seen."New value: +"Which portfolio to read, for accounts with more than one: its id, or its name (exact, case-insensitive, e.g. \"Trading\"). Omit to read this connection's default portfolio; every answer names the portfolio it read in `profile`. An unknown or shared name returns profile_not_found or profile_ambiguous listing the real ones, and list_profiles shows them up front."
    • Changedget_alerts1 field changed
      • changedInput schema / properties / profile / description
        Previous value: -"Which portfolio to read, by name or id, for accounts that have more than one. Omit to use this connection's default. Call list_profiles first if the user names a portfolio you have not seen."New value: +"Which portfolio to read, for accounts with more than one: its id, or its name (exact, case-insensitive, e.g. \"Trading\"). Omit to read this connection's default portfolio; every answer names the portfolio it read in `profile`. An unknown or shared name returns profile_not_found or profile_ambiguous listing the real ones, and list_profiles shows them up front."
    • Changedget_asset_trade_stats1 field changed
      • changedInput schema / properties / profile / description
        Previous value: -"Which portfolio to read, by name or id, for accounts that have more than one. Omit to use this connection's default. Call list_profiles first if the user names a portfolio you have not seen."New value: +"Which portfolio to read, for accounts with more than one: its id, or its name (exact, case-insensitive, e.g. \"Trading\"). Omit to read this connection's default portfolio; every answer names the portfolio it read in `profile`. An unknown or shared name returns profile_not_found or profile_ambiguous listing the real ones, and list_profiles shows them up front."
    • Changedget_capital_flow3 fields changed
      • changedInput schema / properties / bucket / description
        Previous value: -"Time bucket. Defaults to month."New value: +"Granularity of the series. Defaults to month."
      • changedInput schema / properties / period / description
        Previous value: -"Time window. \"last_24h\" for today/since-yesterday, \"all\" for full history. Defaults to the endpoint's own default (usually last 30 days)."New value: +"Time window ending now: last_24h (today / since yesterday), last_7d, last_30d, last_90d, ytd (since 1 January UTC), or all. Omit for all time."
      • changedInput schema / properties / profile / description
        Previous value: -"Which portfolio to read, by name or id, for accounts that have more than one. Omit to use this connection's default. Call list_profiles first if the user names a portfolio you have not seen."New value: +"Which portfolio to read, for accounts with more than one: its id, or its name (exact, case-insensitive, e.g. \"Trading\"). Omit to read this connection's default portfolio; every answer names the portfolio it read in `profile`. An unknown or shared name returns profile_not_found or profile_ambiguous listing the real ones, and list_profiles shows them up front."
    • Changedget_changes2 fields changed
      • changedInput schema / properties / period / description
        Previous value: -"Time window. \"last_24h\" for today/since-yesterday, \"all\" for full history. Defaults to the endpoint's own default (usually last 30 days)."New value: +"Time window ending now: last_24h (today / since yesterday), last_7d, last_30d, last_90d, ytd (since 1 January UTC), or all. Omit for all time."
      • changedInput schema / properties / profile / description
        Previous value: -"Which portfolio to read, by name or id, for accounts that have more than one. Omit to use this connection's default. Call list_profiles first if the user names a portfolio you have not seen."New value: +"Which portfolio to read, for accounts with more than one: its id, or its name (exact, case-insensitive, e.g. \"Trading\"). Omit to read this connection's default portfolio; every answer names the portfolio it read in `profile`. An unknown or shared name returns profile_not_found or profile_ambiguous listing the real ones, and list_profiles shows them up front."
    • Changedget_dashboard1 field changed
      • changedInput schema / properties / profile / description
        Previous value: -"Which portfolio to read, by name or id, for accounts that have more than one. Omit to use this connection's default. Call list_profiles first if the user names a portfolio you have not seen."New value: +"Which portfolio to read, for accounts with more than one: its id, or its name (exact, case-insensitive, e.g. \"Trading\"). Omit to read this connection's default portfolio; every answer names the portfolio it read in `profile`. An unknown or shared name returns profile_not_found or profile_ambiguous listing the real ones, and list_profiles shows them up front."
    • Changedget_deposits1 field changed
      • changedInput schema / properties / profile / description
        Previous value: -"Which portfolio to read, by name or id, for accounts that have more than one. Omit to use this connection's default. Call list_profiles first if the user names a portfolio you have not seen."New value: +"Which portfolio to read, for accounts with more than one: its id, or its name (exact, case-insensitive, e.g. \"Trading\"). Omit to read this connection's default portfolio; every answer names the portfolio it read in `profile`. An unknown or shared name returns profile_not_found or profile_ambiguous listing the real ones, and list_profiles shows them up front."
    • Changedget_fees3 fields changed
      • changedInput schema / properties / bucket / description
        Previous value: -"Time bucket for the fee series."New value: +"Granularity of the fee time series. Defaults to month."
      • changedInput schema / properties / period / description
        Previous value: -"Time window. \"last_24h\" for today/since-yesterday, \"all\" for full history. Defaults to the endpoint's own default (usually last 30 days)."New value: +"Time window ending now: last_24h (today / since yesterday), last_7d, last_30d, last_90d, ytd (since 1 January UTC), or all. Omit for all time."
      • changedInput schema / properties / profile / description
        Previous value: -"Which portfolio to read, by name or id, for accounts that have more than one. Omit to use this connection's default. Call list_profiles first if the user names a portfolio you have not seen."New value: +"Which portfolio to read, for accounts with more than one: its id, or its name (exact, case-insensitive, e.g. \"Trading\"). Omit to read this connection's default portfolio; every answer names the portfolio it read in `profile`. An unknown or shared name returns profile_not_found or profile_ambiguous listing the real ones, and list_profiles shows them up front."
    • Changedget_fiat_stats1 field changed
      • changedInput schema / properties / profile / description
        Previous value: -"Which portfolio to read, by name or id, for accounts that have more than one. Omit to use this connection's default. Call list_profiles first if the user names a portfolio you have not seen."New value: +"Which portfolio to read, for accounts with more than one: its id, or its name (exact, case-insensitive, e.g. \"Trading\"). Omit to read this connection's default portfolio; every answer names the portfolio it read in `profile`. An unknown or shared name returns profile_not_found or profile_ambiguous listing the real ones, and list_profiles shows them up front."
    • Changedget_income_by_source2 fields changed
      • changedInput schema / properties / period / description
        Previous value: -"Time window. \"last_24h\" for today/since-yesterday, \"all\" for full history. Defaults to the endpoint's own default (usually last 30 days)."New value: +"Time window ending now: last_24h (today / since yesterday), last_7d, last_30d, last_90d, ytd (since 1 January UTC), or all. Omit for all time."
      • changedInput schema / properties / profile / description
        Previous value: -"Which portfolio to read, by name or id, for accounts that have more than one. Omit to use this connection's default. Call list_profiles first if the user names a portfolio you have not seen."New value: +"Which portfolio to read, for accounts with more than one: its id, or its name (exact, case-insensitive, e.g. \"Trading\"). Omit to read this connection's default portfolio; every answer names the portfolio it read in `profile`. An unknown or shared name returns profile_not_found or profile_ambiguous listing the real ones, and list_profiles shows them up front."
    • Changedget_income_timeseries3 fields changed
      • changedInput schema / properties / bucket / description
        Previous value: -"Time bucket. Defaults to month."New value: +"Granularity of the series. Defaults to week."
      • changedInput schema / properties / period / description
        Previous value: -"Time window. \"last_24h\" for today/since-yesterday, \"all\" for full history. Defaults to the endpoint's own default (usually last 30 days)."New value: +"Time window ending now: last_24h (today / since yesterday), last_7d, last_30d, last_90d, ytd (since 1 January UTC), or all. Omit for all time."
      • changedInput schema / properties / profile / description
        Previous value: -"Which portfolio to read, by name or id, for accounts that have more than one. Omit to use this connection's default. Call list_profiles first if the user names a portfolio you have not seen."New value: +"Which portfolio to read, for accounts with more than one: its id, or its name (exact, case-insensitive, e.g. \"Trading\"). Omit to read this connection's default portfolio; every answer names the portfolio it read in `profile`. An unknown or shared name returns profile_not_found or profile_ambiguous listing the real ones, and list_profiles shows them up front."
    • Changedget_movements9 fields changed
      • changedInput schema / properties / asset / description
        Previous value: -"Filter to one asset ticker, e.g. \"BTC\"."New value: +"Asset ticker, e.g. \"BTC\"; comma-separate for several (\"BTC,ETH\"). Case-insensitive."
      • changedInput schema / properties / kind / description
        Previous value: -"INFLOW = deposit, OUTFLOW = withdrawal."New value: +"INFLOW = deposit, OUTFLOW = withdrawal, TRANSFER = a move between the user's own accounts, INCOME = yield, staking or airdrop, TRADE = a buy or sell. Omit for every kind."
      • changedInput schema / properties / limit / description
        Previous value: -"Maximum rows to return (capped at 200)."New value: +"How many of the newest matching rows to return, 1-200. Defaults to 100."
      • changedInput schema / properties / period / description
        Previous value: -"Time window. \"last_24h\" for today/since-yesterday, \"all\" for full history. Defaults to the endpoint's own default (usually last 30 days)."New value: +"Time window ending now: last_24h (today / since yesterday), last_7d, last_30d, last_90d, ytd (since 1 January UTC), or all. Omit for all time."
      • changedInput schema / properties / profile / description
        Previous value: -"Which portfolio to read, by name or id, for accounts that have more than one. Omit to use this connection's default. Call list_profiles first if the user names a portfolio you have not seen."New value: +"Which portfolio to read, for accounts with more than one: its id, or its name (exact, case-insensitive, e.g. \"Trading\"). Omit to read this connection's default portfolio; every answer names the portfolio it read in `profile`. An unknown or shared name returns profile_not_found or profile_ambiguous listing the real ones, and list_profiles shows them up front."
      • changedInput schema / properties / q / description
        Previous value: -"Free-text search over the ledger."New value: +"Free-text substring search (case-insensitive) over asset, transaction id, status and stored details such as an address fragment."
      • changedInput schema / properties / source / description
        Previous value: -"Filter to one source label."New value: +"Source label as it appears in countsBySource, e.g. \"deposit\" or \"staking\"; comma-separate for several."
      • changedInput schema / properties / status / description
        Previous value: -"Filter by movement status."New value: +"Status exactly as it appears in countsByStatus. Venue-native and case-sensitive (\"SUCCESS\", \"2\"), so copy it from the facet rather than guessing."
      • changedInput schema / properties / venue / description
        Previous value: -"Filter to one venue, e.g. \"binance\"."New value: +"Venue name as it appears in countsByVenue, e.g. \"binance\"; comma-separate for several."
    • Changedget_pnl_by_symbol4 fields changed
      • changedInput schema / properties / limit / description
        Previous value: -"Maximum rows to return (capped at 200)."New value: +"How many of the best AND how many of the worst pairs to return, 1-100. Up to twice this many rows come back, best first; with fewer pairs than that, every pair comes back. Defaults to 20 (the 20 best and the 20 worst)."
      • changedInput schema / properties / limit / maximum
        Previous value: -200New value: +100
      • changedInput schema / properties / profile / description
        Previous value: -"Which portfolio to read, by name or id, for accounts that have more than one. Omit to use this connection's default. Call list_profiles first if the user names a portfolio you have not seen."New value: +"Which portfolio to read, for accounts with more than one: its id, or its name (exact, case-insensitive, e.g. \"Trading\"). Omit to read this connection's default portfolio; every answer names the portfolio it read in `profile`. An unknown or shared name returns profile_not_found or profile_ambiguous listing the real ones, and list_profiles shows them up front."
      • changedInput schema / properties / quote / description
        Previous value: -"Quote currency to report in, e.g. \"USDT\"."New value: +"Quote currency whose pairs to rank, e.g. \"USDT\" for BTCUSDT and ETHUSDT. Defaults to USDT."
    • Changedget_pnl_overview2 fields changed
      • changedInput schema / properties / period / description
        Previous value: -"Time window. \"last_24h\" for today/since-yesterday, \"all\" for full history. Defaults to the endpoint's own default (usually last 30 days)."New value: +"Time window ending now: last_24h (today / since yesterday), last_7d, last_30d, last_90d, ytd (since 1 January UTC), or all. Omit for all time."
      • changedInput schema / properties / profile / description
        Previous value: -"Which portfolio to read, by name or id, for accounts that have more than one. Omit to use this connection's default. Call list_profiles first if the user names a portfolio you have not seen."New value: +"Which portfolio to read, for accounts with more than one: its id, or its name (exact, case-insensitive, e.g. \"Trading\"). Omit to read this connection's default portfolio; every answer names the portfolio it read in `profile`. An unknown or shared name returns profile_not_found or profile_ambiguous listing the real ones, and list_profiles shows them up front."
    • Changedget_portfolio1 field changed
      • changedInput schema / properties / profile / description
        Previous value: -"Which portfolio to read, by name or id, for accounts that have more than one. Omit to use this connection's default. Call list_profiles first if the user names a portfolio you have not seen."New value: +"Which portfolio to read, for accounts with more than one: its id, or its name (exact, case-insensitive, e.g. \"Trading\"). Omit to read this connection's default portfolio; every answer names the portfolio it read in `profile`. An unknown or shared name returns profile_not_found or profile_ambiguous listing the real ones, and list_profiles shows them up front."
    • Changedget_portfolio_history3 fields changed
      • changedInput schema / properties / period / description
        Previous value: -"Time window. \"last_24h\" for today/since-yesterday, \"all\" for full history. Defaults to the endpoint's own default (usually last 30 days)."New value: +"Time window ending now: last_24h (today / since yesterday), last_7d, last_30d, last_90d, ytd (since 1 January UTC), or all. Omit for all time."
      • changedInput schema / properties / profile / description
        Previous value: -"Which portfolio to read, by name or id, for accounts that have more than one. Omit to use this connection's default. Call list_profiles first if the user names a portfolio you have not seen."New value: +"Which portfolio to read, for accounts with more than one: its id, or its name (exact, case-insensitive, e.g. \"Trading\"). Omit to read this connection's default portfolio; every answer names the portfolio it read in `profile`. An unknown or shared name returns profile_not_found or profile_ambiguous listing the real ones, and list_profiles shows them up front."
      • changedInput schema / properties / slim / description
        Previous value: -"true (recommended) drops the per-asset breakdown and returns only the daily totals."New value: +"true (recommended) returns only the daily totals; false or omitted adds byAsset values and quantities for every asset on every day."
    • Changedget_portfolio_score2 fields changed
      • removedInput schema / properties / period
        Removed value: -{
        -  "description": "Time window. \"last_24h\" for today/since-yesterday, \"all\" for full history. Defaults to the endpoint's own default (usually last 30 days).",
        -  "enum": [
        -    "last_24h",
        -    "last_7d",
        -    "last_30d",
        -    "last_90d",
        -    "ytd",
        -    "all"
        -  ],
        -  "type": "string"
        -}
      • changedInput schema / properties / profile / description
        Previous value: -"Which portfolio to read, by name or id, for accounts that have more than one. Omit to use this connection's default. Call list_profiles first if the user names a portfolio you have not seen."New value: +"Which portfolio to read, for accounts with more than one: its id, or its name (exact, case-insensitive, e.g. \"Trading\"). Omit to read this connection's default portfolio; every answer names the portfolio it read in `profile`. An unknown or shared name returns profile_not_found or profile_ambiguous listing the real ones, and list_profiles shows them up front."
    • Changedget_portfolio_snapshot1 field changed
      • changedInput schema / properties / profile / description
        Previous value: -"Which portfolio to read, by name or id, for accounts that have more than one. Omit to use this connection's default. Call list_profiles first if the user names a portfolio you have not seen."New value: +"Which portfolio to read, for accounts with more than one: its id, or its name (exact, case-insensitive, e.g. \"Trading\"). Omit to read this connection's default portfolio; every answer names the portfolio it read in `profile`. An unknown or shared name returns profile_not_found or profile_ambiguous listing the real ones, and list_profiles shows them up front."
    • Changedget_risk_metrics3 fields changed
      • changedInput schema / properties / benchmark / description
        Previous value: -"Benchmark for beta and correlation. Defaults to BTC."New value: +"Asset to measure beta, correlation and excess return against. Defaults to BTC."
      • changedInput schema / properties / period / description
        Previous value: -"Time window. \"last_24h\" for today/since-yesterday, \"all\" for full history. Defaults to the endpoint's own default (usually last 30 days)."New value: +"Time window ending now: last_24h (today / since yesterday), last_7d, last_30d, last_90d, ytd (since 1 January UTC), or all. Omit for all time."
      • changedInput schema / properties / profile / description
        Previous value: -"Which portfolio to read, by name or id, for accounts that have more than one. Omit to use this connection's default. Call list_profiles first if the user names a portfolio you have not seen."New value: +"Which portfolio to read, for accounts with more than one: its id, or its name (exact, case-insensitive, e.g. \"Trading\"). Omit to read this connection's default portfolio; every answer names the portfolio it read in `profile`. An unknown or shared name returns profile_not_found or profile_ambiguous listing the real ones, and list_profiles shows them up front."
    • Changedget_sync_status1 field changed
      • changedInput schema / properties / profile / description
        Previous value: -"Which portfolio to read, by name or id, for accounts that have more than one. Omit to use this connection's default. Call list_profiles first if the user names a portfolio you have not seen."New value: +"Which portfolio to read, for accounts with more than one: its id, or its name (exact, case-insensitive, e.g. \"Trading\"). Omit to read this connection's default portfolio; every answer names the portfolio it read in `profile`. An unknown or shared name returns profile_not_found or profile_ambiguous listing the real ones, and list_profiles shows them up front."
    • Changedget_target_allocation1 field changed
      • changedInput schema / properties / profile / description
        Previous value: -"Which portfolio to read, by name or id, for accounts that have more than one. Omit to use this connection's default. Call list_profiles first if the user names a portfolio you have not seen."New value: +"Which portfolio to read, for accounts with more than one: its id, or its name (exact, case-insensitive, e.g. \"Trading\"). Omit to read this connection's default portfolio; every answer names the portfolio it read in `profile`. An unknown or shared name returns profile_not_found or profile_ambiguous listing the real ones, and list_profiles shows them up front."
    • Changedget_transfers1 field changed
      • changedInput schema / properties / profile / description
        Previous value: -"Which portfolio to read, by name or id, for accounts that have more than one. Omit to use this connection's default. Call list_profiles first if the user names a portfolio you have not seen."New value: +"Which portfolio to read, for accounts with more than one: its id, or its name (exact, case-insensitive, e.g. \"Trading\"). Omit to read this connection's default portfolio; every answer names the portfolio it read in `profile`. An unknown or shared name returns profile_not_found or profile_ambiguous listing the real ones, and list_profiles shows them up front."
    • Changedget_venues1 field changed
      • changedInput schema / properties / profile / description
        Previous value: -"Which portfolio to read, by name or id, for accounts that have more than one. Omit to use this connection's default. Call list_profiles first if the user names a portfolio you have not seen."New value: +"Which portfolio to read, for accounts with more than one: its id, or its name (exact, case-insensitive, e.g. \"Trading\"). Omit to read this connection's default portfolio; every answer names the portfolio it read in `profile`. An unknown or shared name returns profile_not_found or profile_ambiguous listing the real ones, and list_profiles shows them up front."
    • Changedget_wallets1 field changed
      • changedInput schema / properties / profile / description
        Previous value: -"Which portfolio to read, by name or id, for accounts that have more than one. Omit to use this connection's default. Call list_profiles first if the user names a portfolio you have not seen."New value: +"Which portfolio to read, for accounts with more than one: its id, or its name (exact, case-insensitive, e.g. \"Trading\"). Omit to read this connection's default portfolio; every answer names the portfolio it read in `profile`. An unknown or shared name returns profile_not_found or profile_ambiguous listing the real ones, and list_profiles shows them up front."
    • Changedset_target_allocation1 field changed
      • changedInput schema / properties / profile / description
        Previous value: -"Which portfolio to read, by name or id, for accounts that have more than one. Omit to use this connection's default. Call list_profiles first if the user names a portfolio you have not seen."New value: +"Which portfolio to read, for accounts with more than one: its id, or its name (exact, case-insensitive, e.g. \"Trading\"). Omit to read this connection's default portfolio; every answer names the portfolio it read in `profile`. An unknown or shared name returns profile_not_found or profile_ambiguous listing the real ones, and list_profiles shows them up front."
  5. 26 tool updates
    • First observedget_activity_calendar
    • First observedget_alerts
    • First observedget_asset_trade_stats
    • First observedget_capital_flow
    • First observedget_changes
    • First observedget_dashboard
    • First observedget_deposits
    • First observedget_fees
    • First observedget_fiat_stats
    • First observedget_income_by_source
    • First observedget_income_timeseries
    • First observedget_movements
    • First observedget_pnl_by_symbol
    • First observedget_pnl_overview
    • First observedget_portfolio
    • First observedget_portfolio_history
    • First observedget_portfolio_score
    • First observedget_portfolio_snapshot
    • First observedget_risk_metrics
    • First observedget_sync_status
    • First observedget_target_allocation
    • First observedget_transfers
    • First observedget_venues
    • First observedget_wallets
    • First observedlist_profiles
    • First observedset_target_allocation

Publisher details

Operator
WhereItGoes
Vendor relationship
First-party
Trust center
Not available
Restrictions
Requires a free WhereItGoes account (sign in with Google or a wallet). Read-only; one optional propose-only tool needs a separate consent scope.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Transforms Claude AI into a personal crypto portfolio analyst, enabling wallet analysis, AI-powered recommendations, and portfolio tracking across 8+ blockchains through natural conversation.
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    Connects Claude Desktop to crypto and brokerage portfolio data via Coinbase and SnapTrade, providing read-only tools to inspect accounts, holdings, prices, and provider health.
    5
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    Cost basis tracking, capital gains calculation, and 1099-DA reconciliation for Claude, built on top of the Crypto Portfolio MCP data layer.
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Read-only INDmoney portfolio access for Claude — Indian stocks, mutual funds, US stocks, gold, credit cards, loans, and Experian credit score. OTP browser login with AES-256-GCM encrypted sessions.
    MIT
Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.