Skip to main content
Glama

bls-labor-mcp-server

Query BLS Dataframes

bls_dataframe_query
Read-onlyIdempotent

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.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
sqlYesSingle-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.
previewNoInline row preview count. Defaults to row_limit. Set lower (e.g. 50) when chaining via register_as and only a sample is needed immediately.
row_limitNoHard cap on rows materialized in the response (default 1000, max 10000). Full results live on-canvas under register_as when provided.
register_asNoWhen 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

TableJSON Schema
NameRequiredDescriptionDefault
capNoThe preview or row_limit cap that was applied.
rowsNoMaterialized rows, bounded by preview / row_limit.
errorNoPresent when the call failed. Absent on success.
shownNoNumber of rows returned inline.
noticeNoGuidance 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.
columnsNoColumn names in projection order.
row_countNoRows materialized by the query. Exact when register_as is used; otherwise equals row_limit when truncated is true.
truncatedNoTrue when the returned rows were capped.
expires_atNoISO 8601 expiry for the newly registered dataframe, when applicable.
registered_asNoSet when register_as was supplied and the result was materialized.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed10 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": [
      +      "columns",
      +      "row_count",
      +      "rows"
      +    ]
      +  },
      +  {
      +    "required": [
      +      "error"
      +    ]
      +  }
      +]
    • addedOutput schema / properties / cap
      Added value: +{
      +  "description": "The preview or row_limit cap that was applied.",
      +  "type": "number"
      +}
    • 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: `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"
      +}
    • changedOutput schema / properties / row_count / description
      Previous 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."
    • addedOutput schema / properties / shown
      Added value: +{
      +  "description": "Number of rows returned inline.",
      +  "type": "number"
      +}
    • addedOutput schema / properties / truncated
      Added value: +{
      +  "description": "True when the returned rows were capped.",
      +  "type": "boolean"
      +}
    • removedOutput schema / required
      Removed value: -[
      -  "columns",
      -  "row_count",
      -  "rows"
      -]
  2. Changed1 schema field changed
    • changedOutput schema / properties / notice / description
      Previous value: -"Guidance when results were capped by row_limit — e.g. to use register_as to persist all rows or increase row_limit. Absent when all rows fit in the response."New 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."
  3. Changed1 schema field changed
    • addedOutput schema / properties / notice
      Added value: +{
      +  "description": "Guidance when results were capped by row_limit — e.g. to use register_as to persist all rows or increase row_limit. Absent when all rows fit in the response.",
      +  "type": "string"
      +}
  4. First observed

TDQS

A5/5.0
Behavior5/5

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

Annotations already declare readOnlyHint and idempotentHint, but the description goes further by listing rejected operations (writes, DDL, DROP, COPY, PRAGMA, ATTACH, external-file table functions), denied system catalogs, zero BLS quota consumption, and the fresh TTL behavior for register_as. No contradiction with annotations.

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 a single well-structured paragraph that front-loads the core purpose, then covers limitations, prerequisites, and optional features. Every sentence carries information; there is no filler or 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?

Given the complexity (SQL engine, multiple constraints), the description covers prerequisites, supported SQL features, rejected operations, system catalog denial, quota implications, and chaining via register_as. Since an output schema exists, return format is not required. Nothing essential is missing.

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

Parameters5/5

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

Schema coverage is 100% and each parameter has a description, but the tool description adds significant extra context: sql includes an example and explains referencing dataframes, preview clarifies defaulting to row_limit and suggests lowering for chaining, row_limit explains the hard cap and on-canvas persistence, and register_as explains TTL and chaining benefits. This enriches meaning beyond the schema.

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

Purpose5/5

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

The description states a specific action ('Run a single-statement SELECT against the canvas dataframes') and clearly identifies the resource (dataframes registered by bls_get_series or register_as). It distinguishes itself from sibling bls_dataframe_describe (which lists dataframes) and bls_get_series (which fetches series), so an agent can differentiate without opening schemas.

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

Usage Guidelines5/5

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

It explicitly directs the agent to use bls_dataframe_describe for listing available dataframes, and references bls_get_series as the source of registered dataframes. It also states a prerequisite (CANVAS_PROVIDER_TYPE=duckdb) and clarifies that writes and system catalogs are rejected, giving clear when-to-use and when-not-to-use guidance.

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

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.