Skip to main content
Glama

Server Details

An mcp dedicated to real-time streaming of financial market and research, giving ai agents a pulse on the markets.

Ownership verified
Status
Healthy
Uptime
97.2% over 22 days
OAuth
Works in Glama
Last Tested
Transport
Streamable HTTP · MCP 2025-11-25
URL
ChatGPT
–
Claude
–

TDQS

A3.9/5.0

Scored across 57 tools

Disambiguation3/5

There are many parallel data-access routes (explore_data_catalogue, explore_data_coverage, company_financials, download_dataset, run_saved_query, get_signed_sql_drilldown, get_user_dataset_file) and a dense situation cluster (situate, get_company_snapshot, get_company_event_web, get_event_ontology, list_realtime_events). The descriptions invest heavily in routing guidance and mostly succeed, but the boundaries require long prose to hold apart, which signals real overlap. A couple of pairs (optimize_portfolio vs optimize_portfolio_default, get_latest_report vs generate_report_for_stock vs generate_research_report) are only reliably separable by reading paragraphs of documentation.

Naming Consistency4/5

Almost everything is snake_case with a verb_noun shape (get_*, list_*, save_*, delete_*, draft_*, stream_*), which is predictable and greppable. Exceptions are a handful of noun- or bare-verb names (company_financials, situate, is_market_open, manage_billing) and the _default suffix on optimize_portfolio_default, but these are minor deviations rather than a different convention.

Tool Count2/5

57 tools is well beyond a comfortable surface for one server, even a broad finance platform; the model must route among several near-duplicate paths before it can act. The domain breadth (reports, datasets, templates, streaming, strategy, screening, billing, EDGAR) justifies many tools, but several clusters could be collapsed into one parameterized tool.

Completeness4/5

Coverage is strong on most lifecycles: full CRUD for templates and datasets, save/run/delete for saved queries, schedule/list/delete for tasks, register/stream/delete for subscriptions, and trace->document->section citation chaining. Minor gaps remain — no list endpoint for event subscriptions (acknowledged in the docs), no update for saved queries or datasets (only delete-and-recreate), and no way to remove or manage generated reports.

Available Tools

57 tools
company_financialsA
Read-only
Inspect

A company's EDGAR financial statements, read directly — the targeted tool for "what did X report", revenue / EPS / margins / cash flow by quarter or year, and the tables a financials_release refreshes.

===> Prefer this over explore_data_catalogue for one company's reported statements:
it is synchronous, takes seconds, and returns the rows directly. In a `situate`
result the relation cards of these six tables list it in `read_by`: when one of them
refreshed for the event (a same_chain_run edge to it in the web), call this with
financial_type=<that table>.

TABLES (`financial_type`, each value is the table name):
  edgar_quarterly_financials (default)  standardized quarterly statements
  edgar_annual_financials               standardized annual statements
  edgar_10q_financials / edgar_10k_financials
                                        the facts AS FILED (XBRL concepts) with
                                        accession and filing_url
  edgar_eight_k                         financial tables extracted from 8-Ks
  edgar_financials_yoy_change_m         year-over-year change per line item

PASS line_items. The standardized tables hold dozens of line items per period, the
as-filed tables hundreds of facts: an unfiltered edgar_10q_financials pull cost
28K-108K tokens in testing and was only ever made "for provenance". It is never
needed for that — cite a row with `trace_data_sources` (below) instead.

Rows come back newest period first. A standardized row is {date, fiscal_year, period,
reported_currency, filing_date, accepted_date, symbol, statement, line_item, value,
mapping, last_updated}; `mapping` holds the accession, filing date, XBRL inputs and the
formula. netIncome appears TWICE per quarter — once from the income statement, once
from the cash flow statement; read the `statement` column.

STALE YoY: edgar_financials_yoy_change_m refreshes after the transcript arrives and at
19:00 ET on weekdays, so on the evening of a print it can still show the PRIOR quarter.
Treat it as stale unless its current_period matches the latest date in
edgar_quarterly_financials, and compute the change from the 5 quarters there instead.

CITE A ROW: trace_data_sources(relation=<this financial_type>, keys=[{"symbol": "MAR",
"date": "2026-09-30", "statement": "income statement", "line_item": "epsDiluted"}])
returns the 10-Q / 10-K and its sec.gov link. For the YoY view the keys are symbol,
line_item and current_period.

US SEC filers only (the EDGAR side); a non-filer has no rows here — situate's
`source_routing` says which side a company is on. A 404 means the symbol is not
covered (onboard_symbol), a 422 a bad argument. Requires auth (your MCP client
attaches the OAuth bearer automatically).
NameRequiredDescriptionDefault
limitNoMost recent periods to return (1-12, default 5). 5 quarters include the same quarter last year; lower it only when the year-ago comparison is not wanted.
symbolYesCovered ticker, e.g. "MAR".
line_itemsNoOnly these line items (max 50). Standardized camelCase names for the standardized and growth tables, e.g. ["revenue", "operatingIncome", "netIncome", "epsDiluted", "operatingCashFlow", "freeCashFlow"]; XBRL concepts for the as-filed tables, e.g. ["us-gaap_Revenues"]. Not allowed for edgar_eight_k.
financial_typeNoThe table to read (default edgar_quarterly_financials). Standardized statements: edgar_quarterly_financials / edgar_annual_financials. As filed (XBRL facts with accession and filing_url): edgar_10q_financials / edgar_10k_financials — pass line_items. edgar_eight_k: financial tables from 8-Ks. edgar_financials_yoy_change_m: year-over-year change per line item.edgar_quarterly_financials

Output Schema

NameRequiredDescription
noteNoSet only when no rows came back: what an empty result can mean.
rowsYesNewest period first. Standardized rows: date, fiscal_year, period, reported_currency, filing_date, accepted_date, symbol, statement, line_item, value, mapping (accession, XBRL inputs, formula), last_updated. As-filed rows carry concept, accession and filing_url.
countYesRows returned.
symbolYes
financial_typeYesThe table read; pass it as trace_data_sources(relation=...) to cite a row.

TDQS

A4.8/5.0
Behavior5/5

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

Beyond the readOnlyHint annotation, the description discloses important behavior: the call is synchronous, returns rows directly, and can be very token-expensive if as-filed tables are pulled without line_items. It also warns about newest-first ordering, duplicate netIncome rows across statements, stale YoY data, auth requirements, and error semantics.

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

Conciseness4/5

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

The description is long, but it is front-loaded and clearly structured with headings for tables, line_items, row shape, stale YoY, citation, and access constraints. Nearly every section earns its place, though some prose around the token-cost warning and citation workflow could be tightened.

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

Completeness5/5

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

Given the tool's complexity, multiple financial tables, high token-cost risk, and output schema, the description is complete enough for an agent to call it correctly. It covers routing, table selection, filtering, return ordering, citation, stale data, auth, and error handling.

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

Parameters4/5

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

Schema coverage is already 100%, so the baseline is 3, but the description adds meaningful operational semantics for financial_type values, line_items usage, and the stale YoY caveat. It explains which tables are standardized versus as-filed and how line_items should be chosen, though it adds little beyond the schema for the limit parameter.

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

Purpose5/5

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

The description states a specific verb and resource: reading a company's EDGAR financial statements, including revenue, EPS, margins, and cash flow by quarter or year. It also distinguishes the tool from siblings by explicitly saying to prefer it over explore_data_catalogue for one company's reported statements and by linking its use to situate's read_by relation cards.

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

Usage Guidelines5/5

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

It gives explicit when-to-use guidance: use this for one company's reported statements, especially when a relation card lists it in read_by after a table refresh. It names the main alternative, explore_data_catalogue, explains why this tool is preferable, and adds edge-case guidance for non-filers, 404s, and 422s.

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

delete_event_subscriptionA
DestructiveIdempotent
Inspect

Remove a saved event subscription (DELETE /event-subscription/{subscription_id}).

Returns {"status": "DELETED", "subscription_id": ...}; any stream still open on it
ends with a `closed` frame within one read cycle (<= 8 s). 404 `unknown
subscription_id` for a missing id or another user's id. Registrations are
rate-limited (30/hour), so delete subscriptions the user is done with rather than
registering fresh ones for every session.

Requires auth (your MCP client attaches the OAuth bearer automatically).
NameRequiredDescriptionDefault
subscription_idYesThe `subscription_id` returned by register_event_subscription.

Output Schema

NameRequiredDescription
statusYes"DELETED".
subscription_idYes

TDQS

A4.1/5.0
Behavior5/5

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

Goes well beyond the annotations: exact return payload ({"status": "DELETED", ...}), the side effect that open streams receive a `closed` frame within one read cycle (<= 8 s), the 404 unknown subscription_id behavior (including another user's id), rate limits, and the OAuth requirement. The idempotent/destructive hints are confirmed rather than repeated.

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

Conciseness4/5

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

Front-loads the operation and endpoint, then layers return shape, streaming side effect, error case, rate-limit rationale, and auth in compact sentences. Slightly dense with parentheticals, but every sentence carries decision-relevant information.

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

Completeness5/5

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

Covers the full call surface: auth requirement, error semantics, post-delete streaming consequence, and the rate-limit context that motivates using this tool at all. With an output schema present it need not describe returns further, yet its extra detail is accurate and helpful rather than redundant.

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

Parameters3/5

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

Schema description coverage is 100%, and the schema already ties subscription_id back to register_event_subscription's return value. The description restates the identifier but adds no format, syntax, or lookup detail beyond the schema, so the baseline of 3 applies.

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

Purpose4/5

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

States a specific verb and resource ('Remove a saved event subscription') and even names the HTTP route, so there is no ambiguity about the operation. It does not explicitly name or contrast itself with siblings like register_event_subscription or stream_event_subscription, so it falls short of the top tier that requires sibling differentiation.

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

Usage Guidelines4/5

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

Gives genuine operating guidance: because registrations are rate-limited (30/hour), the agent should delete subscriptions the user is done with rather than re-registering each session. This implies the when-to-use context clearly, though it does not name an alternative tool explicitly or state hard exclusions.

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

delete_saved_queryA
DestructiveIdempotent
Inspect

Delete one of your saved data requests by id.

Deletion is permanent and touches no data, but the query
tokens go with the row and cannot be reconstructed: re-creating it means running
`explore_data_catalogue` again and re-saving. When more than one row looks like a
match, confirm WHICH one with the user before calling.

Ownership is enforced server-side. A 404 ("No saved query with id N") means the id
does not exist OR is not yours — the backend deliberately does not distinguish the
two, so never tell the user the row belongs to someone else.

Returns {"msg": "Deleted saved query <id>"}.

Requires auth (your MCP client attaches the OAuth bearer automatically).
Rate-limited 100/min.
NameRequiredDescriptionDefault
query_idYesThe row's `id` from list_saved_queries — not a list position, not the user_request.

Output Schema

NameRequiredDescription
msgYes

TDQS

A4.4/5.0
Behavior5/5

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

Goes well beyond the annotations: it discloses that deletion is permanent and that query tokens are destroyed and cannot be reconstructed, explains that a 404 deliberately conflates 'does not exist' with 'not yours' so the agent must not leak ownership, documents the return payload, auth requirements, and a 100/min rate limit. This is exactly the behavioral context annotations cannot carry.

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

Conciseness4/5

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

Front-loads the action and keeps each sentence purposeful (destructiveness, ambiguity handling, 404 semantics, auth/rate limits). The explicit return-value line is somewhat redundant since an output schema exists, which keeps it from a 5.

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

Completeness5/5

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

For a single-parameter destructive tool with annotations and an output schema, the description covers everything an agent needs: safety profile, idempotency implications, failure semantics, prerequisites, and rate limits. No gaps remain.

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

Parameters3/5

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

Schema coverage is 100% and the schema itself already clarifies that query_id is the row id from list_saved_queries, not a list position or user_request. The prose adds only the phrase 'by id', so it does not meaningfully extend the schema — baseline 3 is appropriate.

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

Purpose5/5

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

States a specific verb and resource ('Delete one of your saved data requests by id') and scopes it to the user's own saved queries, distinguishing it from siblings like list_saved_queries, run_saved_query, and save_user_query. An agent can identify the operation without inspecting the schema.

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

Usage Guidelines4/5

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

Gives a concrete precondition for invocation ('when more than one row looks like a match, confirm WHICH one with the user') and describes the recovery path for an accidental deletion (re-run explore_data_catalogue and re-save). It does not explicitly name list_saved_queries as the id source in prose (that lives in the schema), so it falls just short of naming alternatives outright.

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

delete_scheduled_taskA
DestructiveIdempotent
Inspect

Delete a scheduled task (cron job) by its name.

For workflow schedules `task_name` is the value `schedule_task` returned as
`task_name`. Deleting removes only
the cron schedule; the saved workflow definition is kept and can be
re-scheduled by re-saving it via `schedule_task`. For a "ds:" dataset schedule the
dataset and its files stay (re-save via `save_user_dataset` to resume;
`delete_user_data_feed` removes the dataset itself). Returns
{"msg": "<task_name> deleted"} on success, or an error if no such task exists.
NameRequiredDescriptionDefault
task_nameYesThe task `name` from list_scheduled_tasks — for workflows the full "wf:{email}:{workflow-name}", NOT the bare workflow name.

Output Schema

NameRequiredDescription
msgYes"<task_name> deleted".

TDQS

A4.8/5.0
Behavior5/5

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

Annotations already declare destructiveHint=true and idempotentHint=true, but the description adds the critical scope of destruction ('removes only the cron schedule; the saved workflow definition is kept'), the error condition if no such task exists, and the exact success payload. This is behavior an agent cannot infer from the annotations.

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

Conciseness4/5

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

Front-loaded with the core action and selector, then scoped pitfalls and return format. Efficient overall, though the middle sentence is slightly convoluted with nested parentheticals that could be tightened.

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

Completeness5/5

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

For a destructive single-parameter tool with annotations and an output schema, the description covers purpose, side effects, preservation semantics, recovery paths, and error/return behavior. Nothing an agent needs to call it correctly is missing.

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

Parameters4/5

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

Schema coverage is 100% and the schema already documents the expected 'wf:{email}:{workflow-name}' format and warns against the bare workflow name. The description adds complementary meaning by explaining that for workflow schedules the value is what schedule_task returned as task_name, tying the parameter to its producer.

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

Purpose5/5

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

States a specific verb and resource ('Delete a scheduled task (cron job) by its name'), and immediately clarifies the key selector semantics. An agent can distinguish this from the other delete_* siblings and from schedule_task.

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

Usage Guidelines5/5

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

Explicitly describes what happens in each case (workflow schedule vs 'ds:' dataset schedule), what is preserved, and how to resume each ('re-saving it via schedule_task', 're-save via save_user_dataset'), even naming delete_user_data_feed as the tool that removes the dataset itself. Strong when/when-not and alternative routing.

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

delete_user_data_feedA
DestructiveIdempotent
Inspect

Delete one of the user's own datasets completely: its schedule, every file it built, its fixed latest_url link, and the saved query behind it. It can't be undone — confirm with the user first, naming the dataset. To only stop the builds and keep the files, use delete_scheduled_task("ds:{email}:{name}") instead.

Returns {msg, dataset_id, schedule_deleted, files_deleted, saved_query_deleted}.
Errors: 404 = the user has no dataset of that name; 503 = a step failed partway —
call again with the same name (every step is safe to repeat).

Requires auth (your MCP client attaches the OAuth bearer automatically).
NameRequiredDescriptionDefault
dataset_nameYesThe dataset's `name`, as list_user_datasets shows it.

Output Schema

NameRequiredDescription
msgYes"Deleted dataset <name>".
dataset_idNo
files_deletedNoPublished files removed.
schedule_deletedNoFalse when the schedule was already gone.
saved_query_deletedNoId of the saved query removed with it.

TDQS

A4.7/5.0
Behavior5/5

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

Annotations already declare destructive=true and idempotent=true, and the description corroborates rather than contradicts them: 'can't be undone' matches destructive, and 'every step is safe to repeat / call again with the same name' matches idempotent. It further adds error semantics (404 = no such dataset; 503 = partial failure with a retry recipe) and the auth requirement, which annotations do not cover.

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

Conciseness5/5

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

Front-loaded with the destructive scope before the alternative, then returns, then errors, then auth — a logical order with no filler. Every sentence carries actionable content; the return-key list is slightly redundant against the output schema but is compact enough not to count as waste.

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

Completeness5/5

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

For a destructive single-parameter tool it covers the full decision surface: what gets destroyed, irreversibility, confirmation duty, the non-destructive alternative, failure modes with recovery, and auth. The output schema exists, yet the inline return summary still helps the agent interpret partial-failure results (e.g., files_deleted vs saved_query_deleted).

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

Parameters3/5

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

Schema description coverage is 100% for the single parameter, and the schema already explains that dataset_name is 'the dataset's `name`, as list_user_datasets shows it.' The description adds only the confirmation-time instruction to name the dataset, so the baseline 3 is appropriate — the schema does the heavy lifting here.

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

Purpose5/5

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

States a specific verb+resource ('Delete one of the user's own datasets completely') and enumerates exactly what is destroyed: schedule, built files, latest_url link, and saved query. It also names the sibling it is not (delete_scheduled_task) and the exact argument form that routes there, so an agent can distinguish it from the other delete_* tools without opening a schema.

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

Usage Guidelines5/5

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

Explicit when-to-use (full dataset removal) and when-not (to only stop builds and keep files, use delete_scheduled_task("ds:{email}:{name}")). It also adds a required procedural precondition — confirm with the user first, naming the dataset — which is guidance beyond mere routing.

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

delete_user_templateA
DestructiveIdempotent
Inspect

Delete the user's saved PDF template so their PDFs go back to the standard layouts.

Permanent and there is no history: re-creating the format means drafting and
saving again. Confirm with the user before calling. Ownership is enforced
server-side, so only the caller's own template can be removed.

Returns {"msg": "Template deleted. Your PDFs will use the standard layouts."}.
A 404 means there was nothing saved to delete.

Requires auth (your MCP client attaches the OAuth bearer automatically).
Rate-limited 100/min.
NameRequiredDescriptionDefault

No parameters

Output Schema

NameRequiredDescription
msgYesConfirmation message.

TDQS

A4.6/5.0
Behavior5/5

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

Annotations already declare destructiveHint=true and idempotentHint=true, but the description adds substantially more: permanence with no history, server-side ownership enforcement limiting it to the caller's own template, the exact success payload, 404 semantics, OAuth auth requirement, and a 100/min rate limit. No claims conflict with the annotations.

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

Conciseness4/5

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

Front-loaded and well-paragraphed with each sentence carrying weight, but the explicit return-value sentence is partially duplicative given a structured output schema already exists.

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

Completeness5/5

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

For a zero-param destructive mutation, the description covers safety, reversibility, ownership, auth, rate limits and error behavior; an agent has everything needed to decide and invoke correctly, and return values are reinforced by the output schema.

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

Parameters4/5

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

The tool takes zero parameters, so the baseline is 4; there is no argument meaning to convey and the description correctly avoids inventing any.

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

Purpose5/5

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

States a specific verb+resource (delete the user's saved PDF template) plus the consequence (PDFs revert to standard layouts), which cleanly separates it from draft_user_template, save_user_template, update_user_template and get_user_template.

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

Usage Guidelines4/5

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

Gives an explicit prerequisite ('Confirm with the user before calling') and a useful negative condition ('A 404 means there was nothing saved to delete'), and the 're-creating the format means drafting and saving again' line implicitly points at the sibling tools for the alternative path. It never names an alternative tool outright, so it stops short of a 5.

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

detect_intraday_outlier_jumpsA
Read-only
Inspect

Live look at TODAY's intraday tape, flagging outlier price jumps.

Pulls today's intraday bars for `symbol` (US/Eastern trading day) live at the given
`frequency` and flags each bar whose move is a statistical outlier versus the stock's
own daily-return volatility — i.e. bars where |z-score| of the move exceeds
`zscore_threshold`. Use it for a quick "is the stock making an abnormal intraday move
right now?" read.

Synchronous — returns the flagged bars directly (no task id to poll). Returns a
404-style error if no intraday data is available yet (e.g. before the market opens or
for an uncovered symbol).

Requires auth (your MCP client attaches the OAuth bearer automatically).
NameRequiredDescriptionDefault
symbolYesCovered ticker, e.g. "AAPL".
frequencyNoIntraday bar size; list_options(kind="intraday_frequency") lists the supported values.one_minute
zscore_thresholdNoDaily-sigma cutoff for flagging a bar: higher = stricter (fewer, more extreme flags), lower = more sensitive.

Output Schema

NameRequiredDescription
latestNoThe most recent bar.
seriesYesEvery bar today, ascending by time.
symbolYes
outliersYesBars with |z-score| >= zscore_threshold.
prior_closeNoPrior session's close, the return baseline.
most_extremeNoToday's bar with the largest |z-score|.
daily_std_20dNoStd dev of the trailing 20 daily log returns (the sigma).
daily_mean_20dNoMean of the trailing 20 daily log returns.
is_outlier_nowYesTrue when the latest bar is beyond the threshold.
zscore_thresholdYesThe |z| cutoff applied.

TDQS

A4.6/5.0
Behavior5/5

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

Goes well beyond the readOnlyHint annotation: synchronous execution with no task id to poll, direct return of flagged bars, a 404-style error when no intraday data exists yet, and automatic OAuth bearer attachment. These are exactly the operational facts an agent needs before calling.

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

Conciseness4/5

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

Front-loaded with the live-today purpose, then mechanics, then error/auth notes. Every sentence carries information, though it runs slightly long for three parameters.

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

Completeness5/5

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

An output schema exists, so return values need not be described; the description nonetheless covers the synchronous contract, the failure mode, and auth. Nothing an agent needs to invoke this correctly is missing.

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

Parameters4/5

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

Schema coverage is already 100%, so the baseline is 3, but the description adds real meaning: moves are scored against the stock's own daily-return volatility, the day is US/Eastern, and frequency is defined as the intraday bar size. Z-score semantics are clarified beyond the schema's terse 'Daily-sigma cutoff'.

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

Purpose5/5

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

States a specific verb+resource+scope: pulls TODAY's live intraday bars for a symbol and flags bars whose |z-score| exceeds a threshold. It is clearly distinguishable from siblings like get_technical_indicator_data, get_aftermarket_data, and get_company_snapshot.

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

Usage Guidelines4/5

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

Gives a clear usage context ('quick is the stock making an abnormal intraday move right now? read') and implicitly scopes to live/today rather than historical analysis. It does not, however, name a sibling alternative or state when NOT to use it (e.g. after-hours vs. get_aftermarket_data).

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

download_datasetA
Read-only
Inspect

Read one of Flexreport's versioned daily data feeds by date — the feeds and what each holds come from list_options("data_feeds"). Use it over explore_data_catalogue when one of those feeds answers the ask for a date or date range. RATE-LIMITED TO 3 CALLS PER WEEK per user (all datasets together, failed calls included): make ONE call with the narrowest dates, never test-fire it, never re-call because a result was trimmed, and never poll it from a dashboard — fetch once and embed the rows.

list_options("data_feeds") lists the feeds — free, no rate limit — each as
{dataset, description, returns, date_field, event_types}: `returns` is "rows" or
"download_links" (presigned URLs), `date_field` is the column the dates filter on,
and `event_types` the valid filter values (null = unsupported). This tool checks
`dataset` and `event_types` against that list before spending a download call. For
open-ended analysis ("why did X move") keep to explore_data_catalogue.

Sizing — always pass explicit ET dates, the narrowest that answer the request, and
widen only when the user asks ("this month", "since launch"): the week ahead for
forward-looking predictions; the most recent completed date for "how did it do"; one
business day for documents (nightly files land after the 22:00 ET build, so before
then the latest complete day is the previous business day). A range with nothing
published returns no rows, not an error.

Shaping, applied here to the one response:
- Every row carries `published_at`, the UTC time its file version was published —
  quote the result's `published_at` as the data's as-of.
- Document feeds (rows with `document_url` + `page_number`) come back ONE ROW PER
  DOCUMENT (page columns dropped, `pages_with_text` added); include_page_text=true
  returns the page rows with their full `content`.
- `download_links` feeds return links valid 12 HOURS: hand each `url` to the user as
  a link, never store it or inline the file.

Returns {dataset, start_date, end_date, bulk_download, latest_file_version, shape,
rows, row_count, truncated, published_at, notes}. `notes` lists what was left out
(pages collapsed, symbols filtered, rows truncated) — tell the user in one line.

Requires auth (your MCP client attaches the OAuth bearer automatically). Also counts
once against the plan's daily request quota.
NameRequiredDescriptionDefault
datasetYesA `dataset` from list_options("data_feeds") (free, no rate limit).
symbolsNoKeep only these tickers, e.g. ["SNOW"]. Applied to the response here (the feed itself has no symbol filter); not possible for `download_links` feeds.
end_dateNoLast date, inclusive, YYYY-MM-DD. Omit for start_date alone (one day).
max_rowsNoCap on rows returned (after filtering and collapsing); the result then carries row_count and truncated: true.
start_dateYesFirst date, inclusive, YYYY-MM-DD, on the feed's `date_field` (ET). Pick the narrowest window that answers the request.
event_typesNoNarrow to these event types, from the feed's `event_types` in list_options("data_feeds"). Omit for all; ignored by feeds whose `event_types` is null.
bulk_downloadNoTrue ignores the dates and returns EVERY published date. Only when the user explicitly asks for full history.
document_urlsNoDocument feeds only: keep only these documents (their `document_url`), e.g. to read one deck's pages.
include_page_textNoDocument feeds (one row per page) only. False (default) = one row per document, no text. True = one row per page with its full `content` — only when the user wants the text, and narrowed with symbols / document_urls.
latest_file_versionNoTrue (default) = newest file version per date. False = every version ever published — only for point-in-time / backtest questions ("what did the model say Monday morning?").

TDQS

A4.8/5.0
Behavior5/5

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

Annotations only declare readOnlyHint=true, so the description carries most of the burden and does so richly: a hard rate limit of 3 calls/week including failed calls, 12-hour presigned link validity, document feeds collapsing to one row per document, published_at as-of semantics, `notes` reporting omissions, and OAuth/quota side effects. This is far beyond what structured fields convey.

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

Conciseness4/5

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

The text is long, but it is front-loaded with purpose and the critical rate limit, then organized into labeled sections (Sizing, Shaping, Returns) with a bulleted constraint list. For a 10-parameter tool with a hard weekly quota, nearly every sentence earns its place; the density is justified rather than padded.

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

Completeness5/5

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

Despite no output schema, the description spells out the return shape ({dataset, start_date, end_date, bulk_download, latest_file_version, shape, rows, row_count, truncated, published_at, notes}), what `notes` contains, and how to surface it. Auth requirements, rate limits, and quota accounting are all covered, leaving nothing an agent needs to invoke the tool correctly.

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

Parameters4/5

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

Schema coverage is 100%, so the schema already documents all 10 parameters. The description still adds meaning the schema doesn't: `date_field` determines what dates filter on, include_page_text is only for page-level reads narrowed by symbols/document_urls, bulk_download is full-history only, and symbols is impossible for download_links feeds. It goes beyond the baseline 3 without fully compensating for anything missing, since the schema already covers syntax.

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

Purpose5/5

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

The description opens with a specific verb+resource: 'Read one of Flexreport's versioned daily data feeds by date.' It immediately names list_options("data_feeds") as the source of valid feeds and explicitly contrasts itself with explore_data_catalogue, so an agent can distinguish it from siblings without opening a schema.

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

Usage Guidelines5/5

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

Usage is fully routed: use this over explore_data_catalogue when a feed answers a date/date-range ask, and keep to explore_data_catalogue for open-ended analysis ('why did X move'). It also gives sizing heuristics (week ahead for predictions, latest completed date for 'how did it do', one business day for documents) and explicit anti-patterns (never test-fire, never poll).

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

download_pdf_from_urlA
Read-only
Inspect

Fetch a presigned S3 PDF URL server-side and return the document as base64.

The FALLBACK for reading a report: `get_latest_report` returns only a presigned
`url`, and fetching that link yourself (an HTTP / web fetch) is the normal way to
read the PDF. Use this only when your client cannot reach the link but can run code
to decode base64 — the result is large (~120K characters for a 5-page note, ~750K
for a 20-page report with charts) and is no use to a model that cannot decode it.
The backend fetches the URL for you and streams the PDF back. Only S3 URLs are
accepted: the host must end in "amazonaws.com" and contain "s3"; any other host is
rejected with a 400 (SSRF guard).

Returns {"file_name": ..., "media_type": "application/pdf", "report": "<base64 pdf>"}
— decode `report` to read, render, or save the PDF.

Requires auth (your MCP client attaches the OAuth bearer automatically).
NameRequiredDescriptionDefault
urlYesPresigned S3 link, e.g. get_latest_report's `url`. The host must end in "amazonaws.com" and contain "s3" (anything else is a 400).
file_nameYesDownload filename to label the document, e.g. "AAPL.pdf".

TDQS

A4.6/5.0
Behavior5/5

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

Goes well beyond the readOnlyHint annotation: it quantifies output size (~120K chars for 5 pages, ~750K for 20), discloses the SSRF guard (host must end in "amazonaws.com" and contain "s3", otherwise a 400), notes the fetch is server-side, and states OAuth is auto-attached. No output schema exists, so the explicit return shape {"file_name", "media_type", "report"} earns further credit.

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

Conciseness4/5

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

Front-loads the verb-and-resource plus the fallback role, then layers conditions, size warnings, constraints, and return shape in a sensible order. Slightly padded by repeating the S3 host rule already present in the schema, and the paragraph break mid-thought about size is a little awkward.

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

Completeness5/5

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

For a two-parameter read tool with no output schema, the description covers selection criteria, size/shape of the result, error behavior, auth handling, and the return payload — everything an agent needs to decide and call correctly. Nothing material is missing.

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

Parameters3/5

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

Schema description coverage is 100%, so both parameters are already documented in the schema, including the same S3 host constraint. The description restates the URL restriction but adds no new semantics for file_name (e.g., whether it affects the returned file_name field) or for URL edge cases. Baseline 3 is appropriate when the schema does the heavy lifting.

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

Purpose5/5

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

States a specific verb and resource ("Fetch a presigned S3 PDF URL server-side and return the document as base64") and immediately frames its role as "The FALLBACK for reading a report." That framing distinguishes it from both get_latest_report (which only returns the URL) and a plain client-side HTTP fetch.

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

Usage Guidelines5/5

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

Explicitly names the preferred path (fetching the link yourself is "the normal way to read the PDF") and states the exact conditions that select this tool: the client cannot reach the link but can run code to decode base64. It even adds a disqualifying condition — the payload is useless to a model that cannot decode it.

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

draft_custom_datasetA
Read-only
Inspect

EVENT/NEWS FEEDS (filings, decks, earnings, news) — RESEARCH FIRST, then draft: 1) situate(event_types=[the feed's event types]) — or situate(question=<the user's words>) — then get_event_ontology(event_type=...) for each to learn where it persists (persisted_in); 2) agree definitions with the user before drafting — universe, window, and what any loose term means (e.g. the proxy for "the AI trade"); 3) ONE SCOPED explore_data_catalogue census of company_event_node for the universe and window (tickers, dates, a row limit in the wording); 4) draft a request naming the table and column for each field, e.g. title from earnings figures, body from eight_k_timeseries_analysis.summary, link sec_link, guid company_event_node.node_id, pub_date occurred_at, ticker symbol_metadata.exchange || ':' || symbol; 5) refine with previous_draft_id + feedback, asking for ONE candidate when variants don't matter. PLAIN DATA FEEDS ("daily close and 20-day volume for AAPL, MSFT") skip all this and draft directly.

RESEARCH REPORTS ARE IN EVERY FEED BY DEFAULT: the build adds a `research_url` column to every row with a `symbol` or `ticker` column — a fixed, signed, sign-in-free link that serves the research report PDF behind that item (rss/atom items show it as a "Research" link). Don't put report links or /get-cached-reports in the request or in a column; just keep a bare `symbol` (or `ticker`) column in the draft (a company_symbol / EXCHANGE:TICKER column alone gets no research link). That link is ONLY for the feed's readers (redistribution): never call it, build one, or offer it as a way to download a report — the user gets reports through get_latest_report, and Flexreport's daily files through download_dataset.

Drafts the user's OWN dataset — a table built from Flexreport data on a schedule and published as a file (parquet, csv, tsv, json, jsonl, xlsx, xml, or an rss/atom feed) behind a fixed link — from a plain-English description. Step 1 of 2: draft (and refine) here, then save_user_dataset schedules it. Use when the user wants a custom data feed or a recurring file of their own ("build me a daily file of...", "an RSS feed of...", "a JSON feed I can poll"); for Flexreport's published feeds use download_dataset, and for a one-off look at the data use explore_data_catalogue. This is THE tool for any request worded as a "datafeed" or "feed" — never substitute schedule_task, an emailed workflow or a one-off file. Every column comes from stored data, and that includes the summaries and analysis Flexreport already stores (deck, press-release and 8-K summaries, key figures, flags, recommendations, research views), so a feed with summary columns is still a dataset; only new written analysis generated fresh each run is a schedule_task workflow (a transform step).

Asynchronous: returns {draft_id, task_id, status: "PENDING"}. Poll task_id with
get_task_status until SUCCESS — a draft takes about 45 s-4 min (validation, planning, a
sample run, finalize). On SUCCESS `result` is:
  {draft_id, validation_status: "OK" | "REJECTED", reason?, suggested_alternative?,
   user_request,
   candidates: [{name, description, columns, row_count, sample_rows, query_token,
                 has_fixed_dates, suggested_date_mode}]}
REJECTED -> relay `reason` and `suggested_alternative`; don't retry blindly.

Show the user each candidate's description, columns, row_count and a few sample_rows
and let them pick one. NEVER show `query_token` — it is an opaque sealed query whose
only use is save_user_dataset — but keep the chosen candidate's for the save.
- Samples show datetime columns as dates only; the built file keeps the full
  timestamp, so never tell the user a timestamp is missing from a sample.
- Candidates can be near-identical: compare their descriptions, not just the samples.

Pass `target_format` when the user has named one (always for rss/atom). A candidate
with a column named `published_at` can't be saved (that name is reserved for the
build time): refine it with feedback asking to rename the column.

To refine, call again with the SAME `request`, previous_draft_id=<draft_id> and
`feedback`; ask for "ONE candidate" when the variants don't matter. Each round is a
new draft with its own draft_id. Ask before saving: name,
schedule, and (if not the suggestion) date mode.

Rate-limited to 20 drafts per hour per user. Requires auth (your MCP client attaches
the OAuth bearer automatically).
NameRequiredDescriptionDefault
requestYesThe dataset to build, in plain English — what rows, which columns, which names, what window (e.g. "daily close and 20-day average volume for AAPL, MSFT, NVDA over the last 3 months"). When refining, re-send the same request.
feedbackNoWhat to change about the previous draft, e.g. "add the 50-day average", "weekly instead of daily". Only with previous_draft_id.
sample_rowsNoRows of each candidate returned as a sample (at most 5).
target_formatNoThe format the user plans to save in. rss/atom make the planner shape one row per feed item (title, link, description, pub_date, guid), newest first. Omit when unknown.
previous_draft_idNodraft_id of an earlier draft to refine (with `feedback`); the planner sees that draft's queries.

Output Schema

NameRequiredDescription
queueNoWorker queue the draft was routed to.
statusYesPENDING on dispatch.
task_idYesPoll this with get_task_status until SUCCESS (same value as draft_id).
draft_idYesThe draft's id: pass to save_user_dataset, or as previous_draft_id to refine it.

TDQS

A4.7/5.0
Behavior5/5

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

Annotations only declare readOnlyHint=true, so the description carries the real weight: it discloses asynchronous execution, a 45s–4min latency, the exact SUCCESS payload (validation_status, candidates, query_token), the REJECTED relay path, and a 20-drafts-per-hour rate limit with OAuth auth. It also flags two non-obvious traps — a `published_at` column blocks saving, and `query_token` must never be shown to the user.

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

Conciseness3/5

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

The numbered research-first protocol is well front-loaded and each step is actionable, but the description runs to roughly five blocks and repeats itself — 'ask for ONE candidate when variants don't matter' appears both in step 5 and again in the refine paragraph. The multi-sentence research_url digression, while behaviorally useful, sits awkwardly inside a tool definition rather than a linked doc.

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

Completeness5/5

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

For a high-complexity async planner tool, nothing an agent needs is missing: trigger conditions, pre-work, async polling, full result contract, save handoff, refinement loop, and failure handling are all covered, even though an output schema exists and would have excused omitting the return shape.

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

Parameters4/5

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

Schema coverage is 100%, so baseline is 3; the description goes beyond it by explaining when `target_format` must be supplied ('always for rss/atom') and what rss/atom does to planner shaping, and by insisting `request` be re-sent identically when refining with `previous_draft_id` + `feedback`. `sample_rows` is left entirely to the schema, which is fine.

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

Purpose5/5

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

The description states a specific verb and artifact: 'Drafts the user's OWN dataset — a table built from Flexreport data on a schedule and published as a file (parquet, csv, ... or an rss/atom feed) behind a fixed link.' It explicitly separates itself from siblings download_dataset, explore_data_catalogue, save_user_dataset and schedule_task, so an agent can route without opening any schema.

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

Usage Guidelines5/5

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

It gives concrete trigger phrases ('build me a daily file of...', 'an RSS feed of...') and a hard exclusion ('never substitute schedule_task, an emailed workflow or a one-off file'), plus a carve-out that plain data feeds skip the research-first protocol. The two-step lifecycle (draft here, save_user_dataset schedules it) and refine path (same request + previous_draft_id + feedback) are spelled out.

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

draft_user_templateAInspect

Render preview images of a user's PDF template so they can approve it BY LOOKING, before anything is saved.

THE POINT: a user wants their research in THEIR format — their masthead, colours,
layout, branding. A saved template is a BLUEPRINT: the author's stylesheet and
page chrome plus one markup pattern per content kind, which the backend applies
automatically to every PDF it renders for that user. A template is a visual
choice, so it is signed off by looking at rendered pages, never by describing
them. This is STEP 1 OF 2: it compiles the submitted HTML into a blueprint under
several treatments and renders each one as PNG previews of the platform's
SPECIMEN DOCUMENT — one of every content kind (title, lead, sections, bullets,
KPI cards, tables, charts, figure, source line, footnote). The user signs off on
how the design treats each kind of content, not on sample text. NOTHING IS
STORED — the draft lives in a 24-hour cache; `save_user_template` is step 2.

THE FLOW:
  1. Author the HTML with the user (or take the page they hand you).
  2. Call this, then poll `get_task_status(task_id)` until SUCCESS. Show the user
     EVERY variant's `preview_urls` (render the images
     inline where the client can; otherwise give the links) with its `name` and
     `rationale`, side by side.
  3. The user picks one -> `save_user_template(draft_id, variant_id)`. Never save
     a treatment the user has not seen and approved. Later changes go through
     `update_user_template`.

`template_type`:
  - "add_on"  — the previews are rendered at the 360pt column width the block
    will occupy inside a report — anything wider is clipped, not scaled — and
    the treatments restyle it to sit inside the host document (its own page
    background, masthead and disclaimer are stripped).
  - "bespoke" — the template drives the whole document: page chrome, stylesheet
    and the pattern for every content kind, previewed as full pages.

TEMPLATE RULES (violations come back as a 422 with `detail.errors`):
  - Real CSS engine: grid, flexbox, gradients, absolute positioning all work.
    Add `break-inside: avoid` to a panel that must not split across pages.
  - Only `data:` URIs for images. External images, stylesheets and web fonts are
    BLOCKED; <script>, <link> and similar are stripped.
  - HTML only, max 200 KB; must parse as HTML.

Asynchronous: returns {"task_id", "status": "PENDING", "next_step"}. Poll
`get_task_status(task_id)`; the finished result is {"draft_id", "variants":
[{"variant_id", "name", "rationale", "preview_urls": [...], "preview_pages"}],
"expires_in_seconds", "next_step", "warnings"} (or {"error", "warnings"} when the
draft failed). `preview_urls` are time-limited presigned PNG links, one per page.
Read `warnings`: a treatment that "changed too much" was dropped, and an empty
variant list means no usable adaptation survived — revise the template rather
than retrying the same one.

Runs a model pass plus a render per treatment, so expect minutes. Requires auth
(your MCP client attaches the OAuth bearer automatically). Rate-limited 30/min.
NameRequiredDescriptionDefault
anchorNoREQUIRED for add_on: the section it applies to, matched as a substring against the document's block keys (e.g. "technical", "financials", "ownership"). Omit for bespoke.
templateYesThe template as a complete HTML page in the intended style (max 200 KB; markdown is a 422). Its sample content is discarded at save — only structure and styling are kept.
template_typeYes"add_on" restyles ONE section of the standard report and keeps the rest; "bespoke" drives the whole document.

Output Schema

NameRequiredDescription
statusYesTask state at dispatch, normally "PENDING".
changedNoupdate_user_template only: the fields that were updated.
task_idYesCelery task id; poll it with get_task_status for the result.
next_stepNoWhat the finished task returns.

TDQS

A4.8/5.0
Behavior5/5

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

Annotations only declare readOnlyHint=false and destructiveHint=false; the description adds far more: nothing is persisted (24h cache), async with PENDING task_id and polling, minutes of latency from a model pass plus per-treatment render, OAuth auth, 30/min rate limit, 422 error shape via detail.errors, and the meaning of warnings/empty variant lists. No contradiction with the annotations.

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

Conciseness4/5

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

Front-loaded and organized with THE POINT / THE FLOW / TEMPLATE RULES headings, and nearly every sentence carries operational weight. It is on the long side, and the finished-result JSON shape partly duplicates the existing output schema, but the density is justified by the tool's complexity.

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

Completeness5/5

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

Covers everything an agent needs for a complex, async, multi-step tool: the two-step workflow, polling requirement, template constraints, error and warning semantics, auth and rate limits, and the type-specific rendering behavior. Output schema exists, so return values are not the description's burden, yet the warnings interpretation is still supplied.

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

Parameters4/5

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

Schema coverage is already 100%, but the description still adds real meaning: it explains the visual consequence of template_type ('add_on' renders at the 360pt column and clips rather than scales, stripping the host page chrome; 'bespoke' previews full pages), restates the anchor substring-matching requirement, and notes the 200KB/markdown-422 constraint on template.

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

Purpose5/5

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

States a specific verb+resource+outcome: renders PNG previews of a user's PDF template so it can be approved visually before saving. It explicitly positions itself as 'STEP 1 OF 2' and names save_user_template as step 2, so an agent can distinguish it from the sibling save/update/get template tools without opening any schema.

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

Usage Guidelines5/5

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

Gives an explicit numbered flow (author HTML -> call this -> poll get_task_status -> user picks -> save_user_template), names the alternatives (save_user_template, update_user_template, get_task_status) and the condition that selects each, and states an exclusion: never save a treatment the user has not seen.

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

explore_data_catalogueAInspect

Explore Flexreport's data platform with an OPEN-ENDED question — designed to handle many research based or open ended questions, enabling interactive EDA.

===> ONE company's reported statements (revenue, EPS, margins, cash flow by quarter
or year, what a 10-Q / 10-K / 8-K reported) are a direct read: `company_financials`,
synchronous, seconds — not a multi-minute job here.

===> BEFORE running this, call `list_saved_queries` and match the question against
the saved rows — on each row's `metadata` (`summary`, `parameters`, `datasets`),
not on the `user_request` label, skipping any row marked `stale`. A row of the
same SHAPE — same metric, same window, only the company / filer / date / count
differs (saved: "the past 8 quarters of sales for TGT"; asked now: "KSS's last 8
quarters of sales") — is answered by `run_saved_query(query_tokens=<that row's
query_placeholder>, values={...}, max_rows=500)` in seconds, the values keyed by
`metadata.parameters`: the backend re-aims the saved queries at the new entities,
no planning job, no draw on the 20/hour budget. An EXACT repeat replays the row's
`queries` tokens as saved. Only when nothing saved fits, or the replay answers 422
(the request could not be mapped onto the saved slots), run this tool.

===> AFTER the exploration completes and the results are shown, offer ONCE to save
it with `save_user_query`: tell the user the request can then be re-run exactly
(same queries, live data) OR re-asked for other companies, filers, or windows in
seconds. Offer when the exploring is done, not mid-iteration, and never save by
reflex.

===> For anything EVENT-DRIVEN about a company (a print, a filing, a 13F move,
news, "what happened"), call `situate(symbols=[...])` first: its table cards say
which relations HAVE refreshed since the event and which tool reads each
(`read_by`) — use that tool when it is not this one, and name only refreshed
relations in this tool's `query`. Otherwise:

===> THE DEFAULT, FIRST-STEP tool whenever the user wants to EXPLORE the data or
understand a topic from the data (e.g. "use Flexreport to explore...", "how are
small caps doing — explore the data", "how have semiconductor margins trended?").
It validates the request, plans queries against the data catalogue, runs them, and
returns the raw result sets to render as INTERACTIVE CHARTS AND TABLES on the
dashboard. Coverage questions are NOT this tool: for an enumeration ("which
symbols / sectors / indicators are available?", "is X covered?") use
`list_options` — instant, no async job; for a coverage COUNT or breakdown ("how
many symbols are in the investor relations dataset?", "what's the count by
sector?") use `explore_data_coverage`, the dedicated public tool. Reach for THIS
tool when the user wants the DATA itself — values, history, trends, comparisons.

*** RUN THIS BY ITSELF FIRST. DO NOT ALSO LAUNCH `generate_research_report` FOR THE
SAME QUESTION. *** These two tools are SEQUENTIAL STEPS, never parallel:
  1. `explore_data_catalogue` (this tool) — the LEAN route: a validate -> plan -> run
     pipeline with no report-rendering step. Usually finishes in about a minute, but
     run time varies with the tables queried and can reach ~5 minutes. Poll it, SHOW
     the user the resulting charts/tables, and let them iterate.
  2. `generate_research_report` — the slow (~10-12 min), professional analyst-grade
     deep-dive. Reach for it ONLY LATER, AFTER the user has seen the exploration and
     EXPLICITLY asks for the full report. Firing both at once wastes a ~10-min job,
     burns the 20/hour budget, and produces confusing duplicate polling.
There is no `delivery` argument here: results always go to the dashboard so the user
can interact with them. The job is rate-limited to 20/hour per user server-side.

Asynchronous: returns {"task_id": "...", "status": "PENDING"}. Poll a SINGLE task
with `get_task_status` until SUCCESS (the result is a plain dict — there is NO nested
task to chase). Expect roughly 1-5 minutes depending on the tables queried — a task
still PENDING after a couple of minutes is normal, so keep polling. On SUCCESS,
`result` is:
  {"user_query": "...", "delivery": "dashboard",
   "results": {"<query name>": {"description": "...", "columns": [...],
                                 "rows": [...the FULL result set...],
                                 "row_count": N}, ...}}
`rows` is NOT sampled — it carries every row (`row_count` matches len(rows)), so
charts/tables reflect the whole population.

Each entry ALSO carries `query_token`: the planned SQL sealed into an opaque token.
It is not readable and never for display — its one use is `save_user_query`, which
stores the tokens so `run_saved_query` can replay THIS EXACT request later in
seconds instead of re-planning it. Saving also makes the backend author a
PLACEHOLDER twin of each query plus a `metadata` slot schema, so the same request
can be re-asked for another company, filer, or window by passing typed `values`
for those slots. That is what to offer once the exploring is done (see the top of
this docstring); a saved row also spares the 20/hour budget.

CITE WHAT YOU PUBLISH: a result's `columns`/`rows` may carry `_source_*` columns —
`_source_url`, `_source_filed_at`, `_source_document_id`, `_source_citations` —
alongside a `sources` block naming each relation, its grain, and whether it is
`traceable` per "row" or only as a "cohort". That is the provenance of the numbers,
not padding: quote `_source_url` when the user asks where a figure came from, and for anything
you are about to publish — or the moment they ask for the filing, the quote, or
"how do you know" — pass `sources.relations[].relation` plus each row's
identifying columns (the grain AND its date) to `trace_data_sources`, which
resolves them to the source document, the
section, or the verified quote behind the number. `get_document_section` then opens
the exact speaker turn a transcript citation points at.

Render each entry as a chart/table for the user. If the request can't be served from
the platform's data, `result` instead carries a `validation_status` of "REJECTED"
(with a reason) — relay that rather than retrying blindly.

EVENT-DRIVEN QUESTIONS: when the question is about something that just HAPPENED
(an earnings print, a 13F move, a downgrade, a news item), consult
get_event_ontology(event_type=...) BEFORE writing the query and name the relations
from its `entails.<layer>` in the query text (prefer gold / analysis relations over
base tables). Skip any relation whose refreshed_by[].schedule[].next_run_at is
after the event — it does not hold the event yet and the query will show the prior
period as if it were the reaction.
NameRequiredDescriptionDefault
queryYesThe exploration question in natural language, e.g. "how have semiconductor margins trended over the past 8 quarters?".

Output Schema

NameRequiredDescription
queueNoWorker queue the job was routed to.
statusYesPENDING on dispatch.
task_idYesPoll this with get_task_status until SUCCESS.

TDQS

A4.4/5.0
Behavior5/5

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

Beyond the sparse annotations, the description discloses the async task model, PENDING/SUCCESS polling with get_task_status, expected runtime, the 20/hour rate limit, dashboard-only delivery, REJECTED validation behavior, and the full result shape including provenance columns. It also explains that rows are not sampled and that query_token is only for save_user_query.

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

Conciseness2/5

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

The description is front-loaded and sectioned with `===>` markers, so its navigation is clear. However, it is extremely long for a one-parameter tool and repeats the save-offer and event-driven guidance multiple times, creating redundancy that makes it harder to scan as a tool definition.

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

Completeness5/5

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

Given the tool's complexity as an asynchronous query-planning job, the description is complete: it covers alternatives, prerequisites, polling, runtime, rate limits, output shape, validation failures, provenance, and follow-up save behavior. The output schema exists, but the description still supplies enough operational context to call and interpret the tool correctly.

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

Parameters3/5

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

The tool has one parameter, `query`, and the input schema already provides 100% coverage with an example. The description adds useful framing — that it should be an open-ended natural-language question and that event-driven queries should name refreshed relations — but it does not materially document a typed or structured parameter beyond what the schema already supplies.

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

Purpose5/5

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

The description states a specific verb and resource: it plans and runs open-ended data-exploration queries against the data catalogue and returns raw result sets for dashboard charts/tables. It clearly distinguishes itself from company_financials, list_options, explore_data_coverage, generate_research_report, and run_saved_query.

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

Usage Guidelines5/5

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

It gives explicit when-to-use and when-not-to-use guidance: use this for exploratory data questions, use company_financials for direct company statement reads, list_options for enumeration, explore_data_coverage for counts, run_saved_query when a saved shape fits, and situate/get_event_ontology for event-driven questions. It also specifies sequencing with generate_research_report and when to offer save_user_query.

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

explore_data_coverageAInspect

Answer a question about WHAT THE PLATFORM COVERS — how many symbols a dataset holds, broken down any way you like.

===> THE TOOL for coverage COUNTS and BREAKDOWNS: "how many symbols are covered in
the investor relations dataset?", "what's that count by sector — how many tech
names?", "how many companies have earnings transcripts / 13F ownership / insider
activity / analyst estimates?", "how far back does the fundamentals history go?",
"what's your UK coverage by sector?". It plans queries over BOTH the catalogue's
own metadata and the datasets themselves, so it can count what no enumeration
endpoint knows: which symbols actually carry a PARTICULAR dataset, grouped by
sector, industry, country, market cap, or year.

PUBLIC at the backend — no account, plan, or entitlement is needed to answer a
coverage question, by design: a prospect evaluating the platform should be able to
ask what it covers. (The MCP connection itself still carries the client's OAuth
session — that gate is transport-wide, not this tool's.) Never treat a coverage
question as plan-gated or quota-gated, and never tell the user it needs an upgrade.

Pick between the three coverage-adjacent routes by what the answer looks like:
  - a LIST of what exists ("which sectors / indicators are available?", "is X
    covered?") -> `list_options`. Instant, no async job.
  - a COUNT or breakdown of coverage -> THIS tool.
  - the DATA itself (values, history, trends, comparisons) ->
    `explore_data_catalogue`.
Do NOT answer a coverage count from memory, from the "2,900+ companies / ~420
indices" headline figures, or by eyeballing the length of a `list_options`
payload — those are universe-wide totals and say nothing about per-dataset
coverage. Run this tool.

There is no `delivery` argument: results always go to the dashboard so the user can
interact with them. Rate-limited to 20/hour server-side.

Asynchronous: returns {"task_id": "...", "status": "PENDING"}. Poll a SINGLE task
with `get_task_status` until SUCCESS (the result is a plain dict — there is NO
nested task to chase). Expect roughly 1-5 minutes depending on the tables queried —
a task still PENDING after a couple of minutes is normal, so keep polling. On
SUCCESS, `result` is:
  {"user_query": "...", "delivery": "dashboard",
   "results": {"<query name>": {"description": "...", "columns": [...],
                                 "rows": [...the FULL result set...],
                                 "row_count": N}, ...}}
`rows` is NOT sampled or truncated — every bucket of a breakdown comes back
(`row_count` matches len(rows)), so the totals you report are the real ones.
Render each entry as a chart/table for the user. If the request can't be served
from the platform's data, `result` instead carries a `validation_status` of
"REJECTED" (with a reason) — relay that rather than retrying blindly.
NameRequiredDescriptionDefault
queryYesThe coverage question in natural language, as asked — e.g. "count the distinct symbols in the investor relations dataset, broken out by sector".

Output Schema

NameRequiredDescription
queueNoWorker queue the job was routed to.
statusYesPENDING on dispatch.
task_idYesPoll this with get_task_status until SUCCESS.

TDQS

A4.8/5.0
Behavior5/5

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

Without leaning on the thin annotations (readOnlyHint=false, destructiveHint=false), the description discloses that the backend is public and not plan/quota-gated, that there is a 20/hour server-side rate limit, that execution is asynchronous returning a task_id polled via get_task_status, the 1-5 minute latency profile, and that rows are not sampled or truncated. It even covers the REJECTED validation_status failure path — far beyond what annotations convey.

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

Conciseness4/5

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

It is long, but front-loaded and information-dense: routing rule first, then gating, then async contract and result shape. A few clauses repeat the coverage-counting idea, costing some tightness, yet nearly every sentence carries operational value an agent needs.

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

Completeness5/5

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

For a single-parameter async tool it covers everything an agent needs: routing, entitlement expectations, polling loop, timing tolerance, success and rejection result shapes, and rendering instructions. An output schema exists, so the embedded result description is a bonus rather than a necessity.

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

Parameters4/5

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

Schema coverage is 100% for the single `query` parameter, so the baseline is 3; the description adds real meaning by specifying the query should be the coverage question in natural language as asked, and by explicitly stating that no `delivery` argument exists and why. That last point resolves an ambiguity the schema alone leaves open.

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

Purpose5/5

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

The description opens with a specific verb+resource ('Answer a question about WHAT THE PLATFORM COVERS') and immediately narrows the scope to counts and breakdowns of coverage, contrasted explicitly against list_options and explore_data_catalogue. An agent can distinguish it from every coverage-adjacent sibling without opening a schema.

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

Usage Guidelines5/5

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

It gives an explicit three-way routing rule keyed on the shape of the expected answer (list -> list_options, count/breakdown -> this tool, actual data -> explore_data_catalogue), plus a negative instruction not to answer from memory or from headline universe totals. When-to-use and when-not-to-use are both stated.

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

generate_report_for_stockAInspect

Write a NEW research note for ONE ticker, now (a few minutes). Never the first call.

The fast path is `get_latest_report([ticker])`: every material event already writes
the symbol's report as it happens, so it usually exists and comes back in seconds
with a `stale` flag. This tool writes a new one on demand: the platform's analyst
researches the symbol's latest episode with its own tools (event web, intraday
tape, technicals, situate, at most one data exploration), states the platform's
official stance, and writes the note. The note REPLACES the symbol's report: later
`get_latest_report` calls return it, and `report_plan` subscribers are notified when
it lands. Two modes:

STANDARD (ticker only): call it ONLY after `get_latest_report` returned the ticker
with `stale: true` or in `missing`, and the user wants a current report rather than
the cached one — tell them it takes a few minutes before you start it. If
`latest_event_at` is only minutes old, that event's own report is probably still
being written: wait for its `report_plan` frame (or call get_latest_report again in
a few minutes) instead. Never fan it out across a list of names.

CUSTOM (`user_override=true` + the shaping fields, a few minutes; like STANDARD it
replaces the symbol's report): the same note, told to cover named line items,
ratios, segments, indicators, estimates, or holders. Bespoke asks usually do NOT
belong here. The better route for "a report on X covering Y":
  1. EXPLORE the items the user named — `list_saved_queries` first, then
     `explore_data_catalogue` (after `situate` when the ask is event-driven);
  2. IDEATE with the user over the result sets — what to keep, cut, add, compare;
  3. OVERLAY the agreed results into their template — `get_user_template` returns
     the markup to lay the content out in with your own document tooling (no
     template saved -> offer `draft_user_template`).
That is faster, iterative, and the user sees the data before it is set in a PDF.
Use CUSTOM mode only when the user explicitly wants the Flexreport note written
around those items and accepts the few-minute wait. Every parameter
after `user_override` shapes a custom report, and the backend rejects it (422)
without `user_override=true` — this tool sets the switch for you whenever any of
them is non-empty, so a shaped request is never silently served the standard
report. Each parameter's description names its vocabulary — never guess, look
each one up.

Event context is SERVER-owned: the backend reads the symbol's latest platform event
and frames the report around it. There is no thesis / change-summary / include_* /
price-date field any more — do not send editorial steer; unknown keys are ignored.

CHOOSING THE SHAPING FIELDS FROM THE ONTOLOGY (custom mode only — do not guess):
read get_company_event_web(ticker) for the latest node, then
get_event_ontology(event_type=node.type), and derive from the card's family and
`informs`:
  earnings family          -> financial_items + estimate_items from the fundamentals
                              and estimates families the card informs; add
                              include_as_report_financials when the user wants the
                              filed statement (a 10-Q / 10-K anchored event)
  institutional_ownership  -> institutional_ownership CIKs from the web node's
                              fetch.cik (a 13F node is FILER-scoped: one filer's
                              move across several names)
  market_movement / news   -> technical_analysis_items from the technical_indicators
                              family (validate names via list_options)
  analyst_activity         -> estimate_items + ratios
Omit anything the ontology gives no reason for.

Not `generate_research_report`: that answers an open-ended or multi-company
question (peer comps, a theme). This tool is one ticker — and in custom mode, a
very clear list of what to cover.

Asynchronous. Returns {"<TICKER>": {"task_id": "...", "status": "PENDING"}}: read
result[ticker]["task_id"] and poll it with `get_task_status` until SUCCESS (a few
minutes). The task result is the note's saved plan, NOT the PDF: on SUCCESS call
get_latest_report([ticker]) for the document (it comes back `stale: false`). With a
`report_plan` subscription open, the ticker's frame says the same without polling.
Requires auth (your MCP client attaches the OAuth bearer automatically).
NameRequiredDescriptionDefault
ratiosNoEDGAR ratios ("grossProfitMargin") from list_options("financial_ratios").
tickerYesThe ONE ticker to write a new note for, e.g. "SNOW".
user_overrideNoCUSTOM mode switch (steer the note toward the shaping fields; it still replaces the symbol's report). Set automatically whenever any shaping field is non-empty.
estimate_itemsNoEstimate line items to compare against actuals.
financial_itemsNoStandardized EDGAR line items ("revenue", "operatingIncome") from list_options("financial_items").
revenue_segmentNoThe filer's revenue segments ("Product revenue") from list_options("revenue_segments", ticker=...).
as_reported_periodsNoWindow(s) the as-reported section shows: "quarter" (discrete three months, the default), "ytd" (cumulative), "annual" (fiscal year, from the 10-K). Several render as adjacent columns. Not cosmetic: a filing states the same concept over more than one window at the SAME period end, so the basis must be explicit.
institutional_ownershipNoManagers to feature, by SEC CIK (any width; the backend pads to 10 digits). Get them from list_options("institutional_managers", q="berkshire") -> {cik, name, category, aum}; pass the `cik` values, never names.
technical_analysis_itemsNoIndicators to chart ("rsi", "macd") from list_options("technical_indicators").
as_reported_financial_itemsNoThe filer's OWN tagged XBRL concepts ("us-gaap_RevenueFromContractWithCustomer...", or a company extension such as "snow_...") from list_options("as_reported_items", ticker=...).
include_as_report_financialsNoAdd an AS-REPORTED statement section built from the SEC filing itself: every figure a tagged XBRL fact deep-linked to its location in the filed document (distinct from the standardized tables, whose derived figures no filer states). Line items come from `as_reported_financial_items` when given, else are chosen to match the narrative.

Output Schema

NameRequiredDescription

No output parameters

TDQS

A4.7/5.0
Behavior5/5

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

Annotations only declare readOnlyHint=false / destructiveHint=false; the description carries the rest and then some — the note REPLACES the symbol's report and triggers `report_plan` subscriber notifications, the call is asynchronous returning a task_id that must be polled to SUCCESS, latency is minutes, a 422 is returned for shaped requests without user_override, unknown keys are ignored, and OAuth auth is required.

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

Conciseness3/5

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

The two opening sentences are well front-loaded, but the body runs ~600 words with repetition: "a few minutes" appears three times, the report-replacement consequence is stated twice, and the explore/ideate/overlay detour is a full alternative workflow argued at length. Capped section headers help navigation but the definition is over-stuffed for an 11-param tool.

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

Completeness5/5

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

Covers everything an agent needs for a complex async mutation: preconditions, the replacement side effect, the exact return shape and how to read it (result[ticker][task_id] → get_task_status → get_latest_report for the PDF, not the task result), the subscription shortcut, auth, and the fact that event context is server-owned with no editorial fields. The output schema is also present, so return values need no further prose.

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

Parameters4/5

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

Schema coverage is 100% and each parameter description already names its `list_options` vocabulary, so much of the parameter discussion restates structured data. The genuinely additive part is the ontology-driven derivation (earnings family → financial_items + estimate_items; institutional_ownership → CIK from fetch.cik; analyst_activity → estimate_items + ratios) and the auto-set user_override rule, which an agent could not infer from the schema.

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

Purpose5/5

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

Opens with a specific verb+resource+scope: "Write a NEW research note for ONE ticker, now (a few minutes)." It explicitly distinguishes itself from both relevant siblings — `get_latest_report` (the cached fast path) and `generate_research_report` (open-ended/multi-company) — so an agent can route correctly without opening any schema.

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

Usage Guidelines5/5

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

Gives hard preconditions ("Never the first call", "call it ONLY after get_latest_report returned the ticker with `stale: true` or in `missing`") plus an exclusion ("Never fan it out across a list of names"). For custom mode it names the preferred alternative workflow (explore → ideate → overlay) and reserves CUSTOM for the case where the user explicitly wants the Flexreport note written.

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

generate_research_reportAInspect

Answer an OPEN-ENDED or THEMATIC research QUESTION (extensible, multi-section).

===> THE RIGHT TOOL when the user's intent is a QUESTION rather than a request for an
existing report — an exploratory or thesis-style ask about a ticker, or a market-wide
theme not tied to one company (e.g. "Are large caps driving earnings season?",
"What's the bull/bear case on NVDA?", "high-growth semis with rising estimates").
For a plain "get me the latest report/research on <ticker>", use `get_latest_report`
instead. This is the slow, professional DEEP-DIVE — reach for it only when the user
EXPLICITLY asks for a full writeup, not for an ordinary exploratory question.

If the user only wants to EXPLORE the data or "use Flexreport to explore...", do NOT
start here — call `explore_data_catalogue` first (interactive charts/tables, usually a
few minutes rather than ~10-12) and
reach for THIS deep-dive only later, once the user has reviewed the exploration and
EXPLICITLY asks for the full report. Never run both for the same question at once.

Rate-limited to 20/hour per user server-side.

Asynchronous: returns {"task_id": "...", "status": "PENDING"}. Poll with
`get_task_status` until SUCCESS, then read its `result`.
NameRequiredDescriptionDefault
queryYesThe research question in natural language, e.g. "high-growth semis with rising estimates".
deliveryNoDelivery channel for the finished report, e.g. "email" or "dashboard".email

Output Schema

NameRequiredDescription
statusYesTask state at dispatch, normally "PENDING".
task_idYesCelery task id; poll it with get_task_status until SUCCESS.

TDQS

A4.6/5.0
Behavior5/5

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

Annotations only declare readOnlyHint=false and destructiveHint=false; the description adds substantial context the annotations do not: a server-side rate limit of 20/hour per user, asynchronous execution semantics, the exact return shape ({"task_id", "status": "PENDING"}), and the polling workflow via `get_task_status`. It also warns this is the slow (~10-12 min) professional deep-dive. No annotation contradiction.

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

Conciseness4/5

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

Front-loaded with a clear '===> THE RIGHT TOOL' marker and logically ordered routing rules. It is longer than needed: the 'EXPLICITLY asks for the full report' constraint is stated twice and the options are spread across several paragraph fragments.

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

Completeness5/5

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

For a rate-limited, asynchronous, multi-minute job with an output schema present, the description supplies everything an agent needs: routing against three siblings, throttling, async return contract, and the polling follow-up. Since an output schema exists, it correctly avoids explaining the final report's contents.

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

Parameters3/5

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

Schema description coverage is 100%, so both `query` and `delivery` are already documented by the schema, establishing the baseline of 3. The description reinforces `query` semantics via inline examples but says nothing about `delivery` channels or the delivery effect, so it does not exceed the schema.

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

Purpose5/5

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

States a specific verb (generate) and resource (research report) with clear scope: open-ended or thematic questions, extensible, multi-section. It explicitly contrasts itself with `get_latest_report` and `explore_data_catalogue`, so an agent can distinguish it from siblings without opening a schema.

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

Usage Guidelines5/5

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

Gives explicit when-to-use ('intent is a QUESTION rather than a request for an existing report'), when-not-to-use (plain 'get me the latest report' → `get_latest_report`; exploration → `explore_data_catalogue` first), and sequencing rules ('never run both for the same question at once', 'reach for THIS deep-dive only later'). Nothing is left to inference.

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

get_aftermarket_dataA
Read-only
Inspect

Query STORED after-hours 15-minute bars (open/high/low/close/volume) for symbols.

Returns the extended-hours bars the backend has captured for `symbols` — one row per
symbol per 15-minute bar, 16:00-20:00 ET, ordered by symbol then bar time DESC (so the
first row per symbol is the latest bar). This is the recorded aftermarket tape, NOT a
live feed; it is polled every 15 minutes from 16:30 ET through the evening, so a bar
can lag the wall clock by up to ~15 minutes. For a live intraday read of the regular
session use `detect_intraday_outlier_jumps` instead.

Leave both datetimes off for "today's aftermarket tape". An empty `bars` list means no
bars were captured in the window (e.g. before 16:30 ET, or on a non-trading day) — not
an error.

Requires auth (your MCP client attaches the OAuth bearer automatically). Rate-limited
to 300/minute per user server-side.
NameRequiredDescriptionDefault
symbolsYesCovered tickers, e.g. ["AAPL", "MSFT"].
end_datetimeNoInclusive upper bound on bar time, ET wall-clock ISO-8601. Omit for 23:59:59 today ET.
start_datetimeNoInclusive lower bound on bar time, ET wall-clock ISO-8601 ("2026-06-24T16:00:00"). Omit for 00:00:00 today ET.

Output Schema

NameRequiredDescription
barsYesOne row per symbol per 15-min bar, 16:00-20:00 ET, ordered by symbol then time DESC (first row per symbol is the latest). Empty means no bars were captured in the window.

TDQS

A4.9/5.0
Behavior5/5

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

Although annotations already declare readOnlyHint=true, the description adds substantial context: the data is a stored tape rather than a live feed, it is polled every 15 minutes from 16:30 ET with up to ~15 min lag, the 16:00-20:00 ET window and row ordering, plus auth (OAuth bearer) and a 300/min rate limit.

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

Conciseness5/5

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

Front-loads the core purpose in the first sentence, then layers scope, caveats, defaults, and edge cases as separate short paragraphs. No sentence is redundant, and the sibling reference is placed at the point of contrast.

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

Completeness5/5

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

An output schema exists so return values need not be described, and the description nonetheless covers the operational profile an agent needs: stored/lagged semantics, time window, ordering, defaults, empty-result behavior, auth, and rate limits.

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

Parameters4/5

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

Schema coverage is 100% and both datetime defaults are already documented in the schema, so the baseline is 3. The description still adds interpretive value by framing omitting both datetimes as 'today's aftermarket tape' and by explaining the sort order (symbol then bar time DESC, latest first) that shapes how the params return data.

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

Purpose5/5

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

States a specific verb and resource with scope: 'Query STORED after-hours 15-minute bars (open/high/low/close/volume) for symbols.' It explicitly distinguishes itself from the sibling detect_intraday_outlier_jumps by contrasting the recorded aftermarket tape with a live intraday read.

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

Usage Guidelines5/5

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

Names the alternative tool and the condition that selects it ('for a live intraday read of the regular session use detect_intraday_outlier_jumps instead'), and gives explicit guidance on omitting datetimes for today's tape. It also pre-empts a common confusion by stating an empty bars list is not an error.

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

get_company_event_webA
Read-only
Inspect

Fetch the connected WEB of what recently HAPPENED to a company — the WHY behind its snapshot, and a time-ordered graph to CHAIN your next calls off.

===> `situate(symbols=[symbol])` already returns this web deduped to the newest
node per event type and per table, PLUS the ontology cards and the table cards
with read_by — start there. Call this tool directly for the RAW graph (every
node, no dedup) or a different `window_days`, and pair every node you chain off
with get_event_ontology(event_type=node.type): the web says WHICH event, the
ontology says what it entails and what to do about it.

===> A FIRST-LINE TOOL FOR SINGLE-COMPANY QUESTIONS, not a specialist one. Reach
for it whenever the user asks what has been going on with a name ("catch me up on
WM", "what happened at NVDA this week", "anything I should know before
earnings?"), asks WHY the company looks the way it does ("why is the thesis
negative?", "what drove the downgrade?", "what changed?"), or whenever you are
about to aim a targeted `explore_data_catalogue`, `generate_report_for_stock`, or
`list_realtime_events` request at ONE symbol. Synchronous and cheap —
one call turns "I'll guess which event types and dates matter" into "here are
the exact events, their timestamps, the tables that were written, and the call
that returns each full payload".

*** THE STANDARD PAIR: `get_company_snapshot` + THIS. *** They answer the two
halves of almost every company question and are usually called TOGETHER:
  - `get_company_snapshot(symbol)` -> the WHAT. Where the company stands now:
    thesis, fundamentals, technicals, ownership, grades. A settled position with
    no account of how it got there.
  - `get_company_event_web(symbol)` -> the WHY. The episode behind that position:
    the earnings print, the 8-K, the transcript update, the rating action, the
    13F refresh — in order, with edges saying how each relates.
Read them together: the snapshot for the current position, the web for the
sequence that explains it. A snapshot delivered alone reads as a verdict with no
evidence; this tool is the evidence. Conversely, do NOT use this tool to state
the company's current position — it carries headlines and pointers, not values.
When the two disagree, say so plainly rather than smoothing it over: the blocks
in a snapshot carry independent as-of dates, so an ownership or grade block can
predate anything in the window (widen `window_days` before concluding a snapshot
move is unexplained).

Returns:
  {"symbol": ..., "as_of": ISO8601, "window_days": N,
   "nodes": [...], "edges": [...], "market_context": [...]}

NODES — two kinds, newest first, each with a short id ("n1", "n2", ...):
  - kind="event"       — a published realtime event. Carries `type` (the event
                         type, e.g. "8k_release"), `family`, and a `fetch` hint
                         — the exact call that returns its FULL payload, e.g.
                         {"tool": "list_realtime_events", "event_type": "8k_release",
                          "related_endpoints": [...]}.
  - kind="data_update" — a write to a backing table for activity that publishes
                         NO event at all (insider filings, 13F refreshes, IPO
                         calendar entries, Companies House events, transcript and
                         fundamentals analysis). Carries `relation` (the table
                         written); most also carry a `fetch` hint (a ready-made
                         `explore_data_catalogue` query for the underlying rows),
                         and on some relations the hint additionally carries
                         `signed_query_url` — see step 2 below.
Every node carries `at` (when the write/publish happened) and `headline` (ONE
line, truncated at 200 chars).

EDGES — {"from": "n3", "to": "n1", "via": <relation>, "cause": ...}, pointing
from the EARLIER node to the LATER one. `cause` is a confidence grade — respect it:
  - "same_chain_run" — OBSERVED. One backend chain run produced both nodes.
  - "lineage"        — DECLARED. One wrote the table the other's event derives from.
  - "co_occurrence"  — TEMPORAL ONLY. Same company, same window, no known causal
                       path. Report these as "happened alongside", NEVER as caused-by.

HOW TO CHAIN (the point of this tool):
  1. Walk the edges back from the newest node to reconstruct the episode, then
     follow each event node's `fetch` to get the detail — e.g.
     `list_realtime_events(event_type="8k_release", tickers=["WM"])`. Always narrow
     by `tickers=[symbol]`; an unfiltered call re-pulls the entire cache.
  2. When a `data_update` node's `fetch` carries `signed_query_url`, call
     `get_signed_sql_drilldown` with it FIRST — zero planning, and it returns the
     new record (`is_new_record: true`) plus the context rows to read it against.
     If it comes back not-authenticated, the link is fine — the user's sign-in
     lapsed; say so rather than altering the token.
     Otherwise use the node's `fetch` query if it has one, or its `relation` and
     `at` to write a PRECISE `explore_data_catalogue` query ("insider filings for
     WM since 2026-08-09", "13F position changes for WM in the last week")
     instead of a vague one.
  3. Feed the concrete events and dates into `explore_data_catalogue` /
     `generate_research_report` so the analysis is scoped to what actually
     happened; for the company's own report, `get_latest_report` returns the
     document written from that event.
  4. Nothing here is a full payload — a headline is a pointer, never the content.
     Do not quote a headline as if it were the event.

`window_days` and `max_nodes` widen the look-back. Beyond the node cap, edges are capped per node (~3 same_chain_run, ~3
co_occurrence) and `market_context` — market-wide macro/economic-calendar nodes
that carry no ticker and so attach to no symbol — is capped at 5. A MISSING edge
is therefore not evidence that two things are unrelated.

Traps:
  - `at` is a WRITE/PUBLISH time, not a business date. An earnings call date or
    fiscal period end can be well before the node's timestamp — never report `at`
    as the date the underlying event occurred.
  - A "<table> refreshed for N symbols" node is universe-wide scheduled
    maintenance rolled up, not company-specific signal.
  - An empty web (`"degraded": true`, empty lists) means the graph has no rows for
    that symbol in the window — NOT that nothing happened and NOT that the symbol
    is uncovered. Say so, and fall back to `list_realtime_events` /
    `explore_data_catalogue` rather than concluding it was a quiet week.

Requires auth (your MCP client attaches the OAuth bearer automatically).
Rate-limited 60/min.

Every node's `type`, `relation` and `family` are join keys into get_event_ontology:
call get_event_ontology(event_type=node.type) to learn what that node ENTAILS (the
tables now fresh, the events that should follow and how soon, where the payload
lives after the 12h cache, whether downstream tables have refreshed yet) before
chaining further calls off it. Web = what happened to THIS company; ontology = what
that KIND of thing means. Read them together.
NameRequiredDescriptionDefault
symbolYesCovered ticker, e.g. "WM".
max_nodesNoNode cap (default 40, max 200).
window_daysNoLook-back in days (default 7, max 90). Widen before calling a snapshot move unexplained.

Output Schema

NameRequiredDescription
as_ofNo
edgesYes
nodesYesNewest first.
symbolYes
degradedNoTrue with empty lists when the graph has no rows for the symbol in the window.
window_daysNo
coverage_fromNoEarliest node in the whole graph; an older window cannot be answered.
market_contextNoMarket-wide macro / economic-calendar nodes.

TDQS

A4.7/5.0
Behavior5/5

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

Annotations only declare readOnlyHint, so the description carries real weight and delivers: synchronous and cheap, requires OAuth bearer, rate-limited 60/min, node/edge/market_context caps, and that a missing edge is not evidence of unrelatedness. It also discloses the degraded-empty-web state and the `at`-is-publish-time trap, which no annotation or schema conveys.

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

Conciseness3/5

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

Content is largely load-bearing, but it is very long with ALL-CAPS emphasis, three separate `===>` blocks, and the get_event_ontology pairing stated twice (opening and closing) — the reader must scan past duplicated guidance. The output shape is front-loaded reasonably, but the length is closer to a manual than a tool description.

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

Completeness5/5

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

For a graph-returning tool it covers nodes, edges, cause grades, market_context, and the chaining procedure, and it even documents semantics of the return despite an output schema existing. Nothing an agent needs in order to call it correctly and interpret the result is missing.

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

Parameters4/5

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

Schema coverage is already 100%, so the barrier is low, but the description adds genuine meaning: `window_days` should be widened before declaring a snapshot move unexplained, and `max_nodes` beyond the cap truncates edges (~3 same_chain_run, ~3 co_occurrence) — a consequence the schema's "Node cap (default 40, max 200)" does not convey. Symbol semantics add little beyond the schema's own "Covered ticker" example.

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

Purpose5/5

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

States a specific verb and resource — fetch the connected event web for one company — and immediately contrasts it with `situate` (deduped, newest-per-type) and `get_company_snapshot` (the WHAT vs the WHY). An agent can distinguish it from every plausible sibling without opening a schema.

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

Usage Guidelines5/5

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

Explicit when-to-use with concrete user phrasings ("catch me up on WM", "why is the thesis negative?"), an explicit alternative (`situate` first, this for the raw graph or a different window), and explicit when-NOT (don't use it to state current position; don't quote headlines as content). Routing conditions are stated, not implied.

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

get_company_snapshotA
Read-only
Inspect

Fetch a structured, POINT-IN-TIME company snapshot — no report generation needed.

Returns thesis/bull/bear, financial overview (Piotroski, valuation signal),
price performance + technical indicators, price targets, institutional
ownership, and analyst grades for `symbol`. Synchronous and cheap — prefer
this for a quick CURRENT read instead of generating a full PDF report.

*** POINT-IN-TIME ONLY — THIS IS NOT A TIME SERIES. *** It captures where the
company stands right now, not how it got there. Do NOT use it to answer
temporal / trend / "over time" questions (e.g. "how has X evolved over the
past year", "quarter-over-quarter trend", "activity over the last 12 months",
"since ..."). Each block is an independently-sourced current cut with its own
as-of date (e.g. the institutional-ownership block is the latest 13F holder
snapshot), so the blocks are NOT a comparable historical series — reading a
trend off them, or treating two blocks' as-of dates as the same instant,
produces spurious results. For anything time-series, historical, or
"how did this change", use `explore_data_catalogue` instead.

Scope caveat: the snapshot is a FIXED set of current blocks, not the full
catalogue. Absence of a data domain HERE does NOT mean Flexreport lacks it —
do not infer coverage gaps from this tool. Route "which symbols / values are
available" enumerations to `list_options` (instant), and "do you have data
on <topic>" questions that need actual data to `explore_data_catalogue`.

*** RUN IT WITH `get_company_event_web(symbol)` — THE STANDARD PAIR. *** This
tool gives the WHAT (where the company stands); the event web gives the WHY
(the events that got it there, in order, and how they connect). The snapshot
states that the thesis shifted, the valuation signal changed, or ownership
moved; it does NOT say what caused any of it — every block here is a settled
output with the episode behind it stripped out. So whenever the user asks
about a company generally, or follows a snapshot with "why?" / "what changed?"
/ "what drove that?", call BOTH and read them together: quote the snapshot for
the current position, and the event web for the sequence that explains it.
Presenting a snapshot alone as the whole picture is the failure mode — it
reads as a verdict with no evidence.

Two honest limits when pairing them. (1) The event web defaults to a 7-day
window; a driver older than that needs `window_days` widened (up to 90) before
you can say the snapshot is unexplained. (2) The blocks here carry their own
as-of dates — a 13F ownership block can be a quarter old, so the event that
explains it may sit well outside any window. Absence of a matching event is
NOT proof the snapshot moved for no reason.
NameRequiredDescriptionDefault
symbolYesCovered ticker, e.g. "AAPL".

Output Schema

NameRequiredDescription
recent_newsNo
price_targetsNoDecommissioned pending a model revamp: values are null and active_coverage is false.
price_overviewNo
company_summaryNoThesis, bull and bear case.
financial_overviewNo
stock_grades_overviewNo
institutional_ownershipNo

TDQS

A4.6/5.0
Behavior5/5

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

Annotations declare only readOnlyHint=true, so the description carries the behavioral burden and does it well: synchronous and cheap, fixed block set, point-in-time semantics, per-block as-of dates (13F can be a quarter old), and the failure mode of misreading blocks as a comparable series. This is context an agent could not get from the structured fields.

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

Conciseness4/5

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

Front-loaded with purpose before the caveats, and the bolded section headers make it scannable. However, the point-in-time constraint is restated several times across the warning and pairing paragraphs, so the piece is longer than a one-parameter read tool strictly requires and carries mild redundancy.

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

Completeness5/5

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

With an output schema present, return values need not be explained, and the description nonetheless enumerates the returned blocks. Combined with the routing rules, timing caveats, and pairing guidance, nothing an agent needs to call this correctly is missing.

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

Parameters3/5

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

Only one parameter (symbol), schema description coverage is 100%, and the description adds nothing beyond the schema's 'Covered ticker, e.g. AAPL.' Baseline 3 for a fully documented single-param schema is appropriate.

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

Purpose5/5

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

Opens with a specific verb+resource — 'Fetch a structured, POINT-IN-TIME company snapshot' — and immediately enumerates what blocks it returns (thesis/bull/bear, financial overview, price performance, targets, ownership, grades). It is clearly distinguished from near-siblings like generate_report_for_stock, explore_data_catalogue, and list_options, so an agent can pick it without opening any schema.

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

Usage Guidelines5/5

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

Explicit when-to-use ('quick CURRENT read', general company questions), explicit when-NOT-to-use (any temporal/trend/'over time' question, coverage enumerations), and names the replacement tool for each case (explore_data_catalogue, list_options). It also instructs the standard pairing with get_company_event_web and states the selection reason (WHAT vs WHY).

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

get_document_sectionA
Read-only
Inspect

Open ONE numbered section of a source document — the exact earnings-call speaker turn a citation points at.

The other end of a `trace_data_sources` citation, and the finest grain the platform
serves: not the whole call, one turn of it. Use it to read the passage behind a
number before quoting it, and to quote a speaker verbatim with the citation intact.

Every argument comes off the citation you are following — a `transcript_section`
citation's URL is `/query-section?name=...&symbol=...&year=...&quarter=...&n=...`,
so read the four values out of it rather than deriving them. If you pulled the row
from `earnings_call_transcript_sections` yourself you already hold all four
(`symbol`, `year`, `quarter`, `section_no`); no text search is needed or wanted.

*** `year` and `quarter` are the FISCAL period of the CALL, never the calendar
quarter a report covers. *** For most covered companies those differ — the call
everyone calls "Q2 2026" is stored as FY2026 Q4 for one name and FY2027 Q1 for
another. Take the period from the citation, the transcript record, or `call_date`.
A 404 here ("no section N for SYM YEARqQ") is almost always this, not a missing
call: re-check the period before concluding the transcript is not covered.

Returns {"name", "symbol", "year", "quarter", "n", "format": "paragraph",
"count", "rows": [{symbol, year, quarter, call_date, section_no, speaker,
content}], "highlights": [...], "total_sections": N, "transcript_url": ...}.
`content` is the turn's text and `speaker` is who said it — attribute the quote to
that speaker, never to "the company". `total_sections` bounds `n`, so neighbouring
turns (n-1, n+1) are how you read a passage in context when it starts mid-answer.
`highlights` are quotes the platform's own analysis pipeline verified against this
section; they mark evidence inside it but are NOT necessarily the passage you are
citing — verify your own quote against `content`. `transcript_url` is frequently
null, so offer "view the full call" only when it is present.

Requires auth (your MCP client attaches the OAuth bearer automatically).
NameRequiredDescriptionDefault
nYes1-based section number (the speaker turn), e.g. the citation's `n` or `section_no`.
nameNoWhitelisted section source; "earnings_transcript" is the only one today.earnings_transcript
yearYesFISCAL year of the call (never the calendar year a report covers), read off the citation URL or transcript record.
symbolYesTicker the cited call belongs to, read off the citation.
quarterYesFISCAL quarter of the call, read off the citation URL or transcript record.

Output Schema

NameRequiredDescription
nYes1-based section number.
nameYes
rowsYes
yearYesFiscal year of the call.
countNo
formatNo
symbolYes
quarterYesFiscal quarter of the call.
highlightsNoQuotes the platform verified against this section.
total_sectionsNoUpper bound for n; read n-1 / n+1 for context.
transcript_urlNoFull call link; frequently null.

TDQS

A4.8/5.0
Behavior5/5

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

With only readOnlyHint=true in annotations, the description carries real behavioral load: auth requirement, the common 404 cause and its remedy, that transcript_url is frequently null (so don't offer a link), that highlights are pipeline-verified but not necessarily your quote, and that total_sections bounds n. None of this contradicts the annotations and none of it is recoverable from structured fields.

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

Conciseness4/5

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

The lead sentence is front-loaded and the purpose is clear before any caveats. It is on the long side, and the bold fiscal-period warning partially duplicates the schema's own field note, but every remaining sentence (citation derivation, 404 remedy, highlights semantics, null transcript_url) earns its place.

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

Completeness5/5

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

For a 5-parameter authenticated read tool with an output schema, the description covers the things the schema cannot: auth handling, error interpretation, how to read a mid-answer passage via adjacent sections, and caveats on two return fields. Nothing material an agent needs to call this correctly is missing.

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

Parameters4/5

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

Schema coverage is 100%, so a baseline of 3 applies, but the description goes beyond the schema by explaining where each value originates (the citation URL's name/symbol/year/quarter/n components, or the transcript row's section_no) and by stressing the fiscal-vs-calendar period trap. It doesn't add per-parameter syntax beyond that, so it stops short of 5.

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

Purpose5/5

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

The opening line states a specific verb and resource with exact scope: 'Open ONE numbered section of a source document — the exact earnings-call speaker turn a citation points at.' It positions itself against siblings by naming trace_data_sources as the counterpart and clarifying the grain ('not the whole call, one turn of it'). An agent can distinguish this from transcript-listing tools without opening a schema.

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

Usage Guidelines5/5

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

It gives explicit when-to-use ('read the passage behind a number before quoting it... quote a speaker verbatim with the citation intact'), describes the alternative acquisition path ('if you pulled the row from earnings_call_transcript_sections yourself you already hold all four'), and instructs to derive args from the citation URL rather than guess. The 404 troubleshooting guidance is an unusually strong when-it-fails clause.

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

get_event_ontologyA
Read-only
Inspect

The platform's event ONTOLOGY — what an event TYPE means for everything else. Call it FIRST on any real-time, single-company, or event-driven request; it is the map the other calls are planned from.

===> For a named company, `situate(symbols=[...])` returns the cards of the event
types in its web and the table cards they entail — start there. Call this tool
directly for the FULL card of an event type, a relation card, a whole family, or
the unfiltered graph.

===> REQUISITE for: any real-time / live-events request, any "what
happened / why / catch me up" question, and BEFORE aiming explore_data_catalogue or
generate_report_for_stock at a company whose situation is event-driven. Cheap,
synchronous, public, symbol-INDEPENDENT and cacheable — fetch once per event type
(or once unfiltered, ~230 KB) and reuse for the whole conversation.

Pass exactly ONE filter for a small card (~5-10 KB):
  event_type="eps_update"        one event type and the relations it entails
  relation="eod_stock_prices"    one table/view and the event types that entail it
  family="earnings"              every event type and relation in a family
No filter -> the whole class graph (21 event types, ~300 relations, 20 tools).
Web nodes from get_company_event_web carry the join keys verbatim: pass a node's
`type` as event_type, its `relation` as relation, its `family` as family.

An event_type card carries:
  about                    what the event is ABOUT: symbol | filer | strategy. A 13F
                           event is a FILER's action across N names — the symbol
                           you asked about is one of them.
  persisted_in             the durable table to read once the event has aged out
                           of the realtime cache (realtime_cache_ttl_hours, 12h) —
                           list_realtime_events returns [] after that; the data is
                           still there, in this relation.
  entails.<layer>          the relations WRITTEN by the same chain run (mined, with
                           run counts) plus every view above them (pg_depend),
                           bucketed base / silver / gold / dimension / analysis.
                           These are the tables that are FRESH because this event
                           fired — name them explicitly in explore queries.
  entails.realtime_events  event types that usually FOLLOW, each with rate,
                           median_lag_hours, lift, mode (trigger = dispatched by the
                           same chain, near-certain; sequence = world-ordered,
                           independently detected, probabilistic), certainty /
                           expected_within_hours when declared, and persisted_in.
  preceded_by              the inverse — what should ALREADY exist earlier in the web.
  episodes                 named world-order sequences (earnings, realtime_mover,
                           thirteen_f_cycle, news) with this event's position — the
                           whole shape of the situation from any one member.
  produced_by[].schedule   the workflow that publishes it, its cron, and
                           next_run_at (America/New_York).
  deepened_by              the tools that deepen this event once you have its payload.
  informs                  DOMAIN causality (news -> prices, earnings -> estimates):
                           what an analyst checks next. Declared and the WEAKEST
                           edge — never present it as lineage.
  evidence                 how many of these events the platform has seen.

A relation card carries: layer, family, cadence (update_frequency + prose),
derives_from / feeds (pg_depend), computed_from / computes (LLM and model writers),
refreshed_by[].schedule with next_run_at, read_by (the MCP tools that read it),
entailed_by (event types), informs.

THE FRESHNESS CHECK — the reason this tool exists: compare a web node's `at` with a
downstream relation's refreshed_by[].schedule[].next_run_at. If next_run_at is AFTER
the event, that relation does NOT reflect the event yet. Do not query it for the
reaction — use the intraday tools (detect_intraday_outlier_jumps,
get_aftermarket_data) or say the daily table lags.

Every edge carries `provenance` (mined | declared | pg_depend | static:tasks.py |
beat | catalogue | data_model), the same honesty axis as the web's `cause`: mined
and pg_depend are facts, declared is the platform's own judgment, informs is domain
intuition. Quote the grade when you lean on an edge. format="triples" returns raw
subject-predicate-object rows instead of cards. degraded=true means the table has
not been built yet, not that the type is unknown.
NameRequiredDescriptionDefault
familyNoOne family, e.g. "earnings" (a web node's `family`).
formatNocards (grouped, default) or triples (raw subject-predicate-object rows).cards
relationNoOne table/view, e.g. "eod_stock_prices" (a web node's `relation`).
event_typeNoOne event type, e.g. "eps_update" (a web node's `type`). Pass exactly one filter.

Output Schema

NameRequiredDescription
as_ofNo
edgesNoSubject-predicate-object rows (triples format).
degradedNoTrue when the ontology table is not built yet, not that the type is unknown.
episodesNo
relationsNoRelation cards, keyed by table/view (cards format).
event_typesNoEvent-type cards, keyed by type (cards format).
realtime_cache_ttl_hoursNo

TDQS

A4.7/5.0
Behavior5/5

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

Annotations only declare readOnlyHint=true; the description adds substantial behavior beyond that — cheap, synchronous, public, symbol-INDEPENDENT, cacheable, ~230 KB unfiltered vs ~5-10 KB filtered, a 12h realtime_cache_ttl_hours boundary, and the meaning of degraded=true ('the table has not been built yet, not that the type is unknown'). It also discloses the provenance honesty axis (mined | declared | pg_depend | static:tasks.py | beat | catalogue | data_model) and warns that `informs` is the weakest edge and must not be presented as lineage.

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

Conciseness3/5

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

Well front-loaded and visually structured with `===>` markers and a clear filter block, but roughly half the text enumerates what an event_type card and relation card carry (about, persisted_in, entails.<layer>, precedes, episodes, deepened_by, informs, evidence), which duplicates return-value documentation that an output schema already covers. Several paragraphs restate the same caching/requisite point, so the length is not fully earned.

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

Completeness5/5

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

For a zero-required-parameter, four-filter ontology reader with an output schema and a readOnly annotation, the description covers everything an agent needs: what it returns, when to prefer `situate`, cost/size expectations, cacheability, and the freshness comparison rule that motivates the tool's existence. No material gap remains for correct invocation.

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

Parameters4/5

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

Schema coverage is 100%, so the schema already documents each filter, but the description adds a real constraint absent from the schema: 'Pass exactly ONE filter for a small card' and the no-filter case ('the whole class graph, 21 event types, ~300 relations'). It also supplies the join contract with `get_company_event_web` (pass a node's `type` as event_type, its `relation` as relation, its `family` as family), which meaningfully disambiguates the string values.

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

Purpose5/5

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

States a specific resource with a precise framing: 'the platform's event ONTOLOGY — what an event TYPE means for everything else', and explicitly separates itself from `situate` and `get_company_event_web` by describing which slice each returns. An agent can distinguish this from all ~55 siblings without opening a schema.

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

Usage Guidelines5/5

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

Explicit when-to-use ('Call it FIRST on any real-time, single-company, or event-driven request', 'REQUISITE for... BEFORE aiming explore_data_catalogue or generate_report_for_stock'), explicit alternative ('for a named company, situate(symbols=[...]) ... start there'), and explicit when-to-call-directly (FULL card, relation card, whole family, unfiltered graph). Also names a downstream decision rule: use intraday tools when next_run_at is after the event.

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

get_latest_reportA
Read-only
Inspect

Get the latest Flexreport research report(s) for one or more tickers. USE THIS BY DEFAULT.

===> THIS IS THE DEFAULT, CORRECT TOOL whenever a user asks for "the report",
"research", "the latest research", "analysis", "a writeup", or "the PDF" for a
ticker (e.g. "get me the latest research on SNOW"). It is the FAST path, and it
only FETCHES: every material event writes the symbol's report as it happens, so
this returns the latest document in seconds, with metadata saying how current it
is. Nothing is built here. ALWAYS call this before `generate_report_for_stock`,
which writes a new note (a few minutes).

If the user's intent is an OPEN-ENDED or exploratory QUESTION rather than a request
for this existing report, do NOT use this tool — default to `explore_data_catalogue`
(fast, interactive), and reach for `generate_research_report` only when the user
EXPLICITLY asks for a full deep-dive writeup. A BESPOKE report ("the SNOW report,
but built around product revenue and RPO") is not a new note either: explore the
items the user named, ideate with them on the result sets, and lay the agreed
results out in their template (`get_user_template`) with your own document tooling.

Accepts one OR many symbols. Returns
{"result":    [{"symbol": "AAPL", "url": "<presigned pdf url>",
                "generated_at": "<iso utc>", "age_hours": 5.2,
                "latest_event_at": "<iso>", "stale": true,
                "template": "default"}, ...],
 "missing":   ["XYZ", ...],
 "stale_symbols": ["AAPL", ...],
 "rendering": {}}
Every symbol lands in exactly ONE of `result` / `missing`; `stale_symbols` lists the
`result` symbols flagged `stale: true`; `rendering` is always empty (kept for older
clients). `template` is "user" when the PDF is the caller's own copy in their saved
template, "default" for the platform layout. Split a list longer than 100 symbols
across calls.

THE PDF IS BEHIND `url`, not in this response: a presigned link valid ~6h from this
call.
  - To GIVE the user the report: hand them the link to open or download.
  - To READ it yourself: download the link (an HTTP / web fetch, or
    `curl -o <SYMBOL>.pdf "<url>"`) and open the PDF; a client that renders PDF
    pages also sees the charts. Quote the report from what you read, not from the
    metadata here.
  - The link serves the symbol's report file as it is when fetched. After ~6h, or
    to pick up a newer report, call this tool again for a fresh link; never edit
    or build a URL.
  - A client that cannot fetch a URL but can run code: download_pdf_from_url(url)
    returns the same PDF as base64 to decode.

READ EACH SYMBOL'S FLAG BEFORE YOU HAND ANYTHING OVER:
  stale: false   -> the PDF reflects the symbol's latest event. Serve it.
  stale: true    -> an event landed after this PDF (`latest_event_at` says when).
                    If that was only minutes ago, the event's own report is probably
                    still being written: say so, and fetch again in a few minutes
                    (or wait for its `report_plan` frame). Otherwise hand over the
                    PDF with that caveat and offer generate_report_for_stock(ticker),
                    which writes a new note in a few minutes. Start it when the user
                    wants it; never fan it out across a list of names unasked.
  stale: null    -> no report inputs are known for the symbol, so freshness cannot
                    be judged. Serve the PDF and quote `generated_at` / `age_hours`.
  in `missing`   -> no report yet. generate_report_for_stock(ticker) writes the first
                    one (a few minutes; later calls here return it) — say so before
                    starting it — or `onboard_symbol` if the ticker is not covered.
To see which symbols have a CURRENT report — by the event that produced it, across
the universe, before anything is pulled — use `list_available_reports`.

Rate-limited to 500/hour. Requires auth (your MCP client attaches the OAuth bearer
automatically).
NameRequiredDescriptionDefault
symbolsYesOne or many tickers, e.g. ["SNOW", "AAPL"]; uppercased and de-duplicated server-side, at most 100 after dedupe (413 above that).

Output Schema

NameRequiredDescription
resultNoSymbols served from the cached PDF.
missingNoSymbols with no report written yet.
renderingNoAlways empty: reports are written at event time and nothing renders on fetch. Kept for older clients.
stale_symbolsNoThe `result` symbols flagged stale: true.

TDQS

A4.8/5.0
Behavior5/5

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

Annotations only supply readOnlyHint=true; the description adds substantial context beyond that: it FETCHES and never builds, returns in seconds, rate limit of 500/hour, OAuth bearer required, and the URL is a presigned link valid ~6h after which a fresh call is required. It also warns never to edit or construct a URL, which prevents a realistic failure mode.

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

Conciseness4/5

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

Front-loaded with the decisive 'USE THIS BY DEFAULT' line, then triggers, then exclusions, then response semantics. It is long, but nearly every block maps to a real decision (stale flag handling, PDF access, list splitting). Minor redundancy: the inline JSON response shape overlaps with the existing output schema.

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

Completeness5/5

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

Covers the full lifecycle an agent must handle: symbol partitioning into `result`/`missing`, stale-true/false/null handling with a recommended next action, 6h link expiry, download-and-read vs hand-the-link distinction, and the >100-symbol split rule. Nothing needed to call this correctly is missing.

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

Parameters4/5

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

Schema coverage is 100% and the single `symbols` parameter is already documented (uppercased, de-duplicated, max 100), so the baseline is 3. The description adds operational meaning with 'Split a list longer than 100 symbols across calls', which tells the agent how to recover from the documented 413 rather than just what the limit is.

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

Purpose5/5

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

States a specific verb and resource ('Get the latest Flexreport research report(s) for one or more tickers') and immediately differentiates itself from siblings: it is the default fetch path, not `generate_report_for_stock`, `generate_research_report`, `explore_data_catalogue`, or `list_available_reports`. An agent can route correctly without opening any schema.

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

Usage Guidelines5/5

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

Gives explicit when-to-use triggers ('the report', 'research', 'the latest research', 'analysis', 'a writeup', 'the PDF'), explicit when-NOT (open-ended/exploratory questions go to `explore_data_catalogue`; bespoke writeups go to template tooling), and ordering guidance ('ALWAYS call this before `generate_report_for_stock`'). This is as close to a routing decision table as a description gets.

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

get_signed_sql_drilldownA
Read-only
Inspect

Fetch the rows behind an event-web drilldown from its server-minted query token.

Some `get_company_event_web` nodes carry `signed_query_url` in their `fetch` hint —
an opaque, tamper-proof link the backend minted for exactly that node. This tool is
the zero-planning rung of the chain: no query to compose, the rows come back
directly. Pass either the full `signed_query_url` or just its `t` parameter; the
URL form is unwrapped automatically. A token that came from a
SAVED request rather than the event web belongs to `run_saved_query` instead — same
endpoint, but it replays every query in the saved row in one call.

Returns {"format": "table", "count": N, "rows": [...]} — rows only, never the
underlying SQL. For contextual drilldowns the NEW record is flagged
`is_new_record: true` and the remaining rows are the prior/context records to read
it against (the node's `signed_query_context` describes what they are).

CITE WHAT YOU PUBLISH: the rows may carry `_source_*` columns — `_source_url`,
`_source_filed_at`, `_source_document_id`, `_source_citations` — and the response a
`sources` block naming each relation, its grain, and whether it is `traceable` per
"row" or only as a "cohort". That is the provenance of the numbers, not padding:
quote `_source_url` when the user asks where a figure came from, and for anything
you are about to publish — or the moment they ask for the filing, the quote, or
"how do you know" — pass `sources.relations[].relation` plus each row's
identifying columns (the grain AND its date) to `trace_data_sources`, which
resolves them to the source document, the
section, or the verified quote behind the number. `get_document_section` then opens
the exact speaker turn a transcript citation points at.

HTTP 403 means the token is invalid or tampered — do not retry with an altered
token; re-fetch the event web for a fresh link instead.

`values` and `prompt` re-aim a token that is a PLACEHOLDER TWIN at other entities;
the response then carries `parameters` (what was substituted) alongside `rows`;
echo those to the user. A 422 means the request could not be
mapped onto the token's slots. The full slot vocabulary of a SAVED row lives in
`list_saved_queries` -> `metadata.parameters`, and replaying a saved row is
`run_saved_query`, not this tool.

An uncapped drilldown that fans out to constituents can return tens of thousands
of rows and stall the client for minutes — pass `max_rows`.

Requires auth (your MCP client attaches the OAuth bearer automatically). A
not-authenticated error means the sign-in lapsed; the fix is signing in, never a
different token.
NameRequiredDescriptionDefault
promptNoPlaceholder-twin tokens only: natural-language fill for slots `values` did not cover, e.g. "the same rows for KSS". Omit for a plain event-web link.
valuesNoPlaceholder-twin tokens only (preferred over prompt): {slot name: typed value}, filled deterministically, e.g. {"symbol": "KSS"}. Omit for a plain link.
max_rowsNoCap on rows returned, e.g. 500; the response then carries row_count and truncated: true. Pass it on any drilldown that can fan out to constituents.
encrypted_query_tokenYesA node's `signed_query_url` from get_company_event_web, or just its `t` parameter — verbatim. Never construct or edit one; never pass SQL.

Output Schema

NameRequiredDescription
rowsNoResult rows (cell values are strings or null). May carry _source_* citation columns. Null on a replay entry whose token failed (see `error`).
countNoRows returned in `rows`.
formatNo"table".
sourcesNoProvenance block; present when the numbers trace to documents.
row_countNoFull row count when `truncated` (max_rows sliced the result).
truncatedNo
parametersNoValues substituted into a placeholder twin; echo them to the user.
ignored_valuesNoKeys in `values` this token does not use.
defaulted_parametersNoSlots that kept the saved example value.

TDQS

A4.8/5.0
Behavior5/5

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

Beyond readOnlyHint, it discloses the response shape (`format`/`count`/`rows`, never the SQL), the `is_new_record` flagging for contextual drilldowns, `_source_*` provenance columns plus a `sources` block with grain/traceability, the 403 and 422 meanings, and the fan-out stall risk. That is far more than the annotation carries.

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

Conciseness4/5

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

Front-loads the core action and the token source before routing, errors, and provenance. The all-caps citation block and the saved-row repetition are a bit heavy, but every paragraph carries information an agent needs to call this correctly.

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

Completeness5/5

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

Even with an output schema present, it explains the return envelope, provenance semantics, and truncation. Combined with auth, 403/422 handling and the downstream trace_data_sources handoff, nothing material about invoking this tool is left unanswered.

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

Parameters4/5

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

Schema coverage is 100%, so the baseline is 3; the description still adds real meaning by saying the URL or just its `t` is accepted and unwrapped, that `values`/`prompt` only apply to placeholder-twin tokens and that substituted `parameters` should be echoed, and that `max_rows` should be used on fan-out drilldowns.

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

Purpose5/5

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

Opens with a specific verb+resource: fetching the rows behind an event-web drilldown from a server-minted query token. It immediately separates itself from siblings by naming run_saved_query (saved-request tokens) and get_company_event_web (the source of signed_query_url).

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

Usage Guidelines5/5

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

Gives explicit routing rules: use this when the node carries `signed_query_url`, use `run_saved_query` for saved-row tokens, use `get_document_section` to open a cited speaker turn. It also states when-not behavior (403 = do not retry with an altered token; not-authenticated = sign in again).

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

get_stock_picksA
Read-only
Inspect

Fetch the latest LLM-selected stock picks (current rebalance holdings).

Returns `picks`, the holdings selected for the most recent rebalance date — the names
the backend's strategies are currently positioned in. Each pick is a record (ticker,
strategy, weight, rebalance date, and related fields; values are strings).
Synchronous and read-only — no auth required.

Each holding also carries a CURRENT-BASKET YTD view: `ytd_return` (that stock's
Jan 1 -> now price return) and the strategy-level weighted `basket_ytd_return`.
For how the strategies have ACTUALLY performed since inception (the realized track
record of the book as held), use `get_strategy_performance_summary` /
`get_strategy_track_record` instead.
NameRequiredDescriptionDefault
strategy_nameNoNarrow to one strategy (e.g. "momentum", "multi_signal", "fundamentals_smid", "value_quality"); omit for picks across all strategies.

Output Schema

NameRequiredDescription
picksYesCurrent holdings, each strategy at its own latest rebalance.

TDQS

A4.4/5.0
Behavior4/5

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

Annotations already declare readOnlyHint=true, but the description adds genuinely useful context: synchronous execution, no auth required, and the semantic distinction between current-basket YTD returns and realized track record. It stops short of rate limits or response-size behavior, keeping it just below the top tier.

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

Conciseness4/5

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

Front-loaded with the core purpose in the first sentence, followed by return semantics and routing guidance in logical order. It is slightly long, but nearly every sentence carries a distinct fact, so there is little waste.

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

Completeness5/5

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

An output schema exists, so return values needn't be enumerated, yet the description still clarifies the semantics of ytd_return and basket_ytd_return. Combined with the no-auth/read-only disclosure and sibling routing, nothing an agent needs to call this correctly is missing.

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

Parameters3/5

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

Schema description coverage is 100% and the single strategy_name parameter is already documented with examples in the schema; the description text adds no further parameter guidance. Baseline 3 is correct when the schema does the heavy lifting.

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

Purpose5/5

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

States a specific verb and resource ('Fetch the latest LLM-selected stock picks') and immediately clarifies scope with 'current rebalance holdings.' It also names the sibling tools it is not, so an agent can route between this and the performance tools without opening schemas.

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

Usage Guidelines5/5

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

Explicitly names the alternatives (get_strategy_performance_summary / get_strategy_track_record) and the condition that selects them ('how the strategies have ACTUALLY performed since inception'), contrasting with the current-basket YTD view this tool provides. Clear when/when-not guidance.

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

get_strategy_performance_summaryA
Read-only
Inspect

Leaderboard of since-inception performance vs the S&P 500 for all strategy books.

THE first stop for "how are the stock picks / strategies performing?". Returns
`strategies`, one headline row per book — the four strategies plus the pooled optimized book — sorted
by excess return over the S&P 500: {strategy_name, display_name, inception_date,
since_inception_return, sp_since_inception, excess_since_inception,
annualized_return, max_drawdown, sharpe, sp_sharpe, ytd_return, growth_strategy,
growth_sp, days_tracked}.

growth_strategy / growth_sp = what `amount` invested at inception would be worth in
the strategy vs the S&P 500.

Drill into one book's full daily series with `get_strategy_track_record`, or its
per-trade adds/drops with `get_strategy_swaps`.
NameRequiredDescriptionDefault
amountNoDollar amount assumed invested at inception; scales growth_strategy / growth_sp.

Output Schema

NameRequiredDescription
strategiesYesOne row per book with history, sorted by excess_since_inception (best first).

TDQS

A4.3/5.0
Behavior4/5

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

Annotations already declare readOnlyHint=true, so the safety profile is covered. The description adds real behavioral context beyond that: the sort key (excess return over the S&P 500), the one-row-per-book granularity, and the meaning of growth_strategy/growth_sp as the value of `amount` invested at inception.

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

Conciseness4/5

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

Front-loaded with the one-line purpose and the headline use case, then supporting detail. The inline enumeration of twelve return fields is somewhat long, but it is the tool's actual payload and each remaining sentence earns its place.

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

Completeness4/5

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

The tool returns a single flat table with one parameter and an output schema, so almost nothing is left to infer: purpose, sorting, row shape, and drill-down paths are all stated. The field list partially duplicates the output schema, which keeps it just short of a 5.

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

Parameters3/5

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

Schema description coverage is 100% and the `amount` param already documents 'dollar amount assumed invested at inception; scales growth_strategy / growth_sp'. The description's explanation of growth_strategy/growth_sp restates that same semantics, so it adds little beyond the schema — baseline 3.

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

Purpose5/5

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

Opens with a specific verb+resource+scope: a since-inception performance leaderboard versus the S&P 500 across all strategy books. It also explicitly distinguishes itself from siblings get_strategy_track_record and get_strategy_swaps, so an agent can route without opening a schema.

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

Usage Guidelines5/5

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

'THE first stop for "how are the stock picks / strategies performing?"' gives an explicit entry-point condition, and the closing sentences name the two alternatives with the conditions that select them (full daily series vs per-trade adds/drops).

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

get_strategy_swapsA
Read-only
Inspect

Per-trade swap ledger (adds/drops) vs the S&P 500 for ONE strategy book.

Answers "were the individual trades good?": across consecutive weekly rebalances,
an ADD is a name newly selected and a DROP a name removed. Each ADD is measured
from entry to its exit (or the latest close if still held); each DROP is the
FOREGONE return from its exit to the latest close. Both are compared to the S&P
500 over the same window.

Returns {strategy_name,
trades: [{symbol, action, event_date, window_end, still_open, name_return,
sp_return, excess}, ...] (newest first),
summary: {n_adds, n_drops, avg_add_excess_vs_sp, avg_drop_foregone_excess_vs_sp,
net_swap_value_add}}.

For the book-level return series use `get_strategy_track_record`.
NameRequiredDescriptionDefault
strategy_nameYesOne of the four strategies, or "pooled" (the pooled optimized book).

Output Schema

NameRequiredDescription
bookNo
tradesYesAdds and drops, newest first.
summaryYes
strategy_nameYes

TDQS

A4.3/5.0
Behavior4/5

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

Annotations only declare readOnlyHint=true, so the description carries the rest of the burden. It discloses the non-obvious measurement semantics (ADD measured entry-to-exit, DROP as foregone return exit-to-latest-close, both vs S&P over the same window), which materially affects interpretation. It omits any statement about data freshness/latency or weighting assumptions, keeping it short of a 5.

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

Conciseness4/5

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

Front-loads the one-line purpose before the methodology and routing note, and each sentence serves a distinct purpose. However, the verbose restatement of the return shape duplicates the existing output schema and could be trimmed.

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

Completeness4/5

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

Read-only annotations plus a full output schema mean the safety and return-value burdens are covered elsewhere. The description adds the computational definitions needed to interpret the numbers correctly, though freshness/coverage caveats for the latest close are unstated.

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

Parameters3/5

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

Schema coverage is 100% and the enum already enumerates the five valid values, so the schema does the heavy lifting. The description adds only the scoping emphasis "ONE strategy book" and does not explain why pooled behaves differently beyond what the enum description states; baseline 3 is appropriate.

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

Purpose5/5

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

States a specific artifact (per-trade swap ledger of adds/drops), the benchmark (S&P 500), and the scope (ONE strategy book). It also explicitly routes the agent elsewhere for the book-level time series, so it is distinguishable from siblings like get_strategy_track_record without reading either schema.

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

Usage Guidelines5/5

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

Gives the exact question this tool answers ("were the individual trades good?") and names the alternative tool plus the condition that selects it (book-level return series -> get_strategy_track_record). When-to-use and when-to-use-something-else are both explicit.

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

get_strategy_track_recordA
Read-only
Inspect

Since-inception daily track record vs the S&P 500 for ONE strategy book.

Returns the realized performance of the book as actually held — chartable series
plus headline stats: {strategy_name, book, inception_date, amount_invested,
summary: {since_inception_return, sp_since_inception, excess_since_inception,
annualized_return, max_drawdown, sharpe, sp_sharpe, ytd_return, growth_strategy,
growth_sp, days_tracked, last_date},
series: [{date, strategy_cumulative, benchmark_cumulative, excess, strategy_ytd,
growth_strategy, growth_sp, sharpe_expanding, holdings_count}, ...]}.

The Sharpe series is expanding-window and annualized (rf=0).

Backend returns 404 when the chosen book has no performance history yet. For the
cross-book leaderboard use `get_strategy_performance_summary`; for the current
holdings themselves use `get_stock_picks`.
NameRequiredDescriptionDefault
bookNo"llm" (LLM-curated selections) or "mechanical" (the raw quantitative screen, no LLM overlay).llm
amountNoDollar amount assumed invested at inception; scales the growth-of-$ fields.
strategy_nameYesOne of the four strategies, or "pooled" (the pooled optimized book).

Output Schema

NameRequiredDescription
bookNo
seriesYesDaily points from inception, ascending by date.
summaryYes
strategy_nameYes
inception_dateNo
amount_investedNo

TDQS

A4.3/5.0
Behavior4/5

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

Annotations only declare readOnlyHint=true, so the description carries the rest and does so well: the 404-on-missing-history behavior and the fact that the Sharpe series is expanding-window and annualized with rf=0 are non-obvious traits an agent could not infer. It does not cover pagination or rate limits, keeping 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.

Conciseness3/5

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

Purpose is front-loaded in one line and the routing/404 caveat is well placed, but the multi-line inline JSON dump of summary and series fields is redundant given an output schema exists — it consumes most of the description without adding selection value.

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

Completeness5/5

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

For a read-only reporting tool with full schema coverage and an output schema, the description supplies everything an agent needs: scope, the 404 edge case, metric semantics, and sibling routing. Nothing required for correct invocation is missing.

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

Parameters3/5

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

Schema description coverage is 100% and both enums (book, strategy_name) are fully documented in the schema, so the baseline of 3 applies. The description adds nothing about the 'amount' scaling or enum choices beyond what the schema already states.

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

Purpose5/5

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

States a specific verb+resource with explicit scope: 'Since-inception daily track record vs the S&P 500 for ONE strategy book.' The 'ONE' and the sibling routing immediately distinguish it from the cross-book summary and the holdings tool.

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

Usage Guidelines5/5

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

Explicitly names two alternatives and the condition that selects each: get_strategy_performance_summary for a cross-book leaderboard, get_stock_picks for current holdings. It also warns that the backend returns 404 when the chosen book has no history, which is a real usage constraint.

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

get_task_statusA
Read-only
Inspect

Poll the status of an async job (generate_report_for_stock, generate_research_report, explore_data_catalogue, explore_data_coverage, draft_custom_dataset, screen_stocks, ...).

Returns {"task_id": ..., "status": ..., "result": ...}. `status` is one of
PENDING, SUCCESS, FAILURE, RETRY. `result` is populated once status is SUCCESS.

A result over ~8 MB is not returned (it would take the server down): the call errors.
Re-run the job scoped narrower (tickers, dates, a row limit in the wording) instead of
polling it again.
NameRequiredDescriptionDefault
task_idYesThe task_id an async tool returned.

TDQS

A4.4/5.0
Behavior5/5

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

Although annotations already declare readOnlyHint, the description adds substantial behavioral context beyond them: the exact status values (PENDING, SUCCESS, FAILURE, RETRY), when result is populated, and the critical >8 MB failure mode with remediation advice. This is exactly the kind of operational detail an agent needs to avoid misusing the tool.

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

Conciseness4/5

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

The description is front-loaded with purpose, then return shape, then a critical caveat, which is a logical structure. The parenthetical list of async job names is somewhat lengthy, but it serves to identify applicable tools and every sentence provides actionable information.

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

Completeness5/5

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

There is no output schema, yet the description fully specifies the return object keys, status enum, result population condition, and the error behavior when results exceed ~8 MB. Nothing essential for correct invocation and result interpretation is missing.

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

Parameters3/5

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

The single task_id parameter has 100% schema description coverage ('The task_id an async tool returned'), so the schema already carries the parameter meaning. The description adds return-shape context but no additional parameter syntax or constraints, making baseline 3 appropriate.

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

Purpose5/5

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

The description opens with a specific verb and resource ('Poll the status of an async job') and enumerates the sibling async generators it applies to (generate_report_for_stock, screen_stocks, etc.). An agent can immediately distinguish it from those generator tools and from synchronous data retrieval tools.

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

Usage Guidelines4/5

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

It clearly indicates this is for polling an async job's status, and gives explicit handling guidance for the oversized-result case ('re-run the job scoped narrower ... instead of polling it again'). It doesn't explicitly say to call this only after an async tool returns a task_id, but that is strongly implied.

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

get_technical_indicator_dataA
Read-only
Inspect

Fetch a historical technical-indicator series for a symbol over a date range.

Call `list_options("technical_indicators")` first for valid indicator names. An
unknown symbol returns 404 and an unknown indicator returns 400 listing valid values.

Returns `rows`, one record per trading day ordered by date (columns beyond
date/symbol/close vary by indicator), or an empty list when there's no data in the range.

Requires auth (your MCP client attaches the OAuth bearer automatically).
NameRequiredDescriptionDefault
symbolYesCovered ticker, e.g. "AAPL" (see list_options("tickers")).
end_dateYesInclusive end date, YYYY-MM-DD.
indicatorYesIndicator name from list_options("technical_indicators"), e.g. "rsi".
start_dateYesInclusive start date, YYYY-MM-DD.

Output Schema

NameRequiredDescription
rowsYesOne row per trading day, ascending by date. Beyond date/symbol/close, columns are indicator-specific (e.g. rsi carries `rsi`, `overbought`, `oversold`). Empty means no data in the range.

TDQS

A4.4/5.0
Behavior5/5

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

Goes well beyond the readOnlyHint annotation: it discloses error semantics (404 for unknown symbol, 400 with valid values for unknown indicator), the return shape (one row per trading day, ordered by date, column set varying by indicator), and the empty-range behavior. Auth requirement is also stated explicitly.

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

Conciseness4/5

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

Front-loaded with the one-line purpose, then prerequisite, then error/return behavior, then auth – a logical order with no filler. The return-shape sentence is partially redundant given an output schema exists, which is the only real trim opportunity.

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

Completeness5/5

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

For a 4-required-param read tool with an output schema, the description still covers the prerequisite dependency, both failure modes, ordering, and auth, so an agent can call it correctly on the first attempt. Nothing material is missing.

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

Parameters3/5

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

Schema description coverage is 100%, including the list_options cross-references and the YYYY-MM-DD inclusive semantics, so the schema already carries parameter meaning. The description adds only the aggregate notion of a date range and per-row date ordering, which is not materially beyond the schema. Baseline 3 applies.

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

Purpose5/5

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

Names a specific verb ("Fetch"), a specific resource ("historical technical-indicator series"), and the scoping axes (symbol, date range). This is clearly distinguishable from siblings like get_company_snapshot or get_aftermarket_data without opening any schema.

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

Usage Guidelines4/5

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

Gives a concrete prerequisite: call list_options("technical_indicators") first to obtain valid indicator names, which prevents a predictable 400. It stops short of stating when-not-to-use this tool or naming an alternative series endpoint, so it is clear context without exclusions.

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

get_user_dataset_fileA
Read-only
Inspect

Read the rows of one of the user's saved datasets AS PUBLISHED — the file behind its latest_url, or a dated version. THIS is how a dashboard or an answer reads a dataset: the published file is reviewed at publish, point-in-time, and the same bytes every consumer gets. Never replay the dataset's saved query instead (run_saved_query re-runs live SQL: different rows, no review). A claude.ai artifact can't reach flexreportfinapi.com itself, so its page calls this tool.

Fetches the file server-side and parses json, jsonl, csv, tsv, xml, rss and atom files
into rows; rss/atom come back one row per item {title, link, description, pub_date,
guid, categories, company_symbols, research_url}. `research_url` (every format, rows
with a ticker) is the feed readers' research link: show it as part of the item, but
never fetch it or offer it as a way to download a report (get_latest_report is the
user's route). parquet and xlsx files can't be parsed here: give the user latest_url
instead.

Returns {dataset_name, format, version, published_at, row_count, rows}. Quote
`published_at` as the data's as-of. A file over the server's size cap errors instead
of returning — use the user's own reader on latest_url for that one.

Requires auth (your MCP client attaches the OAuth bearer automatically).
NameRequiredDescriptionDefault
versionNoOmit for the newest file. Else a build date (YYYY-MM-DD, its newest file) or a file's exact `published_at` from list_user_datasets.
dataset_nameYesThe dataset's `name`, as list_user_datasets shows it.

TDQS

A4.6/5.0
Behavior5/5

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

Beyond readOnlyHint, the description discloses parsing behavior for json/jsonl/csv/tsv/xml/rss/atom, rss/atom row shape, research_url handling, unsupported parquet/xlsx behavior, size-cap errors, return fields, and auth requirements. These are meaningful operational details an agent needs before invoking the tool.

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

Conciseness4/5

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

The description is front-loaded with its core purpose and alternative, then layers in format and error behavior. It is longer than a simple read tool needs, but the added detail is mostly functional rather than redundant.

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

Completeness5/5

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

There is no output schema, but the description compensates by listing the return fields and explaining special cases such as rss/atom rows, unsupported formats, and oversized files. Given the tool's complexity and read-only annotation, the definition is complete enough for correct agent use.

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

Parameters3/5

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

Schema coverage is 100%, so both parameters are already documented in the input schema. The description reinforces version behavior and dataset-name usage but does not add substantial syntax or format details beyond the schema.

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

Purpose5/5

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

The description states a specific verb and resource: reading the rows of a saved dataset as published, including latest_url or a dated version. It explicitly distinguishes this from run_saved_query, which re-runs live SQL and produces different rows without review.

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

Usage Guidelines5/5

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

It gives explicit routing guidance: use this tool to read the published file, never replay the saved query instead, give latest_url for parquet/xlsx, and use get_latest_report as the user's route for reports. The alternatives and exclusions are unambiguous.

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

get_user_templateA
Read-only
Inspect

Return the PDF template this user has saved, or a 404 if they have none.

Call it to answer "what format am I on?", BEFORE `save_user_template` when a
template may already exist (saving replaces it without history, so the user
should know what they are overwriting), and whenever you are about to compose
a PDF yourself from Flexreport data: lay the content out in this markup so the
document matches the user's format. A 404 there is the cue to offer drafting
one (`draft_user_template` -> previews -> `save_user_template`).

Returns {"template_type", "template_format", "anchor", "template", "created_at",
"updated_at"}. `template` is the sanitized markup as submitted (up to 200 KB) —
the compiled blueprint the backend renders from is not returned. To change the
format, draft again and pass the chosen variant to `update_user_template`
rather than editing this text blind.

An HTTP 404 means no template is saved — the standard layouts are in force. An
HTTP 503 means the backend's user_templates table has not been created yet, NOT
that nothing is saved; report that difference.

Requires auth (your MCP client attaches the OAuth bearer automatically).
Rate-limited 100/min.
NameRequiredDescriptionDefault

No parameters

Output Schema

NameRequiredDescription
anchorNoSection an add_on applies to; null for bespoke.
templateNoThe sanitized markup as submitted (up to 200 KB).
created_atNo
updated_atNo
template_typeNo"add_on" or "bespoke".
template_formatNo"html".

TDQS

A4.8/5.0
Behavior5/5

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

Annotations only declare readOnlyHint=true; the description adds substantial context beyond that: the 404-vs-503 semantic distinction, the auth requirement, the 100/min rate limit, the fact that save replaces without history, and that the returned template is sanitized markup capped at 200 KB while the compiled blueprint is withheld.

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

Conciseness4/5

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

Front-loads the return value and the primary usage trigger, then layers routing guidance and edge cases. Most sentences earn their place, though the enumerated Returns listing partially duplicates the output schema and could be trimmed slightly.

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

Completeness5/5

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

Given an output schema exists, return values needn't be spelled out, yet the description adds the useful nuance that the compiled blueprint is not returned and the 200 KB cap. Combined with auth, rate-limit, and error-code semantics, nothing an agent needs to call this correctly is missing.

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

Parameters4/5

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

The tool takes zero parameters, so there is nothing for the description to disambiguate; baseline 4 applies. No parameter detail is needed or missing.

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

Purpose5/5

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

States a specific verb+resource (returns the user's saved PDF template) and immediately distinguishes it from sibling tools like save_user_template, draft_user_template, and update_user_template. The one-line opener plus the 404 case makes the tool's identity unambiguous.

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

Usage Guidelines5/5

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

Explicitly names three triggering scenarios (answering 'what format am I on?', before save_user_template because saving replaces without history, and before composing a PDF so the markup matches). It also names alternatives (draft_user_template, update_user_template) and gives the conditional cue that a 404 is the signal to offer drafting.

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

is_market_openA
Read-only
Inspect

Check whether an exchange is currently open — a cheap routing helper.

Returns `is_open`, a single boolean. An unknown exchange code is an error.

Use it to route between the market-hours tools: when the market is CLOSED,
extended-hours data lives in `get_aftermarket_data`;
when OPEN, `detect_intraday_outlier_jumps` gives the live intraday read.
No auth required.

Unsure which exchange a ticker trades on? `get_company_snapshot` returns an
`exchange` field whose value can be passed here as-is.
NameRequiredDescriptionDefault
exchangeNoExchange code, e.g. "NYSE", "NASDAQ", "LSE" or an ISO MIC like "XNYS" (not full names; case does not matter). NYSE is right for US equities.NYSE

Output Schema

NameRequiredDescription
is_openYesTrue when the exchange is in its regular session right now.

TDQS

A4.7/5.0
Behavior4/5

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

Annotations already declare readOnlyHint=true, and the description adds real context beyond them: it is cheap, it requires no auth, it returns a single boolean, and an unknown exchange code raises an error rather than defaulting. It doesn't cover edge cases like holidays or stale data, so not a full 5, but the disclosure is well above the annotation baseline.

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

Conciseness5/5

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

Front-loads the purpose, then return value, then error behavior, then routing. Every sentence carries distinct information with no repetition of the schema beyond the error rule.

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

Completeness5/5

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

Output schema exists so return format needn't be re-explained, annotations cover safety, schema covers the parameter. The description fills the remaining gaps: cost model, auth, error semantics, and sibling routing. Nothing an agent needs to call this correctly is missing.

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

Parameters4/5

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

Schema coverage is already 100%, so the baseline is 3, but the description adds meaning the schema cannot: it points to `get_company_snapshot`'s `exchange` field as a compatible source for the value, which directly answers where the parameter comes from.

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

Purpose5/5

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

States a specific verb+resource ('check whether an exchange is currently open') and frames the tool's role as a 'cheap routing helper', which immediately distinguishes it from the market-data siblings. An agent knows exactly what it does without opening the schema.

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

Usage Guidelines5/5

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

Explicitly routes the agent: when CLOSED use `get_aftermarket_data`, when OPEN use `detect_intraday_outlier_jumps`, and when the exchange is unknown use `get_company_snapshot` to obtain the code. This is textbook when-to-use/when-to-use-something-else guidance naming concrete alternatives.

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

list_available_reportsA
Read-only
Inspect

List the symbols with a report plan SAVED on or after report_date (default: TODAY) — the real-time research get_latest_report can hand over right now, and whether each is current or has a newer event since.

A plan is saved the moment a material event publishes for a symbol (eps_update,
eps_release, 8k_release, financials_release, ir_publication, transcript_update) and
nightly for company_update. This tool is the INVENTORY of those plans — it does not
build or return a report. Returns `plans`, newest plan first:
  {"plans": [{"symbol": "NVDA", "event_type": "eps_release",
              "planned_at": "<iso>",   # when the plan was built
              "queued_at": "<iso>",    # the report inputs (the event) it was built from
              "fresh": true,           # see below
              "source": "redis"}, ...]}  # redis = built within 3 days, s3 = older durable copy
`fresh` applies the SAME rule get_latest_report (/get-cached-reports) applies:
  fresh: true  -> the report get_latest_report([ticker]) returns was written from
                  this plan and reflects the latest event. These are the names to pull.
  fresh: false -> a newer event landed after the plan. Its own report may still be in
                  progress (minutes); otherwise get_latest_report returns the older PDF
                  flagged stale, and generate_report_for_stock(ticker) writes a new
                  note in a few minutes. Say so before the user waits on it.
  not listed   -> no plan IN THE WINDOW. An empty result does not mean there is no
                  research — it means nothing was planned in the window. Widen
                  `report_date` before concluding a symbol has no plan; only then is
                  a first generate_report_for_stock note (a few minutes) the answer
                  (or `onboard_symbol` if the ticker is not covered).

TWO WAYS IN:
  1) Event-first — list_realtime_events(event_type=...) shows what just published; call
     this with event_types=[<that type>] to confirm which of those symbols have a plan
     and which are fresh, then get_latest_report(symbols=[...]) for the ones the user
     wants. (An event in the 12h cache with no plan here means the plan is still being
     built — check again in a minute.)
  2) Research-first — skip the events step: call this with no filter for every symbol
     planned TODAY, grouped by the event that triggered it. Plans stay listed for 7
     days, so pass report_date="YYYY-MM-DD" (e.g. 3 days back) when the user wants the
     running inventory rather than today's, or when today's list comes back thin.

PICKING THE MOST PERTINENT PLANS ("pull the most relevant reports today", "what's worth
reading right now"): the list is an inventory, not a ranking — a nightly company_update
plan sits next to a transcript that just landed. Rank by the EVENT, not by the plan:
  a) FILTER on what published — list_realtime_events for the material types
     (transcript_update, 8k_release, ir_publication, eps_release / eps_update,
     financials_release) and for the tape (biggest_mover, biggest_loser,
     biggest_gainer). A symbol on BOTH lists — a filing or call AND a move — is
     the strongest candidate.
  b) ANALYZE the content — read the event payloads (what the 8-K discloses, what the
     call said, what the deck guides to) and keep the ones that change something;
     get_event_ontology(event_type=...) says what each type means.
  c) CONFIRM with intraday price action — detect_intraday_outlier_jumps(tickers=[...])
     (and get_aftermarket_data after the close) shows whether the market treated it
     as significant; a print with no reaction ranks below a mover with a filing.
  d) MATCH to plans — call this tool with event_types=[<the types from a>] and keep
     the candidates that appear, preferring `fresh: true`; `queued_at` should be the
     event you just read.
  e) PULL the winners — get_latest_report(symbols=[...]) for the few that survived,
     not the whole list: every one comes back in seconds, flagged fresh or stale.
     Tell the user which is which, and offer generate_report_for_stock (a few minutes
     each) only if they want to wait.

Not `get_latest_report`: that PULLS the reports for named tickers (each one's latest
document). This tool answers "for which symbols could
I get a fresh report?" — by event, across the universe — before anything is pulled. Not
`generate_research_report`: that is the broad, topical, or multi-company writeup;
the plans listed here are single-symbol, event-anchored reports.

Synchronous, cheap (500/hour). Requires auth (your MCP client attaches the OAuth
bearer automatically).
NameRequiredDescriptionDefault
event_typesNoPlan-earning event types to list: eps_update, eps_release, 8k_release, financials_release, ir_publication, transcript_update, biggest_mover, biggest_gainer, biggest_loser, company_update. Any other type is a 422. Omit for every type.
report_dateNoLower bound on when the plan was built, YYYY-MM-DD (default today). Earlier dates widen the window and cost more; plans stay listed for 7 days. A malformed date is a 422.

Output Schema

NameRequiredDescription
plansYesSaved report plans in the window, newest first. Empty means nothing was planned in the window.

TDQS

A5/5.0
Behavior5/5

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

The description goes far beyond basic functionality, providing deep behavioral context. It defines the meaning of `fresh: true` vs `fresh: false` and describes the resulting behavior (e.g., recommending a wait or triggering `generate_report_for_stock`). It also mentions cost constraints (500/hour) and synchronous behavior.

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

Conciseness5/5

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

While extensive, the structure is highly efficient and appropriately sized for the tool's complexity. The use of bold headers (e.g., 'TWO WAYS IN') and categorized bullet points ensures maximum readability and zero-waste information delivery.

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

Completeness5/5

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

The definition is extremely complete for a high-complexity inventory tool. It details internal states (redis vs s3 source), usage patterns, and complex logic flows, leaving no critical decisions for the agent to make without the provided context.

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

Parameters5/5

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

The description significantly adds meaning beyond the schema by listing valid `event_types` and warning about the consequence of using invalid types (422 error). For `report_date`, it explains the trade-off between widening the window and incurring greater cost, as well as the 7-day retention policy.

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

Purpose5/5

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

The description is exceptionally clear, stating that this tool is an 'INVENTORY' of report plans, distinguishing it from 'build' or 'return' functions. It explicitly contrasts itself with sibling tools like get_latest_report (PULL) and generate_research_report (topical writeups). The purpose is stated upfront and refined throughout the document.

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

Usage Guidelines5/5

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

It provides clear, actionable usage guidelines in two distinct modes: 'Event-first' and 'Research-first.' Furthermore, it outlines a comprehensive five-step workflow for finding the most pertinent plans, linking this tool to several other sibling functions.

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

list_earnings_announcementsA
Read-only
Inspect

List scheduled earnings announcements within a date window, Flexreport names only.

Omit both dates for "who reports today". Results are restricted to Flexreport's
covered universe, so symbols outside coverage are dropped silently.

The optional filters are AND-ed together. `sector`, `industry`, and `market_cap` are
validated against fixed enums server-side; invalid values return a 422, so source them
from the tools named in their descriptions rather than guessing.

Returns `announcements` (empty list when nothing matches); values are strings and
"nan" means not available yet.

Requires auth (your MCP client attaches the OAuth bearer automatically).
NameRequiredDescriptionDefault
sectorNoSector values from list_options("sectors").
symbolsNoLimit to these tickers, e.g. ["AAPL","MSFT"].
end_dateNoWindow end, YYYY-MM-DD; defaults to today. Other formats return 422.
industryNoIndustry values from list_sub_industries([...]).
market_capNoMarket-cap buckets: "Small-cap", "Medium-cap", "Large-cap", "Mega-cap".
start_dateNoWindow start, YYYY-MM-DD; defaults to today. Other formats return 422.

Output Schema

NameRequiredDescription
announcementsYesScheduled reports in the window; empty when nothing matches.

TDQS

A4.7/5.0
Behavior5/5

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

Goes well beyond readOnlyHint by disclosing silent dropping of out-of-coverage symbols, server-side enum validation returning 422, the AND-combination of filters, empty-list-on-no-match, and that "nan" means not available. This is rich behavioral context an agent needs.

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

Conciseness5/5

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

Front-loads the core action, then layers date-window defaults, coverage caveats, and validation notes in short, scannable sentences. Every sentence carries distinct information.

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

Completeness5/5

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

For a 6-param read tool with an output schema, the description covers the missing pieces: date defaults, silent filtering, enum validation, auth, and the "nan" convention. Nothing needed to call it correctly is absent.

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

Parameters4/5

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

Schema coverage is 100%, so the baseline is 3, but the description adds real meaning: dates default to today, filters combine with AND, and invalid enum values trigger 422. That goes beyond what the schema properties state individually.

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

Purpose5/5

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

States a specific verb (List), resource (scheduled earnings announcements), and scope (within a date window, Flexreport names only). An agent can immediately distinguish this from predict_earnings_move or get_company_event_web.

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

Usage Guidelines4/5

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

Gives concrete when-to-use guidance ('Omit both dates for "who reports today"') and warns the filters are AND-ed. It also routes the agent to list_options/list_sub_industries for valid enum values. It stops short of naming an alternative tool for overlapping needs, but the context is clear.

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

list_optionsA
Read-only
Inspect

Enumerate the valid values for a parameter, straight from the backend.

Call this BEFORE guessing a parameter value. `kind` selects which catalog; three
kinds are scoped by a query parameter (`ticker`, or `q` / `cik`) and refuse the call
without it:

- "event_types"                  -> valid `event_type` for `list_realtime_events`
                                    (eps_update, company_update, biggest_mover, ...)
- "financial_items"              -> standardized EDGAR line items ("revenue",
                                    "operatingIncome") for a CUSTOM report's
                                    `financial_items` (`generate_report_for_stock`,
                                    or a scheduled "create-full-report" step)
- "financial_ratios"             -> EDGAR ratios ("grossProfitMargin") for that
                                    report's `ratios`
- "as_reported_items"            -> NEEDS `ticker`. The filer's OWN tagged XBRL
                                    concepts from its 10-Q / 10-K filings — {concept,
                                    statement_type, label, standard_concept,
                                    latest_period, facts} — for a custom report's
                                    `as_reported_financial_items`
- "revenue_segments"             -> NEEDS `ticker`. The filer's revenue segments as
                                    tagged in its filings — {product_label,
                                    product_member, product_axis, concept,
                                    latest_period, facts} — for a custom report's
                                    `revenue_segment` (pass the product_label).
                                    A name lookup only: never sum members across it
- "institutional_managers"       -> NEEDS `q` (name fragment, e.g. "berkshire")
                                    and/or `cik` (SEC CIK, any width). Managers as
                                    {cik, name, category, aum}, largest AUM first —
                                    pass the `cik` values as a custom report's
                                    `institutional_ownership`, never the names
- "sectors"                      -> valid `sector` filter values
- "institutional_investor_types" -> investor CATEGORIES (not managers) — the keys
                                    `screen_stocks(institutional_ownership=...)`
                                    filters on
- "countries"                    -> covered countries
- "fiscal_quarter"               -> the most recent fiscal quarter being reported
- "market_cap"                   -> valid `market_cap` buckets (Small-cap,
                                    Medium-cap, Large-cap, Mega-cap)
- "intraday_frequency"           -> supported intraday chart frequencies
- "technical_indicators"         -> indicator names accepted by
                                    `get_technical_indicator_data` (rsi, macd,
                                    sma_50, ...); an unknown indicator there
                                    returns a 400 listing this set
- "tickers"                      -> the full covered symbol universe as bare
                                    symbols — equities PLUS ~420 market indices
                                    as caret-prefixed symbols (^GSPC, ^VIX, ...).
                                    NOTE: thousands of names — a large payload
- "tickers_with_names"           -> {symbol, company_name} pairs for the company
                                    universe ONLY — market indices are NOT in
                                    this list, only in "tickers" (an even larger
                                    payload)
- "data_feeds"                   -> the datasets `download_dataset` accepts, as
                                    {dataset, description, returns ("rows" or
                                    "download_links"), date_field, event_types
                                    (valid filter, null = unsupported)}. Call this,
                                    not download_dataset (3 calls/week), to answer
                                    "what feeds are there"

Returns `options` (the values for that kind). Authoritative and never stale: it
reads the backend's live config, not a hardcoded list. Public — no auth required.
NameRequiredDescriptionDefault
qNoManager name fragment, e.g. "berkshire" (institutional_managers).
cikNoSEC CIK, any width, e.g. "1067983" (institutional_managers).
kindYesWhich catalog to list; see the kinds below.
tickerNoFiler ticker, e.g. "AAPL". Required for as_reported_items and revenue_segments.

Output Schema

NameRequiredDescription
kindYesThe catalog that was listed.
optionsYesThe valid values. Usually a list — bare strings for most kinds, records for event_types, tickers, tickers_with_names, as_reported_items, revenue_segments, institutional_managers and data_feeds. fiscal_quarter is a single quarter number.

TDQS

A4.5/5.0
Behavior3/5

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

Annotations only declare readOnlyHint=true, so the safety profile is covered. The description adds real value beyond them: public/no-auth, authoritative live-config reads (never stale), refusal behavior for under-scoped kinds, and payload-size warnings for `tickers`/`tickers_with_names`. Strong supplemental context for an annotated read-only tool.

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

Conciseness4/5

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

Front-loaded with the core directive before the long catalog list, and each bullet carries unique routing information. Slightly long with chatty asides ('NOTE: thousands of names', parenthetical hints), but almost none of it is removable for a 16-way multiplexer.

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

Completeness5/5

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

With an output schema present, return-shape detail is not owed, and the description still names the `options` payload. It covers scoping prerequisites, auth, sizing, and per-kind destinations, leaving nothing an agent needs to invoke it correctly.

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

Parameters5/5

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

Schema coverage is already 100% with an enum on `kind`, so the baseline would be 3, but the description goes far beyond it: each kind gets a meaning, example values, the structure of what comes back ({concept, statement_type, label, ...}), and which of `ticker`/`q`/`cik` it consumes. This is semantics the schema cannot express.

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

Purpose5/5

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

States a specific verb and resource ('Enumerate the valid values for a parameter, straight from the backend') and then enumerates every catalog the `kind` multiplexer can return. It is unmistakable against siblings like `get_technical_indicator_data` or `download_dataset`, which it explicitly routes to/from.

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

Usage Guidelines5/5

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

Opens with an imperative rule of thumb ('Call this BEFORE guessing a parameter value'), names the three kinds that require a scoping param (`ticker`, `q`/`cik`) and states they refuse the call without it, and explicitly redirects agents away from `download_dataset` (3 calls/week) to this tool for feed discovery.

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

list_realtime_eventsA
Read-only
Inspect

Pull live market events from the backend's Redis-backed cache (12h TTL).

===> Call `situate(...)` FIRST. With symbols, its web says which event types fired
for the company and when — a node older than the 12h cache returns [] here; its
payload lives in the card's `persisted_in` table instead. With no symbols, its
`order` says which types to read and its `cache.count` per type says which are
empty before you call (0 = cold cache, not "nothing happened").
`list_options("event_types")` enumerates the valid type strings. `event_type` is REQUIRED and
has no default on purpose: the type is the whole question, and a default
would answer a different one (eps_update is one slice of the earnings
family, not the family).

Then CONNECT THE DOTS: fetch the type asked about and follow the situation
across its related_events with follow-up calls narrowed by `tickers=[...]`
to the symbols just seen — e.g. eps_update -> check the intraday tape
(detect_intraday_outlier_jumps) -> 8k_release -> ir_publication. One hop
answers most questions; two covers a full earnings cycle. An unfiltered
follow-up call re-pulls the whole cache.

Returns `events`, a list of event objects (keys vary by type). An EMPTY list
means the cache is cold for that event type (12h TTL), not that nothing happened.

A material event here (eps_update, eps_release, 8k_release, financials_release,
ir_publication, transcript_update) means the symbol's report is being written from
it (within minutes; the `report_plan` topic announces each one):
`list_available_reports(event_types=[<this type>])` says which are already current,
and get_latest_report returns them in seconds.

Requires auth (your MCP client attaches the OAuth bearer automatically).
NameRequiredDescriptionDefault
sectorNoSector values from list_options("sectors").
tickersNoNarrow to these tickers; always set on follow-up calls (an unfiltered call re-pulls the whole cache).
industryNoIndustry values from list_sub_industries([...]).
event_typeYesREQUIRED realtime event type from list_options("event_types"), e.g. "eps_update", "8k_release", "ir_publication".
market_capNoMarket-cap buckets, e.g. ["Large-cap", "Mega-cap"].

Output Schema

NameRequiredDescription
eventsYesCached events of the requested type. Empty means the 12h cache is cold for that type, not that nothing happened.

TDQS

A4.8/5.0
Behavior5/5

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

Annotations only declare readOnlyHint, so the description adds the substantive behavior: 12h TTL cache, empty list means cold cache rather than no event, cache.count semantics from situate, coalescing onto report writes, and OAuth bearer requirement. These are exactly the traits an agent would otherwise misread as data absence.

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

Conciseness4/5

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

Well front-loaded with the core action first, then the situate prerequisite, then the follow-up pattern. It is longer than needed for a list tool and the arrow-chain examples and repeated cache-TTL mentions add bulk, but almost every sentence carries actionable information.

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

Completeness5/5

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

For a 5-parameter filtered read with an output schema and only a readOnlyHint annotation, the description covers prerequisites, valid type strings, follow-up narrowing, empty-result interpretation, and where stale data lives. Nothing material is missing before an agent fires the call.

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

Parameters4/5

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

Schema coverage is already 100%, yet the description adds genuine meaning: why event_type has no default (a default would answer a different question) and the eps_update-is-one-slice-of-earnings nuance. It also restates the enumerating source (list_options("event_types")) and the tickers narrowing rule, though sector/industry/market_cap are left entirely to the schema.

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

Purpose5/5

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

States a specific verb and resource ('Pull live market events from the backend's Redis-backed cache') plus the scoping constraint (12h TTL). An agent can tell it retrieves cached realtime event records by type, distinct from report or streaming siblings.

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

Usage Guidelines5/5

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

Explicit ordering rule ('Call situate(...) FIRST') with the reason for each branch (symbols vs no symbols), the alternative when data is older than the cache ('payload lives in the card's persisted_in table'), the required-first-hop pattern, and the downstream alternatives list_available_reports/get_latest_report. When-not guidance is also given ('an unfiltered follow-up call re-pulls the whole cache').

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

list_saved_queriesA
Read-only
Inspect

List the data requests you have saved with save_user_query, newest first.

The entry point to the replay flow: check HERE FIRST, before running
`explore_data_catalogue` for any exploration question — a saved row that matches
the question, exactly OR in shape, means the answer is seconds away via
`run_saved_query` instead of a ~1-5 minute planning job.

Returns {"count": N, "saved_queries": [{"id", "user_request", "queries",
"query_placeholder", "metadata", "created_at", "updated_at"}, ...]}, the calling
user's rows only.

MATCH ON `metadata`, NOT ON THE LABEL. `metadata` describes what the row actually
asks. It is authored after the save and is null until the placeholder task has run
(roughly 10 s to 5 minutes, depending on the size of the queries):
  `summary`        one line with the slots in braces, e.g. "13F positions of
                   {managers} in {symbol}, {date_prior} vs {date_latest}: shares,
                   value, weight, QoQ change"
  `parameters`     [{name, type, description, example}] — the slot schema, one
                   vocabulary across every query in the row. These names are the
                   keys of `run_saved_query(values=...)`.
  `entities`       {slot: resolved example} — what the row was saved for
  `datasets`       the public dataset names the row reads (the same names
                   `explore_data_coverage` reports)
  `topics`         classifier topics for the saved request
  `result_schema`  [{index, summary, slots, columns}] — per query, what its result
                   set holds
  `stale`          true when a query no longer plans (checked nightly)
  `run_count`, `last_run_at`
Match the user's question against `summary` + `parameters` + `datasets`, NOT
against `user_request` — that is only the label the user typed while saving. SKIP
any row marked `stale: true`. For a row whose `metadata` has not landed yet, fall
back to the label and replay `queries` exactly.

Having picked a row:
  - EXACT repeat (same entities, same window) -> `run_saved_query(query_tokens=
    <the row's `queries`>)`; it replays as saved.
  - SAME SHAPE, other values (saved "the past 8 quarters of sales for TGT"; asked
    "KSS's last 8 quarters of sales") -> `run_saved_query(query_tokens=<the row's
    `query_placeholder`>, values={...})`, the values keyed by
    `metadata.parameters`; add a `prompt` only for slots you cannot type.
  - Pick WHICH of the row's queries to run from `result_schema`: the ones that
    answer the question, leaving the `_constituents` companion out — it is a
    7,000-35,000 row payload. Pass `max_rows` (e.g. 500) whatever you run.
  - TELL THE USER which saved row is being reused (its `summary` and `entities`)
    and which values were substituted (the replay's `parameters`).
A row that is merely close in TOPIC but asks a different thing is a DIFFERENT
question; confirm with the user before replaying it.

The other fields:
  - `queries` is the list of opaque query tokens for an exact replay.
  - `query_placeholder` is the list of placeholder-twin tokens, index-aligned with
    `queries`, or null while the placeholder task has not run yet (replay
    `queries` exactly or list again shortly). Never show a token of either kind to
    the user: it is ciphertext, not content, and says nothing about what the query
    does.
  - `user_request` is the user's own label — worth quoting when you say which row
    you reused, but not what to decide on.
  - `id` is what `delete_saved_query` takes.

Duplicate `user_request` values are possible — saving does not upsert.

An HTTP 503 here means the backend's saved-queries table has not been created yet,
NOT that the user has nothing saved — report that difference rather than an empty
list.

Requires auth (your MCP client attaches the OAuth bearer automatically).
Rate-limited 100/min.
NameRequiredDescriptionDefault
limitNoMax rows to return, newest first.

Output Schema

NameRequiredDescription
countYes
saved_queriesYesThe calling user's saved rows, newest first.

TDQS

A4.6/5.0
Behavior5/5

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

Annotations only declare readOnlyHint=true, but the description adds substantial behavioral context: the full return shape, that rows are the calling user's only, the metadata-not-label matching rule, null-until-landed metadata with a 10s-5min window, stale-row skip logic, HTTP 503 meaning, auth requirement and a 100/min rate limit. This goes well beyond what the annotation provides.

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

Conciseness4/5

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

Well front-loaded (the 'check HERE FIRST' directive comes before all detail) and organized with bulleted sections. However, it is very long and devotes substantial space to the downstream run_saved_query replay procedure and metadata field anatomy, which strains the size appropriate for a one-parameter list tool.

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

Completeness5/5

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

Despite an output schema existing, the description fully describes the return payload and every metadata field, plus edge cases (503, stale rows, null placeholder, duplicate user_request). Nothing an agent needs to act on the results is missing.

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

Parameters3/5

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

Schema description coverage is 100% for the single `limit` parameter, so the schema already documents it fully. The description adds no additional semantics for `limit` (its 'max_rows' guidance applies to run_saved_query, not this tool). Baseline 3 is appropriate when the schema carries the parameter semantics.

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

Purpose5/5

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

Opens with a specific verb+resource: 'List the data requests you have saved with `save_user_query`, newest first,' and explicitly names the sibling tool that created the rows. An agent can immediately distinguish this from explore_data_catalogue or run_saved_query.

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

Usage Guidelines5/5

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

Unusually explicit routing: it tells the agent to 'check HERE FIRST, before running explore_data_catalogue,' and contrasts the saved-query replay (seconds) against a '~1-5 minute planning job.' It also names exact-repeat vs same-shape alternatives and their conditions, plus the caveat to confirm with the user when a row is merely topic-close.

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

list_scheduled_tasksA
Read-only
Inspect

List the caller's scheduled tasks (cron jobs created via schedule_task).

Returns `tasks`, only the authenticated user's jobs, each as
{"name", "active", "schedule" (cron string), "args", "kwargs", "enabled",
 "last_run_at", "total_run_count"}. Workflow schedules are named
"wf:{email}:{workflow-name}"; dataset builds (save_user_dataset) are
"ds:{email}:{dataset-name}". Use `name` as the key to remove a job with
`delete_scheduled_task`.
NameRequiredDescriptionDefault

No parameters

Output Schema

NameRequiredDescription
tasksYesThe caller's scheduled tasks; empty when there are none.

TDQS

A4.4/5.0
Behavior4/5

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

readOnlyHint=true already covers the safety profile, so the bar is lower, and the description still adds real context: results are scoped to 'only the authenticated user's jobs' (an auth/isolation constraint not in the annotations) and it documents the naming conventions for workflow vs dataset schedules. It doesn't mention ordering, limits, or pagination, keeping it short of a 5.

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

Conciseness4/5

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

Front-loaded with purpose, then scope, then output shape, then naming, then cross-tool routing — a sensible order. Slightly padded by enumerating the returned fields, which the output schema already provides, but the remainder earns its place.

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

Completeness5/5

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

With an output schema present, the description needn't re-explain return values, yet it supplies the non-obvious parts — user scoping and the wf:/ds: naming scheme that an agent needs to interpret or act on the results. Nothing required to invoke this zero-parameter read correctly is missing.

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

Parameters4/5

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

Zero input parameters, so the baseline is 4; there is nothing for the description to disambiguate. The naming-convention detail (wf:/ds: prefixes) is useful but relates to interpreting results, not to inputs.

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

Purpose5/5

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

States a specific verb+resource ('List the caller's scheduled tasks') and immediately ties it to the origin tool `schedule_task`, which disambiguates it from the many other list_* siblings. An agent can tell this apart from delete_scheduled_task and get_task_status without opening a schema.

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

Usage Guidelines4/5

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

Explicitly routes the agent onward: 'Use `name` as the key to remove a job with `delete_scheduled_task`', giving a concrete when-to-use-this-vs-sibling rule. It doesn't state exclusions or preconditions for the listing itself, but the listing scope is unambiguous.

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

list_sub_industriesA
Read-only
Inspect

List the sub-industries within one or more sectors.

Returns `industries`, the distinct industries used to narrow
`list_realtime_events(industry=[...])`.
NameRequiredDescriptionDefault
sectorsYesSector values from list_options("sectors"), e.g. ["Technology"].

Output Schema

NameRequiredDescription
industriesYesDistinct industries within the requested sectors, for list_realtime_events(industry=[...]).

TDQS

A3.5/5.0
Behavior3/5

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

The readOnlyHint annotation already covers the safety profile, and the description's added detail (result key name, distinctness, downstream linkage) is largely duplicated by the output schema. No edge-case or failure behavior (e.g., unknown sector values) is disclosed, but the annotation bar makes this adequate.

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

Conciseness4/5

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

Two short sentences, front-loaded with the action and following with the return linkage; nothing is padded. The return-detail sentence is partly redundant with the output schema, which keeps it from being flawless.

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

Completeness4/5

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

For a simple one-parameter read tool with an output schema and a readOnlyHint, the definition covers what the tool does and how its output chains into list_realtime_events. Only the upstream call ordering (list_options first) is left implicit.

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

Parameters3/5

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

With a single parameter at 100% schema description coverage, the schema already explains that sectors are drawn from list_options("sectors"). The description only echoes the "one or more sectors" cardinality and adds no syntax or format detail beyond the schema, so baseline 3 applies.

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

Purpose4/5

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

States a specific verb and resource (list sub-industries) scoped to "one or more sectors", so the agent knows exactly what comes back. It does not explicitly contrast with the closest sibling, list_options, which is where sectors themselves come from, so it stops short of full sibling differentiation.

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

Usage Guidelines3/5

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

It gives the downstream purpose of the result (narrowing list_realtime_events(industry=[...])) and the schema notes sectors come from list_options("sectors"), which implies the workflow. However there is no explicit when/when-not guidance or statement of which tool to call first versus this one.

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

list_user_datasetsA
Read-only
Inspect

List the user's own saved datasets (from save_user_dataset) with their build status and download links to the published files.

New items are reviewed before publishing, up to 40 per build (on a large first build
the rest publish as queried and are reviewed on later builds; a failed review publishes
the items as queried). The review can correct text from stored sources, add a one-line
caveat, or hold an item out; links, GUIDs, tickers and dates are never changed. So a
file can hold fewer rows than the query returns, and an item already published
unreviewed can later be corrected or withdrawn.

Returns {count, datasets: [{name, user_request, output_schema, file_format,
date_mode, cron, task_name, last_built_at, last_build_status, last_row_count,
latest_url, files: [{run_date, published_at, url}]}]}. `latest_url` is the fixed,
sign-in-free link to the newest file — the one to give for "what's my feed URL?" (an rss
file's items carry <company:symbol> and <category> for syndication partners). To read a
file's rows (a dashboard, a summary), call get_user_dataset_file. Rows with a symbol or
ticker carry `research_url`, the feed readers' sign-in-free research link — never call
it or offer it as a download (the user's own reports come from get_latest_report).
`last_build_status` is written,
unchanged (same rows as the last file, so no new file), empty, too_large (over 500k
rows), schema_drift (columns no longer match the saved schema — re-draft and re-save
under the same name) or failed.

Each file `url` is a presigned link valid 12 HOURS: hand it to the user as a link; never
fetch the file into the chat or store the link. Not rate-limited beyond the plan
quota. Requires auth (your MCP client attaches the OAuth bearer automatically).
NameRequiredDescriptionDefault
end_dateNoLast build date (YYYY-MM-DD, ET) of the files to list.
start_dateNoFirst build date (YYYY-MM-DD, ET) of the files to list.
latest_onlyNoTrue (default) = newest file per build date; false = every version.
dataset_nameNoOne dataset by name; all of the user's when omitted.

Output Schema

NameRequiredDescription
countYes
datasetsYes

TDQS

A4.6/5.0
Behavior5/5

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

Beyond readOnlyHint=true, the description discloses substantial operational behavior: the review/publishing caveats, what each build status means, that file URLs are presigned and valid for 12 hours, that links must not be fetched into chat, auth requirements, and plan-quota-only rate limiting. This is exactly the kind of context annotations cannot carry.

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

Conciseness4/5

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

The purpose is front-loaded in the first sentence, and the remaining paragraphs are organized around output shape, link handling, and build statuses. It is longer than strictly necessary for a list tool and repeats the return shape even though an output schema exists, but the extra detail is largely substantive rather than rambling.

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

Completeness5/5

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

Given the tool's complexity, 4 fully documented parameters, existing annotations, output schema, and the unusual publishing-review behavior, the description covers everything an agent needs to call it correctly. No meaningful gaps remain for selection or invocation.

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

Parameters3/5

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

Schema description coverage is 100%, so start_date, end_date, latest_only, and dataset_name are already fully explained in the input schema. The description does not add syntax or format details for these parameters beyond the schema, so the high-coverage baseline of 3 applies.

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

Purpose5/5

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

The description states a specific verb and resource: 'List the user's own saved datasets (from save_user_dataset) with their build status and download links.' It explicitly ties the tool to save_user_dataset and later names get_user_dataset_file as the tool for reading rows, so an agent can distinguish it from adjacent dataset siblings without opening schemas.

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

Usage Guidelines5/5

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

It provides explicit routing: use get_user_dataset_file to read a file's rows, give latest_url for 'what's my feed URL?', never call or offer research_url as a download, and use get_latest_report for the user's own reports. These are concrete when-to-use and when-not-to-use alternatives, not just implied context.

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

manage_billingAInspect

Get a link to the Stripe billing portal where the user can view invoices, update their card, or cancel their subscription. Use when the user asks to manage, change, or cancel their billing/subscription.

NameRequiredDescriptionDefault

No parameters

Output Schema

NameRequiredDescription
urlNoThe browser link; null when it could not be generated.
usageNoCurrent plan usage (upgrade_plan only, when known).
messageYesReady-to-show markdown for the user, including the link when there is one.

TDQS

A4.1/5.0
Behavior4/5

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

Annotations declare readOnlyHint=false and destructiveHint=false, which is somewhat confusing for a tool that only returns a link; the description resolves this by explaining the actual mutating actions (updating card, cancelling) happen in the external Stripe portal, not here. That is meaningful added context beyond the annotations. It doesn't cover auth requirements or rate limits, hence not 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.

Conciseness5/5

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

Two sentences, zero waste. The 'what it does' sentence comes first, followed by the routing condition. Every clause earns its place.

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

Completeness4/5

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

An output schema exists, so the return value (the portal link) need not be re-explained, and the description correctly focuses on purpose and routing. The only gap is the missing differentiation from upgrade_plan for plan-change requests.

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

Parameters4/5

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

The tool takes zero parameters, so there is nothing for the description to clarify; baseline is 4. No spurious argument discussion is present, which is appropriate.

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

Purpose4/5

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

Specific verb+resource: 'Get a link to the Stripe billing portal', with concrete scope (view invoices, update card, cancel subscription). An agent immediately knows this returns a URL, not a modification. It does not distinguish itself from the sibling upgrade_plan, which is the one adjacent tool a user query could plausibly route to.

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

Usage Guidelines4/5

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

Explicit trigger condition: 'Use when the user asks to manage, change, or cancel their billing/subscription.' This is clear routing guidance, but it gives no exclusion against upgrade_plan, which is the obvious alternative for plan-change requests.

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

onboard_symbolAInspect

Request onboarding of a NOT-yet-covered ticker (mutating, authenticated).

Kicks off a 30-60 min backend workflow and emails the authenticated user when
the first report is ready. Rate-limited to 5/hour per user. Use only when a
symbol is missing from `list_options("tickers")` / returns no data elsewhere.


Returns {"task_id": ..., "status": "PENDING"}.
NameRequiredDescriptionDefault
symbolYesThe ticker to onboard, e.g. "IMCR".

Output Schema

NameRequiredDescription
statusYesPENDING on submission.
task_idYesOnboarding request id; the user is emailed when the first report is ready.

TDQS

A4.7/5.0
Behavior5/5

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

Goes well beyond the annotations (readOnlyHint=false, destructiveHint=false): it discloses mutating+authenticated nature, a 30-60 min asynchronous backend workflow, email notification on completion, a 5/hour per-user rate limit, and the initial return shape. These are exactly the operational facts an agent needs before invoking an async, rate-limited mutation.

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

Conciseness5/5

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

Four tight sentences, front-loaded with the mutating/authenticated warning, then timing, rate limit, usage condition, and return. Every sentence earns its place with no redundancy.

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

Completeness5/5

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

Despite an output schema existing, the description still states the initial return ({"task_id": ..., "status": "PENDING"}) and covers the async lifecycle, rate limits, and prerequisites. Nothing an agent needs to call it correctly is missing.

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

Parameters3/5

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

Schema coverage is 100% and the single required "symbol" parameter is fully documented (including an "IMCR" example) in the schema itself. The description adds no syntax or format detail beyond what the schema already provides, so the baseline 3 applies.

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

Purpose5/5

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

States a specific verb+resource ("Request onboarding of a NOT-yet-covered ticker") and scopes it to symbols not yet covered, immediately distinguishing it from report/listing siblings like list_options and generate_report_for_stock.

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

Usage Guidelines5/5

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

Explicitly names the trigger condition ("Use only when a symbol is missing from list_options("tickers") / returns no data elsewhere") and routes the agent to the alternative check first. This is a textbook when-to-use rule.

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

optimize_portfolioAInspect

Build a risk-optimized portfolio from a non-empty list of tickers.

Scores `symbols` with the multi-signal scorer, LLM-curates, and risk-optimizes.

Rate-limited to 10/hour
per user server-side.

Asynchronous: returns {"task_id": ..., "status": "PENDING", "supported": [...],
"not_supported": [...]}. Poll `task_id` with `get_task_status` until SUCCESS.
NameRequiredDescriptionDefault
symbolsYesTickers to score and optimize, e.g. ["AAPL", "MSFT", "JNJ"].
deliveryNoResult channel: "dashboard" (returned via get_task_status) or "email".dashboard
risk_toleranceNoWhich risk profile is marked as recommended; omit for the optimizer default.

Output Schema

NameRequiredDescription
statusYesPENDING on submission.
task_idYesPoll with get_task_status until SUCCESS.
supportedNoTickers that will be scored and optimized.
not_supportedNoTickers outside Flexreport coverage, ignored.

TDQS

A3.7/5.0
Behavior4/5

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

Annotations are thin (readOnlyHint=false, destructiveHint=false), and the description carries the load well: it discloses a server-side rate limit of 10/hour per user and that execution is asynchronous with a PENDING task handle. It does not explain what happens to in-flight tasks or whether the submission itself is billable, but the behavioral picture is substantially better than the annotations alone.

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

Conciseness4/5

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

Front-loaded with the core purpose and no filler sentences; the rate limit and async contract are stated compactly. The hard line breaks mid-sentence ('Rate-limited to 10/hour / per user server-side') hurt readability slightly but nothing is wasted.

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

Completeness4/5

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

Given an output schema exists, the description need not explain return fields, yet it usefully describes the async PENDING payload and the supported/not_supported lists up front. Combined with rate-limit and polling instructions, the only meaningful omission is any differentiation from `optimize_portfolio_default`.

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

Parameters3/5

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

Schema description coverage is 100%, so all three parameters (symbols, delivery, risk_tolerance) are already documented in the schema, including enum choices and default. The description only echoes the scoring/optimization role of `symbols` and adds no syntax or constraint detail beyond minItems that the schema already states.

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

Purpose4/5

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

States a specific verb+resource ('Build a risk-optimized portfolio') and enumerates the internal pipeline (multi-signal scoring, LLM curation, risk optimization). It distinguishes itself from most siblings, but never contrasts with the obvious sibling `optimize_portfolio_default`, which an agent could easily confuse it with.

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

Usage Guidelines3/5

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

Gives real operational guidance for the async flow ('Poll `task_id` with `get_task_status` until SUCCESS') and implies the input requirement ('non-empty list of tickers'). It does not say when to pick this over `optimize_portfolio_default`, so sibling selection is left to inference.

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

optimize_portfolio_defaultA
Read-only
Inspect

Build a risk-optimized portfolio from a list of tickers — fast, synchronous, no LLM.

THE DEFAULT optimizer route. Returns the result directly and almost instantly
(no task id, no polling). Reach for `optimize_portfolio` instead only when you
explicitly want the slower LLM-curated variant layered on top of the optimizers.

Symbols are validated against the covered universe; unsupported tickers come back
in `missing`, and in-universe tickers with too little price history come back in
`dropped`.

No auth required (public endpoint).

Synchronous. Returns:
  {"status": "OK",  # or "SKIPPED"/"EMPTY" when too few usable symbols remain
   "holdings": [{"symbol", "source_strategies", "summary", "conviction_level"}, ...],
   "optimizer_results": {"mvo": {...weights+diagnostics}, "hrp": {...}, "mcvar": {...}},
   "risk_profiles": {"profiles": [...], "recommended": "..."},  # present only when status == "OK"
   "dropped": [...],   # in-universe but insufficient price history
   "missing": [...]}   # not in the covered universe
NameRequiredDescriptionDefault
symbolsYesTickers to optimize, e.g. ["AAPL", "MSFT", "JNJ"] (see list_options("tickers")).
risk_toleranceNoWhich risk profile is marked as recommended; omit for the optimizer default.

Output Schema

NameRequiredDescription
reasonNoWhy the run was SKIPPED.
statusYesOK, or SKIPPED / EMPTY when too few usable symbols remain.
droppedNoIn-universe tickers with insufficient price history.
missingNoTickers not in the covered universe.
holdingsNoThe usable holdings that were optimized.
max_weightNoPer-name weight cap the optimizers applied.
risk_profilesNo{profiles: [{risk_profile, method, description, diagnostics, allocation}], recommended}; present only when status is OK.
optimizer_resultsNoPer optimizer (mvo, hrp, mcvar): weights plus diagnostics. Empty unless OK.
lookback_trading_daysNo

TDQS

A4.9/5.0
Behavior5/5

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

Annotations only supply readOnlyHint, and the description adds substantially beyond that: synchronous execution with no task id or polling, no auth required on a public endpoint, symbol validation against a covered universe, and status semantics (OK/SKIPPED/EMPTY) for degenerate inputs. This is real behavioral disclosure the structured fields do not carry.

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

Conciseness5/5

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

Front-loaded with the purpose and the sibling disambiguation in the first two sentences, followed by only the constraints an agent needs. The layout is scannable and no sentence is filler.

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

Completeness5/5

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

For a synchronous, 2-parameter public endpoint, the description covers routing, execution model, auth, and error semantics; with an output schema present it need not have restated the return shape, so nothing required is missing.

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

Parameters4/5

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

Schema description coverage is 100%, so the baseline is 3, but the description adds meaning the schema lacks: symbols are validated against a covered universe, with unsupported tickers surfacing in `missing` and low-history tickers in `dropped`. It says nothing extra about `risk_tolerance`, which the schema already documents well.

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

Purpose5/5

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

"Build a risk-optimized portfolio from a list of tickers" states a specific verb and resource, and the definition explicitly positions itself as "THE DEFAULT optimizer route" versus the `optimize_portfolio` sibling. An agent can distinguish the two tools without opening either schema.

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

Usage Guidelines5/5

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

It names the alternative explicitly ("Reach for `optimize_portfolio` instead only when you explicitly want the slower LLM-curated variant") and gives the exact condition that selects it. When-to-use and when-not-to-use are both spelled out.

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

predict_earnings_moveA
Read-only
Inspect
Predict how big a stock's move will be when it next reports earnings. For each symbol, returns
its next earnings date (today or later) and `expected_move`: the expected absolute move of the
reaction session as a log return (0.05 is about 5%; size only, no direction). For context,
`hist_move_mean` / `hist_move_n` give the stock's own average reaction over its past reports and
how many reports that covers. Symbols with no scheduled report come back in
`no_upcoming_earnings_symbols`; uncovered tickers in `not_supported_symbols`.
NameRequiredDescriptionDefault
symbolsYesTickers to score, e.g. ["AAPL", "NVDA"].

Output Schema

NameRequiredDescription
predictionsYesOne prediction per covered symbol with a scheduled report.
not_supported_symbolsYesRequested tickers outside Flexreport coverage.
no_upcoming_earnings_symbolsYesCovered tickers with no scheduled report.

TDQS

A4.2/5.0
Behavior4/5

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

Annotations only declare readOnlyHint, so the description carries most of the burden and does well: it explains the returned fields (`expected_move` units and semantics, `hist_move_mean`/`hist_move_n` scope) and the two partition buckets for no-data symbols. It doesn't mention latency, rate limits, or coverage limits on historical depth, keeping it short of a 5.

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

Conciseness4/5

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

Front-loaded with the core purpose, then scoped precisely around output fields and edge cases in a tight block. Slightly dense with backticked field names, but every sentence carries information and there is no padding.

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

Completeness5/5

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

An output schema exists, so return-value explanation is optional, yet the description goes further by defining units, direction semantics, historical context fields, and edge-case buckets — everything an agent needs to interpret and route results correctly. No gaps remain for a single-parameter read tool.

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

Parameters4/5

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

Only one parameter with 100% schema description coverage, so the baseline is already high. The description adds value by clarifying that each symbol returns per-symbol results and what happens to symbols without scheduled earnings, which the schema alone does not convey.

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

Purpose5/5

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

States a specific verb (predict) and resource (post-earnings move) with the exact metric being returned (`expected_move` as a log return, size only, no direction). No sibling tool does anything similar — the closest neighbors are earnings announcements listing (`list_earnings_announcements`) and generic stock data, and this is clearly distinguishable from both.

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

Usage Guidelines3/5

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

Usage context is implied by naming the input (symbols) and outputs, but there is no explicit 'use this when vs. list_earnings_announcements' guidance or exclusions. The description covers behavior thoroughly but never tells the agent when this is the right tool over alternatives.

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

register_event_subscriptionAInspect

Save a realtime-event filter server-side and get a subscription_id whose stream resumes where the last read stopped (POST /register-subscription).

For a live-streaming ask, the default is a dashboard (in Claude, an Artifact) that
loops stream_events itself — see stream_events. Register a subscription when the
user wants a saved filter that resumes across sessions.

Returns {subscription_id, filter, cursor, created_at}. `filter` echoes the
normalised filter (topics lower-cased, symbols upper-cased, duplicates dropped);
`cursor` is the position the subscription starts from — only events published
AFTER registration are ever delivered. KEEP the subscription_id: there is no list
endpoint yet, and it is what stream_event_subscription and
delete_event_subscription take.

`topics` / `symbols` as in stream_events: event types and families from
list_options("event_types") plus the stream-only `report_plan` topic; an empty
body listens to everything. Same vocabulary rules — `report_plan` for "stream
full research as it is minted", a family for "everything earnings-related",
symbols for a watchlist. 30 registrations/hour per user; delete what you no
longer need. The stream keeps ~2,000 entries (several days at current volume): a
subscription idle longer than that resumes at the oldest entry and may have
missed events.

Requires auth (your MCP client attaches the OAuth bearer automatically).
NameRequiredDescriptionDefault
topicsNoEvent types or families from list_options("event_types") (e.g. "eps_update", "earnings"), plus the stream-only "report_plan"; case-insensitive. Omit for every topic.
symbolsNoTickers to listen for (13f_* filer events carry the filer's CIK in the symbol slot). Omit for every symbol.

Output Schema

NameRequiredDescription
cursorNoStream position the subscription starts from (events after registration).
filterYesThe normalised filter that was saved.
created_atNo
subscription_idYesKeep it: stream_event_subscription and delete_event_subscription take it.

TDQS

A4.8/5.0
Behavior5/5

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

Annotations only mark it non-read-only and non-destructive; the description carries the real behavioral load: cursor semantics ('only events published AFTER registration are ever delivered'), a 30 registrations/hour rate limit, the ~2,000-entry retention window and what happens to an idle subscription, plus auth via the MCP client's bearer. That is well beyond what the annotations disclose.

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

Conciseness4/5

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

Front-loads the verb, resource and return value in a single bolded opening sentence, then layers usage, return shape, vocabulary and limits. It is long and slightly repetitive — it restates list_options("event_types") guidance already present in the schema — but nearly every sentence adds operational value rather than padding.

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

Completeness5/5

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

Although an output schema exists, the description still supplies what matters for correct invocation: the return shape, the persistence of subscription_id (no list endpoint), which tools consume it, auth, rate limit, and the resumption/retention caveat. 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.

Parameters4/5

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

Schema coverage is 100%, so the baseline is 3, but the description adds usable semantics: empty body listens to everything, the vocabulary rules (report_plan as 'stream full research as it is minted', a family for 'everything earnings-related', symbols for a watchlist), and the filter-normalisation behaviour (topics lower-cased, symbols upper-cased, duplicates dropped).

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

Purpose5/5

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

States a specific verb+resource ('Save a realtime-event filter server-side') and the artifact produced (`subscription_id`), plus the underlying endpoint. It explicitly differentiates itself from the siblings stream_events, stream_event_subscription and delete_event_subscription, so an agent can pick it without opening another schema.

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

Usage Guidelines5/5

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

Gives an explicit alternative and the condition that selects it: 'For a live-streaming ask, the default is a dashboard... that loops stream_events itself — see stream_events. Register a subscription when the user wants a saved filter that resumes across sessions.' When-to-use and when-not-to-use are both stated.

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

run_saved_queryA
Read-only
Inspect

Re-run a saved request's queries against live data — exactly, or re-aimed at other entities with typed values. No planning, no polling.

The payoff of `save_user_query`: pass a saved row's `queries` array from
`list_saved_queries` and each stored query runs as-is, returning fresh rows
synchronously. Use this INSTEAD of `explore_data_catalogue` whenever a saved row
already covers the question — same SQL, current data, seconds instead of minutes,
and it does not draw on the 20/hour exploration budget.

A full signed URL is accepted in `query_tokens` and unwrapped. For a token
that came from a `get_company_event_web` node rather than a saved row, use
`get_signed_sql_drilldown` — same endpoint, single node, drilldown framing.

`values` slots are substituted deterministically, with no language model in the
loop (~0.5-1.2 s per query). Reach for it whenever you already know the entities —
you usually do, having resolved the tickers or CIKs before calling — and keep
`prompt` for what you cannot type.
  - Entity-set slots (type `cik_rows` / `symbol_rows`) take a LIST of {"id",
    "name"} objects: [{"id": "0001454027", "name": "Verition Fund Management LLC"}].
    A name-only entry ({"id": "", "name": "Walleye Capital"}) is resolved
    server-side against the filer registry; a name it cannot resolve comes back as
    a 422, never as a guess.
  - Pass the SAME dict to every token of a row: each twin picks out the slots it
    uses, and keys no twin uses come back as `ignored_values`.
  - A slot left out falls back to `prompt`, and then to the value the row was
    saved with (reported as `defaulted_parameters`).

`prompt`: a plain `queries` token has no slots and replays unchanged. The backend
maps the wording onto the row's typed slots. A 422 means it could not — fall back
to `explore_data_catalogue` for that question. A prompt-only fill can also DRIFT (a
slot the wording never mentions, a date say, can come back re-aimed), which is the
second reason to prefer `values`: never infer the substituted values from the
wording, read them out of `parameters` and say them back to the user.

`max_rows`: uncapped, a saved row's
`_constituents` companion query returns 7,000-35,000 rows (3-8 MB) and the client
stalls for minutes. A sliced result carries `row_count` (the full count) and
`truncated: true`; say the user is seeing a slice of N rows.

Returns {"queries_run": N, "results": [{"index": i, "format": "table",
"count": <rows returned>, "rows": [...], ...}, ...]}, in the SAME ORDER as
`query_tokens`. A re-aimed replay adds, per result:
  `parameters`            the values actually substituted (resolved ids and
                          canonical names)
  `defaulted_parameters`  slots that kept the saved example ("prior quarter kept
                          as 2026-03-31")
  `ignored_values`        keys in `values` this twin does not use
Surface all three: which values were substituted, which kept the saved example,
which were ignored. Rows only — the underlying SQL is never returned, by design.
Describe the results from the row's `metadata.summary`, the substituted
parameters, and the columns, never by asserting what the query does internally.

CITE WHAT YOU PUBLISH: each entry in `results` may carry `_source_*` columns —
`_source_url`, `_source_filed_at`, `_source_document_id`, `_source_citations` —
alongside a `sources` block naming each relation, its grain, and whether it is
`traceable` per "row" or only as a "cohort". That is the provenance of the numbers,
not padding: quote `_source_url` when the user asks where a figure came from, and for anything
you are about to publish — or the moment they ask for the filing, the quote, or
"how do you know" — pass `sources.relations[].relation` plus each row's
identifying columns (the grain AND its date) to `trace_data_sources`, which
resolves them to the source document, the
section, or the verified quote behind the number. `get_document_section` then opens
the exact speaker turn a transcript citation points at.

Each token runs independently: one failure does not stop the rest, and that entry
carries an "error" key instead of rows. A 403 means that token is invalid or
tampered — do not retry it altered; the saved row is dead, so re-run
`explore_data_catalogue`, `save_user_query` a fresh row, and `delete_saved_query`
the old one. A 422 on a re-aimed replay is the mapping failure above (unknown
entity, wrong shape), not a dead row.

A replay returning DIFFERENT rows than when it was saved is normal — it hits live
data. Report what comes back now; never reconcile it against remembered numbers.

Requires auth (your MCP client attaches the OAuth bearer automatically).
NameRequiredDescriptionDefault
promptNoNatural-language fill for slots `values` did not cover, e.g. "the past 8 quarters of sales for KSS". Placeholder tokens only.
valuesNoPREFERRED way to re-aim: {slot name: typed value}, keys from the row's `metadata.parameters`, e.g. {"symbol": "KSS"}. Entity-set slots take [{"id", "name"}] lists. Pass the same dict for every token.
max_rowsNoRow cap per query, e.g. 500 — PASS IT ON EVERY REPLAY.
query_tokensYesA saved row's `queries` list (exact replay) or its `query_placeholder` list (re-aiming with values/prompt), verbatim. Max 25. Never construct or edit a token.

Output Schema

NameRequiredDescription
resultsYesOne entry per query token, in the same order.
queries_runYes

TDQS

A4.4/5.0
Behavior3/5

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

Annotations only declare readOnlyHint=true, and the description goes well beyond that: no planning/polling, exempt from the 20/hour exploration budget, per-token isolation (one failure doesn't stop others), 403 = dead row vs 422 = mapping failure, truncation via row_count/truncated, and prompt-only fills can DRIFT. It also states SQL is never returned and provenance columns may appear. This is unusually rich behavioral disclosure. However, much of it is arguably operational instruction for the caller rather than intrinsic tool behavior, and return-shape detail overlaps the existing output schema.

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

Conciseness4/5

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

Front-loaded with the core purpose before any elaboration, and each subsequent paragraph covers a distinct concern (tokens/values, prompt, max_rows, return shape, provenance, error handling). It is long and dense, and the return-shape and provenance passages drift into guidance that could be trimmed, but almost every sentence carries actionable information.

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

Completeness5/5

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

For a 4-param tool with an output schema, no behavioral or routing gap remains: the exact-vs-re-aimed distinction, fallback chain, error taxonomy, truncation signaling, staleness expectation, and downstream trace_data_sources handoff are all covered. An agent could call this correctly on the first attempt from the description alone.

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

Parameters4/5

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

Schema coverage is 100% so the baseline is 3, but the description adds real semantics beyond the schema: the same `values` dict must be passed to every token, entity-set slots take {"id","name"} lists with server-side name resolution, unused keys surface as `ignored_values`, and slots fall back to `prompt` then the saved default (`defaulted_parameters`). `max_rows` guidance ("PASS IT ON EVERY REPLAY") is also operational context the schema lacks.

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

Purpose5/5

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

Opens with a specific verb+resource ("Re-run a saved request's queries against live data") and immediately distinguishes exact replay from re-aimed replay. It names the sibling producing the input (list_saved_queries), the one it complements (save_user_query), and the ones it competes with (explore_data_catalogue, get_signed_sql_drilldown).

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

Usage Guidelines5/5

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

Explicit routing: "Use this INSTEAD of explore_data_catalogue whenever a saved row already covers the question," and "For a token that came from a get_company_event_web node ... use get_signed_sql_drilldown." It also states when to prefer `values` over `prompt` ("keep prompt for what you cannot type") and gives explicit fallback paths on 422/403.

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

save_user_datasetA
Idempotent
Inspect

Save a drafted candidate as the user's scheduled dataset (step 2 of 2, after draft_custom_dataset): its query, the schema it produces, the file format, the date mode and a cron schedule. Each build publishes a new file only when the rows changed; list_user_datasets returns the files.

New items are reviewed before publishing, up to 40 per build. On a large first build
the rest publish as queried and are reviewed on later builds; a review that fails
publishes the items as queried. The review can correct text from stored sources (a
cut-off sentence, a fiscal period missing from a title), add a one-line caveat, or hold
an item out of the file (junk, misattributed, off-topic, materially wrong). Links,
GUIDs, tickers and dates are never changed, and no figure is published that isn't in
the stored data. So a file can hold fewer rows than the query returns, and an item
already published unreviewed can later be corrected or withdrawn when its review runs.

RSS files (format="rss"; Atom is unchanged) carry what syndication partners (e.g.
Newstex) read:
- `<company:symbol>EXCHANGE:TICKER</company:symbol>` per item, from a `company_symbol`
  / `ticker_exchange` column, else from `symbol` plus its listed exchange; non-US
  venue suffixes are dropped (BARC.L -> LSE:BARC).
- Up to 10 `<category>` tags per item, from `categories`, `category`, `event_type`,
  `document_type`, `doc_type`, `sector` and `industry`.
- One item per guid: a document listed under several tickers is one story carrying
  every ticker.
- The channel carries the title "Flexreport Finance: <name>" and one `<copyright>`
  line; each item's description ends with a "Source: Flexreport Finance" line.
- Each item's description carries a "Research: Flexreport Finance research report"
  link to its `research_url` (atom: a rel="related" link as well).
- JSON source links are unwrapped to their URL.

Every format gets a `research_url` column when the query returns `symbol` or `ticker`:
a fixed, signed link (no sign-in) serving the research report PDF behind that item,
added at build after the review. It exists ONLY so the feed's readers (syndication
partners, the owner's subscribers) can open the research without a Flexreport account:
never call it, build one, or offer it as a download route (reports for the user come
from get_latest_report, Flexreport's daily files from download_dataset). The link is
the entitlement: anyone holding the feed can open the reports in it, and deleting the
dataset kills every link it published. When the user plans to share the feed, tell
them that anyone holding it can open the reports in it.

Confirm the name and schedule with the user before calling. Any valid cron is accepted
(hourly, every 30 minutes, ranges), but one expression can't give each hour a different
minute: for 9:30 and 20:00 offer "0,30 9,20 * * *" (also 9:00 and 20:30) or two
datasets. Limits: 5 datasets per user, and 10 schedules per user shared with
schedule_task workflows.

Errors: 404 = that query_token is not a candidate of the caller's own draft; 422 = bad
cron, a limit reached, a column named `published_at` (reserved — refine the draft to
rename it), or the candidate no longer runs (draft it again); 503 = the dataset saved
but its schedule didn't attach — call this again with the same args.

Returns {dataset_id, dataset_name, saved_query_id, task_name ("ds:{email}:{name}"),
cron, format, date_mode, schema: [{name, pg_type}], feed_fields, latest_url,
placeholder_id_to_poll, build_task_id}.
- `latest_url` is a FIXED link to the newest file with no sign-in (the link itself is
  the key): give it to the user as THE feed URL — for an RSS reader, a syndication
  partner's poller (rss items carry <company:symbol> and up to 10 <category>), Excel /
  Google Sheets web import, or another platform's poller. A dashboard reads the
  published file through get_user_dataset_file, never a replay of `saved_query_id`. It returns 404 until the first
  build writes a file; with build_now that usually takes minutes.
- `feed_fields` (rss/atom only) shows which column fills each item field {title, link,
  description, pub_date, guid}; tell the user if one fell back (null).
list_user_datasets(dataset_name=...) shows the build's last_build_status. The schedule
appears in list_scheduled_tasks as `task_name`; delete_scheduled_task(task_name)
stops the builds but keeps the dataset and its files; delete_user_data_feed removes
the dataset entirely.

Requires auth (your MCP client attaches the OAuth bearer automatically).
NameRequiredDescriptionDefault
cronYesWhen to build, any valid 5-field cron in America/New_York, e.g. "30 18 * * 1-5" (weekdays 18:30), "*/30 * * * *" (every 30 minutes), "0 9-17 * * 1-5" (hourly in market hours).
formatNoFile format of each build: parquet, csv, tsv, json (an array of records), jsonl, xlsx, xml, or rss / atom (a feed of the newest 200 rows as items; rss items carry <company:symbol> and <category> tags for syndication partners). Use the user's choice, else the draft's target_format, else parquet.parquet
draft_idYesdraft_id from draft_custom_dataset.
build_nowNoBuild the first file right away (default) instead of waiting for the cron.
date_modeYesThe candidate's `suggested_date_mode` unless the user says otherwise. rolling = move the query's fixed dates forward to each build date, keeping the window length; snapshot = re-run the query exactly as saved.
query_tokenYesquery_token of the candidate the user chose, from that draft's get_task_status result.
dataset_nameYesLowercase name, 3-64 chars: letters, digits, _ and -. Re-saving the same name replaces the dataset's rules and keeps its files.

Output Schema

NameRequiredDescription
cronYesWhen builds fire (America/New_York).
formatYes
schemaYesColumns every build must produce.
date_modeYes
task_nameYes"ds:{email}:{name}" — the schedule's key in list_scheduled_tasks / delete_scheduled_task.
dataset_idNoId of the saved dataset row.
latest_urlNoFixed, sign-in-free link to the newest file (the link is the key) — for an RSS reader, Excel / Google Sheets web import or a platform's poller. 404 until the first build.
feed_fieldsNorss/atom only: the column filling each item field {title, link, description, pub_date, guid}; null = fallback (description lists the other columns, pub_date = build time, guid = link or a row hash).
dataset_nameYesThe dataset's name (upsert key).
build_task_idNoThe first build's task (build_now); null when not dispatched.
saved_query_idNoThe saved query the builds run.
placeholder_id_to_pollNoTask authoring the rolling-date placeholder twin; no need to poll.

TDQS

A4.7/5.0
Behavior5/5

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

Annotations only declare the write/idempotent/non-destructive profile, while the description supplies the real behavioral model: the review pipeline (40 items per build, what review may change, what it never changes, how rows can shrink or be withdrawn later), quota limits (5 datasets, 10 shared schedules), the entitlement consequence ("deleting the dataset kills every link it published"), and 503 retry semantics. This is context an agent cannot get from 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.

Conciseness3/5

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

Front-loaded correctly — purpose, then the review model, then limits/errors — but it is very long for a save operation. Whole passages (channel title, copyright line, "Source: Flexreport Finance" suffix, JSON link unwrapping) are feed-construction specification rather than selection or invocation guidance, and the return-value field walkthrough duplicates the output schema.

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

Completeness4/5

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

Given the mutation, the quota limits, and the coupled async build, the description is thorough about errors, retry, and downstream lifecycle — nothing needed to call it correctly is missing. It slightly over-delivers by narrating the return object even though an output schema exists, but that redundancy does not create ambiguity.

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

Parameters5/5

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

Schema coverage is 100% so baseline would be 3, but the description adds real semantics the schema lacks: cron expressiveness limits and the workaround, format="rss" implications (company:symbol, up to 10 categories, one item per guid), date_mode choice driven by the candidate's suggested_date_mode, build_now's effect on latest_url returning 404 until the first build, and name reuse replacing rules while keeping files.

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

Purpose5/5

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

States a precise verb and resource ("Save a drafted candidate as the user's scheduled dataset") and explicitly positions itself as "step 2 of 2, after draft_custom_dataset", which cleanly separates it from the sibling that creates drafts. The parenthetical enumeration of what gets saved (query, schema, format, date mode, cron) makes the scope concrete.

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

Usage Guidelines5/5

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

Explicit sequencing (call after draft_custom_dataset), an explicit precondition ("Confirm the name and schedule with the user before calling"), concrete guidance for an ambiguous cron case with a worked alternative ("offer '0,30 9,20 * * *' ... or two datasets"), and routing to alternatives — get_latest_report for the user's own reports, download_dataset for daily files, list_user_datasets for status, delete_scheduled_task vs delete_user_data_feed for teardown.

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

save_user_queryAInspect

Save the queries an exploration already planned, so the request can be replayed instantly — exactly, or re-asked for other entities.

THE POINT: `explore_data_catalogue` is a full validate -> plan -> run pipeline that
re-authors the SQL from scratch on every call (~1-5 min, 20/hour). For a question
the user will ask AGAIN — a recurring check, a dashboard they watch, "my usual
semis screen" — save the tokens that exploration just produced. `run_saved_query`
then re-runs exactly those queries against live data in seconds, with no planning
step and no async job to poll. The backend ALSO authors a PLACEHOLDER twin of each
saved query (its ticker, CIK list, date, interval, and count lifted into typed
slots) plus a `metadata` slot schema naming them, so the same request can later be
re-asked for a different company, filer, or window:
`run_saved_query(query_placeholder, values={"symbol": "KSS"})`.

WHEN TO CALL: once an exploration is done and shown, offer it — say the request can
be re-run exactly or with different values — and save when the user says yes or
clearly implies they will want it again. Do NOT save every exploration by reflex,
and do not save a one-off.

Each query token is opaque ciphertext the server minted around the planned SQL.
NEVER write, guess, or edit one — a token this backend did not mint cannot be
decrypted and fails with 403 at replay time, long after the exploration is gone.

Owner-scoped: the row belongs to the calling user and is invisible to everyone
else. There is NO upsert (unlike `schedule_task`): saving the same request twice
creates a SECOND row. Check `list_saved_queries` first and `delete_saved_query` the
stale one rather than piling up near-duplicates.

Returns {"msg": "Successfully saved query: <user_request>", "id": <row id>,
"placeholder_status": "PENDING" | "NOT_DISPATCHED", "placeholder_id_to_poll"}. `id` is the delete key.
PENDING means the placeholder twins AND the row's `metadata` (its slot schema,
summary, and datasets) are being authored — roughly 10 s to 5 minutes, depending on
the size of the queries. Until they land, `query_placeholder` and `metadata` are
null in `list_saved_queries`, and an exact replay via `queries` works meanwhile.
NOT_DISPATCHED means no twins will be authored for this row: it can be replayed
exactly but not re-aimed.

Requires auth (your MCP client attaches the OAuth bearer automatically).
Rate-limited 100/min.
NameRequiredDescriptionDefault
query_tokensYesThe `query_token` of each exploration `results` entry, passed through EXACTLY as returned (a signed URL is unwrapped). Skip entries with no token. Never write SQL.
user_requestYesThe request being saved, in the user's own words — the row's only human-readable label, so make it specific ("weekly EPS-revision check on my semis basket").

Output Schema

NameRequiredDescription
idYesSaved row id — the delete_saved_query key.
msgYes
placeholder_statusNoPENDING (twins + metadata being authored) or NOT_DISPATCHED.
placeholder_id_to_pollNoTask id of the placeholder-authoring job.

TDQS

A4.8/5.0
Behavior5/5

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

Goes well beyond the annotations: owner-scoping, absence of upsert (duplicate saves create a second row), opaque-ciphertext tokens that fail with 403 if not server-minted, auth requirement, and a 100/min rate limit. It also explains the async placeholder authoring and the PENDING vs NOT_DISPATCHED states, which materially shapes call timing.

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

Conciseness4/5

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

Well front-loaded and organized with THE POINT / WHEN TO CALL headers, and nearly every sentence carries information. It is long, but the async-state and token-safety detail justify most of the length; a few clauses could still be tightened.

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

Completeness5/5

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

Covers the full lifecycle an agent needs: what to save, when, what the backend does next, what the placeholder enables, duplicate handling, and the failure/async states. Nothing necessary for a correct call or a correct follow-up decision is missing.

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

Parameters4/5

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

Schema coverage is 100%, so the baseline is 3, but the description adds real meaning: tokens are opaque server-minted ciphertext that must pass through exactly and never be written or guessed, and user_request is the row's only human-readable label so it should be specific.

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

Purpose5/5

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

States a specific verb (save) and resource (the queries an exploration planned) plus the outcome (exact or re-aimed replay). It clearly distinguishes itself from explore_data_catalogue, run_saved_query, schedule_task, and list_saved_queries by name.

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

Usage Guidelines5/5

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

An explicit WHEN TO CALL block says to offer it once an exploration is done and shown, save on confirmation, and explicitly says NOT to save every exploration or one-offs. It also routes to alternatives (list_saved_queries before saving, delete_saved_query for stale rows).

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

save_user_templateA
Idempotent
Inspect

Save the user's PDF format — one template per account, applied automatically to every PDF the backend renders for them from now on.

STEP 2 OF 2 after `draft_user_template`. Two ways to call it:

SIGNED-OFF (the normal path): pass `draft_id` and the `variant_id` the user chose
from the previews. The EXACT treatment they looked at is stored — no second
adaptation pass that could produce something they never saw. `template`,
`template_type` and `anchor` are taken from the draft and may be omitted. A 404
means the draft expired (24h) or the variant does not exist: draft again.

DIRECT (no previews): pass `template` + `template_type` (+ `anchor` for an
add_on) — only when the user explicitly wants to skip the previews. An add_on
saved this way still gets an unreviewed harmonise pass, so prefer the signed-off
path for anything visual. HTML only: markdown is rejected with a 422.

ONE TEMPLATE PER USER — this is an UPSERT: saving replaces whatever was saved
before, with no history. Call `get_user_template` first and confirm with the user
before overwriting an existing format. To change a saved format afterwards, use
`update_user_template` (any field, or none to recompile it as stored).

WHAT CHANGES AFTERWARDS: the backend lays out this user's reports in the saved
format — every event-time report gets a copy in their template (get_latest_report
serves it, `template: "user"`), as do scheduled reports. The template is a blueprint (stylesheet, page chrome, one
pattern per content kind) applied to the document's own content: nothing is
filled in by hand, and no other call changes. When you compose a PDF yourself
from Flexreport data, `get_user_template` returns the markup to lay it out in.

Asynchronous: returns {"task_id", "status": "PENDING", "next_step"}. Poll
`get_task_status(task_id)`; the finished result is {"status": "SAVED",
"template_type", "anchor", "bytes", "patterns": [content kinds the design styles],
"tones": {positive | negative | neutral | accent: css class}, "warnings"}. Tell
the user which content kinds their design styles. A warning "keeping the
previously compiled blueprint" means the new compile was rejected and the
previous blueprint stays in force; the markup itself is still saved. Validation
failures are a 422 with `detail.errors` listing each problem.

Requires auth (your MCP client attaches the OAuth bearer automatically).
Rate-limited 100/min.
NameRequiredDescriptionDefault
anchorNoDirect path, add_on only (required there): the section it applies to, e.g. "technical".
draft_idNoSigned-off path: the draft_id from draft_user_template's finished task. Pass with variant_id.
templateNoDirect path only: the HTML template (markdown is a 422). Omit on the signed-off path — the draft supplies it.
variant_idNoSigned-off path: the variant_id the user chose from the previews. Pass with draft_id.
template_typeNoDirect path only (required there): "add_on" or "bespoke".

Output Schema

NameRequiredDescription
statusYesTask state at dispatch, normally "PENDING".
changedNoupdate_user_template only: the fields that were updated.
task_idYesCelery task id; poll it with get_task_status for the result.
next_stepNoWhat the finished task returns.

TDQS

A4.9/5.0
Behavior5/5

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

Goes far beyond the annotations: discloses that this is an UPSERT with no history, that the signed-off path deliberately skips a second adaptation pass, that the call is asynchronous with a PENDING task_id and poll loop, and that a warning can leave the previous blueprint in force while the markup is still saved. Auth and rate limits (100/min) are also stated. Nothing here contradicts readOnlyHint=false, idempotentHint=true or destructiveHint=false.

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

Conciseness4/5

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

Front-loaded with the upsert warning and step ordering, and the SIGNED-OFF / DIRECT / WHAT CHANGES AFTERWARDS headings make a long body scannable. It is longer than strictly necessary — the HTML-422 rule and the anchor rule are each stated twice, once narratively and again inside the schema descriptions.

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

Completeness5/5

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

For a zero-required-parameter, dual-mode, async, upsert mutation, the description covers everything an agent needs: mode selection, overwrite risk, async polling contract, terminal result fields (patterns, tones) with a directive to report content kinds to the user, warning interpretation, and the 422 error shape — even though an output schema also exists.

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

Parameters5/5

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

Although schema coverage is 100%, the description adds semantics the schema cannot: the parameters form two mutually exclusive call modes, and on the signed-off path template/template_type/anchor 'are taken from the draft and may be omitted'. It also scopes anchor to add_on only and states the HTML-only constraint that drives a 422.

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

Purpose5/5

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

States a specific verb and resource — saving the user's PDF format as a persistent, account-wide template — plus the scope ('one template per account, applied automatically to every PDF'). It distinguishes itself from draft_user_template (step 1), update_user_template (post-save change) and get_user_template (read-before-overwrite), so an agent can pick it out of a crowded sibling list.

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

Usage Guidelines5/5

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

Gives explicit branching instructions: the signed-off path (draft_id + variant_id) is 'the normal path', the direct path is 'only when the user explicitly wants to skip the previews', and add_on direct saves are discouraged because of the unreviewed harmonise pass. It also names prerequisites (call get_user_template and confirm before overwriting) and failure routes (404 → draft again).

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

schedule_taskAInspect

Save a multi-step WORKFLOW (upsert by name) and attach a recurring cron schedule for its delivery.

Use this when the user wants a sequence of platform calls delivered ON A
SCHEDULE / repeatedly (e.g. "every weekday morning pull 8-K releases, screen
for cheap large-caps, and email me the overlaps as a table"). For a ONE-OFF
request, call the corresponding tools directly instead. A request worded as a
"datafeed" / "feed" / recurring file is draft_custom_dataset, not this — even
with summary columns, since stored summaries are ordinary dataset columns.

`steps` (shape in the parameter description): a transform applies its
instruction to the accumulated results of ALL prior steps (e.g. "intersect the
two event sets by symbol and build a table"), and each step's `label` keys its
result. The LAST step's output is what gets delivered, so end table/summary
workflows with a transform.

Allowlisted fetch endpoints (anything else is rejected with a 422 at save
time; `params` takes the same keys as the matching tool):

- POST, JSON-body params: "get-realtime-events" (`list_realtime_events`),
  "list-upcoming-earnings-announcements" (`list_earnings_announcements`),
  "predict-earnings-announcement-move" ({"symbols": [...]}),
  "data-catalogue-exploration" ({"query": "..."}).
- GET, scalar query params only (no lists/dicts): "get-company-snapshot"
  (requires {"symbol": "..."}), "get-strategy-performance-summary",
  "get-strategy-track-record", "get-strategy-swaps", "get-stock-picks",
  "list-tickers", "list-realtime-event-options", "get-sectors",
  "get-sub-industries".
- EXPENSIVE — max 2 per workflow, and the cron must use a literal minute and
  at most 4 literal hours (no sub-hourly / "*" fields): "screen-stocks"
  (`screen_stocks`), "generate-research-report" ({"query": "..."}),
  "create-full-report" (writes a new note every run, a few minutes, replacing
  the symbol's report; {"ticker": "..."} for the standard note;
  add "user_override": true plus the shaping lists `generate_report_for_stock`
  takes — financial_items, ratios, as_reported_financial_items, revenue_segment,
  technical_analysis_items, estimate_items, institutional_ownership CIKs — for a
  custom one; the backend rejects the lists without the switch),
  "optimize-symbols", "optimize-portfolio", "list-optimized-stock-picks".

Each user may hold at most 10 active schedules.

Returns `status` 201 (created) or 200 (updated) with `workflow_id` and
`task_name` ("wf:{email}:{name}"), the key that `list_scheduled_tasks` shows
and `delete_scheduled_task` takes.
NameRequiredDescriptionDefault
nameYesLowercase slug and upsert key (no spaces, colons, or uppercase); re-saving the same name updates the workflow and re-attaches its schedule.
stepsYesOrdered 1-8 steps; the first must be a fetch. Fetch: {"kind": "fetch", "endpoint": "<allowlisted kebab-case path, no leading slash>", "params": {...}, "label": "..."}. Transform: {"kind": "transform", "instruction": "<=2000 chars, applied to all prior results>", "label": "..."}. `label` is an optional unique lowercase slug (default step_{i}). The last step's output is delivered.
deliveryNoWhere each run's output goes.email
descriptionNoOptional human-readable note saved with the workflow.
regular_cronYesRaw 5-field cron expression for when the workflow fires, e.g. "30 6 * * 1-5" (weekdays 06:30); validated server-side.

Output Schema

NameRequiredDescription
cronNoThe cron expression attached.
nameNoThe workflow name (upsert key).
statusNo201 when the workflow was created, 200 when an existing one was updated.
scheduledNo
task_nameYes"wf:{email}:{name}" — the key list_scheduled_tasks shows and delete_scheduled_task takes.
workflow_idNoId of the saved workflow definition.

TDQS

A4.9/5.0
Behavior5/5

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

Annotations only cover the safety profile (mutation, non-destructive), while the description adds substantial undisclosed behavior: 422 rejection of non-allowlisted endpoints, expensive-endpoint caps of max 2 per workflow with cron minute/hour restrictions, the 10-active-schedules-per-user quota, upsert semantics under the same name, and the 201-vs-200 status distinction with workflow_id/task_name. This is exactly the extra context annotations cannot carry.

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

Conciseness4/5

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

Front-loaded with purpose, then usage, then data-shape detail and a bulleted endpoint allowlist — every section serves a decision the agent must make. It is long, and the deeply nested create-full-report parenthetical is heavier than strictly necessary, but the bulk is load-bearing allowlist/limit information found nowhere else.

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

Completeness5/5

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

For a high-complexity scheduling tool with free-form nested steps, the description supplies the allowlist, quotas, cron limits, and step semantics. An output schema exists, yet the description still names workflow_id/task_name as the keys that list_scheduled_tasks and delete_scheduled_task consume, closing the follow-up loop.

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

Parameters5/5

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

Schema coverage is 100%, but the description adds meaning the schema structurally cannot: steps items are free-form objects, so the allowlisted endpoint vocabulary, per-endpoint param keys, and transform-accumulation semantics ('applies to accumulated results of ALL prior steps', last step's output delivered) are only documented here. It also ties name to upsert behavior and constrains regular_cron beyond the schema's 'validated server-side'.

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

Purpose5/5

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

The first sentence gives a precise verb (save/upsert by name) plus a compound resource (multi-step workflow + recurring cron schedule). It immediately distinguishes itself from the sibling draft_custom_dataset by naming that tool as the wrong path for 'datafeed'/'feed' requests.

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

Usage Guidelines5/5

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

Explicit when-to-use (repeated delivery of a sequence of platform calls), explicit when-not (ONE-OFF requests should call the tools directly), and an explicit alternative for a confusable request shape (draft_custom_dataset, even with summary columns). Sibling routing is fully resolved.

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

screen_stocksAInspect

Screen stocks by financial growth, sector, sub-industry, market cap, analyst ratings, institutional ownership, country, and price performance.

Discover valid filter values with the list tools named in each parameter's
description before guessing.

Rate-limited to 10/hour
per user server-side.

Asynchronous: returns a task id. Poll it with `get_task_status` until SUCCESS.
NameRequiredDescriptionDefault
metricsNoFinancial-growth criteria: metric name -> threshold (number) or required flag (bool), e.g. {"revenue_growth": 0.1, "positive_eps": true}. Interpreted by the screener, not enum-validated.
sectorsNoSector values from list_options("sectors").
countriesNoCountry values from list_options("countries").
market_capNoMarket-cap buckets from list_options("market_cap"), e.g. "Small-cap", "Large-cap".
sub_sectorsNoSub-industry values from list_sub_industries([...]).
analyst_ratingsNoAnalyst consensus ratings to keep, e.g. ["Buy", "Strong Buy"].
price_performanceNoPrice-return criteria: window -> minimum return, e.g. {"ytd": 0.1}.
institutional_ownershipNoInvestor category -> minimum ownership, keyed by list_options("institutional_investor_types") values, e.g. {"Hedge Fund": 5}.

Output Schema

NameRequiredDescription
statusYesPENDING on submission.
task_idYesPoll with get_task_status until SUCCESS.

TDQS

A4.5/5.0
Behavior5/5

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

Adds two traits the annotations do not convey: a server-side rate limit of 10/hour per user and asynchronous execution returning a task id that must be polled. These are exactly the operational facts an agent needs to avoid retry storms and to correctly sequence calls.

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

Conciseness5/5

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

Four short blocks: what it filters, how to obtain valid values, the rate limit, and the async contract. The purpose is front-loaded and every line carries distinct operational value with no filler.

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

Completeness5/5

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

Covers prerequisites, rate limiting, and the async/polling lifecycle, and an output schema exists so return-shape explanation is unnecessary. For an eight-parameter screener, nothing an agent needs to invoke it correctly is missing.

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

Parameters3/5

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

Schema description coverage is 100% and each parameter already names its source list tool, so the description's opening enumeration mostly restates the schema. It adds no threshold syntax or value-format guidance beyond what the schema provides, making the baseline 3 appropriate.

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

Purpose5/5

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

States a specific verb (screen) and resource (stocks) and enumerates the eight filter dimensions it accepts, which cleanly separates it from adjacent tools like get_stock_picks or generate_report_for_stock. An agent can tell what this tool does without opening the schema.

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

Usage Guidelines4/5

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

Gives a concrete prerequisite workflow: discover valid filter values via the list tools named in each parameter description before guessing, and poll the returned task with get_task_status until SUCCESS. It lacks any explicit when-not or comparison against sibling screening/picks tools, so it stops short of a 5.

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

situateA
Read-only
Inspect

THE FIRST CALL. What is going on right now — for named companies, or for the whole market — from the backend's situation endpoints. Call it before any other flexreport tool on anything about a company, an event, or "now".

TWO SCOPES (each parameter's description says which it belongs to):

WITH symbols -> GET /situate-company, one call per symbol. For "catch me up on
X", "what happened at X / why", "how did X's quarter look", and BEFORE aiming
explore_data_catalogue, list_realtime_events or a report tool at a company. When
the question is about one event, pass it as `event_type` AND `card_types` (halves
the payload: the cards of every other present type are dropped):
  situate(symbols=["OKTA"])                                           catch-up
  situate(symbols=["MAR"], event_type="financials_release",
          card_types=["financials_release"])                         the 10-Q
Returns {"situations": {SYMBOL: situation}} (a symbol that failed carries
{"error"} instead):
  present_types   event types with a node in the window
  web             {nodes, edges}: the newest node per event type and per table
                  over window_days. Edges carry `cause`: same_chain_run (observed:
                  one chain run wrote both — a table linked this way to the event
                  REFLECTS it), lineage (declared), co_occurrence (temporal only,
                  never causation). `at` is a write time, not a business date.
                  Node `fetch` hints name the follow-up call; a signed_query_url
                  goes to get_signed_sql_drilldown verbatim.
  cards           event-type cards: persisted_in, sources, entails (tables written
                  by layer, and realtime events that follow with rate and lag),
                  preceded_by
  relations       table cards: layer, refreshed_by (schedule, next_run_at), and
                  read_by — THE TOOL THAT READS IT. Prefer the first tool there
                  over explore_data_catalogue (company_financials for the EDGAR
                  statements, get_company_snapshot, predict_earnings_move, ...).
  entailed        tables the events entail, routed to this company's EDGAR or FMP
                  side; edgar_filer / routed_out / source_routing say which side
                  (never query a routed_out table for it)
Then: a node still inside the 12h cache -> list_realtime_events(event_type,
tickers=[symbol]); older -> its card's persisted_in table. For a table that
refreshed for the event, call its read_by tool first, e.g.
company_financials(symbol, financial_type=<the table>, limit=5, line_items=[...]).
A table whose next_run_at is after the event still shows the prior period. An
empty web is not a quiet week: widen window_days.

NO symbols -> GET /situate-market. For "any earnings today?", "most significant
investor decks today", "which hedge funds exited?", "what's going on?". Name the
types yourself when you can (`event_types`, else `family`); otherwise pass the
user's words as `question`:
  situate(event_types=["ir_publication"])        decks today: step 1 = decks
  situate(family="earnings")                     the earnings episode in order
  situate(question="what's going on?")           a sweep: one entry per episode
Returns {shape, routed_from, order, types, event_types, market, ...}:
  shape           named_types | family | sweep (nothing named: no default family)
  routed_from     what decided the order: `by` = explicit | question | none
  order           up to 4 event types, in reading order
  types[T]        card (trimmed ontology card: persisted_in, followers, ...),
                  cache {count, newest_at, oldest_at, tickers — or members with
                  member_kind cik | strategy | singleton}, reports {fresh, stale,
                  report_date}
  event_types     the whole realtime corpus (type, family, description)
  market          exchange, is_open, session
  degraded_sources  realtime_cache / report_inventory when one was unreachable:
                  counts are then unknown, NOT zero
Then: list_realtime_events(event_type=T, tickers=[...]) in `order`, skipping a
type whose cache.count is 0 (the 12h cache is cold for it — say so; it is not
evidence nothing happened). Narrow every follow-up with tickers=[...] to the names
just seen. For the research on the names that stand out, take the types that carry
`reports` into list_available_reports(event_types=[...],
report_date=<types[T].reports.report_date>), then get_latest_report. A family with no realtime types returns `message` naming
its durable tables.

Skip this tool ONLY for pure enumeration (list_options), an explicit "get me the
existing report" (get_latest_report), or a metric-history question with no event
in it ("Micron EPS over 8 quarters" -> company_financials(symbol="MU", limit=8,
line_items=["epsDiluted"]); cross-company or non-statement history ->
explore_data_catalogue). get_company_event_web gives a company's raw graph;
get_event_ontology gives any full card.

Requires auth (your MCP client attaches the OAuth bearer automatically).
NameRequiredDescriptionDefault
familyNoNO symbols, no type named: one family whose episode is walked in order — earnings, news, market_movement, institutional_ownership, analyst_activity, strategy, prediction, insider.
symbolsNoTickers to situate, e.g. ["MAR"] (max 5; one backend call each, in parallel). Omit for the whole market.
questionNoNO symbols: the user's words verbatim, keyword-routed to event types / a family only when neither event_types nor family is passed. Naming nothing is a market-wide sweep. Ignored with symbols.
sectionsNoWITH symbols: sections to return (default web, cards, relations, entailed). Add episodes for the episode definitions; informs_relations is the largest and least targeted.
card_typesNoWITH symbols: return cards only for these event types instead of every type present; entailed and relations shrink with them. E.g. ["financials_release"] with event_type.
event_typeNoWITH symbols: the realtime event type the question is about, e.g. "financials_release" for a 10-Q/10-K, "ir_publication" for a deck, "8k_release". Its card is returned even when it has no node in the window. Valid values: list_options("event_types").
event_typesNoNO symbols: the realtime event types the question is about, in the order named, e.g. ["ir_publication"] for decks, ["13f_exited"] for hedge-fund exits. Step 1 of the order is the first. Valid values: list_options("event_types").
top_tickersNoNO symbols: members listed per event type from the realtime cache, newest first (default 10, max 50).
window_daysNoWITH symbols: event-web look-back in days (default 14, max 90).
include_keysNoFields kept inside each card. With symbols the default is persisted_in, sources, entails, preceded_by, layer, read_by, refreshed_by ([] = whole cards; keep read_by: it names the tool that reads each table). With no symbols the default is the trimmed market card.
include_fetchNoWITH symbols: keep each web node's `fetch` hint (the follow-up call and, on data updates, a signed_query_url for get_signed_sql_drilldown). false shrinks the web 2-4x.
report_inventoryNoNO symbols: include fresh/stale report-plan counts per plan-earning type.

TDQS

A4.8/5.0
Behavior5/5

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

Annotations only supply readOnlyHint=true, and the description adds substantial context beyond that: OAuth bearer auth is automatic, nodes inside the 12h cache route to list_realtime_events while older ones route to persisted_in tables, `at` is a write time not a business date, a table whose next_run_at postdates the event still shows the prior period, and an empty web means widen window_days rather than a quiet week. It also warns that degraded_sources make counts unknown rather than zero — exactly the kind of failure-mode disclosure annotations cannot carry.

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

Conciseness4/5

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

Front-loaded with the single most important fact ("THE FIRST CALL") and organized under clear WITH symbols / NO symbols headings with worked call examples. It is long, but almost every sentence carries routing, cache, or return-shape information that has no other home given the absence of an output schema; the dense inline formatting is the main readability cost.

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

Completeness5/5

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

For a 12-parameter tool with no output schema and two entirely different response shapes, the description documents both return payloads field by field (present_types, web nodes/edges with cause values, cards, relations/read_by, entailed routing; shape/routed_from/order/types/market/degraded_sources). It also closes the loop on follow-up calls, leaving nothing an agent needs in order to invoke and interpret it.

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

Parameters4/5

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

Schema coverage is 100%, so the baseline is 3; the description earns more by documenting cross-parameter interactions the schema cannot express — passing the same event as both `event_type` and `card_types` halves the payload, `include_fetch=false` shrinks the web 2-4x, and `include_keys` should retain read_by because it names the tool that reads each table. It does not add syntax or format detail beyond what the schema properties already state for individual fields.

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

Purpose5/5

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

Opens with a specific verb+resource+scope ("What is going on right now — for named companies, or for the whole market") and immediately distinguishes itself from every sibling by asserting it is "THE FIRST CALL" to be made before other flexreport tools. The two-scope split (WITH symbols -> /situate-company, NO symbols -> /situate-market) makes the tool's identity unambiguous.

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

Usage Guidelines5/5

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

Explicitly names when to use it ("catch me up on X", "what happened at X / why", before aiming explore_data_catalogue or list_realtime_events at a company) and when to skip it (pure enumeration via list_options, explicit report retrieval via get_latest_report, metric-history questions routed to company_financials or explore_data_catalogue). It also hands off to get_company_event_web and get_event_ontology for raw graph/full card needs, so alternatives are covered rather than implied.

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

stream_eventsA
Read-only
Inspect

Listen for realtime events as they publish — a bounded read of the backend's ad-hoc SSE stream (GET /events), filter passed inline, nothing saved server-side.

DEFAULT DELIVERY — BUILD A DASHBOARD: when the user asks to stream / watch / keep
listening for live events, build a live dashboard (in Claude, an Artifact) whose
page calls this tool in its own loop, carrying `cursor` from read to read, and
renders each event as it lands. One prompt then keeps the stream open with no agent
turn per read. If the page cannot reach this server, run the loop yourself and write
each event into the dashboard's shared store. Stream into chat only when the user
asks for that, or the client cannot render a dashboard.

Returns {"events": [...], "cursor": "<last id seen>", "closed": null | reason,
"elapsed_seconds": n}. Each item in `events` is the same object `list_realtime_events` returns for that
type ({event_type, symbol, payload, published_at, id}) — summary, thesis impact,
significant changes, citations — the moment it publishes, no polling of the 12h
cache. There is NO PDF link on an event: the event's report lands a few minutes
later, announced on the `report_plan` topic, and get_latest_report([symbol]) then
returns it (stale or missing with no report on the way -> generate_report_for_stock,
ticker only).

THE LOOP: a call returns after `max_events` matched events or `max_wait_seconds`
(cap 55 s), whichever comes first — call it again to keep listening, and ALWAYS
pass the returned `cursor` back in: this stream keeps no state, so without it
the next call starts at "now" and drops whatever published in between. The
cursor advances on heartbeats too (events the filter dropped), so an empty
`events` list with a moved cursor is normal on a quiet tape. Omit `cursor` on
the first call to start at now. For a subscription that resumes by itself, use
register_event_subscription + stream_event_subscription instead.

`topics` (optional, any mix; case-insensitive):
  - an event type from list_options("event_types") — eps_update, 8k_release,
    transcript_update, ir_publication, biggest_mover, 13f_new, strategy_update, ...
  - a family from the same listing — earnings, news, market_movement,
    institutional_ownership, analyst_activity, strategy, prediction — which
    matches every type in it, including types added later;
  - `report_plan` (stream-only): a frame each time a symbol's new report is
    PUBLISHED — its document is already written ({symbol, event_type, planned_at,
    queued_at, plan_key, report_key}); the next step is get_latest_report([symbol]),
    which returns it (`stale: false`). Subscribe to it to stream research as it is
    written.
`symbols` (optional): tickers; 13f_* filer events carry the filer's CIK in the
symbol slot. No topics and no symbols = everything. Unknown topic -> 422 with the
allowed lists in `detail`.

`closed` set means the server ended the stream — do not reconnect with that id.
Requires auth (your MCP client attaches the OAuth bearer automatically).
NameRequiredDescriptionDefault
cursorNoThe `cursor` returned by the previous read; resume after it. Omit to start at now.
topicsNoEvent types or families from list_options("event_types") (e.g. "eps_update", "earnings"), plus the stream-only "report_plan"; case-insensitive. Omit for every topic.
symbolsNoTickers to listen for (13f_* filer events carry the filer's CIK in the symbol slot). Omit for every symbol.
max_eventsNoReturn after this many matched events (clamped to 1-200).
max_wait_secondsNoReturn after this many seconds even if fewer events arrived (clamped to 1-55).

Output Schema

NameRequiredDescription
closedNoSet when the server ended the stream (e.g. "subscription deleted"); do not reconnect.
cursorNoLast stream id seen (advances on heartbeats too). Pass it back on the next call; null only when no frame carried an id and none was passed in.
eventsYesMatched events in publish order; empty on a quiet tape. A frame whose data was not JSON comes through as {"raw": <text>}.
elapsed_secondsYesWall time this read held the stream open.

TDQS

A4.8/5.0
Behavior5/5

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

Annotations only declare readOnlyHint=true, and the description is fully consistent while adding substantially more: bounded read semantics, the 55s cap, statelessness of the stream, cursor advancement on heartbeats, the meaning of `closed`, and the OAuth requirement. It also explains downstream behavior (no PDF on an event; report arrives later via `report_plan` + `get_latest_report`) that no annotation conveys.

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

Conciseness4/5

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

Front-loaded with purpose, then labeled sections (DEFAULT DELIVERY, THE LOOP, topics, symbols, closed) so a reader can skim to what matters. It is long and includes delivery-policy instructions (build an Artifact) that sit somewhat outside pure tool description, but nearly every sentence carries operational information an agent needs to loop correctly.

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

Completeness5/5

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

Although an output schema exists, the description still spells out the return contract (`events`, `cursor`, `closed`, `elapsed_seconds`) and what an event item is, plus the loop protocol needed to keep listening without data loss. Auth, error behavior, and termination semantics are all covered, so nothing an agent needs to call this correctly is missing.

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

Parameters4/5

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

Schema coverage is 100%, so the baseline would be 3; the description earns more by explaining that `topics` accepts families that match future types, that `report_plan` is stream-only with its own payload shape, and that 13f filer events put a CIK in the `symbols` slot. It also documents failure behavior (unknown topic -> 422 with allowed lists) and the heartbeat-driven cursor advance, which the schema cannot express. It stops short of full coverage only because the schema already restates much of the cursor/topic semantics.

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

Purpose5/5

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

States a specific verb and resource ('Listen for realtime events as they publish') plus the underlying mechanism ('ad-hoc SSE stream `GET /events`') and the non-persistent scope ('nothing saved server-side'). It explicitly contrasts with `list_realtime_events` (no polling of the 12h cache) and with `register_event_subscription` + `stream_event_subscription`, so an agent can distinguish it from siblings without opening schemas.

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

Usage Guidelines5/5

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

Gives explicit when-to-use rules ('when the user asks to stream / watch / keep listening') and a default delivery pattern (build a dashboard, fall back to agent-run loop, stream into chat only when asked or when the client cannot render). It also names the alternative for stateful resumption: 'For a subscription that resumes by itself, use register_event_subscription + stream_event_subscription instead.'

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

stream_event_subscriptionA
Read-only
Inspect

Read the next events on a saved subscription — a bounded read of GET /event-subscription/{subscription_id} that resumes from the position saved when the previous read closed, so repeated calls see every matching event exactly once with no gap and no replay.

For a live-streaming ask, the default is a dashboard (in Claude, an Artifact) that
loops stream_events with its cursor — see stream_events; a page should prefer that
ad-hoc stream, since a subscription read can stall.

Returns {"events": [...], "cursor": "<last id seen>", "closed": null | reason,
"elapsed_seconds": n} — same shape and event objects as stream_events. A call returns after `max_events`
matched events or `max_wait_seconds` (cap 55 s); call again to keep listening. The
API saves the cursor when this read closes the connection, so the next call resumes
correctly WITHOUT passing `cursor`. Pass `cursor` only to override the saved
position for one connection (e.g. re-read from an earlier frame id); it is not
persisted.

An empty `events` list means nothing matched in the window, not that the
subscription is broken. `closed` = "subscription deleted" means it was removed
mid-stream — do not reconnect. 404 `unknown subscription_id` covers both a missing
id and another user's id (indistinguishable by design).

Acting on what arrives: an `event` item is the full realtime event (use it
directly); to hand over research, get_latest_report([symbol]) first (it returns in
seconds; batch up to 50 symbols per call), and only if it comes back stale/missing
with no report on the way a generate_report_for_stock(ticker) note. A `report_plan`
item means the symbol's new report was just published: get_latest_report([symbol])
returns it.

Requires auth (your MCP client attaches the OAuth bearer automatically).
NameRequiredDescriptionDefault
cursorNoOverride the saved position for this one connection (not persisted). Omit to resume where the last read stopped.
max_eventsNoReturn after this many matched events (clamped to 1-200).
subscription_idYesThe `subscription_id` returned by register_event_subscription.
max_wait_secondsNoReturn after this many seconds even if fewer events arrived (clamped to 1-55).

Output Schema

NameRequiredDescription
closedNoSet when the server ended the stream (e.g. "subscription deleted"); do not reconnect.
cursorNoLast stream id seen (advances on heartbeats too). Pass it back on the next call; null only when no frame carried an id and none was passed in.
eventsYesMatched events in publish order; empty on a quiet tape. A frame whose data was not JSON comes through as {"raw": <text>}.
elapsed_secondsYesWall time this read held the stream open.

TDQS

A4.8/5.0
Behavior5/5

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

Annotations only declare readOnlyHint=true and the description is fully consistent while adding much more: cursor is persisted on read close but the explicit cursor param is not persisted, the read is bounded by max_events/max_wait_seconds (55s cap), stalls are possible, empty events is not an error, and 404 conflates missing and other-user ids by design. Auth requirements are also stated.

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

Conciseness4/5

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

Front-loaded with purpose, then alternatives, then return shape and failure modes, then workflow follow-ups — a logical progression with no filler sentences. It is long (~330 words) and restates the return shape that the output schema already provides, which keeps it from a 5.

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

Completeness5/5

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

For a bounded, resumable, long-poll read with auth, an output schema, and several distinct failure modes, the description covers everything an agent needs: return window, cursor persistence, empty-result interpretation, terminal close reasons, and 404 semantics.

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

Parameters4/5

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

Schema coverage is 100%, so baseline is 3; the description goes further by explaining the retry loop ("call again to keep listening") and that cursor should normally be omitted, only overridden for a one-off re-read. It largely echoes the schema's own cursor/stdout wording, so it is not transformative, but the loop semantics are genuinely additive.

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

Purpose5/5

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

States a specific verb and resource ("Read the next events on a saved subscription") plus the underlying endpoint and the resumable semantics. It explicitly contrasts itself with the sibling stream_events, so an agent can choose between them without opening a schema.

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

Usage Guidelines5/5

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

Gives an explicit routing rule: for live-streaming asks, prefer an ad-hoc stream_events dashboard; use this subscription read for the saved-position case, and note the stall risk. It also prescribes follow-up behavior (call again to keep listening; call get_latest_report before generate_report_for_stock; do not reconnect on "subscription deleted"), which is exactly the when/when-not/alternatives coverage this dimension rewards.

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

trace_data_sourcesA
Read-only
Inspect

Resolve numbers you are holding back to the documents they were read from — the filing, the section, or the verified quote.

The CITATION rung of the chain. `explore_data_catalogue`, `run_saved_query` and
`get_signed_sql_drilldown` hand back rows; this says where each number CAME FROM.
Reach for it whenever the user asks "where does that come from", "which filing",
"can you cite that", "how do you know" — and unprompted before you publish a figure
they are going to act on.

TWO WAYS IN, and the FIRST is preferred:
  1. `relation` + `keys` — `relation` is a name off the result's
     `sources.relations[].relation`; `keys` is one JSON object per row you want
     traced. Pass EVERY identifying column the row carries — the reported grain
     AND the period/date — e.g. [{"symbol": "SNOW", "cik": "0001273087",
     "date": "2026-06-30"}]. Extra columns are ignored; a missing one is fatal.
     Grain alone is NOT always enough: the join behind a view can need a column
     its `grain` does not list (current_institutional_holders_v reports grain
     [symbol, cik] but joins on `date` too, and keys without it resolve nothing).
     No token and no SQL, so this is the form
     that works when you assembled a table across several calls (`run_saved_query`
     issues one call per saved token, so a token only ever describes a fragment of
     what you ended up with) or filtered the rows yourself. Trace the rows you will
     actually cite — a few dozen — not the whole result set.
  2. `encrypted_query_token` — a `trace_token` from a `sources` block, or the
     original `query_token` / `signed_query_url`, for the "I have a link and
     nothing else" case. The URL form is unwrapped automatically. Tokens are minted
     server-side only: never construct, guess or edit one, and never pass SQL here.

THREE CITATION GRAINS come back, finest first. Which one answers depends on the
relation and you do not choose it — one surface over several stacks:
  - `citations` carrying `quotes` — the passages an analysis row was actually
    WRITTEN from, with quotes machine-verified against the section text. The
    strongest evidence in the system; quote these verbatim.
  - `citations` of kind `transcript_section` — a link to ONE numbered speaker turn
    of an earnings call. `get_document_section` fetches that turn's text.
  - `documents` — the whole source document (SEC 13F / Form 4 / 8-K / 10-Q / 10-K,
    an earnings-call transcript, an IR PDF) a number was read from. The fallback,
    and available wherever lineage reaches a document at all.
`providers` names which of those answered, and every relation's traceability is
derived from the live data model — a redefined view re-derives its own on the next
refresh, so trust this response over anything remembered.

READ `traceable` BEFORE YOU WRITE. "row" means each row maps to one document and
may be cited that way. "cohort" means the number is a sum over a SET of filings:
the response carries `cohort` instead of `documents`, only the set can honestly be
cited, and writing "per this filing, hedge funds own 4.1%" off a cohort row is the
exact failure this field exists to prevent. "none" means the lineage reaches no
document — say the number cannot be sourced rather than reaching for a plausible
filing. A `reason` of "no_keys" means a column the join needs was missing
from every key object — nearly always the period/date — not that the number is
unsourced. Add the date and any other identifying column on the row, and re-call.

`company_financials` rows: relation = the row's financial_type; keys = symbol, date,
statement, line_item for the standardized tables, symbol, line_item, current_period
for edgar_financials_yoy_change_m. Which keys the as-filed edgar_10q / edgar_10k /
edgar_eight_k tables need is unresolved (whole rows return no_keys) — cite the
standardized row instead, or quote the as-filed row's own filing_url.

`column` traces ONE column instead of the whole row — necessary for a YoY or
change relation, where each value column resolves to a DIFFERENT filing (the
result's `sources.relations[].per_column` says which columns work this way).

`resolve_passages=True` fetches the speaker turn or filing section text instead of
just a link — a link is already a citation. `values` and `prompt` re-aim a
placeholder-twin token but cannot change WHICH documents come back for a given set
of rows.

Requires auth (your MCP client attaches the OAuth bearer automatically). A 403 means
the token is invalid or tampered — re-fetch the link rather than retrying an altered
one; a 422 means neither `relation`+`keys` nor `t` arrived in a usable form.
NameRequiredDescriptionDefault
keysNoWith `relation`: one object per row to trace, carrying EVERY identifying column (the grain AND the period/date), e.g. [{"symbol": "SNOW", "cik": "0001273087", "date": "2026-06-30"}]. A few dozen rows, not the whole set.
columnNoWith `relation`: trace ONE value column instead of the row — needed for YoY/change relations (see `sources.relations[].per_column`).
promptNoToken form, placeholder twins only: natural-language fill for slots `values` did not cover.
valuesNoToken form, placeholder twins only: {slot name: typed value} that re-aims the token deterministically.
relationNoPreferred form: a relation name from the result's `sources.relations[].relation`. Pass with `keys`.
max_documentsNoCap on documents returned (server default 50).
resolve_passagesNorelation+keys form: also fetch the passage TEXT behind each citation (capped at 20). Ask for it when you intend to quote, not by reflex.
encrypted_query_tokenNoFallback form: a `trace_token` from a sources block, or the original `query_token` / `signed_query_url`, verbatim. Never construct or edit one; never pass SQL.

Output Schema

NameRequiredDescription
modeNo"documents" when resolved; "recipe"/"none" otherwise.
cohortNoThe SET of filings a cohort number sums over.
reasonNoWhy nothing resolved, e.g. "no_keys" (a join column, usually the date, was missing).
relationNo
citationsNoFiner citations: verified quotes or transcript speaker turns.
documentsNoWhole source documents (SEC filings, transcripts, IR PDFs).
providersNoCitation providers that answered.
traceableNo"row", "cohort" or "none" — read before writing.
trace_tokenNo

TDQS

A4.9/5.0
Behavior5/5

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

Annotations only declare readOnlyHint, so the description carries most of the burden and does so richly: three citation grains, the traceable row/cohort/none semantics with the concrete failure mode each prevents, the no_keys reason, token-minting rules, auth behavior, and 403/422 error meanings. This is exactly the behavioral context annotations omit.

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

Conciseness4/5

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

Well structured and front-loaded — purpose first, then TWO WAYS IN, THREE CITATION GRAINS, and a READ traceable section. It is long, and a few clauses (e.g. the re-derivation note, the repeated trace-a-few-dozen-rows advice) could be trimmed, but nearly every sentence earns its place for a tool of this complexity.

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

Completeness5/5

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

Despite an output schema existing, the description still supplies the interconnections an agent needs: which grain answers, what providers means, the per-relation key requirements including the unresolved as-filed tables, and the fallback guidance to cite the standardized row. No decision-relevant gap remains.

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

Parameters5/5

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

Schema coverage is already 100%, but the description still adds real meaning beyond it: how to build keys (include the period/date, extra columns ignored, missing fatal, with the current_institutional_holders_v example), what column does for YoY relations, when resolve_passages is warranted ("not by reflex"), and that values/prompt cannot change which documents return.

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

Purpose5/5

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

States a specific verb and resource ("Resolve numbers ... back to the documents they were read from") and immediately distinguishes itself from the row-returning siblings (explore_data_catalogue, run_saved_query, get_signed_sql_drilldown) by framing itself as "where each number CAME FROM." An agent can select this over its siblings without opening a schema.

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

Usage Guidelines5/5

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

Explicit triggers are given ("where does that come from", "which filing", "can you cite that", "how do you know"), plus a proactive condition (before publishing a figure the user will act on). It also routes between the two input forms, naming relation+keys as preferred and explaining the specific cases that select the token form.

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

update_user_templateA
Idempotent
Inspect

Change your saved PDF template — any field, or none to recompile it as stored.

Complements `draft_user_template` (preview), `save_user_template` (create) and
`delete_user_template` (remove). Every argument is optional; a field you omit
keeps its stored value, and the result is compiled into a blueprint again:
  - a VISUAL change: draft the new page with `draft_user_template`, show the
    previews, then pass the chosen `draft_id` + `variant_id` here (both or
    neither);
  - a change without previews: pass `template`, `template_type` and/or
    `anchor` directly (HTML only — markdown is rejected with a 422);
  - NO arguments: recompiles the stored template with the current compiler —
    how a saved design picks up an improved compile without resubmitting it.

Async: returns {"task_id", "status": "PENDING", "changed": [the fields that
were updated], "next_step"} — poll `get_task_status` for {"status": "SAVED",
"patterns", "tones", "warnings"}. A 404 means nothing is saved yet (use
`save_user_template` first); a 422 lists validation errors in `detail.errors`.

Requires auth (your MCP client attaches the OAuth bearer automatically).
NameRequiredDescriptionDefault
anchorNoNew add_on section, e.g. "technical". Omit to keep the stored one.
draft_idNoVisual change: the draft_id from draft_user_template's finished task. Pass with variant_id.
templateNoNew HTML template without previews (markdown is a 422). Omit to keep the stored one.
variant_idNoVisual change: the variant_id the user chose from the previews. Pass with draft_id.
template_typeNoNew type, "add_on" or "bespoke". Omit to keep the stored one.

Output Schema

NameRequiredDescription
statusYesTask state at dispatch, normally "PENDING".
changedNoupdate_user_template only: the fields that were updated.
task_idYesCelery task id; poll it with get_task_status for the result.
next_stepNoWhat the finished task returns.

TDQS

A4.5/5.0
Behavior3/5

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

Annotations already declare readOnlyHint=false, idempotentHint=true, and destructiveHint=false, so the mutation profile is known. The description adds real value beyond that — async task_id/PENDING flow, get_task_status polling, 404 = nothing saved, 422 = detail.errors — but partially overlaps the idempotent/stored-value semantics the hints already imply. Strong supplementary context, though the async and error details are the main non-annotation contributions.

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

Conciseness5/5

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

Front-loads the core verb/resource, then uses a clean bulleted structure for the three usage modes, and closes with async/error/auth notes. Every sentence carries routing or behavioral information; no filler.

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

Completeness5/5

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

An output schema exists, yet the description still explains the async return shape and the polling path, plus the 404/422 failure meanings and the automatic OAuth requirement. For a 5-param, all-optional mutation tool this is complete.

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

Parameters4/5

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

Schema coverage is 100%, so the schema already documents each parameter including the markdown-422 note and the draft_id/variant_id pairing. The description's added value is framing the parameters into coherent workflows ('both or neither', 'any field or none to recompile'), which contextualizes how the optional fields combine rather than repeating definitions.

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

Purpose5/5

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

States a specific verb ('Change your saved PDF template') and resource, and immediately distinguishes itself from `draft_user_template`, `save_user_template`, and `delete_user_template` by naming each. An agent can route among the four siblings without opening any schema.

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

Usage Guidelines5/5

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

Provides an explicit decision tree: visual change (draft then pass draft_id+variant_id), change without previews (pass template/template_type/anchor), and no-arguments recompile. Each branch states the condition that selects it, leaving nothing to inference.

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

upgrade_planAInspect

Get a secure link for the user to upgrade or change their Flexreport Finance plan (e.g. to raise their request limit). Returns a checkout link — the user completes payment in their browser. Call this whenever the user asks to upgrade, subscribe, pay for, or change their plan, or when they've hit a usage/quota limit.

NameRequiredDescriptionDefault

No parameters

Output Schema

NameRequiredDescription
urlNoThe browser link; null when it could not be generated.
usageNoCurrent plan usage (upgrade_plan only, when known).
messageYesReady-to-show markdown for the user, including the link when there is one.

TDQS

A4.2/5.0
Behavior4/5

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

Annotations already declare readOnlyHint=false and destructiveHint=false, so the safety profile is partly covered. The description adds real behavioral context beyond that: it returns a checkout link and the user completes payment in their browser, implying the plan is not changed server-side by this call alone.

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

Conciseness5/5

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

Three tight sentences with the action and return value front-loaded and the invocation triggers last. Every sentence carries information; nothing is redundant.

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

Completeness5/5

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

An output schema exists, so return-value documentation is not required, yet the description still states the key return (a checkout link). For a zero-parameter, non-destructive tool, this is sufficient for an agent to call it correctly.

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

Parameters4/5

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

The tool takes zero parameters, so the baseline is 4 and there is nothing for the description to disambiguate. No parameter-level detail is needed or missing.

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

Purpose4/5

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

The description states a specific verb and resource ('Get a secure link for the user to upgrade or change their ... plan') and clarifies the outcome ('Returns a checkout link'). It is clear on its own, but it never distinguishes itself from the sibling manage_billing, which an agent could reasonably confuse it with.

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

Usage Guidelines4/5

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

It gives explicit triggering conditions: 'Call this whenever the user asks to upgrade, subscribe, pay for, or change their plan, or when they've hit a usage/quota limit.' That is strong when-to-use guidance, but there is no when-not-to-use and no reference to the alternative manage_billing tool.

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. 2 tool updates
    • Changedcompany_financials1 field changed
      • addedOutput schema / properties / note
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "Set only when no rows came back: what an empty result can mean.",
        +  "title": "Note"
        +}
    • Changedget_latest_report1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "$defs": {
        +    "CachedReport": {
        +      "additionalProperties": true,
        +      "properties": {
        +        "age_hours": {
        +          "anyOf": [
        +            {
        +              "type": "number"
        +            },
        +            {
        +              "type": "null"
        +            }
        +          ],
        +          "default": null,
        +          "description": "Hours since generated_at.",
        +          "title": "Age Hours"
        +        },
        +        "generated_at": {
        +          "anyOf": [
        +            {
        +              "type": "string"
        +            },
        +            {
        +              "type": "null"
        +            }
        +          ],
        +          "default": null,
        +          "description": "When the cached PDF was built (ISO UTC).",
        +          "title": "Generated At"
        +        },
        +        "latest_event_at": {
        +          "anyOf": [
        +            {
        +              "type": "string"
        +            },
        +            {
        +              "type": "null"
        +            }
        +          ],
        +          "default": null,
        +          "description": "Timestamp of the symbol's latest report inputs (the event).",
        +          "title": "Latest Event At"
        +        },
        +        "stale": {
        +          "anyOf": [
        +            {
        +              "type": "boolean"
        +            },
        +            {
        +              "type": "null"
        +            }
        +          ],
        +          "default": null,
        +          "description": "True: the PDF predates the latest event. False: it reflects it. Null: freshness unknown.",
        +          "title": "Stale"
        +        },
        +        "symbol": {
        +          "title": "Symbol",
        +          "type": "string"
        +        },
        +        "template": {
        +          "anyOf": [
        +            {
        +              "type": "string"
        +            },
        +            {
        +              "type": "null"
        +            }
        +          ],
        +          "default": null,
        +          "description": "\"user\": the caller's own copy in their saved template. \"default\": the platform layout.",
        +          "title": "Template"
        +        },
        +        "url": {
        +          "anyOf": [
        +            {
        +              "type": "string"
        +            },
        +            {
        +              "type": "null"
        +            }
        +          ],
        +          "default": null,
        +          "description": "Presigned link to the PDF, valid ~6h from the call. Download it to read the report; hand it to the user to open. Call get_latest_report again for a fresh link; never edit one.",
        +          "title": "Url"
        +        }
        +      },
        +      "required": [
        +        "symbol"
        +      ],
        +      "title": "CachedReport",
        +      "type": "object"
        +    },
        +    "ReportTask": {
        +      "additionalProperties": true,
        +      "properties": {
        +        "status": {
        +          "description": "Task state at dispatch, normally \"PENDING\".",
        +          "title": "Status",
        +          "type": "string"
        +        },
        +        "symbol": {
        +          "anyOf": [
        +            {
        +              "type": "string"
        +            },
        +            {
        +              "type": "null"
        +            }
        +          ],
        +          "default": null,
        +          "description": "Ticker the report task renders.",
        +          "title": "Symbol"
        +        },
        +        "task_id": {
        +          "description": "Celery task id; poll it with get_task_status until SUCCESS.",
        +          "title": "Task Id",
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "task_id",
        +        "status"
        +      ],
        +      "title": "ReportTask",
        +      "type": "object"
        +    }
        +  },
        +  "additionalProperties": true,
        +  "properties": {
        +    "missing": {
        +      "description": "Symbols with no report written yet.",
        +      "items": {
        +        "type": "string"
        +      },
        +      "title": "Missing",
        +      "type": "array"
        +    },
        +    "rendering": {
        +      "additionalProperties": {
        +        "$ref": "#/$defs/ReportTask"
        +      },
        +      "description": "Always empty: reports are written at event time and nothing renders on fetch. Kept for older clients.",
        +      "title": "Rendering",
        +      "type": "object"
        +    },
        +    "result": {
        +      "description": "Symbols served from the cached PDF.",
        +      "items": {
        +        "$ref": "#/$defs/CachedReport"
        +      },
        +      "title": "Result",
        +      "type": "array"
        +    },
        +    "stale_symbols": {
        +      "description": "The `result` symbols flagged stale: true.",
        +      "items": {
        +        "type": "string"
        +      },
        +      "title": "Stale Symbols",
        +      "type": "array"
        +    }
        +  },
        +  "title": "CachedReportsResult",
        +  "type": "object"
        +}
  2. 3 tool updates
    • Addedcompany_financials
    • Changedlist_available_reports1 field changed
      • changedInput schema / properties / event_types / description
        Previous value: -"Plan-earning event types to list: eps_update, eps_release, 8k_release, financials_release, ir_publication, transcript_update, company_update. Any other type is a 422. Omit for every type."New value: +"Plan-earning event types to list: eps_update, eps_release, 8k_release, financials_release, ir_publication, transcript_update, biggest_mover, biggest_gainer, biggest_loser, company_update. Any other type is a 422. Omit for every type."
    • Changedsituate15 fields changed
      • addedInput schema / properties / card_types
        Added value: +{
        +  "anyOf": [
        +    {
        +      "items": {
        +        "type": "string"
        +      },
        +      "maxItems": 20,
        +      "type": "array"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "WITH symbols: return cards only for these event types instead of every type present; entailed and relations shrink with them. E.g. [\"financials_release\"] with event_type.",
        +  "title": "Card Types"
        +}
      • addedInput schema / properties / event_type
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "WITH symbols: the realtime event type the question is about, e.g. \"financials_release\" for a 10-Q/10-K, \"ir_publication\" for a deck, \"8k_release\". Its card is returned even when it has no node in the window. Valid values: list_options(\"event_types\").",
        +  "title": "Event Type"
        +}
      • addedInput schema / properties / event_types
        Added value: +{
        +  "anyOf": [
        +    {
        +      "items": {
        +        "type": "string"
        +      },
        +      "maxItems": 10,
        +      "type": "array"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "NO symbols: the realtime event types the question is about, in the order named, e.g. [\"ir_publication\"] for decks, [\"13f_exited\"] for hedge-fund exits. Step 1 of the order is the first. Valid values: list_options(\"event_types\").",
        +  "title": "Event Types"
        +}
      • addedInput schema / properties / family
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "NO symbols, no type named: one family whose episode is walked in order — earnings, news, market_movement, institutional_ownership, analyst_activity, strategy, prediction, insider.",
        +  "title": "Family"
        +}
      • addedInput schema / properties / include_fetch
        Added value: +{
        +  "default": true,
        +  "description": "WITH symbols: keep each web node's `fetch` hint (the follow-up call and, on data updates, a signed_query_url for get_signed_sql_drilldown). false shrinks the web 2-4x.",
        +  "title": "Include Fetch",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / include_keys
        Added value: +{
        +  "anyOf": [
        +    {
        +      "items": {
        +        "type": "string"
        +      },
        +      "maxItems": 30,
        +      "type": "array"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "Fields kept inside each card. With symbols the default is persisted_in, sources, entails, preceded_by, layer, read_by, refreshed_by ([] = whole cards; keep read_by: it names the tool that reads each table). With no symbols the default is the trimmed market card.",
        +  "title": "Include Keys"
        +}
      • changedInput schema / properties / question / anyOf
        Previous value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "maxLength": 500,
        +    "type": "string"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • changedInput schema / properties / question / description
        Previous value: -"The user's question VERBATIM; keyword-matched to the realtime event types it names, which decides where the plan starts."New value: +"NO symbols: the user's words verbatim, keyword-routed to event types / a family only when neither event_types nor family is passed. Naming nothing is a market-wide sweep. Ignored with symbols."
      • addedInput schema / properties / report_inventory
        Added value: +{
        +  "default": true,
        +  "description": "NO symbols: include fresh/stale report-plan counts per plan-earning type.",
        +  "title": "Report Inventory",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / sections
        Added value: +{
        +  "anyOf": [
        +    {
        +      "items": {
        +        "enum": [
        +          "web",
        +          "cards",
        +          "relations",
        +          "entailed",
        +          "informs_relations",
        +          "episodes"
        +        ],
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "WITH symbols: sections to return (default web, cards, relations, entailed). Add episodes for the episode definitions; informs_relations is the largest and least targeted.",
        +  "title": "Sections"
        +}
      • changedInput schema / properties / symbols / anyOf
        Previous value: -[
        -  {
        -    "items": {
        -      "type": "string"
        -    },
        -    "type": "array"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "items": {
        +      "type": "string"
        +    },
        +    "maxItems": 5,
        +    "type": "array"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • changedInput schema / properties / symbols / description
        Previous value: -"Tickers to situate (max 5; extras are dropped). Omit for the whole market."New value: +"Tickers to situate, e.g. [\"MAR\"] (max 5; one backend call each, in parallel). Omit for the whole market."
      • addedInput schema / properties / top_tickers
        Added value: +{
        +  "anyOf": [
        +    {
        +      "maximum": 50,
        +      "minimum": 0,
        +      "type": "integer"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "NO symbols: members listed per event type from the realtime cache, newest first (default 10, max 50).",
        +  "title": "Top Tickers"
        +}
      • changedInput schema / properties / window_days / description
        Previous value: -"Event-web look-back in days (backend default 7, max 90)."New value: +"WITH symbols: event-web look-back in days (default 14, max 90)."
      • changedOutput schema / (root)
        Previous value: -{
        -  "$defs": {
        -    "SituateMarket": {
        -      "additionalProperties": true,
        -      "properties": {
        -        "exchange": {
        -          "anyOf": [
        -            {
        -              "type": "string"
        -            },
        -            {
        -              "type": "null"
        -            }
        -          ],
        -          "default": null,
        -          "title": "Exchange"
        -        },
        -        "is_open": {
        -          "anyOf": [
        -            {
        -              "type": "boolean"
        -            },
        -            {
        -              "type": "null"
        -            }
        -          ],
        -          "default": null,
        -          "description": "Null when market status was unavailable.",
        -          "title": "Is Open"
        -        },
        -        "session": {
        -          "anyOf": [
        -            {
        -              "type": "string"
        -            },
        -            {
        -              "type": "null"
        -            }
        -          ],
        -          "default": null,
        -          "description": "regular | closed | unknown.",
        -          "title": "Session"
        -        },
        -        "timezone": {
        -          "anyOf": [
        -            {
        -              "type": "string"
        -            },
        -            {
        -              "type": "null"
        -            }
        -          ],
        -          "default": null,
        -          "title": "Timezone"
        -        }
        -      },
        -      "title": "SituateMarket",
        -      "type": "object"
        -    },
        -    "SituatePlanStep": {
        -      "additionalProperties": true,
        -      "properties": {
        -        "args": {
        -          "additionalProperties": true,
        -          "description": "Valid arguments for `tool` — pass them through unchanged.",
        -          "title": "Args",
        -          "type": "object"
        -        },
        -        "auth": {
        -          "anyOf": [
        -            {
        -              "type": "string"
        -            },
        -            {
        -              "type": "null"
        -            }
        -          ],
        -          "default": null,
        -          "description": "required | public.",
        -          "title": "Auth"
        -        },
        -        "optional": {
        -          "anyOf": [
        -            {
        -              "type": "boolean"
        -            },
        -            {
        -              "type": "null"
        -            }
        -          ],
        -          "default": null,
        -          "title": "Optional"
        -        },
        -        "provenance": {
        -          "anyOf": [
        -            {
        -              "type": "string"
        -            },
        -            {
        -              "type": "null"
        -            }
        -          ],
        -          "default": null,
        -          "title": "Provenance"
        -        },
        -        "step": {
        -          "description": "1-based execution order.",
        -          "title": "Step",
        -          "type": "integer"
        -        },
        -        "sync": {
        -          "anyOf": [
        -            {
        -              "type": "boolean"
        -            },
        -            {
        -              "type": "null"
        -            }
        -          ],
        -          "default": null,
        -          "description": "False means the call returns a task_id to poll with get_task_status.",
        -          "title": "Sync"
        -        },
        -        "tool": {
        -          "description": "Exact tool name to call.",
        -          "title": "Tool",
        -          "type": "string"
        -        },
        -        "why": {
        -          "title": "Why",
        -          "type": "string"
        -        }
        -      },
        -      "required": [
        -        "step",
        -        "tool",
        -        "args",
        -        "why"
        -      ],
        -      "title": "SituatePlanStep",
        -      "type": "object"
        -    },
        -    "SituateSkip": {
        -      "additionalProperties": true,
        -      "properties": {
        -        "args": {
        -          "anyOf": [
        -            {
        -              "additionalProperties": true,
        -              "type": "object"
        -            },
        -            {
        -              "type": "null"
        -            }
        -          ],
        -          "default": null,
        -          "title": "Args"
        -        },
        -        "tool": {
        -          "description": "A call NOT to make.",
        -          "title": "Tool",
        -          "type": "string"
        -        },
        -        "why": {
        -          "title": "Why",
        -          "type": "string"
        -        }
        -      },
        -      "required": [
        -        "tool",
        -        "why"
        -      ],
        -      "title": "SituateSkip",
        -      "type": "object"
        -    },
        -    "SituateSuggestion": {
        -      "additionalProperties": true,
        -      "properties": {
        -        "args": {
        -          "additionalProperties": true,
        -          "title": "Args",
        -          "type": "object"
        -        },
        -        "tool": {
        -          "title": "Tool",
        -          "type": "string"
        -        },
        -        "when": {
        -          "description": "Make this call ONLY if this condition arises after the plan has run.",
        -          "title": "When",
        -          "type": "string"
        -        },
        -        "why": {
        -          "title": "Why",
        -          "type": "string"
        -        }
        -      },
        -      "required": [
        -        "tool",
        -        "when",
        -        "why"
        -      ],
        -      "title": "SituateSuggestion",
        -      "type": "object"
        -    },
        -    "SituateSymbolBlock": {
        -      "additionalProperties": true,
        -      "properties": {
        -        "cache": {
        -          "anyOf": [
        -            {
        -              "additionalProperties": true,
        -              "type": "object"
        -            },
        -            {
        -              "type": "null"
        -            }
        -          ],
        -          "default": null,
        -          "description": "Event types still in (present) or aged out of (expired) the realtime cache.",
        -          "title": "Cache"
        -        },
        -        "cards": {
        -          "anyOf": [
        -            {
        -              "additionalProperties": true,
        -              "type": "object"
        -            },
        -            {
        -              "type": "null"
        -            }
        -          ],
        -          "default": null,
        -          "description": "Trimmed ontology cards, keyed by event type.",
        -          "title": "Cards"
        -        },
        -        "deepen": {
        -          "anyOf": [
        -            {
        -              "items": {
        -                "type": "string"
        -              },
        -              "type": "array"
        -            },
        -            {
        -              "type": "null"
        -            }
        -          ],
        -          "default": null,
        -          "description": "Tools the ontology says deepen this event.",
        -          "title": "Deepen"
        -        },
        -        "episode": {
        -          "anyOf": [
        -            {
        -              "additionalProperties": true,
        -              "type": "object"
        -            },
        -            {
        -              "type": "null"
        -            }
        -          ],
        -          "default": null,
        -          "description": "Dominant episode: anchor and members (arrived | pending | overdue).",
        -          "title": "Episode"
        -        },
        -        "error": {
        -          "anyOf": [
        -            {
        -              "type": "string"
        -            },
        -            {
        -              "type": "null"
        -            }
        -          ],
        -          "default": null,
        -          "description": "Set when the event web errored for this symbol.",
        -          "title": "Error"
        -        },
        -        "freshness": {
        -          "anyOf": [
        -            {
        -              "items": {
        -                "additionalProperties": true,
        -                "type": "object"
        -              },
        -              "type": "array"
        -            },
        -            {
        -              "type": "null"
        -            }
        -          ],
        -          "default": null,
        -          "description": "Per relation: last_refresh_at, next_run_at, reflects_newest_event.",
        -          "title": "Freshness"
        -        },
        -        "newest_event": {
        -          "anyOf": [
        -            {
        -              "additionalProperties": true,
        -              "type": "object"
        -            },
        -            {
        -              "type": "null"
        -            }
        -          ],
        -          "default": null,
        -          "title": "Newest Event"
        -        },
        -        "web": {
        -          "additionalProperties": true,
        -          "description": "Newest event-web nodes and the edges among them (full graph via get_company_event_web).",
        -          "title": "Web",
        -          "type": "object"
        -        }
        -      },
        -      "required": [
        -        "web"
        -      ],
        -      "title": "SituateSymbolBlock",
        -      "type": "object"
        -    },
        -    "SituateUniverseBlock": {
        -      "additionalProperties": true,
        -      "properties": {
        -        "asked_types": {
        -          "anyOf": [
        -            {
        -              "items": {
        -                "type": "string"
        -              },
        -              "type": "array"
        -            },
        -            {
        -              "type": "null"
        -            }
        -          ],
        -          "default": null,
        -          "description": "Realtime event types the question named.",
        -          "title": "Asked Types"
        -        },
        -        "cards": {
        -          "anyOf": [
        -            {
        -              "additionalProperties": true,
        -              "type": "object"
        -            },
        -            {
        -              "type": "null"
        -            }
        -          ],
        -          "default": null,
        -          "title": "Cards"
        -        },
        -        "episode": {
        -          "anyOf": [
        -            {
        -              "type": "string"
        -            },
        -            {
        -              "type": "null"
        -            }
        -          ],
        -          "default": null,
        -          "title": "Episode"
        -        },
        -        "event_types": {
        -          "anyOf": [
        -            {
        -              "items": {
        -                "additionalProperties": true,
        -                "type": "object"
        -              },
        -              "type": "array"
        -            },
        -            {
        -              "type": "null"
        -            }
        -          ],
        -          "default": null,
        -          "description": "The whole realtime corpus: event_type, family, description.",
        -          "title": "Event Types"
        -        },
        -        "family": {
        -          "anyOf": [
        -            {
        -              "type": "string"
        -            },
        -            {
        -              "type": "null"
        -            }
        -          ],
        -          "default": null,
        -          "title": "Family"
        -        },
        -        "order": {
        -          "anyOf": [
        -            {
        -              "items": {
        -                "type": "string"
        -              },
        -              "type": "array"
        -            },
        -            {
        -              "type": "null"
        -            }
        -          ],
        -          "default": null,
        -          "description": "Event types in the order the plan walks them.",
        -          "title": "Order"
        -        },
        -        "shape": {
        -          "anyOf": [
        -            {
        -              "type": "string"
        -            },
        -            {
        -              "type": "null"
        -            }
        -          ],
        -          "default": null,
        -          "description": "named_types | family | sweep.",
        -          "title": "Shape"
        -        }
        -      },
        -      "title": "SituateUniverseBlock",
        -      "type": "object"
        -    }
        -  },
        -  "additionalProperties": true,
        -  "properties": {
        -    "as_of": {
        -      "title": "As Of",
        -      "type": "string"
        -    },
        -    "degraded": {
        -      "description": "True when a source was unavailable; guidance[] says which.",
        -      "title": "Degraded",
        -      "type": "boolean"
        -    },
        -    "guidance": {
        -      "description": "Situation-scoped rules to read before writing.",
        -      "items": {
        -        "type": "string"
        -      },
        -      "title": "Guidance",
        -      "type": "array"
        -    },
        -    "market": {
        -      "$ref": "#/$defs/SituateMarket"
        -    },
        -    "ontology_as_of": {
        -      "anyOf": [
        -        {
        -          "type": "string"
        -        },
        -        {
        -          "type": "null"
        -        }
        -      ],
        -      "default": null,
        -      "title": "Ontology As Of"
        -    },
        -    "plan": {
        -      "description": "ORDERED tool calls to execute as-is.",
        -      "items": {
        -        "$ref": "#/$defs/SituatePlanStep"
        -      },
        -      "title": "Plan",
        -      "type": "array"
        -    },
        -    "realtime_cache_ttl_hours": {
        -      "title": "Realtime Cache Ttl Hours",
        -      "type": "integer"
        -    },
        -    "scope": {
        -      "description": "symbol (symbols were named) or universe (the market).",
        -      "title": "Scope",
        -      "type": "string"
        -    },
        -    "skip": {
        -      "description": "Calls that would return nothing or waste a job — do not make them.",
        -      "items": {
        -        "$ref": "#/$defs/SituateSkip"
        -      },
        -      "title": "Skip",
        -      "type": "array"
        -    },
        -    "suggest": {
        -      "description": "Calls to make only if their `when` arises.",
        -      "items": {
        -        "$ref": "#/$defs/SituateSuggestion"
        -      },
        -      "title": "Suggest",
        -      "type": "array"
        -    },
        -    "symbols": {
        -      "anyOf": [
        -        {
        -          "additionalProperties": {
        -            "$ref": "#/$defs/SituateSymbolBlock"
        -          },
        -          "type": "object"
        -        },
        -        {
        -          "type": "null"
        -        }
        -      ],
        -      "default": null,
        -      "description": "Per-symbol blocks (symbol scope only).",
        -      "title": "Symbols"
        -    },
        -    "universe": {
        -      "anyOf": [
        -        {
        -          "$ref": "#/$defs/SituateUniverseBlock"
        -        },
        -        {
        -          "type": "null"
        -        }
        -      ],
        -      "default": null,
        -      "description": "Market-wide block (universe scope only)."
        -    }
        -  },
        -  "required": [
        -    "as_of",
        -    "scope",
        -    "realtime_cache_ttl_hours",
        -    "degraded",
        -    "market",
        -    "plan",
        -    "skip",
        -    "suggest",
        -    "guidance"
        -  ],
        -  "title": "SituateResult",
        -  "type": "object"
        -}New value: +null
  3. 2 tool updates
    • Changedgenerate_report_for_stock2 fields changed
      • changedInput schema / properties / ticker / description
        Previous value: -"The ONE ticker to rebuild, e.g. \"SNOW\"."New value: +"The ONE ticker to write a new note for, e.g. \"SNOW\"."
      • changedInput schema / properties / user_override / description
        Previous value: -"CUSTOM mode switch (build around the shaping fields, not saved). Set automatically whenever any shaping field is non-empty."New value: +"CUSTOM mode switch (steer the note toward the shaping fields; it still replaces the symbol's report). Set automatically whenever any shaping field is non-empty."
    • Changedlist_available_reports2 fields changed
      • changedInput schema / properties / report_date / description
        Previous value: -"Lower bound on when the plan was built, YYYY-MM-DD (default today). Earlier dates widen the window and cost more; plans stay renderable for 7 days. A malformed date is a 422."New value: +"Lower bound on when the plan was built, YYYY-MM-DD (default today). Earlier dates widen the window and cost more; plans stay listed for 7 days. A malformed date is a 422."
      • changedOutput schema / $defs / ReportPlan / properties / fresh / description
        Previous value: -"True: get_latest_report renders it in ~10-20 s. False: a newer event landed; only a rebuild refreshes it."New value: +"True: the report get_latest_report returns reflects the latest event. False: a newer event landed since (its report may be in progress; otherwise generate_report_for_stock writes one)."
  4. 2 tool updates
    • Changeddraft_custom_dataset2 fields changed
      • changedInput schema / properties / sample_rows / description
        Previous value: -"Rows of each candidate returned as a sample (the backend returns at most 5)."New value: +"Rows of each candidate returned as a sample (at most 5)."
      • changedInput schema / properties / sample_rows / maximum
        Previous value: -100New value: +5
    • Addedget_user_dataset_file
  5. 2 tool updates
    • Changeddraft_custom_dataset2 fields changed
      • changedInput schema / properties / sample_rows / default
        Previous value: -25New value: +5
      • changedInput schema / properties / sample_rows / description
        Previous value: -"Rows of each candidate returned as a sample."New value: +"Rows of each candidate returned as a sample (the backend returns at most 5)."
    • Changedsave_user_dataset2 fields changed
      • changedInput schema / properties / cron / description
        Previous value: -"When to build, 5-field cron in America/New_York, e.g. \"30 18 * * 1-5\" (weekdays 18:30). Minute must be a literal number and hour at most 4 literal hours — no `*`, `/` or `-` in either."New value: +"When to build, any valid 5-field cron in America/New_York, e.g. \"30 18 * * 1-5\" (weekdays 18:30), \"*/30 * * * *\" (every 30 minutes), \"0 9-17 * * 1-5\" (hourly in market hours)."
      • changedInput schema / properties / format / description
        Previous value: -"File format of each build: parquet, csv, tsv, json (an array of records), jsonl, xlsx, xml, or rss / atom (a feed of the newest 200 rows as items). Use the user's choice, else the draft's target_format, else parquet."New value: +"File format of each build: parquet, csv, tsv, json (an array of records), jsonl, xlsx, xml, or rss / atom (a feed of the newest 200 rows as items; rss items carry <company:symbol> and <category> tags for syndication partners). Use the user's choice, else the draft's target_format, else parquet."
  6. 4 tool updates
    • Addeddelete_user_data_feed
    • Changeddraft_custom_dataset1 field changed
      • addedInput schema / properties / target_format
        Added value: +{
        +  "anyOf": [
        +    {
        +      "enum": [
        +        "parquet",
        +        "csv",
        +        "tsv",
        +        "json",
        +        "jsonl",
        +        "xlsx",
        +        "xml",
        +        "rss",
        +        "atom"
        +      ],
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "The format the user plans to save in. rss/atom make the planner shape one row per feed item (title, link, description, pub_date, guid), newest first. Omit when unknown.",
        +  "title": "Target Format"
        +}
    • Changedlist_user_datasets1 field changed
      • addedOutput schema / $defs / UserDataset / properties / latest_url
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "Fixed, sign-in-free link to the newest file; 404 until the first build.",
        +  "title": "Latest Url"
        +}
    • Changedsave_user_dataset5 fields changed
      • changedInput schema / properties / format / description
        Previous value: -"File format of each build."New value: +"File format of each build: parquet, csv, tsv, json (an array of records), jsonl, xlsx, xml, or rss / atom (a feed of the newest 200 rows as items). Use the user's choice, else the draft's target_format, else parquet."
      • changedInput schema / properties / format / enum
        Previous value: -[
        -  "parquet",
        -  "csv"
        -]New value: +[
        +  "parquet",
        +  "csv",
        +  "tsv",
        +  "json",
        +  "jsonl",
        +  "xlsx",
        +  "xml",
        +  "rss",
        +  "atom"
        +]
      • addedOutput schema / properties / feed_fields
        Added value: +{
        +  "anyOf": [
        +    {
        +      "additionalProperties": {
        +        "anyOf": [
        +          {
        +            "type": "string"
        +          },
        +          {
        +            "type": "null"
        +          }
        +        ]
        +      },
        +      "type": "object"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "rss/atom only: the column filling each item field {title, link, description, pub_date, guid}; null = fallback (description lists the other columns, pub_date = build time, guid = link or a row hash).",
        +  "title": "Feed Fields"
        +}
      • changedOutput schema / properties / format / enum
        Previous value: -[
        -  "parquet",
        -  "csv"
        -]New value: +[
        +  "parquet",
        +  "csv",
        +  "tsv",
        +  "json",
        +  "jsonl",
        +  "xlsx",
        +  "xml",
        +  "rss",
        +  "atom"
        +]
      • addedOutput schema / properties / latest_url
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "Fixed, sign-in-free link to the newest file (the link is the key) — for an RSS reader, Excel / Google Sheets web import or a platform's poller. 404 until the first build.",
        +  "title": "Latest Url"
        +}
  7. 3 tool updates
    • Addeddraft_custom_dataset
    • Addedlist_user_datasets
    • Addedsave_user_dataset
  8. 2 tool updates
    • Addeddownload_dataset
    • Changedlist_options2 fields changed
      • changedInput schema / properties / kind / enum
        Previous value: -[
        -  "event_types",
        -  "financial_items",
        -  "financial_ratios",
        -  "sectors",
        -  "institutional_investor_types",
        -  "countries",
        -  "fiscal_quarter",
        -  "market_cap",
        -  "intraday_frequency",
        -  "technical_indicators",
        -  "tickers",
        -  "tickers_with_names",
        -  "as_reported_items",
        -  "revenue_segments",
        -  "institutional_managers"
        -]New value: +[
        +  "event_types",
        +  "financial_items",
        +  "financial_ratios",
        +  "sectors",
        +  "institutional_investor_types",
        +  "countries",
        +  "fiscal_quarter",
        +  "market_cap",
        +  "intraday_frequency",
        +  "technical_indicators",
        +  "tickers",
        +  "tickers_with_names",
        +  "as_reported_items",
        +  "revenue_segments",
        +  "institutional_managers",
        +  "data_feeds"
        +]
      • changedOutput schema / properties / options / description
        Previous value: -"The valid values. Usually a list — bare strings for most kinds, records for event_types, tickers, tickers_with_names, as_reported_items, revenue_segments and institutional_managers. fiscal_quarter is a single quarter number."New value: +"The valid values. Usually a list — bare strings for most kinds, records for event_types, tickers, tickers_with_names, as_reported_items, revenue_segments, institutional_managers and data_feeds. fiscal_quarter is a single quarter number."
  9. 1 tool update
    • Changedis_market_open5 fields changed
      • changedInput schema / properties / exchange / description
        Previous value: -"CASE-SENSITIVE uppercase exchange code, e.g. \"NYSE\", \"NASDAQ\", \"LSE\" (not full names). NYSE is right for US equities."New value: +"Exchange code, e.g. \"NYSE\", \"NASDAQ\", \"LSE\" or an ISO MIC like \"XNYS\" (not full names; case does not matter). NYSE is right for US equities."
      • removedOutput schema / $defs
        Removed value: -{
        -  "ExchangeHours": {
        -    "additionalProperties": true,
        -    "properties": {
        -      "closingHour": {
        -        "anyOf": [
        -          {
        -            "type": "string"
        -          },
        -          {
        -            "type": "null"
        -          }
        -        ],
        -        "default": null,
        -        "description": "Session close with UTC offset.",
        -        "title": "Closinghour"
        -      },
        -      "exchange": {
        -        "anyOf": [
        -          {
        -            "type": "string"
        -          },
        -          {
        -            "type": "null"
        -          }
        -        ],
        -        "default": null,
        -        "title": "Exchange"
        -      },
        -      "isMarketOpen": {
        -        "anyOf": [
        -          {
        -            "type": "boolean"
        -          },
        -          {
        -            "type": "null"
        -          }
        -        ],
        -        "default": null,
        -        "description": "The answer: is the exchange open right now.",
        -        "title": "Ismarketopen"
        -      },
        -      "name": {
        -        "anyOf": [
        -          {
        -            "type": "string"
        -          },
        -          {
        -            "type": "null"
        -          }
        -        ],
        -        "default": null,
        -        "title": "Name"
        -      },
        -      "openingHour": {
        -        "anyOf": [
        -          {
        -            "type": "string"
        -          },
        -          {
        -            "type": "null"
        -          }
        -        ],
        -        "default": null,
        -        "description": "Session open with UTC offset.",
        -        "title": "Openinghour"
        -      },
        -      "timezone": {
        -        "anyOf": [
        -          {
        -            "type": "string"
        -          },
        -          {
        -            "type": "null"
        -          }
        -        ],
        -        "default": null,
        -        "title": "Timezone"
        -      }
        -    },
        -    "title": "ExchangeHours",
        -    "type": "object"
        -  }
        -}
      • removedOutput schema / properties / exchanges
        Removed value: -{
        -  "description": "One record for the requested exchange; read its isMarketOpen.",
        -  "items": {
        -    "$ref": "#/$defs/ExchangeHours"
        -  },
        -  "title": "Exchanges",
        -  "type": "array"
        -}
      • addedOutput schema / properties / is_open
        Added value: +{
        +  "description": "True when the exchange is in its regular session right now.",
        +  "title": "Is Open",
        +  "type": "boolean"
        +}
      • changedOutput schema / required
        Previous value: -[
        -  "exchanges"
        -]New value: +[
        +  "is_open"
        +]
  10. 50 tool updates
    • First observeddelete_event_subscription
    • First observeddelete_saved_query
    • First observeddelete_scheduled_task
    • First observeddelete_user_template
    • First observeddetect_intraday_outlier_jumps
    • First observeddownload_pdf_from_url
    • First observeddraft_user_template
    • First observedexplore_data_catalogue
    • First observedexplore_data_coverage
    • First observedgenerate_report_for_stock
    • First observedgenerate_research_report
    • First observedget_aftermarket_data
    • First observedget_company_event_web
    • First observedget_company_snapshot
    • First observedget_document_section
    • First observedget_event_ontology
    • First observedget_latest_report
    • First observedget_signed_sql_drilldown
    • First observedget_stock_picks
    • First observedget_strategy_performance_summary
    • First observedget_strategy_swaps
    • First observedget_strategy_track_record
    • First observedget_task_status
    • First observedget_technical_indicator_data
    • First observedget_user_template
    • First observedis_market_open
    • First observedlist_available_reports
    • First observedlist_earnings_announcements
    • First observedlist_options
    • First observedlist_realtime_events
    • First observedlist_saved_queries
    • First observedlist_scheduled_tasks
    • First observedlist_sub_industries
    • First observedmanage_billing
    • First observedonboard_symbol
    • First observedoptimize_portfolio
    • First observedoptimize_portfolio_default
    • First observedpredict_earnings_move
    • First observedregister_event_subscription
    • First observedrun_saved_query
    • First observedsave_user_query
    • First observedsave_user_template
    • First observedschedule_task
    • First observedscreen_stocks
    • First observedsituate
    • First observedstream_event_subscription
    • First observedstream_events
    • First observedtrace_data_sources
    • First observedupdate_user_template
    • First observedupgrade_plan

Publisher details

Operator
Flexreport Finance · Publisher source
Vendor relationship
First-party
Trust center
Not available
Restrictions
Requires a Flexreport Finance account (OAuth 2.1 sign-in in the browser; free registration via email or Google). Usage is subject to per-plan quotas and rate limits; higher limits require a paid plan (Stripe checkout). No admin approval, regional limits, or custom OAuth app are needed—clients register dynamically.

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    Remote MCP server that lets AI assistants read tracked competitive intelligence — competitor news, reviews, website changes, social posts and ads — and act on it by managing projects, competitors, battle cards, reports and canvases. Every answer carries a source and a date, and write actions only publish when explicitly shared.
    MIT
  • A
    license
    A
    quality
    C
    maintenance
    Enables brand visibility monitoring across major AI platforms like ChatGPT, Claude, Gemini, and Perplexity. It allows users to track visibility scores, analyze competitor data, and receive actionable insights to improve AI-generated brand recommendations.
    16
    23 npm
    1
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Browse IndustryLens's published competitive-intelligence reports and head-to-head competitor comparisons from any AI agent — real, source-backed data.
    MIT
Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.

Resources