bls-labor-mcp-server
Server Details
Fetch US Bureau of Labor Statistics data — CPI, unemployment, wages, JOLTS, and more via MCP.
- Status
- Healthy
- Uptime
- 100.0% over 78 days
- Last Tested
- Transport
- Streamable HTTP · MCP 2025-11-25
- URL
- Repository
- cyanheads/bls-labor-mcp-server
- GitHub Stars
- 1
- Server Listing
- @cyanheads/bls-labor-mcp-server
- ChatGPT
- –
- Claude
- –
TDQS
Scored across 6 tools
Discovery (bls_list_surveys, bls_search_series), retrieval (bls_get_series, bls_get_latest), and canvas analysis (bls_dataframe_describe, bls_dataframe_query) form clear, mostly non-overlapping groups. The only potential confusion is bls_get_latest vs bls_get_series, since the former is effectively a single-observation shortcut, but descriptions clarify the boundary.
All names are snake_case with a consistent bls_ prefix. There's a minor structural deviation: get_latest, get_series, list_surveys, and search_series use verb_noun, while dataframe_describe and dataframe_query use noun_verb, but the pattern remains readable and predictable.
Six tools is well-matched to the scope: two for catalog discovery, two for data retrieval, and two for canvas analysis. Each tool earns its place with no redundant surface.
The pipeline covers discovery, retrieval, and analysis cleanly, with no dead ends for core workflows. Minor gaps exist, such as no explicit tool to drop/clear a registered dataframe or inspect canvas storage limits, but these are workaroundable via TTL-based expiry.
Available Tools
6 toolsbls_dataframe_describeDescribe BLS DataframesARead-onlyIdempotentInspect
List canvas dataframes materialized by bls_get_series or by bls_dataframe_query register_as, with provenance (source tool, query parameters), TTL, row count, and column schema. Use before writing SQL to confirm column names and types. Lazy-sweeps expired tables before responding.
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | Optional dataframe name to describe a single dataframe — a df_XXXXX_XXXXX name from bls_get_series, or a register_as name from bls_dataframe_query. Omit, or leave blank, to list all active dataframes. |
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | Present when the call failed. Absent on success. |
| dataframes | No | Active dataframes for this tenant, newest first. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover read-only and idempotency, but the description adds real behavioral context: returned provenance, TTL, row counts, column schema, and the non-obvious side effect that expired tables are lazy-swept before responding. That sweep behavior is exactly the kind of trait an agent could not infer 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, front-loaded with the verb and resource, then the usage trigger, then the side effect. Nothing is redundant with the tool name or title.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Covers what is listed, where entries come from, what metadata accompanies each entry, when to call it, and a latent side effect — more than enough for a one-optional-parameter read tool, especially with an output schema carrying return structure.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the single `name` parameter is already fully documented in the schema, including the omit-to-list-all case. The description reinforces 'list all' semantics but adds no format or edge-case detail beyond it, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ("List canvas dataframes") and pins down provenance by naming the exact sibling tools that materialize them (bls_get_series, bls_dataframe_query register_as). An agent can immediately tell this apart from the sibling query/series tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
"Use before writing SQL to confirm column names and types" gives a clear selection context. It stops short of an explicit exclusion (e.g., 'use bls_dataframe_query instead if you already know the columns'), so the alternative is implied rather than stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
bls_dataframe_queryQuery BLS DataframesARead-onlyIdempotentInspect
Run a single-statement SELECT against the canvas dataframes registered by bls_get_series or by an earlier register_as. Read-only: writes, DDL, DROP, COPY, PRAGMA, ATTACH, and external-file table functions are rejected. System catalogs (information_schema, pg_catalog, sqlite_master, duckdb_*) are denied at the bridge layer — use bls_dataframe_describe to list available dataframes. Supports JOINs, aggregates, window functions, and CTEs. Optional register_as persists the result as a new dataframe with a fresh TTL for chained analysis. Canvas SQL operations consume zero BLS API quota.
| Name | Required | Description | Default |
|---|---|---|---|
| sql | Yes | Single-statement SELECT against df_<id> tables on the shared canvas. Reference dataframes by the names returned in bls_get_series responses or listed by bls_dataframe_describe. Standard DuckDB SQL — joins, aggregates, window functions, CTEs all supported. Example: SELECT series_id, year, period, value FROM df_AAAAA_BBBBB WHERE year >= '2020' ORDER BY year DESC. | |
| preview | No | Inline row preview count, clamped to the applied row_limit (at most 10000) with notice. Defaults to row_limit. Set lower (e.g. 50) when chaining via register_as and only a sample is needed immediately. | |
| row_limit | No | Response row limit (default 1000); requests above 10000 are clamped with notice. Full results live on-canvas under register_as when provided. | |
| register_as | No | When set, persist the query result as a new dataframe under this name. Fresh TTL — not inherited from parent tables. Use to chain analyses without re-running source SQL or consuming additional BLS quota. |
Output Schema
| Name | Required | Description |
|---|---|---|
| cap | No | The preview or row_limit cap that was applied. |
| rows | No | Materialized rows, bounded by preview / row_limit. |
| error | No | Present when the call failed. Absent on success. |
| shown | No | Number of rows returned inline. |
| notice | No | Requested and applied limits when clamped, plus guidance when preview or row_limit capped the result and how to retrieve the rest. Absent when limits were unchanged and all rows fit. |
| columns | No | Column names in projection order. |
| row_count | No | Rows materialized by the query. Exact when register_as is used; otherwise equals row_limit when truncated is true. |
| truncated | No | True when the returned rows were capped. |
| expires_at | No | ISO 8601 expiry fixed at the new dataframe’s creation, when applicable; reads do not extend it. |
| registered_as | No | Set when register_as was supplied and the result was materialized. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the readOnly/idempotent annotations: it enumerates rejected statement types (writes, DDL, DROP, COPY, PRAGMA, ATTACH, external-file functions), denied system catalogs, the zero-quota characteristic, and that register_as gets a fresh TTL rather than inheriting the parent's. This is exactly the behavioral context annotations cannot convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the core operation, then layers security constraints, capabilities, and the register_as semantics in a tight sequence. Every sentence adds information; nothing is redundant padding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, rich annotations, and fully documented parameters, the description covers the remaining gaps: what SQL is rejected, catalog access rules, quota cost, and result-persistence behavior. An agent has everything needed to invoke it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is already 100%, so baseline is 3, but the description adds real meaning: register_as 'persists the result as a new dataframe with a fresh TTL' and enables chaining without re-running source SQL or consuming quota. Clamping behavior for preview/row_limit is handled in the schema, so the description complements rather than duplicates.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb and resource: 'Run a single-statement SELECT against the canvas dataframes registered by bls_get_series or by an earlier register_as.' It names the sibling tools that produce the dataframes, so an agent can immediately distinguish this query tool from the registration/listing tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly routes the agent to bls_dataframe_describe for listing dataframes rather than relying on catalog queries, and clarifies the dataframes originate from bls_get_series or register_as. It does not spell out when-not-to-use versus the sibling read tools, but the context is clear enough.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
bls_get_latestGet Latest BLS ObservationARead-onlyIdempotentInspect
Return the single most recent observation for BLS series. SeriesIDs are trimmed, uppercased, and deduplicated in first-occurrence order. Processes the first 50 unique IDs and returns the exact remainder as unprocessedSeriesIds for a separate request. Each processed series costs one API query; bls_get_series with a 1-year window uses one query for a batch. Recommended limit: 10 series.
| Name | Required | Description | Default |
|---|---|---|---|
| series_ids | Yes | BLS SeriesIDs, trimmed, uppercased, and deduplicated in first-occurrence order. Processes the first 50 unique IDs; unprocessedSeriesIds names the exact remainder. Each processed ID consumes one daily API query. Recommended: ≤10 series. |
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | Present when the call failed. Absent on success. |
| failed | No | Series that failed to fetch. Inspect seriesId and error for per-item details. Not-found series appear here rather than as a tool-level error. |
| notice | No | Guidance for unprocessed IDs, unavailable observations, or failed series — bls_search_series for a SeriesID BLS rejected, the state or national row to request for an OEWS SeriesID BLS reports nonexistent, or the reason and recovery for a request failure that left other series answered. Absent when all requested IDs returned available observations. |
| results | No | Successfully fetched series with their latest observations. Series that failed appear in failed[] instead. |
| succeeded | No | Number of series with a successfully fetched observation. |
| unprocessedSeriesIds | No | Canonical unique SeriesIDs beyond the 50-series cap, in original order. Request these separately. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the readOnly/idempotent annotations: discloses normalization (trim/uppercase/dedup in first-occurrence order), a hard 50-ID cap, that the remainder is returned as unprocessedSeriesIds, and the per-series query cost. This is exactly the operational context annotations cannot convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Four tight sentences, front-loaded with the core action, then normalization, then truncation behavior, then cost guidance. No filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Output schema exists so return values needn't be explained, and the description still covers the one non-obvious return field (unprocessedSeriesIds) plus truncation and cost. Nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds cost semantics ('each processed series costs one API query') and the recommended batch size, which the schema only partially mirrors. Marginal added value over the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Return the single most recent observation for BLS series') and implicitly contrasts with bls_get_series for batch retrieval. An agent can distinguish it from siblings without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives a concrete recommended limit (10 series) and names bls_get_series with a 1-year window as the cheaper batch alternative. It stops short of an explicit 'use this when / not when' rule, but the cost comparison effectively routes the agent.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
bls_get_seriesGet BLS Time-Series DataARead-onlyIdempotentInspect
Fetch BLS time-series data in a single API request (one query). SeriesIDs are trimmed, uppercased, and deduplicated in first-occurrence order. Processes up to 25 unique IDs without a key or 50 with one; unprocessedSeriesIds names the exact remainder for a separate request. Supports optional year range (up to 20 years; BLS may reduce it) and BLS-computed net/percent changes when available. BLS can publish a '-' missing-value sentinel; check observation.available before arithmetic. Set annual_average to add BLS-computed annual summary rows, which are not additional periods or universally arithmetic means. Large results spill to a dataframe when canvas is configured; otherwise all observations return inline. Use dataset.name in bls_dataframe_describe, then bls_dataframe_query SQL. Use bls_search_series to resolve concepts to SeriesIDs.
| Name | Required | Description | Default |
|---|---|---|---|
| end_year | No | End year for the data range (inclusive). Supplying it without start_year is rejected before the request — BLS applies no default start year alongside an explicit end year. Pair it with start_year, or omit both for the API default window. | |
| series_ids | Yes | BLS SeriesIDs, trimmed, uppercased, and deduplicated in first-occurrence order. Process up to 25 unique IDs without a key or 50 with one; the exact remainder is returned as unprocessedSeriesIds. The batch costs one API query. | |
| start_year | No | Start year for the data range (inclusive). BLS allows up to 20 years per request and requires both bounds or neither: supplying start_year alone resolves end_year to the current year, capped at start_year + 19. BLS may reduce this window for unregistered access. Omit both for the BLS three-calendar-year default, subject to series coverage. | |
| calculations | No | When true, request BLS-computed period-over-period net/percent changes. BLS returns supported types and intervals; some surveys omit calculations while preserving observations, while an explicit unsupported-calculations rejection raises calculations_not_supported. Consult bls_list_surveys for support. Monthly series may return 1, 3, 6, and 12-month intervals. Mirror rows carry no calculations. calculationsApplied is false when any data-bearing series returns no calculation values; notice names affected series and available calculations remain intact. | |
| annual_average | No | When true, include BLS-computed annual summary rows named "Annual": M13 for monthly series, Q05 for quarterly, S03 for semiannual. These are not universally arithmetic means or additional periods; exclude them from sums or averages over periods. Defaults to false. Independent of year bounds. Surveys without annual summaries return the same rows either way; annualAverageRows reports the count. |
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | Present when the call failed. Absent on success. |
| notice | No | Guidance for agents — names any SeriesID that returned zero observations with its reason (including a failed live fallback's reason and recovery for a SeriesID the local observation mirror does not hold, and the state or national row to request for an OEWS SeriesID BLS reports nonexistent), names mirror-served series that lack requested calculations, reports a resolved end_year the 20-year window capped, and reports the bls_dataframe_describe then bls_dataframe_query workflow when results spill to canvas. Absent when every requested series returned data over the window as asked and it all fit inline. |
| series | No | Series data, in request order. |
| dataset | No | Canvas dataframe handle — present when the observation volume exceeded the inline budget. Call bls_dataframe_describe with dataset.name to inspect column_schema, then use that table name in bls_dataframe_query SQL across the full data. |
| spilled | No | True when results spilled to canvas due to inline budget overflow. |
| endYearApplied | No | End year applied to the query. Resolved from start_year when end_year was omitted, so it can differ from the requested range; notice names the cap when the 20-year window decided it. Absent when no year range was in effect. |
| seriesRequested | No | Number of SeriesIDs requested. Do not compare it against series[] length to find empty series — a SeriesID that returned no data is still listed in series[] with observationCount 0. Check observationCount per entry, or read notice, which names every SeriesID that came back empty. |
| startYearApplied | No | Start year applied to the query, whether it was served live or from the local observation mirror. Absent when no year range was in effect. |
| annualAverageRows | No | How many observations across all series are annual-average rows (period M13/Q05/S03). Present only when annual_average is true; 0 means none of the requested surveys publish annual averages. |
| totalObservations | No | Total observation rows across all requested series. |
| calculationsApplied | No | True when calculations were requested and every data-bearing series returned calculation values. False when disabled, no observations returned, or any data-bearing series lacks calculations; notice names affected series. Absent when calculations was omitted. |
| annualAverageApplied | No | Whether annual-average rows were requested. When false, observations hold real periods only; filter on available before aggregation. |
| unprocessedSeriesIds | No | Canonical unique SeriesIDs beyond the request cap, in original order. Request these separately. |
| availableObservations | No | Rows with a published numeric value. |
| unavailableObservations | No | Rows carrying the BLS "-" missing-value sentinel. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnly, idempotent, openWorld), and the description adds substantial behavioral context beyond them: input normalization order, the 25/50 batch ceiling with an explicit remainder field, the '-' missing-value sentinel with the need to check observation.available, and the fact that large results spill to a dataframe only when canvas is configured. These are exactly the operational details an agent cannot infer from annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the purpose and the batching constraints, and most sentences carry distinct information. It is somewhat long and repeats schema content such as the 25/50 ID limit and trimming/dedup behavior that also appears verbatim in the parameter descriptions.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a read-only fetch tool with full schema coverage and an output schema, nothing essential is missing: normalization, batch limits, overflow handling, missing-value sentinels, year-range defaults, and the downstream handoff to dataframe tools are all covered. Return-format explanation is correctly omitted since an output schema exists.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents every parameter, including the start_year/end_year pairing rule and the semantics of annual_average. The description restates much of this (year range up to 20, annual_average caveat) rather than adding syntax or format details the schema lacks, so baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Fetch BLS time-series data') plus the batching model ('one query'), which is the defining characteristic versus a per-series fetch. It contrasts with siblings implicitly by naming bls_search_series as the lookup step and bls_dataframe_query as the follow-on, so an agent can place it in the workflow without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit routing is present: 'Use bls_search_series to resolve concepts to SeriesIDs' and 'Use dataset.name in bls_dataframe_describe, then bls_dataframe_query SQL.' It also gives a hard precondition (up to 25/50 IDs; remainder in unprocessedSeriesIds). The only gap is that it never says when to prefer bls_get_latest over this tool, so the closest sibling is left for inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
bls_list_surveysList BLS SurveysARead-onlyIdempotentInspect
List BLS survey programs with their abbreviation codes, full names, and metadata about calculation support and annual averages. Use to discover which survey covers a topic before calling bls_search_series; each row's searchCoverage says whether bls_search_series reaches the survey through its offline index, composes its SeriesIDs from code tables, or cannot search it, in which case its series are fetched by SeriesID with bls_get_series. Optional category filter narrows results to prices, employment, wages, productivity, injuries, or time_use surveys.
| Name | Required | Description | Default |
|---|---|---|---|
| category | No | Optional category filter. One of: prices, employment, wages, productivity, injuries, time_use. A survey can appear under more than one category (CES under employment and wages). Omit to list all surveys. |
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | Present when the call failed. Absent on success. |
| total | No | Total surveys returned. |
| surveys | No | BLS survey programs matching the filter, sorted alphabetically by abbreviation. |
| categoryFilter | No | Category filter applied, if any. Absent when all surveys were listed. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnly, idempotent, openWorld), so the bar is lower; the description instead spends its words on the meaningful behavioral detail an agent needs — the three possible values of searchCoverage and what each implies for the next call. It does not mention result size, pagination, or whether the survey catalog is static, which keeps it from a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose is front-loaded in the first clause, and the remaining sentences all carry routing or filtering information. It is dense — one long compound sentence about searchCoverage is harder to parse than a short list would be — but nothing is filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present and full schema coverage on the single parameter, the description only needs to supply the routing logic, and it does: how to pick a survey and what to call next for each coverage case. 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.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the enum is fully documented in the schema, including the nuance that a survey can appear under multiple categories. The description restates the same category list with no added semantics (no defaults beyond 'omit', no ordering or matching behavior), so baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('List BLS survey programs') plus what each row contains (abbreviation codes, full names, calculation-support and annual-average metadata). It is clearly separable from siblings because it names bls_search_series and bls_get_series as the downstream tools rather than overlapping with them.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit trigger ('Use to discover which survey covers a topic before calling bls_search_series') and then spells out the routing decision: searchCoverage tells the agent whether bls_search_series can reach the survey, composes SeriesIDs from code tables, or requires bls_get_series by SeriesID. The alternative path is named with its condition, not left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
bls_search_seriesSearch BLS SeriesARead-onlyInspect
Search the BLS series catalog by natural language query, survey code, geographic area, or keywords to resolve cryptic SeriesIDs. Returns matching series with decoded components (survey, area, item, seasonal flag, and publication frequency for surveys that publish one series at several frequencies) and plain-language names, ranked by relevance and paged with limit and offset. Use this before bls_get_series when you have a concept but not a SeriesID. Operates offline against an index of the major surveys — no API quota consumed. Survey filter accepts the two-letter code of an indexed survey (CU, AP, CE, LN, LA, PC, WP, JT, EC, PR, MP, CW, CM, CI, SM, LE, EI, SU, IP, BD, LU, EE; OE is indexed only when the server sets BLS_CATALOG_INCLUDE_OES=true, and otherwise composes OEWS SeriesIDs from the OE code tables, marked composed and unconfirmed); series in other surveys are fetched by SeriesID with bls_get_series. Area filter accepts place names, USPS state codes, and FIPS, CBSA, and CPI area codes.
| Name | Required | Description | Default |
|---|---|---|---|
| area | No | Geographic area to narrow results to, case-insensitive. A USPS state code (TX) or 2-digit state FIPS code (48) selects the state: its statewide series, the areas in it (metro areas reaching into it included), and series with no decoded area whose title names it. An all-digit area code selects the area it names in every survey that decodes it: 3 digits a combined statistical area (CSA), 4 a CPI area (0000 is the U.S. city average), 5 a county FIPS or metro area (CBSA) code, 7 a city or town (state + place FIPS); a code naming no indexed area returns no series. Any other value, such as a place name ("Seattle") or a SeriesID fragment ("ST48"), matches as a substring of the area name, title, or SeriesID; another all-digit value does too, with a notice. Under survey OE with no indexed OE rows, area selects the OEWS areas to compose: a USPS state code or 2-digit state FIPS names the state, a 5-digit CBSA or 7-digit OEWS area code (0042660, 5300000) its area, and any other value is a substring of an OEWS area name; each metro or state it names also composes its state and national rows. Without this filter, a place query names in full (every distinctive word of it, e.g. "black hawk") ranks first among the series matching every query word; otherwise, among equal matches, current series rank first, then national ones. | |
| limit | No | Maximum number of results to return (default 10); requests above 50 are clamped to 50 with notice. | |
| query | Yes | Natural language or keyword query (e.g. "unemployment rate", "CPI food", "nonfarm payrolls"). Also accepts a SeriesID directly for exact lookup. | |
| offset | No | Number of ranked results to skip, for paging past the first page (default 0). Pass nextOffset from the previous response to get the next page. | |
| survey | No | Two-letter LABSTAT survey abbreviation to filter results, case-insensitive (e.g. CU for CPI, CE for CES, SM for state and metro CES, LN for CPS, LA for LAUS, JT for JOLTS). Indexed surveys: CU, AP, CE, LN, LA, PC, WP, JT, EC, PR, MP, CW, CM, CI, SM, LE, EI, SU, IP, BD, LU, EE; OE (OEWS) is indexed only when the server sets BLS_CATALOG_INCLUDE_OES=true, and without its rows OE composes OEWS SeriesIDs from the OE code tables for the occupation, place, and data type the query and area name, each marked composed. Omit to search all indexed surveys. | |
| seasonal_adjustment | No | When true, return only seasonally adjusted series. When false, return only not-seasonally-adjusted. Omit to return both. |
Output Schema
| Name | Required | Description |
|---|---|---|
| cap | No | The result limit that capped the returned list. |
| error | No | Present when the call failed. Absent on success. |
| shown | No | Number of series returned in this response. |
| capped | No | True when the FTS candidate pool, after the survey/area/seasonal filters, reached the internal cap (~1000), or when composing OEWS SeriesIDs stopped at its 50-row cap with combinations left. totalCount is then a lower bound and offset paging stops at the pool. Narrow the query, add filters, or use a direct SeriesID to get an exact count. |
| notice | No | Requested and applied limits when clamped, and how an all-digit area that is not an area code matched, plus guidance on unindexed surveys, offsets past the end, empty results, the next page, the capped candidate pool, or composed OEWS SeriesIDs, which stay unconfirmed until bls_get_series returns them. Absent when neither applies and this page ends an uncapped list of indexed series. |
| series | No | Matching series, ordered by relevance. |
| truncated | No | True when ranked results remain past this page; nextOffset fetches them. |
| areaFilter | No | Area filter applied, trimmed. Absent when no area filter was passed or it was blank. |
| nextOffset | No | Offset of the next page. Present only when truncated is true. |
| totalCount | No | Total ranked results after every filter (area included), before limit and offset. A lower bound when capped is true — the catalog index may contain more matching series. |
| catalogSize | No | Total series in the loaded catalog index. Distinguishes an empty-result search from a failed catalog load. |
| limitApplied | No | Result limit in effect (defaults to 10 when omitted). |
| surveyFilter | No | Survey filter applied, trimmed and uppercased. Absent when no survey filter was passed or it was blank. |
| offsetApplied | No | Offset in effect (defaults to 0 when omitted). |
| effectiveQuery | No | Query string as the server received and searched on. Confirms interpretation for self-correction. |
| seasonalFilter | No | Seasonal-adjustment filter applied, if any. Absent when not passed. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Well beyond the annotations: it discloses that the tool runs offline against a bundled index with no API quota consumed (consistent with openWorldHint=false), that limits above 50 are clamped with a notice, how results are ranked, and the OE caveat that composed SeriesIDs are 'marked composed and unconfirmed.' These are exactly the operational traits an agent needs and that annotations cannot express.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded correctly: purpose first, then the use-before-bls_get_series rule, then operational caveats. It is dense but mostly earns its length; the main waste is re-listing the 23 survey codes and area-code digits that the schema already carries, which could have been left to the schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a six-parameter discovery tool with an output schema, the description covers everything material: what it returns in outline, ranking and paging behavior, offline/quota status, and the OE indexing dependency. Nothing an agent needs in order to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the schema already documents all six parameters, including the survey and area code semantics. The description largely restates that content (the survey code list is duplicated verbatim in the schema), adding little parameter-level meaning beyond the schema; at most it reinforces default paging via limit/offset. Baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Search the BLS series catalog') and enumerates the accepted query forms (natural language, survey code, area, keywords) plus the goal ('resolve cryptic SeriesIDs'). It explicitly distinguishes itself from bls_get_series, so an agent can route correctly 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit ordering rule: 'Use this before bls_get_series when you have a concept but not a SeriesID,' and closes the loop with 'series in other surveys are fetched by SeriesID with bls_get_series.' The offline/no-quota note further signals when this is the cheap first move.
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.
4 tool updates
- Changed
bls_get_latest1 field changed- changed
Output schema / properties / notice / descriptionPrevious value: -"Guidance for unprocessed IDs, unavailable observations, or failed series — bls_search_series for a SeriesID BLS rejected, or the reason and recovery for a request failure that left other series answered. Absent when all requested IDs returned available observations."New value: +"Guidance for unprocessed IDs, unavailable observations, or failed series — bls_search_series for a SeriesID BLS rejected, the state or national row to request for an OEWS SeriesID BLS reports nonexistent, or the reason and recovery for a request failure that left other series answered. Absent when all requested IDs returned available observations."
- Changed
bls_get_series2 fields changed- changed
Output schema / properties / error / properties / data / properties / reason / descriptionPrevious value: -"Machine-readable failure mode. Declared by this tool: `service_unavailable`: BLS is unreachable or returns an unsuccessful HTTP response and no mirrored observations can answer the request. `serialization_failure`: BLS returns malformed JSON and no mirrored observations can answer the request. `upstream_timeout`: The live BLS request exceeds its shared 45-second operation budget and no mirrored observations can answer the request. `invalid_api_key`: BLS rejected the configured BLS_API_KEY as invalid. `quota_exceeded`: The BLS API 500 query/day limit has been reached. `request_rejected`: BLS returned a non-success status with a message matching no known failure mode, both for the request and for its automatic re-issue without catalog metadata — e.g. a rejected combination of request parameters. `series_not_found`: No requested series returned data and at least one SeriesID does not exist. A batch mixing an invalid SeriesID with one BLS has no data for lands here too, and names both in the message and recovery hint. `series_locked`: The BLS database is temporarily locked for the requested series. `no_data_for_period`: The requested year range is unusable before the request — start_year after end_year, a span of 20 years or more, or end_year without start_year — or BLS returned data for none of the requested series over the range. `calculations_not_supported`: calculations=true was requested for a survey that does not support it. `canvas_registration_failed`: The result set exceeds the inline budget and canvas is configured, but registering the dataframe failed. Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `service_unavailable`: BLS is unreachable or returns an unsuccessful HTTP response and no mirrored observations can answer the request. `serialization_failure`: BLS returns malformed JSON and no mirrored observations can answer the request. `upstream_timeout`: The live BLS request exceeds its shared 45-second operation budget and no mirrored observations can answer the request. `invalid_api_key`: BLS rejected the configured BLS_API_KEY as invalid. `quota_exceeded`: The BLS API 500 query/day limit has been reached. `request_rejected`: BLS returned a non-success status with a message matching no known failure mode, both for the request and for its automatic re-issue without catalog metadata — e.g. a rejected combination of request parameters. `series_not_found`: No requested series returned data and at least one SeriesID does not exist. A batch mixing an invalid SeriesID with one BLS has no data for lands here too, and names both in the message and recovery hint. For an OEWS SeriesID, usually an occupation–area cell BLS does not publish, the hint names the state or national row to request instead of bls_search_series. `series_locked`: The BLS database is temporarily locked for the requested series. `no_data_for_period`: The requested year range is unusable before the request — start_year after end_year, a span of 20 years or more, or end_year without start_year — or BLS returned data for none of the requested series over the range. `calculations_not_supported`: calculations=true was requested for a survey that does not support it. `canvas_registration_failed`: The result set exceeds the inline budget and canvas is configured, but registering the dataframe failed. Other values are possible when a failure originates below the handler." - changed
Output schema / properties / notice / descriptionPrevious value: -"Guidance for agents — names any SeriesID that returned zero observations with its reason (including a failed live fallback's reason and recovery for a SeriesID the local observation mirror does not hold), names mirror-served series that lack requested calculations, reports a resolved end_year the 20-year window capped, and reports the bls_dataframe_describe then bls_dataframe_query workflow when results spill to canvas. Absent when every requested series returned data over the window as asked and it all fit inline."New value: +"Guidance for agents — names any SeriesID that returned zero observations with its reason (including a failed live fallback's reason and recovery for a SeriesID the local observation mirror does not hold, and the state or national row to request for an OEWS SeriesID BLS reports nonexistent), names mirror-served series that lack requested calculations, reports a resolved end_year the 20-year window capped, and reports the bls_dataframe_describe then bls_dataframe_query workflow when results spill to canvas. Absent when every requested series returned data over the window as asked and it all fit inline."
- Changed
bls_list_surveys2 fields changed- added
Output schema / properties / surveys / items / properties / searchCoverageAdded value: +{ + "description": "How bls_search_series reaches this survey: \"indexed\" when its offline index holds the survey's series (seriesIndexed counts them), \"composed\" when it builds the survey's SeriesIDs from code tables instead (OE without indexed OE rows), \"none\" when it cannot search the survey — fetch those series by SeriesID with bls_get_series. Absent while the catalog index is still loading at startup.", + "enum": [ + "indexed", + "composed", + "none" + ], + "type": "string" +} - added
Output schema / properties / surveys / items / properties / seriesIndexedAdded value: +{ + "description": "Series of this survey in the offline index. Present only when searchCoverage is \"indexed\".", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" +}
- Changed
bls_search_series8 fields changed- changed
Input schema / properties / area / descriptionPrevious value: -"State name, MSA name, or FIPS area code to narrow results to a geographic area — a case-insensitive substring of the area name, title, or SeriesID. Without this filter, specific whole place names in query are relevance preferences; otherwise national series rank first among equal matches."New value: +"Geographic area to narrow results to, case-insensitive. A USPS state code (TX) or 2-digit state FIPS code (48) selects the state: its statewide series, the areas in it (metro areas reaching into it included), and series with no decoded area whose title names it. An all-digit area code selects the area it names in every survey that decodes it: 3 digits a combined statistical area (CSA), 4 a CPI area (0000 is the U.S. city average), 5 a county FIPS or metro area (CBSA) code, 7 a city or town (state + place FIPS); a code naming no indexed area returns no series. Any other value, such as a place name (\"Seattle\") or a SeriesID fragment (\"ST48\"), matches as a substring of the area name, title, or SeriesID; another all-digit value does too, with a notice. Under survey OE with no indexed OE rows, area selects the OEWS areas to compose: a USPS state code or 2-digit state FIPS names the state, a 5-digit CBSA or 7-digit OEWS area code (0042660, 5300000) its area, and any other value is a substring of an OEWS area name; each metro or state it names also composes its state and national rows. Without this filter, a place query names in full (every distinctive word of it, e.g. \"black hawk\") ranks first among the series matching every query word; otherwise, among equal matches, current series rank first, then national ones." - changed
Input schema / properties / survey / descriptionPrevious value: -"Two-letter LABSTAT survey abbreviation to filter results, case-insensitive (e.g. CU for CPI, CE for CES, LN for CPS, LA for LAUS, JT for JOLTS). Indexed surveys: CU, AP, CE, LN, LA, PC, WP, JT, EC, PR, MP, CW, CM, CI; OE (OEWS) only when the server sets BLS_CATALOG_INCLUDE_OES=true. Omit to search all indexed surveys."New value: +"Two-letter LABSTAT survey abbreviation to filter results, case-insensitive (e.g. CU for CPI, CE for CES, SM for state and metro CES, LN for CPS, LA for LAUS, JT for JOLTS). Indexed surveys: CU, AP, CE, LN, LA, PC, WP, JT, EC, PR, MP, CW, CM, CI, SM, LE, EI, SU, IP, BD, LU, EE; OE (OEWS) is indexed only when the server sets BLS_CATALOG_INCLUDE_OES=true, and without its rows OE composes OEWS SeriesIDs from the OE code tables for the occupation, place, and data type the query and area name, each marked composed. Omit to search all indexed surveys." - changed
Output schema / properties / capped / descriptionPrevious value: -"True when the FTS candidate pool, after the survey/area/seasonal filters, reached the internal cap (~1000). totalCount is then a lower bound and offset paging stops at the pool. Narrow the query, add filters, or use a direct SeriesID to get an exact count."New value: +"True when the FTS candidate pool, after the survey/area/seasonal filters, reached the internal cap (~1000), or when composing OEWS SeriesIDs stopped at its 50-row cap with combinations left. totalCount is then a lower bound and offset paging stops at the pool. Narrow the query, add filters, or use a direct SeriesID to get an exact count." - changed
Output schema / properties / error / properties / data / properties / reason / descriptionPrevious value: -"Machine-readable failure mode. Declared by this tool: `catalog_unavailable`: The catalog index failed to load at startup. Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `catalog_loading`: The catalog index holds no series yet and a LABSTAT harvest is building it — at first startup, or while an hourly check retries an empty index. `catalog_unavailable`: The catalog index holds no series and no harvest is running — every LABSTAT download failed, or the load stopped with an error. Other values are possible when a failure originates below the handler." - changed
Output schema / properties / error / properties / data / properties / reason / examplesPrevious value: -[ - "catalog_unavailable" -]New value: +[ + "catalog_loading", + "catalog_unavailable" +] - changed
Output schema / properties / notice / descriptionPrevious value: -"Requested and applied limits when clamped, plus guidance on unindexed surveys, offsets past the end, empty results, the next page, or the capped candidate pool. Absent when the limit was unchanged and this page ends an uncapped list with results."New value: +"Requested and applied limits when clamped, and how an all-digit area that is not an area code matched, plus guidance on unindexed surveys, offsets past the end, empty results, the next page, the capped candidate pool, or composed OEWS SeriesIDs, which stay unconfirmed until bls_get_series returns them. Absent when neither applies and this page ends an uncapped list of indexed series." - added
Output schema / properties / series / items / properties / composedAdded value: +{ + "description": "True when the SeriesID was composed from the OEWS code tables rather than read from the index: unconfirmed until bls_get_series returns data for it, since BLS leaves many occupation–area cells unpublished. Composed rows carry no coverage years. Absent for indexed series.", + "type": "boolean" +} - changed
Output schema / properties / series / items / properties / frequency / descriptionPrevious value: -"Publication frequency — \"Monthly\", \"Semi-Annual\", \"Quarterly\", or \"Annual\" — for CU, CW, and LN, which publish one series at several frequencies under the same title. Absent for other surveys."New value: +"Publication frequency — \"Monthly\", \"Semi-Annual\", \"Quarterly\", or \"Annual\" — for CU, CW, LN, and BD, which publish one series at several frequencies under the same title. Absent for other surveys."
6 tool updates
- Changed
bls_dataframe_describe1 field changed- changed
Output schema / properties / dataframes / items / properties / expires_at / descriptionPrevious value: -"ISO 8601 expiry timestamp (sliding TTL)."New value: +"ISO 8601 table expiry fixed at creation; reads do not extend it."
- Changed
bls_dataframe_query8 fields changed- changed
Input schema / properties / preview / descriptionPrevious value: -"Inline row preview count. Defaults to row_limit. Set lower (e.g. 50) when chaining via register_as and only a sample is needed immediately."New value: +"Inline row preview count, clamped to the applied row_limit (at most 10000) with notice. Defaults to row_limit. Set lower (e.g. 50) when chaining via register_as and only a sample is needed immediately." - changed
Input schema / properties / preview / maximumPrevious value: -10000New value: +9007199254740991 - changed
Input schema / properties / row_limit / descriptionPrevious value: -"Hard cap on rows materialized in the response (default 1000, max 10000). Full results live on-canvas under register_as when provided."New value: +"Response row limit (default 1000); requests above 10000 are clamped with notice. Full results live on-canvas under register_as when provided." - changed
Input schema / properties / row_limit / maximumPrevious value: -10000New value: +9007199254740991 - changed
Output schema / properties / error / properties / data / properties / reason / descriptionPrevious value: -"Machine-readable failure mode. Declared by this tool: `canvas_unavailable`: The DataCanvas service is not configured for this deployment. Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `register_as_clash`: The requested register_as name already identifies a dataframe. `canvas_unavailable`: The DataCanvas service is not configured for this deployment. Other values are possible when a failure originates below the handler." - changed
Output schema / properties / error / properties / data / properties / reason / examplesPrevious value: -[ - "canvas_unavailable" -]New value: +[ + "register_as_clash", + "canvas_unavailable" +] - changed
Output schema / properties / expires_at / descriptionPrevious value: -"ISO 8601 expiry for the newly registered dataframe, when applicable."New value: +"ISO 8601 expiry fixed at the new dataframe’s creation, when applicable; reads do not extend it." - changed
Output schema / properties / notice / descriptionPrevious value: -"Guidance when results were capped by preview or row_limit — names which parameter was the binding limiter and suggests how to retrieve the rest. Absent when all rows fit in the response."New value: +"Requested and applied limits when clamped, plus guidance when preview or row_limit capped the result and how to retrieve the rest. Absent when limits were unchanged and all rows fit."
- Changed
bls_get_latest12 fields changed- changed
Input schema / properties / series_ids / descriptionPrevious value: -"One or more BLS SeriesIDs (1–50). Each consumes one daily API query. Use bls_search_series to resolve concepts to SeriesIDs. Recommended: ≤10 series."New value: +"BLS SeriesIDs, trimmed, uppercased, and deduplicated in first-occurrence order. Processes the first 50 unique IDs; unprocessedSeriesIds names the exact remainder. Each processed ID consumes one daily API query. Recommended: ≤10 series." - removed
Input schema / properties / series_ids / maxItemsRemoved value: -50 - changed
Output schema / properties / error / properties / data / properties / reason / descriptionPrevious value: -"Machine-readable failure mode. Declared by this tool: `invalid_api_key`: BLS rejected the configured BLS_API_KEY as invalid, and no requested series returned an observation. `quota_exceeded`: The BLS API 500 query/day limit has been reached, and no requested series returned an observation. `series_locked`: The BLS database is temporarily locked for the requested series, and no requested series returned an observation. Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `service_unavailable`: BLS is unreachable or returns an unsuccessful HTTP response, and no requested series returned an observation. `serialization_failure`: BLS returns malformed JSON, and no requested series returned an observation. `upstream_timeout`: The live BLS request exceeds its shared 45-second operation budget, and no requested series returned an observation. `invalid_api_key`: BLS rejected the configured BLS_API_KEY as invalid, and no requested series returned an observation. `quota_exceeded`: The BLS API 500 query/day limit has been reached, and no requested series returned an observation. `series_locked`: The BLS database is temporarily locked for the requested series, and no requested series returned an observation. Other values are possible when a failure originates below the handler." - changed
Output schema / properties / error / properties / data / properties / reason / examplesPrevious value: -[ - "invalid_api_key", - "quota_exceeded", - "series_locked" -]New value: +[ + "service_unavailable", + "serialization_failure", + "upstream_timeout", + "invalid_api_key", + "quota_exceeded", + "series_locked" +] - changed
Output schema / properties / failed / items / properties / error / descriptionPrevious value: -"Error message. Common values: \"Series does not exist\" or \"Invalid Series\" (invalid SeriesID — use bls_search_series to find valid IDs), \"No observations returned\" (series exists but has no current data). The generic \"Your request has failed. Please check your input parameters, and try your request again.\" means BLS rejected the request itself twice, the second time without catalog metadata, rather than naming the SeriesID. A quota, API-key, or database-lock failure lands here only when another series returned an observation; notice then names its reason and recovery."New value: +"Error message. Common values: \"Series does not exist\" or \"Invalid Series\" (invalid SeriesID — use bls_search_series to find valid IDs), \"No observations returned\" (series exists but has no current data). The generic \"Your request has failed. Please check your input parameters, and try your request again.\" means BLS rejected the request itself twice, the second time without catalog metadata, rather than naming the SeriesID. A quota, API-key, database-lock, availability, malformed-response, or timeout failure lands here only when another series returned an observation; notice then names its reason and recovery." - changed
Output schema / properties / notice / descriptionPrevious value: -"Guidance when one or more series failed — bls_search_series for a SeriesID BLS rejected, or the reason and recovery for a quota, API-key, or database-lock failure that left other series answered. Absent when all series returned data."New value: +"Guidance for unprocessed IDs, unavailable observations, or failed series — bls_search_series for a SeriesID BLS rejected, or the reason and recovery for a request failure that left other series answered. Absent when all requested IDs returned available observations." - changed
Output schema / properties / results / items / properties / area / descriptionPrevious value: -"Geographic area when returned by the API."New value: +"Geographic area from BLS or the catalog index." - changed
Output schema / properties / results / items / properties / item / descriptionPrevious value: -"Item/subject when returned by the API."New value: +"Item/subject from BLS or the catalog index." - added
Output schema / properties / results / items / properties / measureAdded value: +{ + "description": "Measurement type and units supplied by BLS.", + "type": "string" +} - changed
Output schema / properties / results / items / properties / seasonal / descriptionPrevious value: -"Seasonality indicator when returned by the API."New value: +"Seasonality indicator from BLS or the catalog index." - changed
Output schema / properties / results / items / properties / title / descriptionPrevious value: -"Series name when returned by the API."New value: +"Series name from BLS or the catalog index." - added
Output schema / properties / unprocessedSeriesIdsAdded value: +{ + "description": "Canonical unique SeriesIDs beyond the 50-series cap, in original order. Request these separately.", + "items": { + "type": "string" + }, + "type": "array" +}
- Changed
bls_get_series12 fields changed- changed
Input schema / properties / annual_average / descriptionPrevious value: -"When true, add each year's annual-average row to the observations. An annual average is the mean of that year's real periods, returned as an extra row named \"Annual\" with period M13 (monthly series), Q05 (quarterly) or S03 (semiannual) — not an additional month or quarter, so it must be excluded from any sum or average over observations. Defaults to false, which returns real periods only; check available before aggregating. Independent of start_year/end_year. Surveys that publish no annual averages return the same rows either way; enrichment.annualAverageRows reports how many rows were actually added."New value: +"When true, include BLS-computed annual summary rows named \"Annual\": M13 for monthly series, Q05 for quarterly, S03 for semiannual. These are not universally arithmetic means or additional periods; exclude them from sums or averages over periods. Defaults to false. Independent of year bounds. Surveys without annual summaries return the same rows either way; annualAverageRows reports the count." - changed
Input schema / properties / calculations / descriptionPrevious value: -"When true, request BLS-computed period-over-period calculations. The flag is a single boolean (you cannot select an individual calculation type), but the API returns whichever the survey supports and omits the rest — CPI and PPI return percent change only (the inflation rate), and a survey that supports neither simply returns its observations without calculation fields. Requesting calculations never fails, so it is always safe to set; consult bls_list_surveys (allowsNetChange / allowsPercentChange) only to predict which fields will come back. Monthly-cadence series return each supported change type over 1, 3, 6, and 12-month intervals; other cadences return a subset. A series served from the local observation mirror, when the server runs one, carries no calculation fields; enrichment.calculationsApplied is then false and notice names it."New value: +"When true, request BLS-computed period-over-period net/percent changes. BLS returns supported types and intervals; some surveys omit calculations while preserving observations, while an explicit unsupported-calculations rejection raises calculations_not_supported. Consult bls_list_surveys for support. Monthly series may return 1, 3, 6, and 12-month intervals. Mirror rows carry no calculations. calculationsApplied is false when any data-bearing series returns no calculation values; notice names affected series and available calculations remain intact." - changed
Input schema / properties / series_ids / descriptionPrevious value: -"One or more BLS SeriesIDs (1–50). The entire batch counts as one API query. Use bls_search_series to resolve concepts to SeriesIDs."New value: +"BLS SeriesIDs, trimmed, uppercased, and deduplicated in first-occurrence order. Process up to 25 unique IDs without a key or 50 with one; the exact remainder is returned as unprocessedSeriesIds. The batch costs one API query." - removed
Input schema / properties / series_ids / maxItemsRemoved value: -50 - changed
Input schema / properties / start_year / descriptionPrevious value: -"Start year for the data range (inclusive). The BLS API allows up to 20 years per request and requires both bounds or neither: supplying start_year alone resolves end_year to the current year, capped at start_year + 19 so the window stays inside the 20-year limit. Omit both for the API default (typically 3–20 years depending on survey)."New value: +"Start year for the data range (inclusive). BLS allows up to 20 years per request and requires both bounds or neither: supplying start_year alone resolves end_year to the current year, capped at start_year + 19. BLS may reduce this window for unregistered access. Omit both for the BLS three-calendar-year default, subject to series coverage." - changed
Output schema / properties / calculationsApplied / descriptionPrevious value: -"Whether BLS net/percent-change calculations were applied. True when calculations=true reached BLS for every returned series; false when calculations=false, or when the local observation mirror served a series, whose rows carry no calculations — notice then names those series. Absent when calculations was omitted."New value: +"True when calculations were requested and every data-bearing series returned calculation values. False when disabled, no observations returned, or any data-bearing series lacks calculations; notice names affected series. Absent when calculations was omitted." - changed
Output schema / properties / dataset / properties / expires_at / descriptionPrevious value: -"ISO 8601 expiry timestamp (sliding 24h window)."New value: +"ISO 8601 per-table expiry, fixed at creation; the enclosing canvas has a separate sliding lifetime." - changed
Output schema / properties / error / properties / data / properties / reason / descriptionPrevious value: -"Machine-readable failure mode. Declared by this tool: `invalid_api_key`: BLS rejected the configured BLS_API_KEY as invalid. `quota_exceeded`: The BLS API 500 query/day limit has been reached. `request_rejected`: BLS returned a non-success status with a message matching no known failure mode, both for the request and for its automatic re-issue without catalog metadata — e.g. a rejected combination of request parameters. `series_not_found`: No requested series returned data and at least one SeriesID does not exist. A batch mixing an invalid SeriesID with one BLS has no data for lands here too, and names both in the message and recovery hint. `series_locked`: The BLS database is temporarily locked for the requested series. `no_data_for_period`: The requested year range is unusable before the request — start_year after end_year, a span of 20 years or more, or end_year without start_year — or BLS returned data for none of the requested series over the range. `calculations_not_supported`: calculations=true was requested for a survey that does not support it. `canvas_unavailable`: The result set exceeds the inline budget and canvas (DuckDB) is not configured. `canvas_registration_failed`: The result set exceeds the inline budget and canvas is configured, but registering the dataframe failed. Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `service_unavailable`: BLS is unreachable or returns an unsuccessful HTTP response and no mirrored observations can answer the request. `serialization_failure`: BLS returns malformed JSON and no mirrored observations can answer the request. `upstream_timeout`: The live BLS request exceeds its shared 45-second operation budget and no mirrored observations can answer the request. `invalid_api_key`: BLS rejected the configured BLS_API_KEY as invalid. `quota_exceeded`: The BLS API 500 query/day limit has been reached. `request_rejected`: BLS returned a non-success status with a message matching no known failure mode, both for the request and for its automatic re-issue without catalog metadata — e.g. a rejected combination of request parameters. `series_not_found`: No requested series returned data and at least one SeriesID does not exist. A batch mixing an invalid SeriesID with one BLS has no data for lands here too, and names both in the message and recovery hint. `series_locked`: The BLS database is temporarily locked for the requested series. `no_data_for_period`: The requested year range is unusable before the request — start_year after end_year, a span of 20 years or more, or end_year without start_year — or BLS returned data for none of the requested series over the range. `calculations_not_supported`: calculations=true was requested for a survey that does not support it. `canvas_registration_failed`: The result set exceeds the inline budget and canvas is configured, but registering the dataframe failed. Other values are possible when a failure originates below the handler." - changed
Output schema / properties / error / properties / data / properties / reason / examplesPrevious value: -[ - "invalid_api_key", - "quota_exceeded", - "request_rejected", - "series_not_found", - "series_locked", - "no_data_for_period", - "calculations_not_supported", - "canvas_unavailable", - "canvas_registration_failed" -]New value: +[ + "service_unavailable", + "serialization_failure", + "upstream_timeout", + "invalid_api_key", + "quota_exceeded", + "request_rejected", + "series_not_found", + "series_locked", + "no_data_for_period", + "calculations_not_supported", + "canvas_registration_failed" +] - added
Output schema / properties / series / items / properties / measureAdded value: +{ + "description": "Measurement type and units supplied by BLS.", + "type": "string" +} - changed
Output schema / properties / series / items / properties / observations / items / properties / period / descriptionPrevious value: -"BLS period code: M01–M12 are months, Q01–Q04 quarters, S01–S02 semiannual halves. M13, Q05 and S03 are not further periods — each is the mean of that year's real observations, named \"Annual\", and appears only when annual_average is true. Exclude them from any sum or average over observations."New value: +"BLS period code: M01–M12 are months, Q01–Q04 quarters, S01–S02 semiannual halves. M13, Q05 and S03 are BLS-computed annual summaries, named \"Annual\", and appear only when annual_average is true. They are not additional periods or universally arithmetic means. Exclude them from sums or averages over periods." - added
Output schema / properties / unprocessedSeriesIdsAdded value: +{ + "description": "Canonical unique SeriesIDs beyond the request cap, in original order. Request these separately.", + "items": { + "type": "string" + }, + "type": "array" +}
- Changed
bls_list_surveys2 fields changed- changed
Output schema / properties / error / properties / data / properties / reason / descriptionPrevious value: -"Machine-readable failure mode. Declared by this tool: `invalid_api_key`: BLS rejected the configured BLS_API_KEY as invalid. `service_unavailable`: BLS /surveys API is unreachable or returns a non-200 response. `serialization_failure`: BLS /surveys response cannot be parsed (malformed JSON or unexpected schema). Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `invalid_api_key`: BLS rejected the configured BLS_API_KEY as invalid. `service_unavailable`: BLS /surveys API is unreachable or returns a non-200 response. `serialization_failure`: BLS /surveys response cannot be parsed as JSON. `upstream_timeout`: The live BLS request exceeds its shared 45-second operation budget. Other values are possible when a failure originates below the handler." - changed
Output schema / properties / error / properties / data / properties / reason / examplesPrevious value: -[ - "invalid_api_key", - "service_unavailable", - "serialization_failure" -]New value: +[ + "invalid_api_key", + "service_unavailable", + "serialization_failure", + "upstream_timeout" +]
- Changed
bls_search_series3 fields changed- changed
Input schema / properties / limit / descriptionPrevious value: -"Maximum number of results to return (1–50, default 10)."New value: +"Maximum number of results to return (default 10); requests above 50 are clamped to 50 with notice." - changed
Input schema / properties / limit / maximumPrevious value: -50New value: +9007199254740991 - changed
Output schema / properties / notice / descriptionPrevious value: -"Guidance on the result: the survey is not in the offline index, the offset is past the last result, nothing matched, which offset fetches the next page, or, on the last page of a capped list, that other series may also match. Absent when this page ends an uncapped list with results."New value: +"Requested and applied limits when clamped, plus guidance on unindexed surveys, offsets past the end, empty results, the next page, or the capped candidate pool. Absent when the limit was unchanged and this page ends an uncapped list with results."
1 tool update
- Changed
bls_search_series4 fields changed- changed
Input schema / properties / area / descriptionPrevious value: -"State name, MSA name, or FIPS area code to narrow results to a geographic area — a case-insensitive substring of the area name, title, or SeriesID. Omit for national series, which then rank first among equal matches."New value: +"State name, MSA name, or FIPS area code to narrow results to a geographic area — a case-insensitive substring of the area name, title, or SeriesID. Without this filter, specific whole place names in query are relevance preferences; otherwise national series rank first among equal matches." - added
Output schema / properties / series / items / properties / endPeriodAdded value: +{ + "description": "Last observation period code in the catalog snapshot, when available (e.g. M08 or Q04).", + "type": "string" +} - added
Output schema / properties / series / items / properties / endYearAdded value: +{ + "description": "Last observation year in the catalog snapshot, when available.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" +} - added
Output schema / properties / series / items / properties / startYearAdded value: +{ + "description": "First observation year in the catalog snapshot, when available; does not imply continuous coverage.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" +}
3 tool updates
- Changed
bls_dataframe_describe5 fields changed- changed
Input schema / properties / name / descriptionPrevious value: -"Optional table name (df_XXXXX_XXXXX) to describe a single dataframe. Omit to list all active dataframes."New value: +"Optional dataframe name to describe a single dataframe — a df_XXXXX_XXXXX name from bls_get_series, or a register_as name from bls_dataframe_query. Omit, or leave blank, to list all active dataframes." - changed
Output schema / properties / dataframes / items / properties / column_schema / items / properties / type / descriptionPrevious value: -"DuckDB column type (VARCHAR, BIGINT, DOUBLE, ...)."New value: +"Column type as the canvas reports it: VARCHAR, INTEGER, BIGINT, DOUBLE, BOOLEAN, DATE, TIMESTAMP, JSON, or BLOB. A register_as column of another DuckDB type (DECIMAL, HUGEINT from SUM over integers, SMALLINT, a list) reports VARCHAR, though SQL still sees its real type." - changed
Output schema / properties / dataframes / items / properties / name / descriptionPrevious value: -"Canvas table name (df_XXXXX_XXXXX)."New value: +"Canvas table name — df_XXXXX_XXXXX, or the register_as name given." - changed
Output schema / properties / error / properties / data / properties / reason / descriptionPrevious value: -"Machine-readable failure mode. Declared by this tool: `canvas_unavailable`: The DataCanvas service is not configured for this deployment. Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `canvas_unavailable`: The DataCanvas service is not configured for this deployment. `invalid_dataframe_name`: name is not blank and is not a canvas identifier, so no dataframe can carry it. Other values are possible when a failure originates below the handler." - changed
Output schema / properties / error / properties / data / properties / reason / examplesPrevious value: -[ - "canvas_unavailable" -]New value: +[ + "canvas_unavailable", + "invalid_dataframe_name" +]
- Changed
bls_get_latest3 fields changed- changed
Output schema / properties / error / properties / data / properties / reason / descriptionPrevious value: -"Machine-readable failure mode. Declared by this tool: `invalid_api_key`: BLS rejected the configured BLS_API_KEY as invalid. `quota_exceeded`: The BLS API 500 query/day limit has been reached. `series_locked`: The BLS database is temporarily locked for the requested series. Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `invalid_api_key`: BLS rejected the configured BLS_API_KEY as invalid, and no requested series returned an observation. `quota_exceeded`: The BLS API 500 query/day limit has been reached, and no requested series returned an observation. `series_locked`: The BLS database is temporarily locked for the requested series, and no requested series returned an observation. Other values are possible when a failure originates below the handler." - changed
Output schema / properties / failed / items / properties / error / descriptionPrevious value: -"Error message. Common values: \"Series does not exist\" (invalid SeriesID — use bls_search_series to find valid IDs), \"No observations returned\" (series exists but has no current data). BLS does not always name the series it rejected: an invalid SeriesID sometimes comes back as the generic \"Your request has failed. Please check your input parameters, and try your request again.\", which means the same thing here."New value: +"Error message. Common values: \"Series does not exist\" or \"Invalid Series\" (invalid SeriesID — use bls_search_series to find valid IDs), \"No observations returned\" (series exists but has no current data). The generic \"Your request has failed. Please check your input parameters, and try your request again.\" means BLS rejected the request itself twice, the second time without catalog metadata, rather than naming the SeriesID. A quota, API-key, or database-lock failure lands here only when another series returned an observation; notice then names its reason and recovery." - changed
Output schema / properties / notice / descriptionPrevious value: -"Guidance when one or more series failed — e.g. to use bls_search_series to verify SeriesIDs. Absent when all series returned data."New value: +"Guidance when one or more series failed — bls_search_series for a SeriesID BLS rejected, or the reason and recovery for a quota, API-key, or database-lock failure that left other series answered. Absent when all series returned data."
- Changed
bls_get_series4 fields changed- changed
Input schema / properties / calculations / descriptionPrevious value: -"When true, request BLS-computed period-over-period calculations. The flag is a single boolean (you cannot select an individual calculation type), but the API returns whichever the survey supports and omits the rest — CPI and PPI return percent change only (the inflation rate), and a survey that supports neither simply returns its observations without calculation fields. Requesting calculations never fails, so it is always safe to set; consult bls_list_surveys (allowsNetChange / allowsPercentChange) only to predict which fields will come back. Monthly-cadence series return each supported change type over 1, 3, 6, and 12-month intervals; other cadences return a subset."New value: +"When true, request BLS-computed period-over-period calculations. The flag is a single boolean (you cannot select an individual calculation type), but the API returns whichever the survey supports and omits the rest — CPI and PPI return percent change only (the inflation rate), and a survey that supports neither simply returns its observations without calculation fields. Requesting calculations never fails, so it is always safe to set; consult bls_list_surveys (allowsNetChange / allowsPercentChange) only to predict which fields will come back. Monthly-cadence series return each supported change type over 1, 3, 6, and 12-month intervals; other cadences return a subset. A series served from the local observation mirror, when the server runs one, carries no calculation fields; enrichment.calculationsApplied is then false and notice names it." - changed
Output schema / properties / calculationsApplied / descriptionPrevious value: -"Whether BLS net/percent-change calculations were requested."New value: +"Whether BLS net/percent-change calculations were applied. True when calculations=true reached BLS for every returned series; false when calculations=false, or when the local observation mirror served a series, whose rows carry no calculations — notice then names those series. Absent when calculations was omitted." - changed
Output schema / properties / error / properties / data / properties / reason / descriptionPrevious value: -"Machine-readable failure mode. Declared by this tool: `invalid_api_key`: BLS rejected the configured BLS_API_KEY as invalid. `quota_exceeded`: The BLS API 500 query/day limit has been reached. `request_rejected`: BLS returned a non-success status with a message matching no known failure mode — e.g. a rejected combination of request parameters. `series_not_found`: No requested series returned data and at least one SeriesID does not exist. A batch mixing an invalid SeriesID with one BLS has no data for lands here too, and names both in the message and recovery hint. `series_locked`: The BLS database is temporarily locked for the requested series. `no_data_for_period`: The requested year range is unusable before the request — start_year after end_year, a span of 20 years or more, or end_year without start_year — or BLS returned data for none of the requested series over the range. `calculations_not_supported`: calculations=true was requested for a survey that does not support it. `canvas_unavailable`: The result set exceeds the inline budget and canvas (DuckDB) is not configured. `canvas_registration_failed`: The result set exceeds the inline budget and canvas is configured, but registering the dataframe failed. Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `invalid_api_key`: BLS rejected the configured BLS_API_KEY as invalid. `quota_exceeded`: The BLS API 500 query/day limit has been reached. `request_rejected`: BLS returned a non-success status with a message matching no known failure mode, both for the request and for its automatic re-issue without catalog metadata — e.g. a rejected combination of request parameters. `series_not_found`: No requested series returned data and at least one SeriesID does not exist. A batch mixing an invalid SeriesID with one BLS has no data for lands here too, and names both in the message and recovery hint. `series_locked`: The BLS database is temporarily locked for the requested series. `no_data_for_period`: The requested year range is unusable before the request — start_year after end_year, a span of 20 years or more, or end_year without start_year — or BLS returned data for none of the requested series over the range. `calculations_not_supported`: calculations=true was requested for a survey that does not support it. `canvas_unavailable`: The result set exceeds the inline budget and canvas (DuckDB) is not configured. `canvas_registration_failed`: The result set exceeds the inline budget and canvas is configured, but registering the dataframe failed. Other values are possible when a failure originates below the handler." - changed
Output schema / properties / notice / descriptionPrevious value: -"Guidance for agents — names any SeriesID that returned zero observations, reports a resolved end_year the 20-year window capped, and reports the bls_dataframe_describe then bls_dataframe_query workflow when results spill to canvas. Absent when every requested series returned data over the window as asked and it all fit inline."New value: +"Guidance for agents — names any SeriesID that returned zero observations with its reason (including a failed live fallback's reason and recovery for a SeriesID the local observation mirror does not hold), names mirror-served series that lack requested calculations, reports a resolved end_year the 20-year window capped, and reports the bls_dataframe_describe then bls_dataframe_query workflow when results spill to canvas. Absent when every requested series returned data over the window as asked and it all fit inline."
1 tool update
- Changed
bls_search_series2 fields changed- changed
Input schema / properties / survey / descriptionPrevious value: -"Two-letter LABSTAT survey abbreviation to filter results, case-insensitive (e.g. CU for CPI, CE for CES, LN for CPS, LA for LAUS, JT for JOLTS). Indexed surveys: CU, AP, CE, LN, LA, PC, WP, JT, EC, PR, MP, CW; OE (OEWS) only when the server sets BLS_CATALOG_INCLUDE_OES=true. Omit to search all indexed surveys."New value: +"Two-letter LABSTAT survey abbreviation to filter results, case-insensitive (e.g. CU for CPI, CE for CES, LN for CPS, LA for LAUS, JT for JOLTS). Indexed surveys: CU, AP, CE, LN, LA, PC, WP, JT, EC, PR, MP, CW, CM, CI; OE (OEWS) only when the server sets BLS_CATALOG_INCLUDE_OES=true. Omit to search all indexed surveys." - added
Output schema / properties / series / items / properties / frequencyAdded value: +{ + "description": "Publication frequency — \"Monthly\", \"Semi-Annual\", \"Quarterly\", or \"Annual\" — for CU, CW, and LN, which publish one series at several frequencies under the same title. Absent for other surveys.", + "type": "string" +}
2 tool updates
- Changed
bls_list_surveys1 field changed- changed
Input schema / properties / category / descriptionPrevious value: -"Optional category filter. One of: prices, employment, wages, productivity, injuries, time_use. Omit to list all surveys."New value: +"Optional category filter. One of: prices, employment, wages, productivity, injuries, time_use. A survey can appear under more than one category (CES under employment and wages). Omit to list all surveys."
- Changed
bls_search_series12 fields changed- changed
Input schema / properties / area / descriptionPrevious value: -"State name, MSA name, or FIPS area code to narrow results to a geographic area. Omit for national series."New value: +"State name, MSA name, or FIPS area code to narrow results to a geographic area — a case-insensitive substring of the area name, title, or SeriesID. Omit for national series, which then rank first among equal matches." - added
Input schema / properties / offsetAdded value: +{ + "default": 0, + "description": "Number of ranked results to skip, for paging past the first page (default 0). Pass nextOffset from the previous response to get the next page.", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" +} - changed
Input schema / properties / survey / descriptionPrevious value: -"Two-letter LABSTAT survey abbreviation to filter results (e.g. CU for CPI, CE for CES, LN for CPS, LA for LAUS, JT for JOLTS, OE for OEWS). Omit to search all loaded surveys."New value: +"Two-letter LABSTAT survey abbreviation to filter results, case-insensitive (e.g. CU for CPI, CE for CES, LN for CPS, LA for LAUS, JT for JOLTS). Indexed surveys: CU, AP, CE, LN, LA, PC, WP, JT, EC, PR, MP, CW; OE (OEWS) only when the server sets BLS_CATALOG_INCLUDE_OES=true. Omit to search all indexed surveys." - changed
Output schema / anyOfPrevious value: -[ - { - "not": { - "required": [ - "error" - ] - }, - "required": [ - "series", - "totalCount", - "capped", - "catalogSize", - "effectiveQuery", - "limitApplied" - ] - }, - { - "required": [ - "error" - ] - } -]New value: +[ + { + "not": { + "required": [ + "error" + ] + }, + "required": [ + "series", + "totalCount", + "capped", + "catalogSize", + "effectiveQuery", + "limitApplied", + "offsetApplied" + ] + }, + { + "required": [ + "error" + ] + } +] - changed
Output schema / properties / areaFilter / descriptionPrevious value: -"Area filter applied, if any. Absent when no area filter was passed."New value: +"Area filter applied, trimmed. Absent when no area filter was passed or it was blank." - changed
Output schema / properties / capped / descriptionPrevious value: -"True when the FTS candidate pool reached the internal cap (~1000). totalCount is then a lower bound, not an exact match count. Narrow the query, add survey/area filters, or use a direct SeriesID to get an exact count."New value: +"True when the FTS candidate pool, after the survey/area/seasonal filters, reached the internal cap (~1000). totalCount is then a lower bound and offset paging stops at the pool. Narrow the query, add filters, or use a direct SeriesID to get an exact count." - added
Output schema / properties / nextOffsetAdded value: +{ + "description": "Offset of the next page. Present only when truncated is true.", + "type": "number" +} - changed
Output schema / properties / notice / descriptionPrevious value: -"Guidance when no results matched — e.g. how to broaden the query or remove filters. Absent when results are returned."New value: +"Guidance on the result: the survey is not in the offline index, the offset is past the last result, nothing matched, which offset fetches the next page, or, on the last page of a capped list, that other series may also match. Absent when this page ends an uncapped list with results." - added
Output schema / properties / offsetAppliedAdded value: +{ + "description": "Offset in effect (defaults to 0 when omitted).", + "type": "number" +} - changed
Output schema / properties / surveyFilter / descriptionPrevious value: -"Survey filter applied, if any. Absent when no survey filter was passed."New value: +"Survey filter applied, trimmed and uppercased. Absent when no survey filter was passed or it was blank." - changed
Output schema / properties / totalCount / descriptionPrevious value: -"Total candidates scored before the limit was applied. A lower bound when capped is true — the catalog index may contain more matching series."New value: +"Total ranked results after every filter (area included), before limit and offset. A lower bound when capped is true — the catalog index may contain more matching series." - changed
Output schema / properties / truncated / descriptionPrevious value: -"True when more candidates matched than the limit returned."New value: +"True when ranked results remain past this page; nextOffset fetches them."
2 tool updates
- Changed
bls_get_latest1 field changed- changed
Output schema / properties / failed / items / properties / error / descriptionPrevious value: -"Error message. Common values: \"Series does not exist\" (invalid SeriesID — use bls_search_series to find valid IDs), \"No observations returned\" (series exists but has no current data)."New value: +"Error message. Common values: \"Series does not exist\" (invalid SeriesID — use bls_search_series to find valid IDs), \"No observations returned\" (series exists but has no current data). BLS does not always name the series it rejected: an invalid SeriesID sometimes comes back as the generic \"Your request has failed. Please check your input parameters, and try your request again.\", which means the same thing here."
- Changed
bls_get_series6 fields changed- changed
Input schema / properties / end_year / descriptionPrevious value: -"End year for the data range (inclusive). Defaults to the current year when omitted."New value: +"End year for the data range (inclusive). Supplying it without start_year is rejected before the request — BLS applies no default start year alongside an explicit end year. Pair it with start_year, or omit both for the API default window." - changed
Input schema / properties / start_year / descriptionPrevious value: -"Start year for the data range (inclusive). The BLS API allows up to 20 years per request. Omit for the API default (typically 3–20 years depending on survey)."New value: +"Start year for the data range (inclusive). The BLS API allows up to 20 years per request and requires both bounds or neither: supplying start_year alone resolves end_year to the current year, capped at start_year + 19 so the window stays inside the 20-year limit. Omit both for the API default (typically 3–20 years depending on survey)." - changed
Output schema / properties / endYearApplied / descriptionPrevious value: -"End year in effect, when a range was requested."New value: +"End year applied to the query. Resolved from start_year when end_year was omitted, so it can differ from the requested range; notice names the cap when the 20-year window decided it. Absent when no year range was in effect." - changed
Output schema / properties / error / properties / data / properties / reason / descriptionPrevious value: -"Machine-readable failure mode. Declared by this tool: `invalid_api_key`: BLS rejected the configured BLS_API_KEY as invalid. `quota_exceeded`: The BLS API 500 query/day limit has been reached. `request_rejected`: BLS returned a non-success status with a message matching no known failure mode — e.g. a rejected combination of request parameters. `series_not_found`: One or more SeriesIDs do not exist in BLS data. `series_locked`: The BLS database is temporarily locked for the requested series. `no_data_for_period`: No data is available for the requested year range. `calculations_not_supported`: calculations=true was requested for a survey that does not support it. `canvas_unavailable`: The result set exceeds the inline budget and canvas (DuckDB) is not configured. `canvas_registration_failed`: The result set exceeds the inline budget and canvas is configured, but registering the dataframe failed. Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `invalid_api_key`: BLS rejected the configured BLS_API_KEY as invalid. `quota_exceeded`: The BLS API 500 query/day limit has been reached. `request_rejected`: BLS returned a non-success status with a message matching no known failure mode — e.g. a rejected combination of request parameters. `series_not_found`: No requested series returned data and at least one SeriesID does not exist. A batch mixing an invalid SeriesID with one BLS has no data for lands here too, and names both in the message and recovery hint. `series_locked`: The BLS database is temporarily locked for the requested series. `no_data_for_period`: The requested year range is unusable before the request — start_year after end_year, a span of 20 years or more, or end_year without start_year — or BLS returned data for none of the requested series over the range. `calculations_not_supported`: calculations=true was requested for a survey that does not support it. `canvas_unavailable`: The result set exceeds the inline budget and canvas (DuckDB) is not configured. `canvas_registration_failed`: The result set exceeds the inline budget and canvas is configured, but registering the dataframe failed. Other values are possible when a failure originates below the handler." - changed
Output schema / properties / notice / descriptionPrevious value: -"Guidance for agents — names any SeriesID that returned zero observations, and reports the bls_dataframe_describe then bls_dataframe_query workflow when results spill to canvas. Absent when every requested series returned data and it all fit inline."New value: +"Guidance for agents — names any SeriesID that returned zero observations, reports a resolved end_year the 20-year window capped, and reports the bls_dataframe_describe then bls_dataframe_query workflow when results spill to canvas. Absent when every requested series returned data over the window as asked and it all fit inline." - changed
Output schema / properties / startYearApplied / descriptionPrevious value: -"Start year in effect, when a range was requested."New value: +"Start year applied to the query, whether it was served live or from the local observation mirror. Absent when no year range was in effect."
2 tool updates
- Changed
bls_get_latest3 fields changed- added
Output schema / properties / results / items / properties / latestObservation / properties / availableAdded value: +{ + "description": "False when BLS published the \"-\" missing-value sentinel for this period.", + "type": "boolean" +} - changed
Output schema / properties / results / items / properties / latestObservation / properties / value / descriptionPrevious value: -"Observation value as a string, matching BLS API output. Parse to float for arithmetic."New value: +"Raw observation value from BLS. The literal \"-\" means unavailable; check available before arithmetic and read footnotes for the reason." - changed
Output schema / properties / results / items / properties / latestObservation / requiredPrevious value: -[ - "year", - "period", - "value" -]New value: +[ + "year", + "period", + "value", + "available" +]
- Changed
bls_get_series11 fields changed- changed
Input schema / properties / annual_average / descriptionPrevious value: -"When true, add each year's annual-average row to the observations. An annual average is the mean of that year's real periods, returned as an extra row named \"Annual\" with period M13 (monthly series), Q05 (quarterly) or S03 (semiannual) — not an additional month or quarter, so it must be excluded from any sum or average over observations. Defaults to false, which returns real periods only and is safe to aggregate directly. Independent of start_year/end_year. Surveys that publish no annual averages return the same rows either way; enrichment.annualAverageRows reports how many rows were actually added."New value: +"When true, add each year's annual-average row to the observations. An annual average is the mean of that year's real periods, returned as an extra row named \"Annual\" with period M13 (monthly series), Q05 (quarterly) or S03 (semiannual) — not an additional month or quarter, so it must be excluded from any sum or average over observations. Defaults to false, which returns real periods only; check available before aggregating. Independent of start_year/end_year. Surveys that publish no annual averages return the same rows either way; enrichment.annualAverageRows reports how many rows were actually added." - changed
Output schema / anyOfPrevious value: -[ - { - "not": { - "required": [ - "error" - ] - }, - "required": [ - "series", - "spilled", - "totalObservations", - "seriesRequested", - "annualAverageApplied" - ] - }, - { - "required": [ - "error" - ] - } -]New value: +[ + { + "not": { + "required": [ + "error" + ] + }, + "required": [ + "series", + "spilled", + "totalObservations", + "availableObservations", + "unavailableObservations", + "seriesRequested", + "annualAverageApplied" + ] + }, + { + "required": [ + "error" + ] + } +] - changed
Output schema / properties / annualAverageApplied / descriptionPrevious value: -"Whether annual-average rows were requested. When false, observations hold real periods only and can be summed or averaged directly."New value: +"Whether annual-average rows were requested. When false, observations hold real periods only; filter on available before aggregation." - added
Output schema / properties / availableObservationsAdded value: +{ + "description": "Rows with a published numeric value.", + "type": "number" +} - added
Output schema / properties / series / items / properties / availableObservationCountAdded value: +{ + "description": "Rows with a published numeric value, excluding the BLS \"-\" sentinel.", + "type": "number" +} - changed
Output schema / properties / series / items / properties / observationCount / descriptionPrevious value: -"Total observations for this series. When spilled to canvas, all observations are on the dataframe; inline only shows a preview."New value: +"Total period rows for this series, including unavailable BLS placeholder rows. When spilled to canvas, all rows are on the dataframe; inline only shows a preview." - added
Output schema / properties / series / items / properties / observations / items / properties / availableAdded value: +{ + "description": "False when BLS published the \"-\" missing-value sentinel for this period.", + "type": "boolean" +} - changed
Output schema / properties / series / items / properties / observations / items / properties / value / descriptionPrevious value: -"Observation value as a string matching BLS output. Parse to float for arithmetic."New value: +"Raw observation value from BLS. The literal \"-\" means unavailable; check available before arithmetic and read footnotes for the reason." - changed
Output schema / properties / series / items / properties / observations / items / requiredPrevious value: -[ - "year", - "period", - "value" -]New value: +[ + "year", + "period", + "value", + "available" +] - changed
Output schema / properties / series / items / requiredPrevious value: -[ - "seriesId", - "observationCount", - "observations" -]New value: +[ + "seriesId", + "observationCount", + "availableObservationCount", + "observations" +] - added
Output schema / properties / unavailableObservationsAdded value: +{ + "description": "Rows carrying the BLS \"-\" missing-value sentinel.", + "type": "number" +}
2 tool updates
- Changed
bls_dataframe_describe3 fields changed- removed
Output schema / properties / dataframes / items / properties / max_rowsRemoved value: -{ - "description": "Materialization cap that produced truncated, when applicable.", - "type": "number" -} - removed
Output schema / properties / dataframes / items / properties / truncatedRemoved value: -{ - "description": "True when the upstream had more rows than were materialized.", - "type": "boolean" -} - changed
Output schema / properties / dataframes / items / requiredPrevious value: -[ - "name", - "source_tool", - "query_params", - "created_at", - "expires_at", - "row_count", - "truncated", - "column_schema" -]New value: +[ + "name", + "source_tool", + "query_params", + "created_at", + "expires_at", + "row_count", + "column_schema" +]
- Changed
bls_get_series5 fields changed- changed
Output schema / properties / dataset / descriptionPrevious value: -"Canvas dataframe handle — present when the observation volume exceeded the inline budget. Use bls_dataframe_query with dataset.name to run SQL across the full data."New value: +"Canvas dataframe handle — present when the observation volume exceeded the inline budget. Call bls_dataframe_describe with dataset.name to inspect column_schema, then use that table name in bls_dataframe_query SQL across the full data." - changed
Output schema / properties / dataset / properties / name / descriptionPrevious value: -"Canvas table name (df_XXXXX_XXXXX). Pass to bls_dataframe_query."New value: +"Canvas table name (df_XXXXX_XXXXX). Pass to bls_dataframe_describe first to inspect column_schema, then use it in bls_dataframe_query SQL." - removed
Output schema / properties / dataset / properties / truncatedRemoved value: -{ - "description": "True when the upstream response had more rows than the canvas materialization cap.", - "type": "boolean" -} - changed
Output schema / properties / dataset / requiredPrevious value: -[ - "name", - "row_count", - "expires_at", - "truncated" -]New value: +[ + "name", + "row_count", + "expires_at" +] - changed
Output schema / properties / notice / descriptionPrevious value: -"Guidance for agents — names any SeriesID that returned zero observations, and reports when results spilled to canvas and SQL is needed for full access. Absent when every requested series returned data and it all fit inline."New value: +"Guidance for agents — names any SeriesID that returned zero observations, and reports the bls_dataframe_describe then bls_dataframe_query workflow when results spill to canvas. Absent when every requested series returned data and it all fit inline."
6 tool updates
- Changed
bls_dataframe_describe6 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Input schema / additionalPropertiesAdded value: +false - changed
Output schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Output schema / anyOfAdded value: +[ + { + "not": { + "required": [ + "error" + ] + }, + "required": [ + "dataframes" + ] + }, + { + "required": [ + "error" + ] + } +] - added
Output schema / properties / errorAdded value: +{ + "additionalProperties": {}, + "description": "Present when the call failed. Absent on success.", + "properties": { + "code": { + "description": "JSON-RPC error code for this failure.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "data": { + "additionalProperties": {}, + "properties": { + "reason": { + "description": "Machine-readable failure mode. Declared by this tool: `canvas_unavailable`: The DataCanvas service is not configured for this deployment. Other values are possible when a failure originates below the handler.", + "examples": [ + "canvas_unavailable" + ], + "type": "string" + }, + "recovery": { + "additionalProperties": {}, + "description": "Actionable next step for the caller.", + "properties": { + "hint": { + "type": "string" + } + }, + "required": [ + "hint" + ], + "type": "object" + }, + "retryable": { + "description": "Whether retrying may succeed.", + "type": "boolean" + } + }, + "type": "object" + }, + "message": { + "description": "Human-readable description of what went wrong.", + "type": "string" + } + }, + "required": [ + "code", + "message" + ], + "type": "object" +} - removed
Output schema / requiredRemoved value: -[ - "dataframes" -]
- Changed
bls_dataframe_query10 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Input schema / additionalPropertiesAdded value: +false - changed
Output schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Output schema / anyOfAdded value: +[ + { + "not": { + "required": [ + "error" + ] + }, + "required": [ + "columns", + "row_count", + "rows" + ] + }, + { + "required": [ + "error" + ] + } +] - added
Output schema / properties / capAdded value: +{ + "description": "The preview or row_limit cap that was applied.", + "type": "number" +} - added
Output schema / properties / errorAdded value: +{ + "additionalProperties": {}, + "description": "Present when the call failed. Absent on success.", + "properties": { + "code": { + "description": "JSON-RPC error code for this failure.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "data": { + "additionalProperties": {}, + "properties": { + "reason": { + "description": "Machine-readable failure mode. Declared by this tool: `canvas_unavailable`: The DataCanvas service is not configured for this deployment. Other values are possible when a failure originates below the handler.", + "examples": [ + "canvas_unavailable" + ], + "type": "string" + }, + "recovery": { + "additionalProperties": {}, + "description": "Actionable next step for the caller.", + "properties": { + "hint": { + "type": "string" + } + }, + "required": [ + "hint" + ], + "type": "object" + }, + "retryable": { + "description": "Whether retrying may succeed.", + "type": "boolean" + } + }, + "type": "object" + }, + "message": { + "description": "Human-readable description of what went wrong.", + "type": "string" + } + }, + "required": [ + "code", + "message" + ], + "type": "object" +} - changed
Output schema / properties / row_count / descriptionPrevious value: -"Total rows the query produced (may exceed rows.length when capped by row_limit)."New value: +"Rows materialized by the query. Exact when register_as is used; otherwise equals row_limit when truncated is true." - added
Output schema / properties / shownAdded value: +{ + "description": "Number of rows returned inline.", + "type": "number" +} - added
Output schema / properties / truncatedAdded value: +{ + "description": "True when the returned rows were capped.", + "type": "boolean" +} - removed
Output schema / requiredRemoved value: -[ - "columns", - "row_count", - "rows" -]
- Changed
bls_get_latest6 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Input schema / additionalPropertiesAdded value: +false - changed
Output schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Output schema / anyOfAdded value: +[ + { + "not": { + "required": [ + "error" + ] + }, + "required": [ + "results", + "succeeded", + "failed" + ] + }, + { + "required": [ + "error" + ] + } +] - added
Output schema / properties / errorAdded value: +{ + "additionalProperties": {}, + "description": "Present when the call failed. Absent on success.", + "properties": { + "code": { + "description": "JSON-RPC error code for this failure.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "data": { + "additionalProperties": {}, + "properties": { + "reason": { + "description": "Machine-readable failure mode. Declared by this tool: `invalid_api_key`: BLS rejected the configured BLS_API_KEY as invalid. `quota_exceeded`: The BLS API 500 query/day limit has been reached. `series_locked`: The BLS database is temporarily locked for the requested series. Other values are possible when a failure originates below the handler.", + "examples": [ + "invalid_api_key", + "quota_exceeded", + "series_locked" + ], + "type": "string" + }, + "recovery": { + "additionalProperties": {}, + "description": "Actionable next step for the caller.", + "properties": { + "hint": { + "type": "string" + } + }, + "required": [ + "hint" + ], + "type": "object" + }, + "retryable": { + "description": "Whether retrying may succeed.", + "type": "boolean" + } + }, + "type": "object" + }, + "message": { + "description": "Human-readable description of what went wrong.", + "type": "string" + } + }, + "required": [ + "code", + "message" + ], + "type": "object" +} - removed
Output schema / requiredRemoved value: -[ - "results", - "succeeded", - "failed" -]
- Changed
bls_get_series6 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Input schema / additionalPropertiesAdded value: +false - changed
Output schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Output schema / anyOfAdded value: +[ + { + "not": { + "required": [ + "error" + ] + }, + "required": [ + "series", + "spilled", + "totalObservations", + "seriesRequested", + "annualAverageApplied" + ] + }, + { + "required": [ + "error" + ] + } +] - added
Output schema / properties / errorAdded value: +{ + "additionalProperties": {}, + "description": "Present when the call failed. Absent on success.", + "properties": { + "code": { + "description": "JSON-RPC error code for this failure.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "data": { + "additionalProperties": {}, + "properties": { + "reason": { + "description": "Machine-readable failure mode. Declared by this tool: `invalid_api_key`: BLS rejected the configured BLS_API_KEY as invalid. `quota_exceeded`: The BLS API 500 query/day limit has been reached. `request_rejected`: BLS returned a non-success status with a message matching no known failure mode — e.g. a rejected combination of request parameters. `series_not_found`: One or more SeriesIDs do not exist in BLS data. `series_locked`: The BLS database is temporarily locked for the requested series. `no_data_for_period`: No data is available for the requested year range. `calculations_not_supported`: calculations=true was requested for a survey that does not support it. `canvas_unavailable`: The result set exceeds the inline budget and canvas (DuckDB) is not configured. `canvas_registration_failed`: The result set exceeds the inline budget and canvas is configured, but registering the dataframe failed. Other values are possible when a failure originates below the handler.", + "examples": [ + "invalid_api_key", + "quota_exceeded", + "request_rejected", + "series_not_found", + "series_locked", + "no_data_for_period", + "calculations_not_supported", + "canvas_unavailable", + "canvas_registration_failed" + ], + "type": "string" + }, + "recovery": { + "additionalProperties": {}, + "description": "Actionable next step for the caller.", + "properties": { + "hint": { + "type": "string" + } + }, + "required": [ + "hint" + ], + "type": "object" + }, + "retryable": { + "description": "Whether retrying may succeed.", + "type": "boolean" + } + }, + "type": "object" + }, + "message": { + "description": "Human-readable description of what went wrong.", + "type": "string" + } + }, + "required": [ + "code", + "message" + ], + "type": "object" +} - removed
Output schema / requiredRemoved value: -[ - "series", - "spilled", - "totalObservations", - "seriesRequested", - "annualAverageApplied" -]
- Changed
bls_list_surveys6 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Input schema / additionalPropertiesAdded value: +false - changed
Output schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Output schema / anyOfAdded value: +[ + { + "not": { + "required": [ + "error" + ] + }, + "required": [ + "surveys", + "total" + ] + }, + { + "required": [ + "error" + ] + } +] - added
Output schema / properties / errorAdded value: +{ + "additionalProperties": {}, + "description": "Present when the call failed. Absent on success.", + "properties": { + "code": { + "description": "JSON-RPC error code for this failure.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "data": { + "additionalProperties": {}, + "properties": { + "reason": { + "description": "Machine-readable failure mode. Declared by this tool: `invalid_api_key`: BLS rejected the configured BLS_API_KEY as invalid. `service_unavailable`: BLS /surveys API is unreachable or returns a non-200 response. `serialization_failure`: BLS /surveys response cannot be parsed (malformed JSON or unexpected schema). Other values are possible when a failure originates below the handler.", + "examples": [ + "invalid_api_key", + "service_unavailable", + "serialization_failure" + ], + "type": "string" + }, + "recovery": { + "additionalProperties": {}, + "description": "Actionable next step for the caller.", + "properties": { + "hint": { + "type": "string" + } + }, + "required": [ + "hint" + ], + "type": "object" + }, + "retryable": { + "description": "Whether retrying may succeed.", + "type": "boolean" + } + }, + "type": "object" + }, + "message": { + "description": "Human-readable description of what went wrong.", + "type": "string" + } + }, + "required": [ + "code", + "message" + ], + "type": "object" +} - removed
Output schema / requiredRemoved value: -[ - "surveys", - "total" -]
- Changed
bls_search_series6 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Input schema / additionalPropertiesAdded value: +false - changed
Output schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Output schema / anyOfAdded value: +[ + { + "not": { + "required": [ + "error" + ] + }, + "required": [ + "series", + "totalCount", + "capped", + "catalogSize", + "effectiveQuery", + "limitApplied" + ] + }, + { + "required": [ + "error" + ] + } +] - added
Output schema / properties / errorAdded value: +{ + "additionalProperties": {}, + "description": "Present when the call failed. Absent on success.", + "properties": { + "code": { + "description": "JSON-RPC error code for this failure.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "data": { + "additionalProperties": {}, + "properties": { + "reason": { + "description": "Machine-readable failure mode. Declared by this tool: `catalog_unavailable`: The catalog index failed to load at startup. Other values are possible when a failure originates below the handler.", + "examples": [ + "catalog_unavailable" + ], + "type": "string" + }, + "recovery": { + "additionalProperties": {}, + "description": "Actionable next step for the caller.", + "properties": { + "hint": { + "type": "string" + } + }, + "required": [ + "hint" + ], + "type": "object" + }, + "retryable": { + "description": "Whether retrying may succeed.", + "type": "boolean" + } + }, + "type": "object" + }, + "message": { + "description": "Human-readable description of what went wrong.", + "type": "string" + } + }, + "required": [ + "code", + "message" + ], + "type": "object" +} - removed
Output schema / requiredRemoved value: -[ - "series", - "totalCount", - "capped", - "catalogSize", - "effectiveQuery", - "limitApplied" -]
2 tool updates
- Changed
bls_get_series5 fields changed- added
Input schema / properties / annual_averageAdded value: +{ + "default": false, + "description": "When true, add each year's annual-average row to the observations. An annual average is the mean of that year's real periods, returned as an extra row named \"Annual\" with period M13 (monthly series), Q05 (quarterly) or S03 (semiannual) — not an additional month or quarter, so it must be excluded from any sum or average over observations. Defaults to false, which returns real periods only and is safe to aggregate directly. Independent of start_year/end_year. Surveys that publish no annual averages return the same rows either way; enrichment.annualAverageRows reports how many rows were actually added.", + "type": "boolean" +} - added
Output schema / properties / annualAverageAppliedAdded value: +{ + "description": "Whether annual-average rows were requested. When false, observations hold real periods only and can be summed or averaged directly.", + "type": "boolean" +} - added
Output schema / properties / annualAverageRowsAdded value: +{ + "description": "How many observations across all series are annual-average rows (period M13/Q05/S03). Present only when annual_average is true; 0 means none of the requested surveys publish annual averages.", + "type": "number" +} - changed
Output schema / properties / series / items / properties / observations / items / properties / period / descriptionPrevious value: -"Period code (e.g. M01–M13, Q01–Q05, A01)."New value: +"BLS period code: M01–M12 are months, Q01–Q04 quarters, S01–S02 semiannual halves. M13, Q05 and S03 are not further periods — each is the mean of that year's real observations, named \"Annual\", and appears only when annual_average is true. Exclude them from any sum or average over observations." - changed
Output schema / requiredPrevious value: -[ - "series", - "spilled", - "totalObservations", - "seriesRequested" -]New value: +[ + "series", + "spilled", + "totalObservations", + "seriesRequested", + "annualAverageApplied" +]
- Changed
bls_list_surveys1 field changed- changed
Output schema / properties / surveys / items / properties / hasAnnualAverages / descriptionPrevious value: -"True when the survey publishes annual average observations."New value: +"True when BLS reports that the survey publishes annual average observations. Advisory only: it does not predict whether a given series returns annual-average rows for bls_get_series with annual_average=true — LN, CE, LA and SM report true yet return none. Read annualAverageRows on that response for what actually came back."
1 tool update
- Changed
bls_get_series3 fields changed- changed
Input schema / properties / calculations / descriptionPrevious value: -"When true, request BLS-computed period-over-period calculations. The flag is a single boolean (you cannot select an individual calculation type), but the API returns whichever the survey supports: CPI and PPI return percent change only (the inflation rate), with no error. Only surveys that support neither net nor percent change (e.g. AP average price data) return an error — check bls_list_surveys first. Monthly-cadence series return each supported change type over 1, 3, 6, and 12-month intervals; other cadences return a subset."New value: +"When true, request BLS-computed period-over-period calculations. The flag is a single boolean (you cannot select an individual calculation type), but the API returns whichever the survey supports and omits the rest — CPI and PPI return percent change only (the inflation rate), and a survey that supports neither simply returns its observations without calculation fields. Requesting calculations never fails, so it is always safe to set; consult bls_list_surveys (allowsNetChange / allowsPercentChange) only to predict which fields will come back. Monthly-cadence series return each supported change type over 1, 3, 6, and 12-month intervals; other cadences return a subset." - changed
Output schema / properties / notice / descriptionPrevious value: -"Guidance for agents — e.g. when results spilled to canvas and SQL is needed for full access. Absent when all observations fit inline."New value: +"Guidance for agents — names any SeriesID that returned zero observations, and reports when results spilled to canvas and SQL is needed for full access. Absent when every requested series returned data and it all fit inline." - changed
Output schema / properties / seriesRequested / descriptionPrevious value: -"Number of SeriesIDs requested. Compare against the returned series[] length to detect series that returned no data."New value: +"Number of SeriesIDs requested. Do not compare it against series[] length to find empty series — a SeriesID that returned no data is still listed in series[] with observationCount 0. Check observationCount per entry, or read notice, which names every SeriesID that came back empty."
1 tool update
- Changed
bls_get_series5 fields changed- changed
Input schema / properties / calculations / descriptionPrevious value: -"When true, request BLS-computed period-over-period calculations. The flag is a single boolean (you cannot select an individual calculation type), but the API returns whichever the survey supports: CPI and PPI return percent change only (the inflation rate), with no error. Only surveys that support neither net nor percent change (e.g. AP average price data) return an error — check bls_list_surveys first."New value: +"When true, request BLS-computed period-over-period calculations. The flag is a single boolean (you cannot select an individual calculation type), but the API returns whichever the survey supports: CPI and PPI return percent change only (the inflation rate), with no error. Only surveys that support neither net nor percent change (e.g. AP average price data) return an error — check bls_list_surveys first. Monthly-cadence series return each supported change type over 1, 3, 6, and 12-month intervals; other cadences return a subset." - added
Output schema / properties / series / items / properties / observations / items / properties / netChange3MonthAdded value: +{ + "description": "3-month net change (when calculations=true).", + "type": "string" +} - added
Output schema / properties / series / items / properties / observations / items / properties / netChange6MonthAdded value: +{ + "description": "6-month net change (when calculations=true).", + "type": "string" +} - added
Output schema / properties / series / items / properties / observations / items / properties / pctChange3MonthAdded value: +{ + "description": "3-month percent change (when calculations=true).", + "type": "string" +} - added
Output schema / properties / series / items / properties / observations / items / properties / pctChange6MonthAdded value: +{ + "description": "6-month percent change (when calculations=true).", + "type": "string" +}
Related MCP Connectors
Econdata MCP — wraps BLS (Bureau of Labor Statistics) public API v2…
BLS MCP — Bureau of Labor Statistics public data API (v2)
Query US Treasury national debt, interest rates, exchange rates, and fiscal datasets via MCP.
Query U.S. Census Bureau data, variables, and geography via MCP.
Related MCP Servers
- AlicenseAqualityDmaintenanceEnables access to Bureau of Labor Statistics (BLS) economic data including Consumer Price Index, employment statistics, and other labor market indicators. Supports fetching data series, listing available datasets, and retrieving metadata through natural language queries.4MIT
- AlicenseNot gradedqualityBmaintenanceEnables querying and retrieving official U.S. labor market and price data from the Bureau of Labor Statistics, including unemployment, CPI, PPI, and employment figures, via series search, latest observations, and popular series.729 npmMIT
- FlicenseNot gradedqualityDmaintenanceProvides access to U.S. labor market data including employment statistics, Consumer Price Index inflation rates, and wage information. Users can query specific time series data or use shortcuts for common economic indicators like unemployment and industry-specific employment.3-
- AlicenseAqualityDmaintenanceProvides curated US economic data from Treasury, FRED, BLS, BEA, and other sources through an MCP interface. Enables querying economic series, fetching data with provenance tracking, and accessing cached artifacts.9MIT
Glama MCP Gateway
Add one secure layer between your agents and this server.