Skip to main content
Glama

bls-labor-mcp-server

Search BLS Series

bls_search_series
Read-only

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; OE only when the server sets BLS_CATALOG_INCLUDE_OES=true); series in other surveys are fetched by SeriesID with bls_get_series. Area filter accepts state names, MSA names, or FIPS area codes.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
areaNoState 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.
limitNoMaximum number of results to return (1–50, default 10).
queryYesNatural language or keyword query (e.g. "unemployment rate", "CPI food", "nonfarm payrolls"). Also accepts a SeriesID directly for exact lookup.
offsetNoNumber of ranked results to skip, for paging past the first page (default 0). Pass nextOffset from the previous response to get the next page.
surveyNoTwo-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.
seasonal_adjustmentNoWhen true, return only seasonally adjusted series. When false, return only not-seasonally-adjusted. Omit to return both.

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
capNoThe result limit that capped the returned list.
errorNoPresent when the call failed. Absent on success.
shownNoNumber of series returned in this response.
cappedNoTrue 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.
noticeNoGuidance 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.
seriesNoMatching series, ordered by relevance.
truncatedNoTrue when ranked results remain past this page; nextOffset fetches them.
areaFilterNoArea filter applied, trimmed. Absent when no area filter was passed or it was blank.
nextOffsetNoOffset of the next page. Present only when truncated is true.
totalCountNoTotal 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.
catalogSizeNoTotal series in the loaded catalog index. Distinguishes an empty-result search from a failed catalog load.
limitAppliedNoResult limit in effect (defaults to 10 when omitted).
surveyFilterNoSurvey filter applied, trimmed and uppercased. Absent when no survey filter was passed or it was blank.
offsetAppliedNoOffset in effect (defaults to 0 when omitted).
effectiveQueryNoQuery string as the server received and searched on. Confirms interpretation for self-correction.
seasonalFilterNoSeasonal-adjustment filter applied, if any. Absent when not passed.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed2 schema fields changed
    • changedInput schema / properties / survey / description
      Previous 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."
    • addedOutput schema / properties / series / items / properties / frequency
      Added 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. Changed12 schema fields changed
    • changedInput schema / properties / area / description
      Previous 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."
    • addedInput schema / properties / offset
      Added 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"
      +}
    • changedInput schema / properties / survey / description
      Previous 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."
    • changedOutput schema / anyOf
      Previous 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"
      +    ]
      +  }
      +]
    • changedOutput schema / properties / areaFilter / description
      Previous 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."
    • changedOutput schema / properties / capped / description
      Previous 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."
    • addedOutput schema / properties / nextOffset
      Added value: +{
      +  "description": "Offset of the next page. Present only when truncated is true.",
      +  "type": "number"
      +}
    • changedOutput schema / properties / notice / description
      Previous 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."
    • addedOutput schema / properties / offsetApplied
      Added value: +{
      +  "description": "Offset in effect (defaults to 0 when omitted).",
      +  "type": "number"
      +}
    • changedOutput schema / properties / surveyFilter / description
      Previous 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."
    • changedOutput schema / properties / totalCount / description
      Previous 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."
    • changedOutput schema / properties / truncated / description
      Previous value: -"True when more candidates matched than the limit returned."New value: +"True when ranked results remain past this page; nextOffset fetches them."
  3. Changed6 schema fields changed
    • changedInput schema / $schema
      Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
    • addedInput schema / additionalProperties
      Added value: +false
    • changedOutput schema / $schema
      Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
    • addedOutput schema / anyOf
      Added value: +[
      +  {
      +    "not": {
      +      "required": [
      +        "error"
      +      ]
      +    },
      +    "required": [
      +      "series",
      +      "totalCount",
      +      "capped",
      +      "catalogSize",
      +      "effectiveQuery",
      +      "limitApplied"
      +    ]
      +  },
      +  {
      +    "required": [
      +      "error"
      +    ]
      +  }
      +]
    • addedOutput schema / properties / error
      Added 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"
      +}
    • removedOutput schema / required
      Removed value: -[
      -  "series",
      -  "totalCount",
      -  "capped",
      -  "catalogSize",
      -  "effectiveQuery",
      -  "limitApplied"
      -]
  4. Changed7 schema fields changed
    • addedOutput schema / properties / cap
      Added value: +{
      +  "description": "The result limit that capped the returned list.",
      +  "type": "number"
      +}
    • changedOutput schema / properties / capped / description
      Previous value: -"True when the FTS candidate pool reached the internal cap (~1000). totalFound 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 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."
    • addedOutput schema / properties / shown
      Added value: +{
      +  "description": "Number of series returned in this response.",
      +  "type": "number"
      +}
    • addedOutput schema / properties / totalCount
      Added value: +{
      +  "description": "Total candidates scored before the limit was applied. A lower bound when capped is true — the catalog index may contain more matching series.",
      +  "type": "number"
      +}
    • removedOutput schema / properties / totalFound
      Removed value: -{
      -  "description": "Total candidates scored before the limit was applied. A lower bound when capped is true — the catalog index may contain more matching series.",
      -  "type": "number"
      -}
    • addedOutput schema / properties / truncated
      Added value: +{
      +  "description": "True when more candidates matched than the limit returned.",
      +  "type": "boolean"
      +}
    • changedOutput schema / required
      Previous value: -[
      -  "series",
      -  "totalFound",
      -  "capped",
      -  "catalogSize",
      -  "effectiveQuery",
      -  "limitApplied"
      -]New value: +[
      +  "series",
      +  "totalCount",
      +  "capped",
      +  "catalogSize",
      +  "effectiveQuery",
      +  "limitApplied"
      +]
  5. Changed9 schema fields changed
    • addedOutput schema / properties / capped
      Added value: +{
      +  "description": "True when the FTS candidate pool reached the internal cap (~1000). totalFound 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.",
      +  "type": "boolean"
      +}
    • addedOutput schema / properties / series / items / properties / area
      Added value: +{
      +  "description": "Geographic area name, when decoded.",
      +  "type": "string"
      +}
    • removedOutput schema / properties / series / items / properties / areaName
      Removed value: -{
      -  "description": "Geographic area name, when decoded.",
      -  "type": "string"
      -}
    • addedOutput schema / properties / series / items / properties / item
      Added value: +{
      +  "description": "Item or subject name, when decoded.",
      +  "type": "string"
      +}
    • removedOutput schema / properties / series / items / properties / itemName
      Removed value: -{
      -  "description": "Item or subject name, when decoded.",
      -  "type": "string"
      -}
    • changedOutput schema / properties / series / items / properties / seasonal / description
      Previous value: -"True when seasonally adjusted."New value: +"Seasonality descriptor matching the data-tool form: \"Seasonally Adjusted\" or \"Not Seasonally Adjusted\"."
    • changedOutput schema / properties / series / items / properties / seasonal / type
      Previous value: -"boolean"New value: +"string"
    • changedOutput schema / properties / totalFound / description
      Previous value: -"Total matches in the catalog before the limit was applied."New value: +"Total candidates scored before the limit was applied. A lower bound when capped is true — the catalog index may contain more matching series."
    • changedOutput schema / required
      Previous value: -[
      -  "series",
      -  "totalFound",
      -  "catalogSize",
      -  "effectiveQuery",
      -  "limitApplied"
      -]New value: +[
      +  "series",
      +  "totalFound",
      +  "capped",
      +  "catalogSize",
      +  "effectiveQuery",
      +  "limitApplied"
      +]
  6. Changed6 schema fields changed
    • addedOutput schema / properties / areaFilter
      Added value: +{
      +  "description": "Area filter applied, if any. Absent when no area filter was passed.",
      +  "type": "string"
      +}
    • addedOutput schema / properties / effectiveQuery
      Added value: +{
      +  "description": "Query string as the server received and searched on. Confirms interpretation for self-correction.",
      +  "type": "string"
      +}
    • addedOutput schema / properties / limitApplied
      Added value: +{
      +  "description": "Result limit in effect (defaults to 10 when omitted).",
      +  "type": "number"
      +}
    • addedOutput schema / properties / seasonalFilter
      Added value: +{
      +  "description": "Seasonal-adjustment filter applied, if any. Absent when not passed.",
      +  "type": "boolean"
      +}
    • addedOutput schema / properties / surveyFilter
      Added value: +{
      +  "description": "Survey filter applied, if any. Absent when no survey filter was passed.",
      +  "type": "string"
      +}
    • changedOutput schema / required
      Previous value: -[
      -  "series",
      -  "totalFound",
      -  "catalogSize"
      -]New value: +[
      +  "series",
      +  "totalFound",
      +  "catalogSize",
      +  "effectiveQuery",
      +  "limitApplied"
      +]
  7. Changed5 schema fields changed
    • changedOutput schema / properties / catalogSize / description
      Previous value: -"Total series in the loaded catalog index. Used to distinguish an empty-result search from a failed catalog load."New value: +"Total series in the loaded catalog index. Distinguishes an empty-result search from a failed catalog load."
    • addedOutput schema / properties / notice
      Added value: +{
      +  "description": "Guidance when no results matched — e.g. how to broaden the query or remove filters. Absent when results are returned.",
      +  "type": "string"
      +}
    • removedOutput schema / properties / total
      Removed value: -{
      -  "description": "Total matches in the catalog before the limit was applied.",
      -  "type": "number"
      -}
    • addedOutput schema / properties / totalFound
      Added value: +{
      +  "description": "Total matches in the catalog before the limit was applied.",
      +  "type": "number"
      +}
    • changedOutput schema / required
      Previous value: -[
      -  "series",
      -  "total",
      -  "catalogSize"
      -]New value: +[
      +  "series",
      +  "totalFound",
      +  "catalogSize"
      +]
  8. First observed

TDQS

A4.7/5.0
Behavior5/5

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

Annotations already declare readOnlyHint=true and openWorldHint=true, and the description adds meaningful behavioral detail: it operates offline against an index, consumes no API quota, only covers listed indexed surveys, and includes OE only when the server sets BLS_CATALOG_INCLUDE_OES=true. This is additive and consistent with the annotations, with no contradiction.

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

Conciseness5/5

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

The description is dense but not padded: the first sentence front-loads purpose and output, and every subsequent sentence earns its place—usage guidance, offline behavior, supported surveys, and area filtering. The long survey-code list is necessary precision, not redundancy.

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

Completeness5/5

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

The description covers purpose, when to use it, output components, ranking and pagination, offline/no-quota behavior, supported surveys, and the boundary case of non-indexed surveys. Because an output schema exists, return-value details are already covered structurally, so nothing needed for correct invocation is missing.

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

Parameters3/5

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

Schema description coverage is 100%, so the schema already documents every parameter thoroughly, including examples, defaults, limits, and case-insensitivity. The description repeats some parameter semantics, such as the survey-code list and area filter formats, but adds little parameter-level meaning beyond what the schema already provides.

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

Purpose5/5

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

The description states a specific verb and resource: 'Search the BLS series catalog by natural language query, survey code, geographic area, or keywords to resolve cryptic SeriesIDs.' It also distinguishes the tool from bls_get_series by framing it as the pre-lookup step and by describing its output: decoded components, plain-language names, relevance ranking, and pagination.

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

Usage Guidelines5/5

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

The description gives explicit usage guidance: 'Use this before bls_get_series when you have a concept but not a SeriesID.' It also states an exclusion—series in non-indexed surveys are fetched by SeriesID with bls_get_series—and adds context that it operates offline with no API quota consumed, helping an agent choose it over an API-backed alternative.

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

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.