Skip to content

io.github.cyanheads/bls-labor-mcp-server

by cyanheads · remote · local (npm) · no sign-in to list tools · v0.6.0 · published by GitHub user cyanheads

Fetch US Bureau of Labor Statistics data — CPI, unemployment, wages, JOLTS, and more via MCP.

Changes · 1

6 tools: 1 change things and 5 only read.

Scanned Oct 21, 2026

1 check matched something in the tool definitions. No finding is not a safety guarantee. How the checks work.

claude mcp add --transport http bls-labor https://bls-labor.caseyjhand.com/mcp

Findings

Matches of fixed checks, with the text they matched.

1 warn
  • The server marks "bls_dataframe_query" read-only (readOnlyHint: true), and its own name, description or input give the class "write".

    Matched text

    annotations: {"readOnlyHint":true,"idempotentHint":true,"openWorldHint":false}; The name "bls_dataframe_query" says "query": a word for reading.

    What this check looks for

Tools

Open a tool for its reasons and full description. Descriptions are the publisher's own words, shown as they are.

6 tools

Changes things 1Claude Code: ask
  • bls_dataframe_queryRun a single-statement SELECT against the canvas dataframes registered by bls_get_series or by an earlier register_as.1 finding

    Why it is in this group

    • The name "bls_dataframe_query" says "query": a word for reading.
    • The description's first verb is "run": a word for a change.
    • The input takes a statement or command to run (input.sql), which can change anything it can reach.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.

    Findings

    • Warn

      The server marks "bls_dataframe_query" read-only (readOnlyHint: true), and its own name, description or input give the class "write".

      Matched text

      annotations: {"readOnlyHint":true,"idempotentHint":true,"openWorldHint":false}; The name "bls_dataframe_query" says "query": a word for reading.

      What this check looks for
    Description, as the server wrote it
    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. Requires CANVAS_PROVIDER_TYPE=duckdb.

    Annotations the server declares

    • readOnlyHint: true
    • idempotentHint: true
    • openWorldHint: false
Only reads 5Claude Code: allow
  • bls_dataframe_describeList 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.No findings

    Why it is in this group

    • The name "bls_dataframe_describe" says "describe": a word for reading.
    Description, as the server wrote it
    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. Requires CANVAS_PROVIDER_TYPE=duckdb.

    Annotations the server declares

    • readOnlyHint: true
    • idempotentHint: true
    • openWorldHint: false
  • bls_get_latestReturn the single most recent observation for one or more BLS series.No findings

    Why it is in this group

    • The name "bls_get_latest" says "get": a word for reading.
    Description, as the server wrote it
    Return the single most recent observation for one or more BLS series. Use for "what is X right now" questions — the current unemployment rate, the latest CPI reading, etc. Each series consumes one API query against the 500/day limit; for the current value of many series, bls_get_series with a 1-year window is more quota-efficient (one query for up to 50 series). Recommended limit: 10 series; maximum: 50.

    Annotations the server declares

    • readOnlyHint: true
    • idempotentHint: true
    • openWorldHint: true
  • bls_get_seriesFetch time-series data for 1–50 BLS series by SeriesID in a single API request (one query against the 500/day limit).No findings

    Why it is in this group

    • The name "bls_get_series" says "get": a word for reading.
    Description, as the server wrote it
    Fetch time-series data for 1–50 BLS series by SeriesID in a single API request (one query against the 500/day limit). Supports optional year range (up to 20 years per request) and BLS-computed period-over-period calculations (net change and percent change; a survey returns whichever it supports and silently omits the rest — CPI and PPI return percent change only, the inflation rate). BLS can publish a '-' missing-value sentinel; check observation.available before arithmetic. Set annual_average to add each year's annual-average row, which is that year's mean rather than an additional period. When the total observation count would exceed the inline context budget, results spill to a canvas dataframe and the response includes a dataset.name handle. Call bls_dataframe_describe with that name to inspect the dataframe schema, then use the name in bls_dataframe_query SQL. Use bls_search_series first if you need to resolve a concept to a SeriesID.

    Annotations the server declares

    • readOnlyHint: true
    • openWorldHint: true
  • bls_list_surveysList BLS survey programs with their abbreviation codes, full names, and metadata about calculation support and annual averages.No findings

    Why it is in this group

    • The name "bls_list_surveys" says "list": a word for reading.
    Description, as the server wrote it
    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; bls_search_series covers only the surveys in its offline index, and series in the others are fetched by SeriesID with bls_get_series. Optional category filter narrows results to prices, employment, wages, productivity, injuries, or time_use surveys.

    Annotations the server declares

    • readOnlyHint: true
    • idempotentHint: true
    • openWorldHint: false
  • bls_search_seriesSearch the BLS series catalog by natural language query, survey code, geographic area, or keywords to resolve cryptic SeriesIDs.No findings

    Why it is in this group

    • The name "bls_search_series" says "search": a word for reading.
    Description, as the server wrote it
    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.

    Annotations the server declares

    • readOnlyHint: true
    • openWorldHint: true

This is not a safety rating, and no finding is not a safety guarantee. The groups and findings come from fixed checks of the tool definitions the server returned on the date shown, not from its code or what it does when called. How the checks work.

Install

Pinned to version 0.6.0. Values in angle brackets are yours to fill in.

claude mcp add --transport http bls-labor https://bls-labor.caseyjhand.com/mcp

Run in a terminal.

Claude Code permissions

Allow the reads, ask before a change, deny the deletes. Paste into .claude/settings.json.

.claude/settings.json
{
  "permissions": {
    "allow": [
      "mcp__bls-labor__bls_list_surveys",
      "mcp__bls-labor__bls_search_series",
      "mcp__bls-labor__bls_get_latest",
      "mcp__bls-labor__bls_get_series",
      "mcp__bls-labor__bls_dataframe_describe"
    ],
    "ask": [
      "mcp__bls-labor__bls_dataframe_query"
    ],
    "deny": []
  }
}

Tools that only read are allowed, tools that change things ask first, tools that delete or cancel are denied. It assumes the server is installed as bls-labor. Review it before you use it.