Skip to content

grantsgov mcp server changelog

io.github.cyanheads/grantsgov-mcp-server

7 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 +14 −12

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

    − **Version:** 0.1.3− **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.15`− | `grantsgov_list_reference` | The reference snapshot (2× `search2` with `rows: 0`, cached 24 h), or none for `sort_options` / `keyword_syntax` | Every recovery hint for an unknown code routes here |− No resources, no prompts, no DataCanvas — every record is reachable through `grantsgov_get_opportunity` (see the decisions log).− - **Resilience:** `withRetry` (2 retries, 25 s deadline) outside, the process-wide pacer (`maxConcurrent: 4`) inside, so each attempt re-queues. Per-attempt timeout 15 s.− - **Reference snapshot:** process-local (public vocabulary, not tenant data — never `ctx.state`), 24 h TTL, one in-flight build shared by concurrent callers. A failed refresh serves the stale snapshot and blocks retries for 60 s; a failure with no snapshot reaches every waiting caller, and each tool's contract fills its own recovery hint.− | No close-date filter exists | — | `scanClosingWindow` over `closeDate\|asc` posted pages, cut by `cutClosingWindow`, 2,000-row ceiling |− Agency-authored text (titles, descriptions, eligibility narratives, contact blocks, file names, labels) is data. `decodeEntities()` decodes at most two layers of HTML5 references in human-readable fields; identifiers and standalone URLs stay intact. `html-to-text.ts` then converts recognized narrative markup in a forward scan, preserving source newlines, comparisons, and non-HTML angle text. `structuredContent` carries the normalized text. In `format()`, use the HTML-escaping `render.ts` helpers−     totalCount: z.number().describe('Entries returned.'),− 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` | Packaging surface checks — `server.json`/`manifest.json` env-var parity, plugin manifest identity, README version badge (run by devcheck) |− `bun run bundle` produces `dist/grantsgov-mcp-server.mcpb` 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 i+ **Version:** 0.2.0+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.18`+ | `grantsgov_list_reference` | The reference snapshot (2× `search2` with `rows: 0`, cached 24 h), or none for `sort_options` / `keyword_syntax` | Filters the full scope before paging (default 50, cap 100), with complete continuation arguments. Every recovery hint for an unknown code routes here |+ The `grantsgov-guide` skill is served as `skill://grantsgov-guide/SKILL.md` through `createApp({ skills: 'skills' })`. No record resources, prompts, or DataCanvas — every opportunity record is reachable through `grantsgov_get_opportunity` (see the decisions log).+ - **Resilience:** `withRetry` (2 retries, 25 s deadline) outside, the process-wide pacer (`maxConcurrent: 4`) inside, so each attempt re-queues. Per-attempt timeout 15 s; closing-window pages share one 25 s scan deadline.+ - **Reference snapshot:** process-local public vocabulary, 24 h TTL, one detached build shared by concurrent callers. An operational refresh failure serves the stale snapshot with `reference_cache` provenance on both response surfaces, retaining its timestamp and blocking retries for 60 s. Healthy refresh clears degradation. Each waiter cancels independently; cold failures retain each caller's tool recovery hint.+ | No close-date filter exists | — | `scanClosingWindow` over `closeDate\|asc` posted pages, cut by `cutClosingWindow`, 2,000-row ceiling; incomplete scans report lower-bound counts and stay truncated even on empty pages |+ Agency-authored text (titles, descriptions, eligibility narratives, contact blocks, file names, labels) is data. `decodeEntities()` decodes at most two layers of HTML5 references in human-readable fields; identifiers and standalone URLs stay intact. `html-to-text.ts` converts narrative markup in a forward scan, preserving source newlines, comparisons, non-HTML angle text, and table cell/row boundaries. `structuredContent` carries the normalized text. In `format()`, use `inline()`, `tableCell()`,+     totalCount: z.number().describe('Total entries matching the filters before paging.'),+ Development 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.+ Root `skills/` is author-managed guidance for agents that install this server. Author it with `add-skill` when a workflow earns its place; maintenance never creates or copies it. Published skills ship in npm, Docker, and MCPB, and reach MCP clients through `createApp({ skills: 'skills' })`. `lint:packaging` validates the tree.+ | `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, README version badge, and published `skills/` (run by devcheck) |+ `bun run bundle` produces `dist/grantsgov-mcp-server.mcpb` for one-click install in Claude Desktop. `mcpb pack` reads `.mcpbignore`, so every root development path needs a root-anchored exclusion; root `skills/` ships on purpose. The pack step is followed by `scripts/clean-mcpb.ts`, which prunes dev dependencies (`mcpb clean`) and strips dependency-shipped agent docs, platform-specific native bindings, node-gyp build output that records the packing machine's paths (compiled `*.node` addons stay)

  • Resources +1
    • + grantsgov-guide

  • server.json 1 field changed
    version
    0.1.3 0.2.0

  • AGENTS.md +6 −5

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

    − **Version:** 0.1.2− **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.14`− | `grantsgov_get_opportunity` | `search2` per opportunity number (resolution, all statuses), then `fetchOpportunity` per id, fanned out through the pacer | Up to 5 identifiers; misses and ambiguous numbers are `unresolved[]` entries, upstream failures throw the whole call |− Agency-authored text (titles, descriptions, eligibility narratives, contact blocks, file names, labels) is data. `html-to-text.ts` normalizes HTML and bare-entity text for both surfaces; `structuredContent` otherwise stays verbatim. In `format()`, use the `render.ts` helpers: `inline()` for text inside a line, `tableCell()` inside a table cell, `blockquote()` for multi-line fields. They split on every line break a renderer may honor (CR, LF, and the Unicode breaks U+000B, U+000C, U+0085, U+2028,− - **Blank means unset.** `optionalText(schema)` and `optionalList(item, max)` read `''`, whitespace, and emptied lists as `undefined`. Never `.min(1)` on an optional string.+ **Version:** 0.1.3+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.15`+ | `grantsgov_get_opportunity` | `search2` per selected opportunity number (all statuses), then `fetchOpportunity` per selected id, fanned out through the pacer | Accepts 50 nonblank entries per list / 50 unique combined; selects 5 identifiers before resolution, ids first; deferred arrays and executable continuation preserve section offsets. Six independent section pages; misses and ambiguity are `unresolved[]`, upstream failures throw |+ Literal identifier slots use `codeSpan()` or `tableCodeSpan()` from `render.ts`, which choose a backtick fence and preserve literal characters. Never HTML-escape inside Markdown code spans. The table variant adds only the pipe escape consumed by GFM; existing backslashes stay literal.+ Agency-authored text (titles, descriptions, eligibility narratives, contact blocks, file names, labels) is data. `decodeEntities()` decodes at most two layers of HTML5 references in human-readable fields; identifiers and standalone URLs stay intact. `html-to-text.ts` then converts recognized narrative markup in a forward scan, preserving source newlines, comparisons, and non-HTML angle text. `structuredContent` carries the normalized text. In `format()`, use the HTML-escaping `render.ts` helpers+ - **Blank means unset.** `optionalText(schema)` and `optionalList(item, max)` read `''`, whitespace, and emptied lists as `undefined`. Lists advertise one whole string or an array without splitting. The helper rejects numeric scalars directly; MCP argument repair can normalize them for numeric identifiers and eligibility codes, so wire acceptance needs raw MCP checks. Blank cleanup precedes the nonblank-member bound; advertise no raw `maxItems` that rejects blank padding. Never `.min(1)` on an o

  • server.json 1 field changed
    version
    0.1.2 0.1.3

  • AGENTS.md +11 −10

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

    − **Version:** 0.1.1− **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.6`− - **Reason propagation:** every service throw carries `data.reason` plus the calling tool's `ctx.recoveryFor(reason)`. Each tool declares `upstream_unavailable`, `rate_limited`, and `upstream_route_unavailable` with `thrownBy: 'service'`, each with its own tool-named recovery text.− - **Reference snapshot:** process-local (public vocabulary, not tenant data — never `ctx.state`), 24 h TTL, one in-flight build shared by concurrent callers. A failed refresh serves the stale snapshot and blocks retries for 60 s; a failure with no snapshot is re-issued to each caller with its own recovery hint.−           ...ctx.recoveryFor('unknown_parent_code'),− | `ctx.fail(reason, …)` | Throws a typed contract error for a declared reason. |− | `ctx.recoveryFor(reason)` | Returns `{ recovery: { hint } }` for a declared reason — spread into `ctx.fail` data, and passed by the service into its own throws. |− **Typed error contract on every tool.** Declare `errors: [{ reason, code, when, recovery, retryable?, severity?, thrownBy? }]` and throw with `ctx.fail(reason, message, data)`. `recovery` is required (≥ 5 words, lint-validated) — the single source of truth for the agent's next move. Forward it with `ctx.recoveryFor('reason')`, or pass an explicit `{ recovery: { hint } }` when runtime context sharpens it (the search tool names the unknown agency code in its hint). Forwarding is lint-enforced per − **Service-layer throws** use factories (`serviceUnavailable`, `httpErrorFromResponse`) with `data: { reason, ...ctx.recoveryFor(reason) }`, so clients see the same `error.data.reason` they'd see from `ctx.fail`.− **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.1.2+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.14`+ - **Reason propagation:** every service throw carries `data.reason` and no recovery hint; the framework fills the calling tool's declared hint for that reason. Each tool declares `upstream_unavailable`, `rate_limited`, and `upstream_route_unavailable` with `thrownBy: 'service'`, each with its own tool-named recovery text.+ - **Reference snapshot:** process-local (public vocabulary, not tenant data — never `ctx.state`), 24 h TTL, one in-flight build shared by concurrent callers. A failed refresh serves the stale snapshot and blocks retries for 60 s; a failure with no snapshot reaches every waiting caller, and each tool's contract fills its own recovery hint.+ ### Session posture and shutdown+ `src/index.ts` declares `sessionMode: 'stateless'`: no tool asks the caller for input mid-handler, and a stateless data API needs no session store. `sessionMode` declares the HTTP session posture in `src/` instead of leaving it to a deployment's `MCP_SESSION_MODE`, which still wins whenever it carries a meaningful value (an empty string and an unsubstituted `${…}` placeholder read as unset and fall through to the option). Add `require: 'stateful'` when a tool asks the caller for input mid-handle+ `teardown(core)` is the `setup()` counterpart — here it disposes the service, whose pacer holds a timer. It runs after the transport stops and before the logger closes, on every shutdown path, and a signal-triggered shutdown then exits the process explicitly (0, or 1 if a step never settles within the framework's 10 s ceiling).+ | `ctx.fail(reason, …)` | Throws a typed contract error for a declared reason; the framework fills the entry's recovery hint unless the throw passes its own. |+ **Typed error contract on every tool.** Declare `errors: [{ reason, code, when, recovery, retryable?, severity?, thrownBy? }]` and throw with `ctx.fail(reason, message, data)`. `recovery` is required (≥ 5 words, lint-validated) — the single source of truth for the agent's next move. The framework puts it on the wire whenever a failure carrying that `reason` arrives without a hint — a bare `ctx.fail('reason')` or a service throw with `data: { reason }` — as `data.recovery.hint`, mirrored into `co+ **Service-layer throws** use factories (`serviceUnavailable`, `httpErrorFromResponse`) with `data: { reason }`, so clients see the same `error.data.reason` and recovery hint they'd see from `ctx.fail` — the calling tool's contract supplies the hint.+ **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 3 fields changed
    remote_url
    — https://grantsgov.caseyjhand.com/mcp
    transport
    stdio streamable-http
    version
    0.1.1 0.1.2