Skip to content

census mcp server changelog

io.github.cyanheads/census-mcp-server

11 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.

  • AGENTS.md +25 −11

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

    − **Version:** 0.7.1− import { DATASET_LATEST_YEARS, getVariableCacheService } from '@/services/variable-cache/variable-cache-service.js';−       concept: z.string().describe('Concept group'),−     total_matches: z.number().describe('Total variables matched before the limit'),−   }),−     const dataset = input.dataset?.trim() || 'acs/acs5';−       { query: input.query, dataset, year, limit },−         concept: v.concept,−       total_matches: totalMatches,−       dataset,−       year,+ **Version:** 0.7.2+ import { DATASET_LATEST_YEARS, getVariableCacheService, resolveDataset } from '@/services/variable-cache/variable-cache-service.js';+     offset: z.number().int().min(0).optional().describe('Matching variables to skip (default: 0)'),+       concept: z.string().optional().describe('Concept group, absent for shared columns'),+   }),+   enrichment: {+     totalMatches: z.number().describe('Total variables matched before offset and limit'),+     truncated: z.boolean().describe('More matching variables remain after this page'),+     nextOffset: z.number().optional().describe('Next page offset, only when truncated is true'),+   },+     const dataset = resolveDataset(input.dataset, 'acs/acs5');+     const offset = input.offset ?? 0;+       { query: input.query, dataset, year, limit, offset },+     const nextOffset = offset + variables.length;+     const truncated = nextOffset < totalMatches;+     ctx.enrich({ totalMatches, truncated, ...(truncated && { nextOffset }), dataset, year });+         ...(v.concept !== undefined && { concept: v.concept }),+ ### Census call contracts+ The three keyed handlers construct `censusCallContext(ctx)` once and pass that scoped context to every service call. `census-request.ts` preserves the original caller signal and Context property descriptors while carrying one absolute 50-second upstream budget through metadata, data, retries, body reads, backoff, and error-enrichment probes. `withCensusRetry` also retains the data request's 40-second ceiling within the time left. Exhausted transport failures carry `upstream_error` or `variables_+ `census_query_data`, `census_compare_geographies`, `census_search_variables`, and `census_list_predicate_values` page the complete ordered match set with `offset` and `limit`. `truncated` means rows remain after this page; `nextOffset` is present only then. Totals precede slicing. Final and past-end pages have no continuation, and an empty slice of existing matches is not a no-match error.+ Comparison numeric ties use competition ranks (`1, 2, 2, 4`), ordered by GEOID within each tie and assigned before paging. Nonnumeric rows follow numbers with sequential ordinals; an entirely nonnumeric comparison preserves upstream order and says it is unranked. With a nonempty `geographies` filter, `rank` is within the matched subset and `scope_rank` is within the fetched scope, whose size is `scopeCount`.+ Live predicate discovery takes `geography_level` (default `us`); an explicit level must permit an unparented wildcard in the dataset/year metadata. Its effective level is echoed and included in the cache identity. Dictionary lists do not validate publication at a supplied level. `acs-coverage.ts` supplies dataset-specific publication guidance as a possible cause of missing data, never a population diagnosis; fallback datasets require rediscovering their variable codes.+       census-request.ts                 # Shared keyed-call budget, retry and body-read boundary+     tools/acs-coverage.ts               # ACS publication guidance for missing geographies+ This server enables that option for `skills/census-guide/`, served over the MCP SDK's SEP-2640 Skills extension. It registers no Census data resources or prompts.

  • Resources +1
    • + census-guide
  • server.json 1 field changed
    version
    0.7.1 0.7.2

  • AGENTS.md +38 −14

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

    − **Version:** 0.6.2− **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.15`− **MCP SDK:** `@modelcontextprotocol/server` ^2.3.1− **Zod:** ^4.6.5− import { getServerConfig } from '@/config/server-config.js';−     const { defaultYear } = getServerConfig();− const ServerConfigSchema = z.object({−   censusApiKey: z.string().describe('Census Bureau API key'),− `parseEnvConfig` maps Zod schema paths → env var names so errors name the variable (`CENSUS_API_KEY`) not the path (`censusApiKey`). Throws `ConfigurationError`, which the framework prints as a clean startup banner.− 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− **Agent skill directory:** Copy skills into the directory your agent discovers (Claude Code: `.claude/skills/`, others: equivalent). Skills then load as context without referencing `framework-skills/` paths. After framework updates, run the `maintenance` skill — Phase B re-syncs the agent directory.− | `bun run lint:packaging` | Packaging surface checks — `server.json`/`manifest.json` env-var parity (run by devcheck) |− `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 − **Every release goes through a release PR, straight-through** — `git-wrapup`'s "Release PR mode", mode `straight-through`. One run: `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-and-publish` then fast-forwards `main` locally with `git merge --ff-only`, creates the tag on `main`'s tip, pushes `main` and the tag, deletes the + **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+ **Zod:** ^4.6.7+ import { getDiscoveryConfig } from '@/config/server-config.js';+     const { defaultYear } = getDiscoveryConfig();+ // Discovery config — no key. Tools that never call the Census Data API read only this.+ const DiscoveryConfigSchema = z.object({+ let _discoveryConfig: z.infer<typeof DiscoveryConfigSchema> | undefined;+ export function getDiscoveryConfig() {+   _discoveryConfig ??= parseEnvConfig(DiscoveryConfigSchema, {+     defaultYear: 'CENSUS_DEFAULT_YEAR',+     variableCacheTtlHours: 'CENSUS_VARIABLE_CACHE_TTL_HOURS',+   });+   return _discoveryConfig;+ }+ // Full config — adds the key, optional so an unset one reaches the keyed paths as missing_api_key.+ const ServerConfigSchema = DiscoveryConfigSchema.extend({+   censusApiKey: z.string().optional().describe('Census Bureau API key'),+ });+ Tool handlers read `getDiscoveryConfig()`; only the Census API service calls `getServerConfig()`, through one helper (`requireApiKey`) that its two keyed methods — `queryData` and `fetchPredicateValues` — call before building a request. An unset key there throws `missing_api_key` (Unauthorized), so the keyless tools keep working without `CENSUS_API_KEY` and the keyed ones (`census_query_data`, `census_compare_geographies`, and `census_list_predicate_values`'s live route and publication check) fa+ `parseEnvConfig` maps Zod schema paths → env var names so errors name the variable (`CENSUS_DEFAULT_YEAR`) not the path (`defaultYear`), and reads a blank, whitespace-only, or unsubstituted `${…}` value as unset. Throws `ConfigurationError`, which the framework prints as a clean startup banner — a malformed year or TTL still does; the key no longer can.+     tools/notice-trailer.ts             # Notice render that escapes quoted caller and upstream text in content[]+ Two skill trees live at the project root, written for different readers:+ | Tree | For | Written by |+ |:-----|:----|:-----------|+ | `framework-skills/` | The agent developing this server — framework skills (`metadata.audience: external`) plus any development-time skills of the server's own | `init` and the `maintenance` sync (framework skills); you (your own) |+ | `skills/` | The agents that install the server and call its tools — tool chains, identifier sources, gates | You, deliberately, with the `add-skill` skill. Absent by default; usually none is needed |+ 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. Their 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 them to every agent that installs it.+ A published skill in root `skills/` reaches plugin installs and the `.mcpb` bundle as-is, ships in the npm package and the Docker image, and reaches MCP clients when the server opts in with `createApp({ skills: 'skills' })`. `lint:packaging` validates the tree.+ **Agent skill directory:** Copy `framework-skills/` into the directory your agent discovers (Claude Code: `.claude/skills/`, others: equivalent). Skills then load as context without referencing `framework-skills/` paths. After framework updates, run the `maintenance` skill — Phase B re-syncs the agent directory.+ | `add-skill` | Author a skill the server publishes in root `skills/` for the agents that use it — when one earns its place, structure, validation, delivery |+ | `bun run lint:packaging` | Packaging surface checks — `server.json`/`manifest.json` env-var parity, `.mcpbignore` coverage, plugin manifests, the published `skills/` tree (run by devcheck) |+ `bun run bundle` produces a `.mcpb` extension bundle for one-click install in Claude Desktop. `mcpb pack` reads `.mcpbignore`, not `.gitignore`, so every root path the bundle must not ship — source, tests, docs, caches, logs, agent docs, plugin manifests, gitignored local files — needs a root-anchored line there (`/docs/`, never `docs/`, which also strips `node_modules/*/docs/`); `lint:packaging` names each missing or unanchored line. Root `skills/` ships on purpose. The pack step is followed by+ **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+ - [ ] `plugin.json` + `mcp.json` (Agent Plugins 1.0.0 — read by Codex, Cursor, GitHub Copilot, VS Code, Kiro) populated — `plugin.json` `name` = the unscoped repo name (lowercase `a-z`, `0-9`, `-`, `.` only); `version`, `description`, `author`, `homepage`, `repository`, `license`, `keywords` from `package.json`; no other top-level field, `extensions` included. `mcp.json` holds one `"type": "stdio"` entry keyed by the unscoped name, `npx -y <full package name>`, and the same fixed `env` as `.code+ - [ ] Any new root dev path has a root-anchored line in `.mcpbignore`; `bun run bundle` and `lint:packaging` pass+ - [ ] If the server publishes a skill in root `skills/`: `lint:packaging` passes, and `field-test` verified every gate and identifier claim in it against live tool behavior
  • server.json 1 field changed
    version
    0.7.0 0.7.1

  • server.json 1 field changed
    version
    0.6.3 0.7.0

  • server.json 1 field changed
    version
    0.6.2 0.6.3

  • AGENTS.md +5 −5

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

    − **Version:** 0.6.1− **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.14`− **MCP SDK:** `@modelcontextprotocol/server` ^2.2.0−       variable-cache-service.ts         # In-process variables.json cache, attribute columns, universes, dataset resolution−       variable-search.ts                # Whole-word variable search and ranking rule+ **Version:** 0.6.2+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.15`+ **MCP SDK:** `@modelcontextprotocol/server` ^2.3.1+       variable-cache-service.ts         # In-process variables.json cache, attribute columns, universes, dataset resolution, sibling-dataset lookup+       variable-search.ts                # Code and whole-word variable search, and its ranking rule

  • server.json 1 field changed
    version
    0.6.1 0.6.2

  • AGENTS.md +11 −9

    https://raw.githubusercontent.com/cyanheads/census-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.6`− **MCP SDK:** `@modelcontextprotocol/server` ^2.0.0− | `ctx.state` | Tenant-scoped KV — `.get(key)`, `.set(key, value, { ttl? })`, `.delete(key)`, `.getMany(keys)`, `.list(prefix, { cursor, limit })`. Accepts any serializable value. |− | `ctx.inputs` | Reader over a retried request's responses — `.accepted(key, schema)`, `.view(key)`, `.state()`, `.dropped`. Empty on the first round. |− | `ctx.requestId` | Unique request ID. |− **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. Pass `ctx.recoveryFor('reaso−   if (!item) throw ctx.fail('no_match', `No item ${input.id}`, ctx.recoveryFor('no_match'));− **Every release goes through a release PR, straight-through** — `git-wrapup`'s "Release PR mode", mode `straight-through`. One run: `git-wrapup` lands the commit stack on `release/<version>`, pushes it, and opens the PR (title = the release commit subject, body = the changelog entry plus a gates section); `release-and-publish` then fast-forwards `main` locally with `git merge --ff-only`, creates the tag on `main`'s tip, pushes `main` and the tag, deletes the branch, and publishes. A caller's bri+ **Version:** 0.6.1+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.14`+ **MCP SDK:** `@modelcontextprotocol/server` ^2.2.0+ - **Cut noise.** Add only what earns its place: no speculative generality, no guards for states the framework already prevents (Zod-validated params, classified errors), no abstraction until a third caller proves it, no option nothing sets.+ | `ctx.state` | Tenant-scoped KV — `.get(key)`, `.set(key, value, { ttl? })`, `.delete(key)`, `.getMany(keys)`, `.list(prefix, { cursor, limit })`. Accepts any JSON-serializable value; reads return its JSON form (a `Date` comes back as an ISO string). |+ | `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.requestId` | Request ID — the one every log record of the call carries and its error envelope returns as `data.requestId`. |+ **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+   if (!item) throw ctx.fail('no_match', `No item ${input.id}`);+ **Every release goes through a release PR, straight-through** — `git-wrapup`'s "Release PR mode", mode `straight-through`. One run: `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-and-publish` then fast-forwards `main` locally with `git merge --ff-only`, creates the tag on `main`'s tip, pushes `main` and the tag, deletes the 
  • server.json 1 field changed
    version
    0.6.0 0.6.1