WhereItGoes
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.
- 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
Scored across 15 tools
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.
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.
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.
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 toolsget_alertsConfigured price and value alertsARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| profile | No | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | 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. |
| alerts | No | |
| message | No | Plain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user. |
| profile | No | The portfolio these figures belong to (absent on list_profiles). |
| next_step | No | What the user should do about data_status. |
| source_url | No | Link to the page in the WhereItGoes app that shows this data. |
| data_status | No | 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. |
| upgrade_url | No | Where the user can upgrade, on an upgrade_required refusal. |
| stock_assets | No | Present when the answer mentions Binance Stocks shares (EQ_<ticker>): they are US stocks, not crypto. |
TDQS
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.
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.
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.
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.
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.
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 eventsARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| period | No | Time window ending now: last_24h, last_7d, last_30d, last_90d, ytd (since 1 January UTC) or all. Omit for all time. | |
| profile | No | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | 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. |
| notes | No | |
| events | No | Ranked, most significant first. |
| window | No | The time window these figures cover. |
| message | No | Plain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user. |
| profile | No | The portfolio these figures belong to (absent on list_profiles). |
| next_step | No | What the user should do about data_status. |
| truncated | No | 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. |
| source_url | No | Link to the page in the WhereItGoes app that shows this data. |
| data_status | No | 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. |
| events_note | No | Present only when `events` was cut; says how many rows were kept and which. |
| upgrade_url | No | Where the user can upgrade, on an upgrade_required refusal. |
| countsByType | No | Events detected per type before the list was capped. |
| stock_assets | No | Present when the answer mentions Binance Stocks shares (EQ_<ticker>): they are US stocks, not crypto. |
TDQS
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.
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.
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.
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.
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.
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 statusARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| view | Yes | Which 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. | |
| profile | No | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
| view | No | Which view answered; its data is under the key of the same name. |
| error | No | 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. |
| message | No | Plain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user. |
| profile | No | The portfolio these figures belong to (absent on list_profiles). |
| wallets | No | Present when view = "wallets". |
| exchanges | No | Present when view = "exchanges". |
| next_step | No | What the user should do about data_status. |
| source_url | No | Link to the page in the WhereItGoes app that shows this data. |
| data_status | No | 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. |
| sync_status | No | Present when view = "sync_status". |
| upgrade_url | No | Where the user can upgrade, on an upgrade_required refusal. |
| stock_assets | No | Present when the answer mentions Binance Stocks shares (EQ_<ticker>): they are US stocks, not crypto. |
TDQS
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.
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.
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.
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.
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.
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 typeARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| bucket | No | Granularity of the fee series. Defaults to month. | |
| period | No | Time window ending now: last_24h, last_7d, last_30d, last_90d, ytd (since 1 January UTC) or all. Omit for all time. | |
| profile | No | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | 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. |
| notes | No | What the coverage leaves out. |
| bucket | No | |
| byType | No | Largest first. |
| points | No | Oldest bucket first. |
| window | No | The time window these figures cover. |
| byAsset | No | Largest first. |
| byVenue | No | Largest first. |
| message | No | Plain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user. |
| partial | No | true when some fees could not be valued or are not tracked. |
| profile | No | The portfolio these figures belong to (absent on list_profiles). |
| feeCount | No | Fee-bearing events counted. |
| totalUsd | No | All fees in the window, USD, each valued on the day paid. |
| avgFeeUsd | No | |
| next_step | No | What the user should do about data_status. |
| truncated | No | 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. |
| source_url | No | Link to the page in the WhereItGoes app that shows this data. |
| data_status | No | 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. |
| points_note | No | Present only when `points` was cut; says how many rows were kept and which. |
| upgrade_url | No | Where the user can upgrade, on an upgrade_required refusal. |
| stock_assets | No | Present when the answer mentions Binance Stocks shares (EQ_<ticker>): they are US stocks, not crypto. |
| unpricedAssets | No |
TDQS
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.
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.
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.
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.
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.
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, airdropsARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| view | Yes | Which 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. | |
| bucket | No | by_asset only. Granularity of the series. Defaults to week. | |
| period | No | Time window ending now: last_24h, last_7d, last_30d, last_90d, ytd (since 1 January UTC) or all. Omit for all time. | |
| profile | No | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
| view | No | Which view answered; its data is under the key of the same name. |
| error | No | 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. |
| message | No | Plain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user. |
| profile | No | The portfolio these figures belong to (absent on list_profiles). |
| by_asset | No | Present when view = "by_asset". |
| next_step | No | What the user should do about data_status. |
| source_url | No | Link to the page in the WhereItGoes app that shows this data. |
| by_category | No | Present when view = "by_category". |
| data_status | No | 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. |
| upgrade_url | No | Where the user can upgrade, on an upgrade_required refusal. |
| stock_assets | No | Present when the answer mentions Binance Stocks shares (EQ_<ticker>): they are US stocks, not crypto. |
TDQS
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.
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.
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.
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.
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.
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, fiatARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| view | Yes | Which 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. | |
| bucket | No | capital_flow only. Granularity of the series. Defaults to month. | |
| period | No | Time window ending now: last_24h, last_7d, last_30d, last_90d, ytd (since 1 January UTC) or all. Omit for all time. | |
| profile | No | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
| fiat | No | Present when view = "fiat". |
| view | No | Which view answered; its data is under the key of the same name. |
| error | No | 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. |
| message | No | Plain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user. |
| profile | No | The portfolio these figures belong to (absent on list_profiles). |
| deposits | No | Present when view = "deposits". |
| next_step | No | What the user should do about data_status. |
| transfers | No | Present when view = "transfers". |
| source_url | No | Link to the page in the WhereItGoes app that shows this data. |
| data_status | No | 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. |
| upgrade_url | No | Where the user can upgrade, on an upgrade_required refusal. |
| capital_flow | No | Present when view = "capital_flow". |
| stock_assets | No | Present when the answer mentions Binance Stocks shares (EQ_<ticker>): they are US stocks, not crypto. |
TDQS
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.
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.
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.
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.
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.
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 ledgerARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| q | No | Case-insensitive substring search over asset, transaction id, status and stored details such as an address fragment. | |
| kind | No | 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. | |
| asset | No | Asset ticker, e.g. "BTC"; comma-separate for several ("BTC,ETH"). Case-insensitive. | |
| limit | No | How many of the newest matching rows to return, 1-200. Defaults to 100. | |
| venue | No | Venue name as it appears in countsByVenue, e.g. "binance"; comma-separate for several. | |
| period | No | Time window ending now: last_24h, last_7d, last_30d, last_90d, ytd (since 1 January UTC) or all. Omit for all time. | |
| source | No | Source label as it appears in countsBySource, e.g. "deposit" or "staking"; comma-separate for several. | |
| status | No | Status exactly as it appears in countsByStatus, e.g. "SUCCESS" or "2". | |
| profile | No | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | 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. |
| items | No | Newest first, at most `limit` rows. |
| total | No | Every row matching the filters, before `limit`. |
| message | No | Plain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user. |
| profile | No | The portfolio these figures belong to (absent on list_profiles). |
| next_step | No | What the user should do about data_status. |
| truncated | No | 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. |
| hiddenSpam | No | Rows hidden because they were marked spam. |
| items_note | No | Present only when `items` was cut; says how many rows were kept and which. |
| source_url | No | Link to the page in the WhereItGoes app that shows this data. |
| data_status | No | 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. |
| upgrade_url | No | Where the user can upgrade, on an upgrade_required refusal. |
| stock_assets | No | Present when the answer mentions Binance Stocks shares (EQ_<ticker>): they are US stocks, not crypto. |
| countsByVenue | No | |
| countsBySource | No | |
| countsByStatus | No |
TDQS
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.
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.
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.
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.
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.
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 pairARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| slim | No | history only. true returns the daily totals alone; false or omitted adds a USD value and a quantity for every asset on every day. | |
| view | Yes | Which 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. | |
| limit | No | realized_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. | |
| quote | No | realized_by_pair only. Quote currency whose pairs to rank, e.g. "USDT" for BTCUSDT. Defaults to USDT. | |
| period | No | Time window ending now: last_24h, last_7d, last_30d, last_90d, ytd (since 1 January UTC) or all. Omit for all time. | |
| profile | No | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
| pnl | No | Present when view = "pnl". |
| view | No | Which view answered; its data is under the key of the same name. |
| error | No | 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. |
| history | No | Present when view = "history". |
| message | No | Plain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user. |
| profile | No | The portfolio these figures belong to (absent on list_profiles). |
| next_step | No | What the user should do about data_status. |
| source_url | No | Link to the page in the WhereItGoes app that shows this data. |
| data_status | No | 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. |
| upgrade_url | No | Where the user can upgrade, on an upgrade_required refusal. |
| stock_assets | No | Present when the answer mentions Binance Stocks shares (EQ_<ticker>): they are US stocks, not crypto. |
| realized_by_pair | No | Present when view = "realized_by_pair". |
TDQS
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.
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.
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.
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.
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.
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 changeARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| view | Yes | 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. | |
| profile | No | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
| view | No | Which view answered; its data is under the key of the same name. |
| error | No | 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. |
| message | No | Plain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user. |
| profile | No | The portfolio these figures belong to (absent on list_profiles). |
| holdings | No | Present when view = "holdings". |
| overview | No | Present when view = "overview". |
| next_step | No | What the user should do about data_status. |
| source_url | No | Link to the page in the WhereItGoes app that shows this data. |
| data_status | No | 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. |
| upgrade_url | No | Where the user can upgrade, on an upgrade_required refusal. |
| stock_assets | No | Present when the answer mentions Binance Stocks shares (EQ_<ticker>): they are US stocks, not crypto. |
| recent_change | No | Present when view = "recent_change". |
TDQS
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.
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.
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.
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.
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.
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)ARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| view | Yes | Which 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. | |
| period | No | Time window ending now: last_24h, last_7d, last_30d, last_90d, ytd (since 1 January UTC) or all. Omit for all time. | |
| profile | No | 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. | |
| benchmark | No | metrics only. Asset to measure beta, correlation and excess return against. Defaults to BTC. |
Output Schema
| Name | Required | Description |
|---|---|---|
| view | No | Which view answered; its data is under the key of the same name. |
| error | No | 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. |
| score | No | Present when view = "score". |
| message | No | Plain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user. |
| metrics | No | Present when view = "metrics". |
| profile | No | The portfolio these figures belong to (absent on list_profiles). |
| next_step | No | What the user should do about data_status. |
| source_url | No | Link to the page in the WhereItGoes app that shows this data. |
| data_status | No | 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. |
| upgrade_url | No | Where the user can upgrade, on an upgrade_required refusal. |
| stock_assets | No | Present when the answer mentions Binance Stocks shares (EQ_<ticker>): they are US stocks, not crypto. |
TDQS
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.
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.
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.
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.
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.
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 planARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| profile | No | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
| set | No | null when no target allocation is set. |
| error | No | 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. |
| message | No | Plain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user. |
| profile | No | The portfolio these figures belong to (absent on list_profiles). |
| next_step | No | What the user should do about data_status. |
| source_url | No | Link to the page in the WhereItGoes app that shows this data. |
| data_status | No | 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. |
| upgrade_url | No | Where the user can upgrade, on an upgrade_required refusal. |
| stock_assets | No | Present when the answer mentions Binance Stocks shares (EQ_<ticker>): they are US stocks, not crypto. |
TDQS
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.
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.
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.
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.
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.
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 calendarARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| view | Yes | Which 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. | |
| profile | No | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
| view | No | Which view answered; its data is under the key of the same name. |
| error | No | 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. |
| message | No | Plain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user. |
| profile | No | The portfolio these figures belong to (absent on list_profiles). |
| calendar | No | Present when view = "calendar". |
| next_step | No | What the user should do about data_status. |
| source_url | No | Link to the page in the WhereItGoes app that shows this data. |
| data_status | No | 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. |
| upgrade_url | No | Where the user can upgrade, on an upgrade_required refusal. |
| asset_totals | No | Present when view = "asset_totals". |
| stock_assets | No | Present when the answer mentions Binance Stocks shares (EQ_<ticker>): they are US stocks, not crypto. |
TDQS
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.
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.
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.
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.
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.
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 accountARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | 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. |
| items | No | |
| active | No | Id of the portfolio active in the app. |
| message | No | Plain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user. |
| profile | No | The portfolio these figures belong to (absent on list_profiles). |
| next_step | No | What the user should do about data_status. |
| source_url | No | Link to the page in the WhereItGoes app that shows this data. |
| data_status | No | 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. |
| upgrade_url | No | Where the user can upgrade, on an upgrade_required refusal. |
| stock_assets | No | Present when the answer mentions Binance Stocks shares (EQ_<ticker>): they are US stocks, not crypto. |
TDQS
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.
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.
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.
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.
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.
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)ADestructiveInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| op | No | Fire when the watched value goes above or below the threshold. | |
| kind | No | create 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, %). | |
| note | No | A short note shown with the alert, up to 200 characters. On update, an empty string clears it. | |
| action | Yes | create a new alert, update an existing one (by alertId), or delete one (by alertId). | |
| repeat | No | once (default): fire one time, then pause. recurring: fire every time the level is crossed again, no more often than cooldownSec. | |
| alertId | No | update and delete only: the `id` of the alert, exactly as get_alerts returns it. | |
| enabled | No | update only: false pauses the alert, true turns it back on. | |
| profile | No | 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. | |
| subject | No | create only, and required for asset_price, asset_pct, holding_value and holding_weight: the ticker, e.g. BTC. Omit for the portfolio kinds. | |
| channels | No | Where 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). | |
| threshold | No | The 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. | |
| cooldownSec | No | recurring 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
| Name | Required | Description |
|---|---|---|
| error | No | 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. |
| title | No | What the change does. |
| status | No | Always this: nothing has been applied yet. |
| message | No | Plain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user. |
| preview | No | Courtesy diff; the authoritative one is on the confirm page. |
| profile | No | The portfolio these figures belong to (absent on list_profiles). |
| next_step | No | What the user should do about data_status. |
| expires_at | No | When the link stops working, epoch ms. |
| source_url | No | Link to the page in the WhereItGoes app that shows this data. |
| confirm_url | No | The link the user must open and approve; tell them to open it. |
| data_status | No | 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. |
| upgrade_url | No | Where the user can upgrade, on an upgrade_required refusal. |
| stock_assets | No | Present when the answer mentions Binance Stocks shares (EQ_<ticker>): they are US stocks, not crypto. |
TDQS
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.
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.
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.
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.
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.
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)ADestructiveInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| dim | Yes | Which axis the plan is on. Every subject must belong to this axis. | |
| rows | Yes | The COMPLETE plan. Anything omitted is removed. | |
| profile | No | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | 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. |
| title | No | What the change does. |
| status | No | Always this: nothing has been applied yet. |
| message | No | Plain-language explanation that accompanies `error`, `data_status` or a proposed change. Read it to the user. |
| preview | No | Courtesy diff; the authoritative one is on the confirm page. |
| profile | No | The portfolio these figures belong to (absent on list_profiles). |
| next_step | No | What the user should do about data_status. |
| expires_at | No | When the link stops working, epoch ms. |
| source_url | No | Link to the page in the WhereItGoes app that shows this data. |
| confirm_url | No | The link the user must open and approve; tell them to open it. |
| data_status | No | 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. |
| upgrade_url | No | Where the user can upgrade, on an upgrade_required refusal. |
| stock_assets | No | Present when the answer mentions Binance Stocks shares (EQ_<ticker>): they are US stocks, not crypto. |
TDQS
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.
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.
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.
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.
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.
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 tool update
- Changed
get_portfolio12 fields changed- changed
Output schema / properties / holdings / properties / assets / items / properties / position / anyOfPrevious 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" + } +] - changed
Output schema / properties / holdings / properties / assets / items / properties / positions / items / properties / kind / descriptionPrevious 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." - changed
Output schema / properties / holdings / properties / assets / items / properties / positions / items / properties / underlying / descriptionPrevious 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 \"/\"." - added
Output schema / properties / holdings / properties / assets / items / properties / userTrustedAdded 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" +} - changed
Output schema / properties / holdings / properties / assets / items / properties / weightPct / descriptionPrevious value: -"Share of totalUsdt, percent."New value: +"Share of grossUsdt (the holdings), percent." - removed
Output schema / properties / holdings / properties / excludedDebtRemoved 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" -} - added
Output schema / properties / holdings / properties / grossUsdtAdded value: +{ + "description": "What the holdings rows are worth, USD (sum of assets[].valueUsdt).", + "type": "string" +} - added
Output schema / properties / holdings / properties / liabilitiesAdded 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" +} - added
Output schema / properties / holdings / properties / liabilitiesUsdtAdded value: +{ + "description": "What is owed against them, USD, a POSITIVE amount (sum of liabilities[].valueUsdt).", + "type": "string" +} - changed
Output schema / properties / holdings / properties / totalUsdt / descriptionPrevious 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." - added
Output schema / properties / holdings / properties / unverifiedAdded 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" +} - added
Output schema / properties / holdings / properties / unverifiedSummaryAdded 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" +}
15 tool updates
- Changed
get_alerts3 fields changed- added
Output schema / properties / data_statusAdded 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" +} - changed
Output schema / properties / message / descriptionPrevious 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." - added
Output schema / properties / next_stepAdded 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" +}
- Changed
get_changes3 fields changed- added
Output schema / properties / data_statusAdded 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" +} - changed
Output schema / properties / message / descriptionPrevious 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." - added
Output schema / properties / next_stepAdded 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" +}
- Changed
get_connections3 fields changed- added
Output schema / properties / data_statusAdded 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" +} - changed
Output schema / properties / message / descriptionPrevious 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." - added
Output schema / properties / next_stepAdded 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" +}
- Changed
get_fees3 fields changed- added
Output schema / properties / data_statusAdded 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" +} - changed
Output schema / properties / message / descriptionPrevious 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." - added
Output schema / properties / next_stepAdded 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" +}
- Changed
get_income3 fields changed- added
Output schema / properties / data_statusAdded 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" +} - changed
Output schema / properties / message / descriptionPrevious 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." - added
Output schema / properties / next_stepAdded 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" +}
- Changed
get_money_flows3 fields changed- added
Output schema / properties / data_statusAdded 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" +} - changed
Output schema / properties / message / descriptionPrevious 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." - added
Output schema / properties / next_stepAdded 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" +}
- Changed
get_movements3 fields changed- added
Output schema / properties / data_statusAdded 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" +} - changed
Output schema / properties / message / descriptionPrevious 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." - added
Output schema / properties / next_stepAdded 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" +}
- Changed
get_performance3 fields changed- added
Output schema / properties / data_statusAdded 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" +} - changed
Output schema / properties / message / descriptionPrevious 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." - added
Output schema / properties / next_stepAdded 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" +}
- Changed
get_portfolio6 fields changed- added
Output schema / properties / data_statusAdded 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" +} - added
Output schema / properties / holdings / properties / assets / items / properties / positionAdded 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." +} - added
Output schema / properties / holdings / properties / assets / items / properties / positionsAdded 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" +} - added
Output schema / properties / holdings / properties / excludedDebtAdded 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" +} - changed
Output schema / properties / message / descriptionPrevious 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." - added
Output schema / properties / next_stepAdded 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" +}
- Changed
get_risk3 fields changed- added
Output schema / properties / data_statusAdded 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" +} - changed
Output schema / properties / message / descriptionPrevious 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." - added
Output schema / properties / next_stepAdded 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" +}
- Changed
get_target_allocation3 fields changed- added
Output schema / properties / data_statusAdded 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" +} - changed
Output schema / properties / message / descriptionPrevious 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." - added
Output schema / properties / next_stepAdded 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" +}
- Changed
get_trading_activity3 fields changed- added
Output schema / properties / data_statusAdded 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" +} - changed
Output schema / properties / message / descriptionPrevious 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." - added
Output schema / properties / next_stepAdded 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" +}
- Changed
list_profiles3 fields changed- added
Output schema / properties / data_statusAdded 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" +} - changed
Output schema / properties / message / descriptionPrevious 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." - added
Output schema / properties / next_stepAdded 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" +}
- Changed
propose_alert_change3 fields changed- added
Output schema / properties / data_statusAdded 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" +} - changed
Output schema / properties / message / descriptionPrevious 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." - added
Output schema / properties / next_stepAdded 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" +}
- Changed
set_target_allocation3 fields changed- added
Output schema / properties / data_statusAdded 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" +} - changed
Output schema / properties / message / descriptionPrevious 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." - added
Output schema / properties / next_stepAdded 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" +}
33 tool updates
- Removed
get_activity_calendar - Changed
get_alerts1 field changed- changed
Output 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" +}
- Removed
get_asset_trade_stats - Removed
get_capital_flow - Changed
get_changes2 fields changed- changed
Input schema / properties / period / descriptionPrevious 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." - changed
Output 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" +}
- Added
get_connections - Removed
get_dashboard - Removed
get_deposits - Changed
get_fees3 fields changed- changed
Input schema / properties / bucket / descriptionPrevious value: -"Granularity of the fee time series. Defaults to month."New value: +"Granularity of the fee series. Defaults to month." - changed
Input schema / properties / period / descriptionPrevious 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." - changed
Output 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" +}
- Removed
get_fiat_stats - Added
get_income - Removed
get_income_by_source - Removed
get_income_timeseries - Added
get_money_flows - Changed
get_movements5 fields changed- changed
Input schema / properties / kind / descriptionPrevious 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." - changed
Input schema / properties / period / descriptionPrevious 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." - changed
Input schema / properties / q / descriptionPrevious 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." - changed
Input schema / properties / status / descriptionPrevious 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\"." - changed
Output 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" +}
- Added
get_performance - Removed
get_pnl_by_symbol - Removed
get_pnl_overview - Changed
get_portfolio3 fields changed- added
Input schema / properties / viewAdded 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" +} - added
Input schema / requiredAdded value: +[ + "view" +] - changed
Output 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" +}
- Removed
get_portfolio_history - Removed
get_portfolio_score - Removed
get_portfolio_snapshot - Added
get_risk - Removed
get_risk_metrics - Removed
get_sync_status - Changed
get_target_allocation1 field changed- changed
Output 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" +}
- Added
get_trading_activity - Removed
get_transfers - Removed
get_venues - Removed
get_wallets - Changed
list_profiles1 field changed- changed
Output 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" +}
- Added
propose_alert_change - Changed
set_target_allocation1 field changed- changed
Output 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" +}
25 tool updates
- Changed
get_activity_calendar1 field changed- changed
Input schema / properties / profile / descriptionPrevious 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."
- Changed
get_alerts1 field changed- changed
Input schema / properties / profile / descriptionPrevious 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."
- Changed
get_asset_trade_stats1 field changed- changed
Input schema / properties / profile / descriptionPrevious 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."
- Changed
get_capital_flow3 fields changed- changed
Input schema / properties / bucket / descriptionPrevious value: -"Time bucket. Defaults to month."New value: +"Granularity of the series. Defaults to month." - changed
Input schema / properties / period / descriptionPrevious 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." - changed
Input schema / properties / profile / descriptionPrevious 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."
- Changed
get_changes2 fields changed- changed
Input schema / properties / period / descriptionPrevious 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." - changed
Input schema / properties / profile / descriptionPrevious 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."
- Changed
get_dashboard1 field changed- changed
Input schema / properties / profile / descriptionPrevious 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."
- Changed
get_deposits1 field changed- changed
Input schema / properties / profile / descriptionPrevious 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."
- Changed
get_fees3 fields changed- changed
Input schema / properties / bucket / descriptionPrevious value: -"Time bucket for the fee series."New value: +"Granularity of the fee time series. Defaults to month." - changed
Input schema / properties / period / descriptionPrevious 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." - changed
Input schema / properties / profile / descriptionPrevious 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."
- Changed
get_fiat_stats1 field changed- changed
Input schema / properties / profile / descriptionPrevious 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."
- Changed
get_income_by_source2 fields changed- changed
Input schema / properties / period / descriptionPrevious 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." - changed
Input schema / properties / profile / descriptionPrevious 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."
- Changed
get_income_timeseries3 fields changed- changed
Input schema / properties / bucket / descriptionPrevious value: -"Time bucket. Defaults to month."New value: +"Granularity of the series. Defaults to week." - changed
Input schema / properties / period / descriptionPrevious 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." - changed
Input schema / properties / profile / descriptionPrevious 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."
- Changed
get_movements9 fields changed- changed
Input schema / properties / asset / descriptionPrevious value: -"Filter to one asset ticker, e.g. \"BTC\"."New value: +"Asset ticker, e.g. \"BTC\"; comma-separate for several (\"BTC,ETH\"). Case-insensitive." - changed
Input schema / properties / kind / descriptionPrevious 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." - changed
Input schema / properties / limit / descriptionPrevious value: -"Maximum rows to return (capped at 200)."New value: +"How many of the newest matching rows to return, 1-200. Defaults to 100." - changed
Input schema / properties / period / descriptionPrevious 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." - changed
Input schema / properties / profile / descriptionPrevious 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." - changed
Input schema / properties / q / descriptionPrevious 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." - changed
Input schema / properties / source / descriptionPrevious value: -"Filter to one source label."New value: +"Source label as it appears in countsBySource, e.g. \"deposit\" or \"staking\"; comma-separate for several." - changed
Input schema / properties / status / descriptionPrevious 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." - changed
Input schema / properties / venue / descriptionPrevious value: -"Filter to one venue, e.g. \"binance\"."New value: +"Venue name as it appears in countsByVenue, e.g. \"binance\"; comma-separate for several."
- Changed
get_pnl_by_symbol4 fields changed- changed
Input schema / properties / limit / descriptionPrevious 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)." - changed
Input schema / properties / limit / maximumPrevious value: -200New value: +100 - changed
Input schema / properties / profile / descriptionPrevious 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." - changed
Input schema / properties / quote / descriptionPrevious 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."
- Changed
get_pnl_overview2 fields changed- changed
Input schema / properties / period / descriptionPrevious 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." - changed
Input schema / properties / profile / descriptionPrevious 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."
- Changed
get_portfolio1 field changed- changed
Input schema / properties / profile / descriptionPrevious 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."
- Changed
get_portfolio_history3 fields changed- changed
Input schema / properties / period / descriptionPrevious 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." - changed
Input schema / properties / profile / descriptionPrevious 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." - changed
Input schema / properties / slim / descriptionPrevious 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."
- Changed
get_portfolio_score2 fields changed- removed
Input schema / properties / periodRemoved 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" -} - changed
Input schema / properties / profile / descriptionPrevious 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."
- Changed
get_portfolio_snapshot1 field changed- changed
Input schema / properties / profile / descriptionPrevious 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."
- Changed
get_risk_metrics3 fields changed- changed
Input schema / properties / benchmark / descriptionPrevious value: -"Benchmark for beta and correlation. Defaults to BTC."New value: +"Asset to measure beta, correlation and excess return against. Defaults to BTC." - changed
Input schema / properties / period / descriptionPrevious 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." - changed
Input schema / properties / profile / descriptionPrevious 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."
- Changed
get_sync_status1 field changed- changed
Input schema / properties / profile / descriptionPrevious 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."
- Changed
get_target_allocation1 field changed- changed
Input schema / properties / profile / descriptionPrevious 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."
- Changed
get_transfers1 field changed- changed
Input schema / properties / profile / descriptionPrevious 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."
- Changed
get_venues1 field changed- changed
Input schema / properties / profile / descriptionPrevious 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."
- Changed
get_wallets1 field changed- changed
Input schema / properties / profile / descriptionPrevious 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."
- Changed
set_target_allocation1 field changed- changed
Input schema / properties / profile / descriptionPrevious 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."
26 tool updates
- First observed
get_activity_calendar - First observed
get_alerts - First observed
get_asset_trade_stats - First observed
get_capital_flow - First observed
get_changes - First observed
get_dashboard - First observed
get_deposits - First observed
get_fees - First observed
get_fiat_stats - First observed
get_income_by_source - First observed
get_income_timeseries - First observed
get_movements - First observed
get_pnl_by_symbol - First observed
get_pnl_overview - First observed
get_portfolio - First observed
get_portfolio_history - First observed
get_portfolio_score - First observed
get_portfolio_snapshot - First observed
get_risk_metrics - First observed
get_sync_status - First observed
get_target_allocation - First observed
get_transfers - First observed
get_venues - First observed
get_wallets - First observed
list_profiles - First observed
set_target_allocation
Publisher details
- Operator
- WhereItGoes
- Operator website
- https://whereitgoes.money
- Vendor relationship
- First-party
- Documentation
- https://whereitgoes.money/connect-ai · Publisher source
- 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
Connect your portfolio to Claude, ChatGPT, or Codex to analyze it and make smarter investments.
Read-only crypto and traditional portfolio: holdings, PNL, FIFO tax figures and market data.
- Era ContextOAuthapp.era
Personal finance, bank account, and shared memory connector for Claude, ChatGPT, Gemini Spark & more
Ask your AI about your own accounts, debts, budgets and goals. OAuth sign-in, read-only by default.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceTransforms Claude AI into a personal crypto portfolio analyst, enabling wallet analysis, AI-powered recommendations, and portfolio tracking across 8+ blockchains through natural conversation.MIT
- AlicenseAqualityDmaintenanceConnects Claude Desktop to crypto and brokerage portfolio data via Coinbase and SnapTrade, providing read-only tools to inspect accounts, holdings, prices, and provider health.5MIT
- AlicenseNot gradedqualityDmaintenanceCost basis tracking, capital gains calculation, and 1099-DA reconciliation for Claude, built on top of the Crypto Portfolio MCP data layer.MIT
- AlicenseNot gradedqualityBmaintenanceRead-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
Glama MCP Gateway
Add one secure layer between your agents and this server.