Skip to content

bls labor mcp server changelog

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

9 changes recorded. MCP Harbor re-reads this listing from the official registry every six hours and asks the live server what it exposes, and keeps what changed between one look and the next.

  • server.json 1 field changed
    version
    0.7.1 0.8.0
  • AGENTS.md +6 −4

    https://raw.githubusercontent.com/cyanheads/bls-labor-mcp-server/HEAD/AGENTS.md

    − **Version:** 0.7.1− **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.18`− **Engines:** Bun ≥1.4.0, Node ≥24.0.0− **Dependency hold:** `@duckdb/node-api` stays on `^1.5.6-r.2`. DuckDB 2 produces `PRAGMA_SHOW` for `SHOW <table>`, which the framework SQL gate refuses (mcp-ts-core #800); the 1.5 line preserves the working schema query.+ **Version:** 0.8.0+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.22`+ **Engines:** Bun ≥1.4.4, Node ≥24.0.0+       series-id-markdown.ts             # SeriesIDs in Markdown: code spans in rows, notices, and error literals+     labstat/+       labstat-stream.ts                 # Streamed LABSTAT reads (header and per-chunk stall deadlines, line regrouping) shared by the catalog harvest and the observation ingester

  • AGENTS.md +19 −15

    https://raw.githubusercontent.com/cyanheads/bls-labor-mcp-server/HEAD/AGENTS.md

    − **Version:** 0.6.0− **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.15`− **MCP SDK:** `@modelcontextprotocol/server` ^2.3.1− - 50 series per `bls_get_series` request; 20-year history window per request.− - `calculations: true` requests BLS-server-side net change and percent change; a survey returns whichever it supports (CPI/PPI return percent change only). A survey supporting neither returns its observations without calculation fields, never an error — `bls_list_surveys` predicts which come back. Mirror-served series carry no calculations; `bls_get_series` then reports `calculationsApplied: false`.− **Catalog search is offline.** `bls_search_series` queries an on-disk SQLite index of the LABSTAT flat files, harvested at startup when the index is missing or stale and re-checked by an hourly `bls-catalog-refresh` job — no API quota consumed. A harvest removes rows its survey's `.series` file no longer lists and rows of surveys no longer configured. Bump `CATALOG_INDEX_VERSION` in `bls-catalog-service.ts` whenever the harvest's output changes, so persisted indexes re-harvest after an upgrade; − Validate domain config at the start of `createApp().setup()`, before initializing services. Select the tool surface before startup with normalized env values and nonthrowing `safeParse`; do not call `getServerConfig()` at module scope. This lets the framework report invalid BLS variables through its startup configuration banner. Framework-owned settings such as `CANVAS_PROVIDER_TYPE` stay in framework config.−       bls-observations-service.ts       # defineMirror wrapper — SQLite store + query−       ingester.ts                       # LABSTAT .data.* sync generator−       period-codes.ts                   # Annual-average period semantics (M13/Q05/S03) — shared by the API and mirror paths− Skills are modular instructions in `framework-skills/` at the project root. Read them directly when a task matches — e.g., `framework-skills/add-tool/SKILL.md` when adding a tool. `bun run list-skills` prints the full registry. The directory is deliberately not `skills/`: Claude Code and Codex auto-load a plugin's root `skills/`, so a server that ships `.claude-plugin/` or `.codex-plugin/` would hand these development skills to every agent that installs it. Keep `skills/` free for skills meant f− | `bun run lint:packaging` | Validate env var alignment between `manifest.json` and `server.json` stdio block |− `bun run bundle` produces a `.mcpb` extension bundle for one-click install in Claude Desktop. The pack step is followed by `scripts/clean-mcpb.ts`, which prunes dev dependencies (`mcpb clean`) and strips two classes of `node_modules/**` content that root-anchored `.mcpbignore` patterns cannot reach: dependency-shipped agent docs (`framework-skills/`, `skills/`, `.claude/`, `.agents/`, `SKILL.md`) and platform-specific native bindings, which would otherwise lock the bundle to the platform it was − - [ ] Error contracts declared for quota_exceeded, series_not_found, series_locked, no_data_for_period, calculations_not_supported, catalog_unavailable (see design.md)− - [ ] `calculations: true` preserves observations when a survey supports neither calculation; mirror-served rows report `calculationsApplied: false`+ **Version:** 0.7.1+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.18`+ **MCP SDK:** `@modelcontextprotocol/server` ^2.4.0+ **Dependency hold:** `@duckdb/node-api` stays on `^1.5.6-r.2`. DuckDB 2 produces `PRAGMA_SHOW` for `SHOW <table>`, which the framework SQL gate refuses (mcp-ts-core #800); the 1.5 line preserves the working schema query.+ - 25 series without an API key or 50 with one per `bls_get_series` request; 20-year requested history window per request (BLS can reduce the applied window).+ - `calculations: true` requests BLS-server-side net change and percent change; a survey returns whichever it supports (CPI/PPI return percent change only). A “does not produce calculations” advisory preserves observations without calculation fields; an explicit “does not support calculations” rejection raises `calculations_not_supported` — `bls_list_surveys` predicts which come back. Mirror-served series carry no calculations; `bls_get_series` then reports `calculationsApplied: false`.+ **Catalog search is offline.** `bls_search_series` queries an on-disk SQLite index of the LABSTAT flat files, harvested at startup when the index is missing or stale and re-checked by an hourly `bls-catalog-refresh` job — no API quota consumed. Freshness is per survey: each hourly check harvests only the surveys whose TTL lapsed or whose failure backoff (counted in checks, capped at `BLS_CATALOG_CACHE_TTL_HOURS`) ran out, and an index with no series harvests every survey. A harvest removes rows + Validate domain config at the start of `createApp().setup()`, before initializing services. Select the tool surface before startup with normalized env values and nonthrowing `safeParse`; do not call `getServerConfig()` at module scope. This lets the framework report invalid BLS variables through its startup configuration banner. Framework-owned settings such as `CANVAS_PROVIDER_TYPE` stay in framework config; `src/index.ts` only defaults that one to `duckdb` (the framework's default is `none`), +       area-filter.ts                    # `area` grammar: state, CSA, CPI area, county/CBSA, and place codes → area names+       oews-composition.ts               # OEWS SeriesIDs composed from the OE code tables when OE rows are not indexed+       bls-observations-service.ts       # defineMirror wrapper — SQLite store, query, footnote tables+       ingester.ts                       # LABSTAT .data.* sync generator (directory-listing discovery, checkpoint)+       period-codes.ts                   # Period-code semantics: annual-average codes (M13/Q05/S03) and the live API's period names — shared by the API and mirror paths+ Development skills live in `framework-skills/`; root `skills/` holds deliberately authored skills for agents that install the server. Published skills ship in npm, Docker, and MCPB and are served over MCP through `createApp({ skills: 'skills' })`; `lint:packaging` validates the tree. Development skills are modular instructions. Read them directly when a task matches — e.g., `framework-skills/add-tool/SKILL.md` when adding a tool. `bun run list-skills` prints the full registry. The directory is d+ | `add-skill` | Author a root `skills/` skill for agents that use this server — structure, validation, delivery |+ | `bun run lint:packaging` | Check environment parity, bundle exclusions, plugin manifests, and published skills |+ `bun run bundle` produces a `.mcpb` extension bundle for one-click install in Claude Desktop. The pack step is followed by `scripts/clean-mcpb.ts`, which prunes dev dependencies (`mcpb clean`) and strips package-root agent docs, platform-specific native bindings, node-gyp build output that exposes the packing host's paths, and build-only framework and better-sqlite3 source files under `node_modules/`. Compiled `*.node` addons stay. Every excluded root path has a root-anchored `.mcpbignore` line;+ - [ ] Error contracts declared for quota_exceeded, series_not_found, series_locked, no_data_for_period, calculations_not_supported, catalog_loading, catalog_unavailable (see design.md)+ - [ ] A “does not produce calculations” advisory preserves observations; an explicit “does not support calculations” rejection retains `calculations_not_supported`; mirror-served rows report `calculationsApplied: false`

  • Resources +1
    • + bls-labor-guide
  • server.json 1 field changed
    version
    0.6.0 0.7.1

  • server.json 1 field changed
    version
    0.5.8 0.6.0

  • AGENTS.md +37 −61

    https://raw.githubusercontent.com/cyanheads/bls-labor-mcp-server/HEAD/AGENTS.md

    − **Version:** 0.5.7− **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.9`− **MCP SDK:** @modelcontextprotocol/server ^2.1.0− ## Planned Tool Surface− ```ts− import { tool, z } from '@cyanheads/mcp-ts-core';− import { getBlsCatalogService } from '@/services/bls-catalog/bls-catalog-service.js';− export const searchSeriesTool = tool('bls_search_series', {−   description: 'Search BLS series catalog by query, survey, area, or keywords to resolve cryptic SeriesIDs.',−   annotations: { readOnlyHint: true, openWorldHint: true },−   input: z.object({−     query: z.string().describe('Natural language or keyword query'),−     survey: z.string().optional().describe('Two-letter LABSTAT survey code (e.g., CU, CE, LN, LA, JT)'),−     area: z.string().optional().describe('State name, MSA, or FIPS area code'),−     seasonal_adjustment: z.boolean().optional().describe('Filter to seasonally adjusted series'),−     limit: z.number().int().min(1).max(50).default(10).describe('Max results to return'),−   }),−   output: z.object({−     series: z.array(z.object({−       seriesId: z.string().describe('BLS SeriesID'),−       title: z.string().describe('Plain-language series name'),−       survey: z.string().describe('Survey code'),−       area: z.string().optional().describe('Geographic area name'),−       item: z.string().optional().describe('Item/subject name'),−       seasonal: z.boolean().describe('Seasonally adjusted'),−     })).describe('Matching series'),−     total: z.number().describe('Total matches in catalog'),−   }),−   async handler(input, ctx) {−     ctx.log.info('Executing bls_search_series', { query: input.query });−     const result = await getBlsCatalogService().search(input);−     return result;−   },−   format: (result) => [{−     type: 'text',−     text: result.series.map(s =>−       `**${s.seriesId}** — ${s.title}${s.area ? ` · ${s.area}` : ''}${s.seasonal ? ' (SA)' : ''}`−     ).join('\n') + `\n\n_${result.total} total matches_`,−   }],− });+ **Version:** 0.5.8+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.14`+ **MCP SDK:** `@modelcontextprotocol/server` ^2.3.1+ ## Tool Surface+ | `bls_dataframe_describe` | Describe one canvas dataframe or list the tenant's registered dataframes; requires DataCanvas. |+ | `bls_dataframe_query` | Query registered dataframes with SELECT SQL and optionally register the result; requires DataCanvas. |+ | `bls_dataframe_drop` | Drop a registered dataframe; requires DataCanvas and BLS_DATAFRAME_DROP_ENABLED=true. |+ `src/mcp-server/tools/definitions/bls-search-series.tool.ts` is the representative definition: `tool()` declares input, output, typed errors, and enrichment inline. The handler checks catalog readiness, normalizes filters, searches the catalog, and returns decoded `series[]`; it records totals, paging, and query context through `ctx.enrich`. `format()` renders every returned series field into `content[]`, while the framework appends enrichment to both response surfaces. Read this definition befo+   apiKey: z.string().default('').describe('BLS v2 API key — optional'),+   dataframeDropEnabled: z.stringbool().default(false).describe('Expose bls_dataframe_drop'),+     dataframeDropEnabled: 'BLS_DATAFRAME_DROP_ENABLED',+ Validate domain config at the start of `createApp().setup()`, before initializing services. Select the tool surface before startup with normalized env values and nonthrowing `safeParse`; do not call `getServerConfig()` at module scope. This lets the framework report invalid BLS variables through its startup configuration banner. Framework-owned settings such as `CANVAS_PROVIDER_TYPE` stay in framework config.+ ### Server identity and instructions+ `createApp()` forwards optional identity fields to the SDK's `initialize` response and `/.well-known/mcp.json`. Set `name` and `title` to `bls-labor-mcp-server`; `package.json` supplies the description. Its `instructions` provide session-level orientation: survey discovery, SeriesID resolution, fetching observations, and the deployment's DataCanvas availability. Use this shared context instead of repeating it across tool descriptions.+   setup(core) {+     getServerConfig();+     initBlsCatalogService(core.config, core.storage);+   },+ | `ctx.log` | Request-scoped logger — `.debug()`, `.info()`, `.notice()`, `.warning()`, `.error()`. Auto-correlates requestId, traceId, tenantId. Dual-sink: Pino **and** `notifications/message` to the client, so treat it as client-visible. |+ | `ctx.inputs` | The request's responses — `.accepted(key, schema)`, `.view(key)`, `.state()`, `.dropped` — limited to what the client declared (`elicitation` and its form/url modes, `sampling`, `roots`). Client-supplied: a consent gate trusts only a `ctx.state` record it stored when it asked, bound to the operation, caller, and target (see the `api-context` skill). |+ | `ctx.clientCapabilities` | What the client declared for this request, `undefined` when no view exists. Decides whether to ask for optional context (e.g. roots); never a reason to skip a consent prompt. |+ | `ctx.enrich` | Success-path agent context (empty-result notices, query echo, pagination totals) — `ctx.enrich(...)` or `.notice()` / `.total()` / `.echo()` / `.truncated()`. Reaches `structuredContent` and `content[]`; lands only when the definition declares an `enrichment` block (no-op otherwise). |+ | `ctx.requestId` | Request ID — the one every log record of the call carries and its error envelope returns as `data.requestId`. |+ | `ctx.tenantId` | Tenant ID from JWT; `'default'` for stdio or HTTP with auth off. |+ **Recommended: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable?, severity?, thrownBy? }]` on `tool()` / `resource()` to receive `ctx.fail(reason, …)` typed against the reason union. TypeScript catches typos at compile time, `data.reason` is auto-populated for observability, linter enforces conformance against the handler body. `recovery` is required (≥ 5 words, lint-validated) — the single source of truth for the agent's next move. The framework puts it on the+   { reason: 'series_not_found', code: JsonRpcErrorCode.NotFound,+     throw ctx.fail('quota_exceeded', 'BLS daily query quota exhausted.');+ | `maintenance` | Investigate changelogs, adopt upstream changes, sync skills to agent dirs |+ | `orchestrations` | Chain task skills into a gated multi-phase pipeline — build-out, QA-fix, update-ship — when you can spawn sub-agents |+ | `techniques` | Catalog of response/data-shaping techniques — overflow handling, payload shaping, retrieval patterns |+ | `api-mirror` | MirrorService: persistent self-refreshing local mirror (embedded SQLite + FTS5) of a bulk upstream dataset — Tier 3 opt-in |+ When you complete a skill's checklist, check the boxes and add a completion timestamp at the end (e.g., `Completed: 2026-03-11`).+ **Runtime:** Scripts use Bun's native TypeScript execution — `bun run <cmd>` is the standard invocation. `npm run <cmd>` also works (npm delegates to Bun).+ | `bun run test` | Run tests (Vitest — use `bun run test`, not `bun test`) |+ **README install badges** and config-generation commands are ship-time concerns — follow `framework-skills/polish-docs-meta/references/readme.md`.+ **Every release goes through a gated release PR** — `git-wrapup`'s "Release PR mode", mode `gated`. Three separate runs, never one: `git-wrapup` lands the commit stack on `release/<version>`, pushes it, and opens the PR (title = the release commit subject, body = the release digest: theme line, `## Changes`, `## Gates`, changelog link last); `release-pr-review` reviews and fixes on that branch (each fix an ordinary commit on top of the stack, pushed plainly — nothing already pushed is ever rewri+ - [ ] `calculations: true` preserves observations when a survey supports neither calculation; mirror-served rows report `calculationsApplied: false`

    Showing the first 40 of each.

  • server.json 1 field changed
    version
    0.5.7 0.5.8