@cyanheads/federal-regulations-mcp-server 0.2.4 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +21 -14
- package/CLAUDE.md +21 -14
- package/README.md +96 -131
- package/changelog/0.2.x/0.2.5.md +23 -0
- package/changelog/template.md +9 -26
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +56 -3
- package/dist/index.js.map +1 -1
- package/dist/mcp-server/resources/definitions/cfr-section.resource.d.ts +1 -0
- package/dist/mcp-server/resources/definitions/cfr-section.resource.d.ts.map +1 -1
- package/dist/mcp-server/resources/definitions/cfr-section.resource.js +1 -0
- package/dist/mcp-server/resources/definitions/cfr-section.resource.js.map +1 -1
- package/dist/mcp-server/resources/definitions/index.d.ts +1 -0
- package/dist/mcp-server/resources/definitions/index.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/browse-cfr.tool.d.ts +3 -0
- package/dist/mcp-server/tools/definitions/browse-cfr.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/browse-cfr.tool.js +3 -0
- package/dist/mcp-server/tools/definitions/browse-cfr.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/find-comments.tool.d.ts +2 -0
- package/dist/mcp-server/tools/definitions/find-comments.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/find-comments.tool.js +2 -0
- package/dist/mcp-server/tools/definitions/find-comments.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/format-utils.d.ts +9 -1
- package/dist/mcp-server/tools/definitions/format-utils.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/format-utils.js +10 -2
- package/dist/mcp-server/tools/definitions/format-utils.js.map +1 -1
- package/dist/mcp-server/tools/definitions/get-cfr-section.tool.d.ts +1 -0
- package/dist/mcp-server/tools/definitions/get-cfr-section.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/get-cfr-section.tool.js +1 -0
- package/dist/mcp-server/tools/definitions/get-cfr-section.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/get-docket.tool.d.ts +3 -0
- package/dist/mcp-server/tools/definitions/get-docket.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/get-docket.tool.js +3 -0
- package/dist/mcp-server/tools/definitions/get-docket.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/index.d.ts +9 -0
- package/dist/mcp-server/tools/definitions/index.d.ts.map +1 -1
- package/dist/services/ecfr/ecfr-service.js +2 -2
- package/dist/services/ecfr-mirror/ecfr-mirror.d.ts +10 -6
- package/dist/services/ecfr-mirror/ecfr-mirror.d.ts.map +1 -1
- package/dist/services/ecfr-mirror/ecfr-mirror.js +17 -12
- package/dist/services/ecfr-mirror/ecfr-mirror.js.map +1 -1
- package/dist/services/federal-register/federal-register-service.js +2 -2
- package/dist/services/regulations-gov/regulations-gov-service.js +2 -2
- package/dist/services/upstream-failure.d.ts +1 -1
- package/package.json +19 -9
- package/server.json +3 -3
package/AGENTS.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# Developer Protocol
|
|
2
2
|
|
|
3
3
|
**Server:** federal-regulations-mcp-server
|
|
4
|
-
**Version:** 0.2.
|
|
5
|
-
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `0.
|
|
6
|
-
**Engines:** Bun ≥1.
|
|
4
|
+
**Version:** 0.2.5
|
|
5
|
+
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `0.13.6`
|
|
6
|
+
**Engines:** Bun ≥1.4.0, Node ≥24.0.0
|
|
7
7
|
**MCP SDK:** `@modelcontextprotocol/server` ^2.0.0
|
|
8
|
-
**Zod:** ^4.
|
|
8
|
+
**Zod:** ^4.6.5
|
|
9
9
|
|
|
10
10
|
> **Read the framework docs first:** `node_modules/@cyanheads/mcp-ts-core/CLAUDE.md` contains the full API reference — builders, Context, error codes, exports, patterns. This file covers server-specific conventions only.
|
|
11
11
|
|
|
@@ -165,7 +165,7 @@ Handlers receive a unified `ctx` object. Key properties:
|
|
|
165
165
|
|
|
166
166
|
Handlers throw — the framework catches, classifies, and formats.
|
|
167
167
|
|
|
168
|
-
**Recommended: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable? }]` 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('reason')` as the throw's data to put it on the wire (`data.recovery.hint`, mirrored into `content[]` text); override with an explicit `{ recovery: { hint: '...' } }` when dynamic runtime context matters. Baseline codes (`InternalError`, `ServiceUnavailable`, `Timeout`, `ValidationError`, `SerializationError`) bubble freely and don't need declaring.
|
|
168
|
+
**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('reason')` as the throw's data to put it on the wire (`data.recovery.hint`, mirrored into `content[]` text unless the message already contains it verbatim); override with an explicit `{ recovery: { hint: '...' } }` when dynamic runtime context matters. Forwarding it is lint-enforced per throw site (`error-contract-recovery-unforwarded`). Mark an entry the service layer throws with `thrownBy: 'service'` so `error-contract-unthrown` skips it — lint-only metadata, nothing at runtime reads it. Baseline codes (`InternalError`, `ServiceUnavailable`, `Timeout`, `ValidationError`, `SerializationError`, `RequestCancelled`) bubble freely and don't need declaring.
|
|
169
169
|
|
|
170
170
|
```ts
|
|
171
171
|
import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
|
|
@@ -247,9 +247,9 @@ No prompts in v1 — `createApp({ prompts: [] })`.
|
|
|
247
247
|
|
|
248
248
|
## Skills
|
|
249
249
|
|
|
250
|
-
Skills are modular instructions in `skills/` at the project root. Read them directly when a task matches — e.g., `skills/add-tool/SKILL.md` when adding a tool. `bun run list-skills` prints the full registry.
|
|
250
|
+
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 for those agents.
|
|
251
251
|
|
|
252
|
-
**Agent skill directory:** Copy skills into the directory your agent discovers (Claude Code: `.claude/skills/`, others: equivalent). Skills then load as context without referencing `skills/` paths. After framework updates, run the `maintenance` skill — Phase B re-syncs the agent directory.
|
|
252
|
+
**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.
|
|
253
253
|
|
|
254
254
|
Available skills:
|
|
255
255
|
|
|
@@ -268,8 +268,9 @@ Available skills:
|
|
|
268
268
|
| `security-pass` | Audit server for MCP-flavored security gaps: output injection, scope blast radius, input sinks, tenant isolation |
|
|
269
269
|
| `code-simplifier` | Post-session cleanup against `git diff` — modernize syntax, consolidate duplication, align with the codebase |
|
|
270
270
|
| `polish-docs-meta` | Finalize docs, README, metadata, and agent protocol for shipping |
|
|
271
|
-
| `git-wrapup` | Land working-tree changes as a
|
|
272
|
-
| `release-
|
|
271
|
+
| `git-wrapup` | Land working-tree changes as a commit stack — version bump, changelog, verify, commit by concern, release commit on top. No tag, no push to main; opens the release PR when the project declares release PR mode |
|
|
272
|
+
| `release-pr-review` | Review pass on an open release PR — simplifier + correctness review, fixes as ordinary commits on top of the stack, PR body kept in sync. Release PR mode only |
|
|
273
|
+
| `release-and-publish` | Fast-forward merge (release PR mode) + tag + push + npm + MCP Registry + GH Release + Docker. Picks up from `git-wrapup` |
|
|
273
274
|
| `maintenance` | Investigate changelogs, adopt upstream changes, sync skills to agent dirs |
|
|
274
275
|
| `orchestrations` | Chain task skills into a gated multi-phase pipeline — build-out, QA-fix, update-ship — when you can spawn sub-agents |
|
|
275
276
|
| `report-issue-framework` | File a bug or feature request against `@cyanheads/mcp-ts-core` via `gh` CLI |
|
|
@@ -288,7 +289,7 @@ Available skills:
|
|
|
288
289
|
| `api-telemetry` | OTel catalog: spans, metrics, completion logs, env config, cardinality rules |
|
|
289
290
|
| `api-workers` | Cloudflare Workers runtime |
|
|
290
291
|
|
|
291
|
-
**Chaining skills into pipelines.** When the user wants a multi-phase effort — build this server out, QA-and-fix the surface, update-and-ship — *and you can spawn sub-agents*, `skills/orchestrations/SKILL.md` sequences the task skills above into a gated pipeline with verification at each step. Read it to drive the run. Optional: skip it if you can't orchestrate sub-agents, and ignore it entirely if you were *spawned* as one — you've already been scoped to a single phase.
|
|
292
|
+
**Chaining skills into pipelines.** When the user wants a multi-phase effort — build this server out, QA-and-fix the surface, update-and-ship — *and you can spawn sub-agents*, `framework-skills/orchestrations/SKILL.md` sequences the task skills above into a gated pipeline with verification at each step. Read it to drive the run. Optional: skip it if you can't orchestrate sub-agents, and ignore it entirely if you were *spawned* as one — you've already been scoped to a single phase.
|
|
292
293
|
|
|
293
294
|
When you complete a skill's checklist, check the boxes and add a completion timestamp at the end (e.g., `Completed: 2026-03-11`).
|
|
294
295
|
|
|
@@ -325,11 +326,11 @@ When you complete a skill's checklist, check the boxes and add a completion time
|
|
|
325
326
|
|
|
326
327
|
## Bundling
|
|
327
328
|
|
|
328
|
-
`npm 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 (`skills/`, `.claude/`, `.agents/`, `SKILL.md`) and platform-specific native bindings, which would otherwise lock the bundle to the platform it was packed on. A server using DataCanvas therefore ships a portable bundle without the DuckDB native — `@duckdb/node-api` is an optional peer loaded lazily, so canvas tools report an actionable install hint and every other tool works normally. MCPB is stdio-only — HTTP and Cloudflare Workers deployments are unaffected. Consumers who don't need it can delete `manifest.json` and `.mcpbignore`; `lint:packaging` skips cleanly.
|
|
329
|
+
`npm 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 packed on. A server using DataCanvas therefore ships a portable bundle without the DuckDB native — `@duckdb/node-api` is an optional peer loaded lazily, so canvas tools report an actionable install hint and every other tool works normally. MCPB is stdio-only — HTTP and Cloudflare Workers deployments are unaffected. Consumers who don't need it can delete `manifest.json` and `.mcpbignore`; `lint:packaging` skips cleanly.
|
|
329
330
|
|
|
330
|
-
**Adding an env var requires both files:** `server.json` (registry discovery, `environmentVariables[]`) and `manifest.json` (bundle install UX, `mcp_config.env` + `user_config`). `lint:packaging` (run by `devcheck`) verifies the env var names match.
|
|
331
|
+
**Adding an env var requires both files:** `server.json` (registry discovery, `environmentVariables[]`) and `manifest.json` (bundle install UX, `mcp_config.env` + `user_config`). `lint:packaging` (run by `devcheck`) verifies the env var names match, that every `user_config` option is wired into `mcp_config.env` as `"X": "${user_config.X}"` (the host substitutes nothing else — `"${X}"` reaches the server as that literal string), and that an optional string option carries `"default": ""`.
|
|
331
332
|
|
|
332
|
-
**README install badges** (Claude Desktop `.mcpb`, Cursor, VS Code) and the `base64` / `encodeURIComponent` config-generation commands are ship-time concerns — run the `polish-docs-meta` skill, which carries the badge format, layout, and generation snippets in `skills/polish-docs-meta/references/readme.md`.
|
|
333
|
+
**README install badges** (Claude Desktop `.mcpb`, Cursor, VS Code) and the `base64` / `encodeURIComponent` config-generation commands are ship-time concerns — run the `polish-docs-meta` skill, which carries the badge format, layout, and generation snippets in `framework-skills/polish-docs-meta/references/readme.md`.
|
|
333
334
|
|
|
334
335
|
---
|
|
335
336
|
|
|
@@ -354,12 +355,18 @@ security: false # optional — true ONLY for a source
|
|
|
354
355
|
|
|
355
356
|
`agent-notes` is an optional free-form field for maintenance agents processing the release downstream. Content here won't appear in the rendered CHANGELOG — it's consumed by agents running the `maintenance` skill. Use it for adoption instructions that don't fit the human-facing sections: new files to create, fields to populate, one-time migration steps. Omit entirely when there's nothing to say.
|
|
356
357
|
|
|
357
|
-
**Section order
|
|
358
|
+
**Section order:** the Keep a Changelog sequence — Added, Changed, Deprecated, Removed, Fixed, Security — then `Dependencies` last. Include only sections with entries — don't ship empty headers.
|
|
358
359
|
|
|
359
360
|
**Tag annotations** render as GitHub Release bodies via `--notes-from-tag`. They must be structured markdown — never a flat comma-separated string. Subject omits the version number (GitHub prepends it). See `changelog/template.md` for the full format reference.
|
|
360
361
|
|
|
361
362
|
---
|
|
362
363
|
|
|
364
|
+
## Publishing
|
|
365
|
+
|
|
366
|
+
**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 brief may run a given release as `gated` instead — a `release-pr-review` pass on the open PR before `release-and-publish`. **Never merge through the GitHub UI or `gh pr merge`**: squash and rebase-merge are disabled in the repo settings because both rewrite the stack (rebase-merge also strips the SSH signatures), and a merge commit breaks the linear history.
|
|
367
|
+
|
|
368
|
+
---
|
|
369
|
+
|
|
363
370
|
## Imports
|
|
364
371
|
|
|
365
372
|
```ts
|
package/CLAUDE.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# Developer Protocol
|
|
2
2
|
|
|
3
3
|
**Server:** federal-regulations-mcp-server
|
|
4
|
-
**Version:** 0.2.
|
|
5
|
-
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `0.
|
|
6
|
-
**Engines:** Bun ≥1.
|
|
4
|
+
**Version:** 0.2.5
|
|
5
|
+
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `0.13.6`
|
|
6
|
+
**Engines:** Bun ≥1.4.0, Node ≥24.0.0
|
|
7
7
|
**MCP SDK:** `@modelcontextprotocol/server` ^2.0.0
|
|
8
|
-
**Zod:** ^4.
|
|
8
|
+
**Zod:** ^4.6.5
|
|
9
9
|
|
|
10
10
|
> **Read the framework docs first:** `node_modules/@cyanheads/mcp-ts-core/CLAUDE.md` contains the full API reference — builders, Context, error codes, exports, patterns. This file covers server-specific conventions only.
|
|
11
11
|
|
|
@@ -165,7 +165,7 @@ Handlers receive a unified `ctx` object. Key properties:
|
|
|
165
165
|
|
|
166
166
|
Handlers throw — the framework catches, classifies, and formats.
|
|
167
167
|
|
|
168
|
-
**Recommended: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable? }]` 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('reason')` as the throw's data to put it on the wire (`data.recovery.hint`, mirrored into `content[]` text); override with an explicit `{ recovery: { hint: '...' } }` when dynamic runtime context matters. Baseline codes (`InternalError`, `ServiceUnavailable`, `Timeout`, `ValidationError`, `SerializationError`) bubble freely and don't need declaring.
|
|
168
|
+
**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('reason')` as the throw's data to put it on the wire (`data.recovery.hint`, mirrored into `content[]` text unless the message already contains it verbatim); override with an explicit `{ recovery: { hint: '...' } }` when dynamic runtime context matters. Forwarding it is lint-enforced per throw site (`error-contract-recovery-unforwarded`). Mark an entry the service layer throws with `thrownBy: 'service'` so `error-contract-unthrown` skips it — lint-only metadata, nothing at runtime reads it. Baseline codes (`InternalError`, `ServiceUnavailable`, `Timeout`, `ValidationError`, `SerializationError`, `RequestCancelled`) bubble freely and don't need declaring.
|
|
169
169
|
|
|
170
170
|
```ts
|
|
171
171
|
import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
|
|
@@ -247,9 +247,9 @@ No prompts in v1 — `createApp({ prompts: [] })`.
|
|
|
247
247
|
|
|
248
248
|
## Skills
|
|
249
249
|
|
|
250
|
-
Skills are modular instructions in `skills/` at the project root. Read them directly when a task matches — e.g., `skills/add-tool/SKILL.md` when adding a tool. `bun run list-skills` prints the full registry.
|
|
250
|
+
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 for those agents.
|
|
251
251
|
|
|
252
|
-
**Agent skill directory:** Copy skills into the directory your agent discovers (Claude Code: `.claude/skills/`, others: equivalent). Skills then load as context without referencing `skills/` paths. After framework updates, run the `maintenance` skill — Phase B re-syncs the agent directory.
|
|
252
|
+
**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.
|
|
253
253
|
|
|
254
254
|
Available skills:
|
|
255
255
|
|
|
@@ -268,8 +268,9 @@ Available skills:
|
|
|
268
268
|
| `security-pass` | Audit server for MCP-flavored security gaps: output injection, scope blast radius, input sinks, tenant isolation |
|
|
269
269
|
| `code-simplifier` | Post-session cleanup against `git diff` — modernize syntax, consolidate duplication, align with the codebase |
|
|
270
270
|
| `polish-docs-meta` | Finalize docs, README, metadata, and agent protocol for shipping |
|
|
271
|
-
| `git-wrapup` | Land working-tree changes as a
|
|
272
|
-
| `release-
|
|
271
|
+
| `git-wrapup` | Land working-tree changes as a commit stack — version bump, changelog, verify, commit by concern, release commit on top. No tag, no push to main; opens the release PR when the project declares release PR mode |
|
|
272
|
+
| `release-pr-review` | Review pass on an open release PR — simplifier + correctness review, fixes as ordinary commits on top of the stack, PR body kept in sync. Release PR mode only |
|
|
273
|
+
| `release-and-publish` | Fast-forward merge (release PR mode) + tag + push + npm + MCP Registry + GH Release + Docker. Picks up from `git-wrapup` |
|
|
273
274
|
| `maintenance` | Investigate changelogs, adopt upstream changes, sync skills to agent dirs |
|
|
274
275
|
| `orchestrations` | Chain task skills into a gated multi-phase pipeline — build-out, QA-fix, update-ship — when you can spawn sub-agents |
|
|
275
276
|
| `report-issue-framework` | File a bug or feature request against `@cyanheads/mcp-ts-core` via `gh` CLI |
|
|
@@ -288,7 +289,7 @@ Available skills:
|
|
|
288
289
|
| `api-telemetry` | OTel catalog: spans, metrics, completion logs, env config, cardinality rules |
|
|
289
290
|
| `api-workers` | Cloudflare Workers runtime |
|
|
290
291
|
|
|
291
|
-
**Chaining skills into pipelines.** When the user wants a multi-phase effort — build this server out, QA-and-fix the surface, update-and-ship — *and you can spawn sub-agents*, `skills/orchestrations/SKILL.md` sequences the task skills above into a gated pipeline with verification at each step. Read it to drive the run. Optional: skip it if you can't orchestrate sub-agents, and ignore it entirely if you were *spawned* as one — you've already been scoped to a single phase.
|
|
292
|
+
**Chaining skills into pipelines.** When the user wants a multi-phase effort — build this server out, QA-and-fix the surface, update-and-ship — *and you can spawn sub-agents*, `framework-skills/orchestrations/SKILL.md` sequences the task skills above into a gated pipeline with verification at each step. Read it to drive the run. Optional: skip it if you can't orchestrate sub-agents, and ignore it entirely if you were *spawned* as one — you've already been scoped to a single phase.
|
|
292
293
|
|
|
293
294
|
When you complete a skill's checklist, check the boxes and add a completion timestamp at the end (e.g., `Completed: 2026-03-11`).
|
|
294
295
|
|
|
@@ -325,11 +326,11 @@ When you complete a skill's checklist, check the boxes and add a completion time
|
|
|
325
326
|
|
|
326
327
|
## Bundling
|
|
327
328
|
|
|
328
|
-
`npm 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 (`skills/`, `.claude/`, `.agents/`, `SKILL.md`) and platform-specific native bindings, which would otherwise lock the bundle to the platform it was packed on. A server using DataCanvas therefore ships a portable bundle without the DuckDB native — `@duckdb/node-api` is an optional peer loaded lazily, so canvas tools report an actionable install hint and every other tool works normally. MCPB is stdio-only — HTTP and Cloudflare Workers deployments are unaffected. Consumers who don't need it can delete `manifest.json` and `.mcpbignore`; `lint:packaging` skips cleanly.
|
|
329
|
+
`npm 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 packed on. A server using DataCanvas therefore ships a portable bundle without the DuckDB native — `@duckdb/node-api` is an optional peer loaded lazily, so canvas tools report an actionable install hint and every other tool works normally. MCPB is stdio-only — HTTP and Cloudflare Workers deployments are unaffected. Consumers who don't need it can delete `manifest.json` and `.mcpbignore`; `lint:packaging` skips cleanly.
|
|
329
330
|
|
|
330
|
-
**Adding an env var requires both files:** `server.json` (registry discovery, `environmentVariables[]`) and `manifest.json` (bundle install UX, `mcp_config.env` + `user_config`). `lint:packaging` (run by `devcheck`) verifies the env var names match.
|
|
331
|
+
**Adding an env var requires both files:** `server.json` (registry discovery, `environmentVariables[]`) and `manifest.json` (bundle install UX, `mcp_config.env` + `user_config`). `lint:packaging` (run by `devcheck`) verifies the env var names match, that every `user_config` option is wired into `mcp_config.env` as `"X": "${user_config.X}"` (the host substitutes nothing else — `"${X}"` reaches the server as that literal string), and that an optional string option carries `"default": ""`.
|
|
331
332
|
|
|
332
|
-
**README install badges** (Claude Desktop `.mcpb`, Cursor, VS Code) and the `base64` / `encodeURIComponent` config-generation commands are ship-time concerns — run the `polish-docs-meta` skill, which carries the badge format, layout, and generation snippets in `skills/polish-docs-meta/references/readme.md`.
|
|
333
|
+
**README install badges** (Claude Desktop `.mcpb`, Cursor, VS Code) and the `base64` / `encodeURIComponent` config-generation commands are ship-time concerns — run the `polish-docs-meta` skill, which carries the badge format, layout, and generation snippets in `framework-skills/polish-docs-meta/references/readme.md`.
|
|
333
334
|
|
|
334
335
|
---
|
|
335
336
|
|
|
@@ -354,12 +355,18 @@ security: false # optional — true ONLY for a source
|
|
|
354
355
|
|
|
355
356
|
`agent-notes` is an optional free-form field for maintenance agents processing the release downstream. Content here won't appear in the rendered CHANGELOG — it's consumed by agents running the `maintenance` skill. Use it for adoption instructions that don't fit the human-facing sections: new files to create, fields to populate, one-time migration steps. Omit entirely when there's nothing to say.
|
|
356
357
|
|
|
357
|
-
**Section order
|
|
358
|
+
**Section order:** the Keep a Changelog sequence — Added, Changed, Deprecated, Removed, Fixed, Security — then `Dependencies` last. Include only sections with entries — don't ship empty headers.
|
|
358
359
|
|
|
359
360
|
**Tag annotations** render as GitHub Release bodies via `--notes-from-tag`. They must be structured markdown — never a flat comma-separated string. Subject omits the version number (GitHub prepends it). See `changelog/template.md` for the full format reference.
|
|
360
361
|
|
|
361
362
|
---
|
|
362
363
|
|
|
364
|
+
## Publishing
|
|
365
|
+
|
|
366
|
+
**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 brief may run a given release as `gated` instead — a `release-pr-review` pass on the open PR before `release-and-publish`. **Never merge through the GitHub UI or `gh pr merge`**: squash and rebase-merge are disabled in the repo settings because both rewrite the stack (rebase-merge also strips the SSH signatures), and a merge commit breaks the linear history.
|
|
367
|
+
|
|
368
|
+
---
|
|
369
|
+
|
|
363
370
|
## Imports
|
|
364
371
|
|
|
365
372
|
```ts
|
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
<div align="center">
|
|
9
9
|
|
|
10
|
-
[](./CHANGELOG.md) [](./LICENSE) [](https://github.com/users/cyanheads/packages/container/package/federal-regulations-mcp-server) [](https://modelcontextprotocol.io/) [](https://www.npmjs.com/package/@cyanheads/federal-regulations-mcp-server) [](https://www.typescriptlang.org/) [](https://bun.sh/)
|
|
11
11
|
|
|
12
12
|
</div>
|
|
13
13
|
|
|
@@ -27,145 +27,134 @@
|
|
|
27
27
|
|
|
28
28
|
---
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
## Overview
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
US federal regulatory law across three official sources: the Federal Register (proposed/final rules and notices), the eCFR (codified, point-in-time CFR text), and Regulations.gov (rulemaking dockets and public comments). Search rules, trace a document from proposal through comments to codified text, and read CFR sections from any MCP client. Runs as a stdio process, a local Streamable HTTP server, or the public hosted endpoint above.
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
### Tools
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
| Tool | Description |
|
|
37
|
+
|:---|:---|
|
|
38
|
+
| `regulations_search_rules` | Search Federal Register proposed rules, final rules, notices, and presidential documents by query, type, agency, date range, and comment status |
|
|
39
|
+
| `regulations_get_document` | Fetch one Federal Register document by number, with the docket ID, CFR parts, and comment count that chain into other tools |
|
|
40
|
+
| `regulations_browse_cfr` | Walk the CFR hierarchy or full-text-search the codified CFR |
|
|
41
|
+
| `regulations_get_cfr_section` | Read codified CFR text for a section, whole part, or appendix, current or as of a past date |
|
|
42
|
+
| `regulations_get_docket` | Pull a rulemaking docket and its filed documents from Regulations.gov (key required) |
|
|
43
|
+
| `regulations_find_comments` | Fetch public comments on a document or docket, or one comment's full body and attachments (key required) |
|
|
44
|
+
| `regulations_list_open_comments` | List rules currently open for public comment, sorted by closing date |
|
|
37
45
|
|
|
38
|
-
|
|
39
|
-
|:---|:---|:---|:---|
|
|
40
|
-
| `regulations_search_rules` | Federal Register | — | Search proposed rules, final rules, notices, and presidential documents by query, type, agency, date range, and open-for-comment status. The primary discovery entry point. |
|
|
41
|
-
| `regulations_get_document` | Federal Register | — | Fetch one FR document by number — full metadata plus the cross-source handles (docket ID, CFR parts, comment count) that chain into the comment and codified-text tools. |
|
|
42
|
-
| `regulations_browse_cfr` | eCFR | — | Walk the CFR hierarchy (structure mode) or full-text-search the codified CFR (search mode). Both feed `regulations_get_cfr_section`. |
|
|
43
|
-
| `regulations_get_cfr_section` | eCFR | — | Read the codified text at a CFR location — a section, a whole part, or an appendix — current or as of a past date. |
|
|
44
|
-
| `regulations_get_docket` | Regulations.gov | ✔ | Pull a rulemaking docket and the documents filed in it (NPRM, final rule, supporting materials). |
|
|
45
|
-
| `regulations_find_comments` | Regulations.gov | ✔ | Fetch public comments on a document or docket, or one comment's full body and attachments. Flags attachment-only submissions. |
|
|
46
|
-
| `regulations_list_open_comments` | Federal Register (+ Reg.gov enrich) | optional | Rules currently open for public comment, closing soonest first. Fully functional keyless; enriches comment counts when keyed. |
|
|
46
|
+
### Resources
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
| Resource | Description |
|
|
49
|
+
|:---|:---|
|
|
50
|
+
| `regulations://document/{documentNumber}` | A single Federal Register document — metadata plus cross-source handles (mirrors `regulations_get_document`) |
|
|
51
|
+
| `regulations://cfr/{title}/{part}/{section}` | Codified text of a current CFR section (mirrors `regulations_get_cfr_section`) |
|
|
49
52
|
|
|
50
|
-
|
|
53
|
+
All resource data is also reachable via the tool surface — tool-only MCP clients lose nothing.
|
|
51
54
|
|
|
52
|
-
|
|
55
|
+
## Capability reference
|
|
53
56
|
|
|
54
|
-
|
|
55
|
-
- Filter by document `type` (`PRORULE`, `RULE`, `NOTICE`, `PRESDOCU`), `agencies` (Federal Register agency slug), and publication date window (`published_after` / `published_before`)
|
|
56
|
-
- Each result carries the `documentNumber`, `docketIds`, `regulationIdNumbers` (RIN), and `cfrReferences` that chain into the document, comment, and codified-text tools
|
|
57
|
-
- Pagination up to 100 per page, 50 pages; the Federal Register caps navigation at 5,000 records and the match count at 10,000 — when a result set is larger, the tool flags it and steers you to date-windowing
|
|
57
|
+
### `regulations_search_rules` <sub>tool</sub>
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
- Keyless; full-text `query` optional, or browse by `type` (`PRORULE`/`RULE`/`NOTICE`/`PRESDOCU`), `agencies` (Federal Register slug), and `published_after`/`published_before`
|
|
60
|
+
- `per_page` 2–100 (default 20), `page` 1–50 — the Federal Register caps navigation at 50 pages / 5,000 records and total matches at 10,000; narrow the date window rather than paging deeper
|
|
61
|
+
- Each result carries `documentNumber` (→ `regulations_get_document`), `docketIds` (→ `regulations_get_docket` / `find_comments`), `regulationIdNumbers`, and `cfrReferences` (→ `regulations_get_cfr_section`)
|
|
62
|
+
- `upstream_unavailable` on a Federal Register 5xx/timeout, retryable after a brief wait
|
|
60
63
|
|
|
61
|
-
|
|
64
|
+
---
|
|
62
65
|
|
|
63
|
-
|
|
66
|
+
### `regulations_get_document` <sub>tool</sub>
|
|
64
67
|
|
|
65
|
-
-
|
|
66
|
-
-
|
|
67
|
-
- `include_full_text` inlines the
|
|
68
|
+
- Keyless; fetch one document by `document_number` (format `\d{4}-\d+`, e.g. `2025-14555`)
|
|
69
|
+
- Full metadata (title, type, agencies, abstract, action, effective/comment dates, RINs) plus cross-source handles: `docketId`, `regulationsGovDocumentId`, `commentCount`, and `cfrReferences`
|
|
70
|
+
- `include_full_text` (default `false`) inlines the plain-text body — final rules can run tens of thousands of words, so it's opt-in
|
|
71
|
+
- `not_found` when the FR number doesn't exist; `upstream_unavailable` on a 5xx/timeout
|
|
68
72
|
|
|
69
73
|
---
|
|
70
74
|
|
|
71
|
-
### `regulations_browse_cfr`
|
|
75
|
+
### `regulations_browse_cfr` <sub>tool</sub>
|
|
72
76
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
- `
|
|
76
|
-
-
|
|
77
|
-
- `
|
|
78
|
-
- A live search hit's `hierarchyPath` names the part it sits in (`Part 51 — Requirements for Preparation, Adoption, and Submittal of Implementation Plans`), so the subject matter is readable without a second call; a mirror hit's path stays structural, because the index stores no level names
|
|
79
|
-
- Search is served from the synced local mirror (FTS5) only when the mirror's title coverage can answer the request — a title it does not hold, an all-titles query against a scoped mirror, a `date`, or a cold deploy all go to the live eCFR search API instead
|
|
80
|
-
- Every search result reports `source` (`mirror` or `live`) and `sourceScope` — which corpus answered and what it covers, so an empty result is never mistaken for "no such regulation"
|
|
81
|
-
- `date` searches the section text in effect that day; eCFR indexes 2017-01-03 onward, and an undated search is pinned to eCFR's current index date rather than spanning every historical version
|
|
82
|
-
- Both modes assemble a `cfrCite` that feeds `regulations_get_cfr_section`, and both hand appendices back the same way: an `appendix` field with eCFR's verbatim identifier, and a cite that names the appendix rather than the part around it
|
|
83
|
-
- The mirror indexes section text only, so it never matches an appendix — its `sourceScope` says so, since otherwise an appendix that exists and one that does not both read as zero matches
|
|
77
|
+
- Keyless; `mode: "structure"` walks the CFR tree (all 50 titles, or one title's chapters → parts → sections); `mode: "search"` full-text-searches the codified CFR
|
|
78
|
+
- `title` (1–50) and `part` scope both modes; a `part` without `title` is rejected (`title_required_for_part`) since part numbers repeat across titles
|
|
79
|
+
- Search accepts `date` (point-in-time; eCFR indexes 2017-01-03 onward) and `per_page` (1–50, default 20)
|
|
80
|
+
- Every search result reports `source` (`mirror`/`live`) and `sourceScope` — the mirror only answers a title it holds, never an all-titles query when scoped, so anything it can't answer falls through to the live eCFR API
|
|
81
|
+
- `query_required` when `mode="search"` has no query; `title_not_found` / `date_out_of_range` / `upstream_unavailable` round out the errors
|
|
84
82
|
|
|
85
83
|
---
|
|
86
84
|
|
|
87
|
-
### `regulations_get_cfr_section`
|
|
88
|
-
|
|
89
|
-
Read the codified text at a CFR location — one section, a whole part, or one appendix — via eCFR. Keyless.
|
|
85
|
+
### `regulations_get_cfr_section` <sub>tool</sub>
|
|
90
86
|
|
|
91
|
-
-
|
|
92
|
-
-
|
|
93
|
-
-
|
|
94
|
-
- A whole-part
|
|
95
|
-
-
|
|
96
|
-
-
|
|
97
|
-
- A cite that names nothing returns the tool's not-found error with a recovery hint pointing at `regulations_browse_cfr` structure mode, not a bare upstream status
|
|
98
|
-
- eCFR retains historical versions back to roughly 2017; a date before coverage is rejected with guidance
|
|
87
|
+
- Keyless; reads one section (`title`+`part`+`section`), a whole part (`title`+`part`, `section` omitted), or one appendix (`title`(+`part`)+`appendix`) — `section` and `appendix` are mutually exclusive (`conflicting_target`)
|
|
88
|
+
- `date` for point-in-time text; eCFR retains history back to ~2017-01-03, rejected earlier as `date_out_of_range`
|
|
89
|
+
- `appendix` must be passed verbatim as eCFR / `regulations_browse_cfr` emits it (e.g. `Appendix A-1 to Part 50`), not a short form
|
|
90
|
+
- A whole-part fetch lists its appendices' identifiers and headings without inlining their text — call again with `appendix` to read one
|
|
91
|
+
- `source` (`mirror`/`live`) reports provenance; current single-section reads are mirror-served when ready, everything else (historical dates, whole-part, appendix reads) falls back to the live eCFR versioner
|
|
92
|
+
- `not_found` / `location_required` / `upstream_unavailable` round out the errors
|
|
99
93
|
|
|
100
94
|
---
|
|
101
95
|
|
|
102
|
-
### `regulations_get_docket` · key required
|
|
103
|
-
|
|
104
|
-
Pull a rulemaking docket from Regulations.gov by docket ID (e.g. `EPA-HQ-OAR-2025-0194`). Requires `REGULATIONS_GOV_API_KEY`.
|
|
96
|
+
### `regulations_get_docket` <sub>tool</sub> · key required
|
|
105
97
|
|
|
106
|
-
-
|
|
107
|
-
-
|
|
108
|
-
-
|
|
109
|
-
-
|
|
110
|
-
-
|
|
98
|
+
- Requires `REGULATIONS_GOV_API_KEY`; fetch a docket by `docket_id` (e.g. `EPA-HQ-OAR-2025-0194`)
|
|
99
|
+
- `document_types` filters to `Proposed Rule` / `Rule` / `Notice` / `Supporting & Related Material` / `Other` — a docket often holds hundreds of supporting materials
|
|
100
|
+
- `per_page` 5–250 (default 25), `page` 1–20 — Regulations.gov caps a query at 5,000 records
|
|
101
|
+
- Each document's `objectId` chains into `regulations_find_comments`; `frDocNum` chains back to `regulations_get_document`
|
|
102
|
+
- `auth_required` (missing/rejected key) names the env var and signup URL; `not_found` / `rate_limited` (429, 1,000 req/hr) / `upstream_unavailable` round out the errors
|
|
111
103
|
|
|
112
104
|
---
|
|
113
105
|
|
|
114
|
-
### `regulations_find_comments` · key required
|
|
106
|
+
### `regulations_find_comments` <sub>tool</sub> · key required
|
|
115
107
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
-
|
|
119
|
-
-
|
|
120
|
-
-
|
|
121
|
-
- When a comment's real content is a PDF/DOCX attachment, the body is a stub and `attachmentOnly` is `true`; the attachment download URLs are returned so the agent knows where the substance lives. The flag reaches both the structured output and the rendered text
|
|
122
|
-
- Regulations.gov caps a query at 20 pages (5,000 records); a high-volume docket surfaces a sample, flagged as truncated
|
|
108
|
+
- Requires `REGULATIONS_GOV_API_KEY`; exactly one of `docket_id`, `document_object_id`, `fr_document_number`, or `comment_id` — zero or two is rejected (`target_required` / `multiple_targets`), never resolved by precedence
|
|
109
|
+
- `comment_id` returns one comment's full body and attachments; the other three list a set — the list endpoint carries no body text, so read a comment's substance via `comment_id`
|
|
110
|
+
- When a comment's substance is a PDF/DOCX attachment, `bodyText` is a stub and `attachmentOnly` is `true`, with the attachment download URLs
|
|
111
|
+
- `per_page` 5–250 (default 25), `page` 1–20 — Regulations.gov caps a query at 5,000 records; narrow a high-volume docket with `document_object_id`
|
|
112
|
+
- `auth_required` / `not_found` / `rate_limited` (429, 1,000 req/hr) / `upstream_unavailable` round out the errors
|
|
123
113
|
|
|
124
114
|
---
|
|
125
115
|
|
|
126
|
-
### `regulations_list_open_comments`
|
|
116
|
+
### `regulations_list_open_comments` <sub>tool</sub>
|
|
117
|
+
|
|
118
|
+
- Keyless; lists rules currently open for public comment, sorted by closing date soonest first — filter by `query`, `agencies`, and `closing_before`
|
|
119
|
+
- `per_page` 2–100 (default 20), `page` 1–50 — same Federal Register 5,000-record navigation ceiling as `regulations_search_rules`
|
|
120
|
+
- Each row carries `daysRemaining`, `documentNumber` (→ `regulations_get_document`), and `docketIds` (→ `regulations_find_comments`)
|
|
121
|
+
- Fully functional keyless; when `REGULATIONS_GOV_API_KEY` is set, `commentCount` is enriched from the Federal Register document's own embedded Regulations.gov info (no extra call) — `keyed` reports which
|
|
122
|
+
- `upstream_unavailable` on a Federal Register 5xx/timeout
|
|
127
123
|
|
|
128
|
-
|
|
124
|
+
---
|
|
129
125
|
|
|
130
|
-
|
|
131
|
-
- Each row carries `daysRemaining`, the `documentNumber` (→ `regulations_get_document`), and `docketIds` (→ `regulations_find_comments`)
|
|
132
|
-
- Fully functional without a key; when `REGULATIONS_GOV_API_KEY` is configured, each row is enriched with the comment count read from the Federal Register document's own embedded Regulations.gov info (no extra rate-limited call)
|
|
126
|
+
### `regulations://document/{documentNumber}` <sub>resource</sub>
|
|
133
127
|
|
|
134
|
-
|
|
128
|
+
- Same payload as `regulations_get_document` with `include_full_text` omitted — metadata plus cross-source handles, full text never inlined
|
|
129
|
+
- `documentNumber` format `\d{4}-\d+` (e.g. `2025-14555`)
|
|
130
|
+
- `not_found` / `upstream_unavailable` mirror the tool's errors
|
|
135
131
|
|
|
136
|
-
|
|
137
|
-
|:---|:---|:---|
|
|
138
|
-
| Resource | `regulations://document/{documentNumber}` | A single Federal Register document — metadata plus cross-source handles (mirrors `regulations_get_document`, full text omitted). |
|
|
139
|
-
| Resource | `regulations://cfr/{title}/{part}/{section}` | Codified text of a current CFR section (mirrors `regulations_get_cfr_section` at the current date, mirror-backed with a live eCFR fallback). |
|
|
132
|
+
---
|
|
140
133
|
|
|
141
|
-
|
|
134
|
+
### `regulations://cfr/{title}/{part}/{section}` <sub>resource</sub>
|
|
142
135
|
|
|
143
|
-
|
|
136
|
+
- Same payload as `regulations_get_cfr_section` at the current date — sections only; read an appendix via the tool's `appendix` input instead
|
|
137
|
+
- Mirror-backed with a live eCFR fallback; `source` (`mirror`/`live`) reports provenance
|
|
138
|
+
- `not_found` / `upstream_unavailable` mirror the tool's errors
|
|
144
139
|
|
|
145
|
-
|
|
140
|
+
## Features
|
|
146
141
|
|
|
147
|
-
-
|
|
148
|
-
- Unified error handling — handlers throw, framework catches, classifies, and formats; typed error contracts with recovery hints
|
|
149
|
-
- Pluggable auth: `none`, `jwt`, `oauth`
|
|
150
|
-
- Swappable storage backends: `in-memory`, `filesystem`, `Supabase`, `Cloudflare KV/R2/D1`
|
|
151
|
-
- Structured logging with optional OpenTelemetry tracing
|
|
152
|
-
- STDIO and Streamable HTTP transports
|
|
142
|
+
Built on [`@cyanheads/mcp-ts-core`](https://github.com/cyanheads/mcp-ts-core): stdio and Streamable HTTP transports, pluggable auth (`none` / `jwt` / `oauth`), swappable storage (`in-memory`, `filesystem`, `Supabase`, `Cloudflare KV/R2/D1`), structured logging with optional OpenTelemetry tracing.
|
|
153
143
|
|
|
154
|
-
Federal-
|
|
144
|
+
Federal Register / eCFR / Regulations.gov-specific:
|
|
155
145
|
|
|
156
|
-
- One workflow over three official sources — the agent sees regulatory verbs, not three API clients
|
|
146
|
+
- One workflow over three official sources — the agent sees regulatory verbs (`search_rules`, `get_cfr_section`, `find_comments`), not three API clients
|
|
157
147
|
- Cross-source stitching — every Federal Register document surfaces its docket ID and CFR-part handles next to the tools that consume them, priming the proposal → comments → final → codified-text trace
|
|
158
|
-
- Keyless core — Federal Register + eCFR (5 of 7
|
|
159
|
-
- Locally mirrored codified CFR — the eCFR is synced once into embedded SQLite + FTS5 and queried by exact cite or full text,
|
|
160
|
-
-
|
|
161
|
-
- A 45-second wall-clock budget per request, shared across every upstream call, retry, and backoff a tool makes — a stalled source answers with an actionable `upstream_unavailable` well inside a client's request timeout, rather than leaving the caller to time out with nothing on the wire
|
|
148
|
+
- Keyless core — the Federal Register + eCFR tools (5 of 7) are a complete deployment with no API key; the Regulations.gov leg (`REGULATIONS_GOV_API_KEY`) layers on top
|
|
149
|
+
- Locally mirrored codified CFR — the eCFR is synced once into embedded SQLite + FTS5 and queried by exact cite or full text, falling back to the live API whenever the mirror's title coverage can't answer
|
|
150
|
+
- A 45-second wall-clock budget per request, shared across every upstream call, retry, and backoff a tool makes — a stalled source answers with an actionable `upstream_unavailable` well inside a client's request timeout
|
|
162
151
|
|
|
163
152
|
Agent-friendly output:
|
|
164
153
|
|
|
165
|
-
- Provenance
|
|
166
|
-
- Honest truncation — Federal Register (50-page
|
|
154
|
+
- Provenance — `source: "mirror" | "live"` on every CFR read, and a `sourceScope` line on search naming what that corpus covers
|
|
155
|
+
- Honest truncation — Federal Register (50-page/5,000-record) and Regulations.gov (20-page/5,000-record) ceilings are surfaced via `truncated`/`notice` enrichment, never silently dropped
|
|
167
156
|
- Attachment-aware comments — `attachmentOnly` flags when a comment's substance is a file rather than inline text, with the download URLs, on both the structured and text surfaces
|
|
168
|
-
- Actionable `auth_required` errors — the two keyed tools name the env var and
|
|
157
|
+
- Actionable `auth_required` errors — the two keyed tools name the env var and free signup URL rather than passing through a raw 401/403
|
|
169
158
|
|
|
170
159
|
## Getting started
|
|
171
160
|
|
|
@@ -306,36 +295,6 @@ All configuration is validated at startup via Zod schemas in `src/config/server-
|
|
|
306
295
|
|
|
307
296
|
See [`.env.example`](./.env.example) for the full list of optional overrides.
|
|
308
297
|
|
|
309
|
-
## The eCFR mirror
|
|
310
|
-
|
|
311
|
-
The codified CFR is large (~50 titles, hundreds of MB of XML — Title 40 alone is ~150 MB) but changes far less often than it is queried. This server mirrors it once into an embedded SQLite + FTS5 index and serves `regulations_get_cfr_section` (current single sections) and `regulations_browse_cfr` (full-text search) from it, falling back to the live eCFR API when the mirror has not yet completed an init or a historical/whole-part read is requested.
|
|
312
|
-
|
|
313
|
-
**The mirror answers only what it holds.** A scoped mirror (`ECFR_MIRROR_TITLES`) is consulted for a search only when the requested title is in the set, and never for an all-titles search — a three-title index reporting zero matches for the whole CFR is worse than no index at all. Those requests go to live eCFR instead, and every search result names the corpus that answered it in `sourceScope`.
|
|
314
|
-
|
|
315
|
-
**The mirror is populated out-of-band, never on startup** — a full title sweep takes a while and must not block the server. Build it with:
|
|
316
|
-
|
|
317
|
-
```sh
|
|
318
|
-
# Mirror all 50 CFR titles (idempotent, resumable from the persisted cursor)
|
|
319
|
-
bun run mirror:init
|
|
320
|
-
|
|
321
|
-
# Or scope to specific titles to build a smaller, faster mirror
|
|
322
|
-
ECFR_MIRROR_TITLES=21,40 bun run mirror:init
|
|
323
|
-
|
|
324
|
-
# Incremental refresh against the latest eCFR issues
|
|
325
|
-
bun run mirror:refresh
|
|
326
|
-
|
|
327
|
-
# Report row counts, the last-synced issue date, and whether the index is stale
|
|
328
|
-
bun run mirror:verify
|
|
329
|
-
```
|
|
330
|
-
|
|
331
|
-
**A sync pass deletes only what it read in full.** Each title's pass removes the rows the pass did not rewrite — how a withdrawn section leaves the index. A response that arrives cut short parses to a prefix of the title, which is indistinguishable from a title that shrank, so the pass first checks that the document it read is whole (its root element is closed) and leaves the title untouched when it is not, rather than deleting everything past the cut and reporting the run complete.
|
|
332
|
-
|
|
333
|
-
**An index written by an older ingester is not served.** The ingester stamps a version into the mirror once a run has re-derived every title the index holds; when a release changes how rows are derived, an index carrying an older stamp is treated exactly like a cold one — every read falls back to live eCFR until `bun run mirror:refresh` re-derives it. `mirror:verify` says so explicitly, and a refresh removes the rows the previous ingester filed under keys the current one no longer produces. A run that skips a title — a failed fetch, or an `ECFR_MIRROR_TITLES` scope narrower than the index — names it in the log and leaves the stamp unwritten, because those untouched titles are exactly the ones a stamp would certify wrongly.
|
|
334
|
-
|
|
335
|
-
Until the mirror has completed an init, the codified-text tools run against the live eCFR API — so the server is useful immediately on a fresh deploy; the mirror is a latency/throughput optimization, not a hard dependency. On the HTTP transport a weekly refresh is scheduled automatically (`ECFR_MIRROR_REFRESH_CRON`); stdio operators run the lifecycle scripts above.
|
|
336
|
-
|
|
337
|
-
The mirror database (`./data/ecfr-mirror.sqlite`) is a local artifact — it is git-ignored and excluded from the `.mcpb` bundle, never shipped in the package.
|
|
338
|
-
|
|
339
298
|
## Running the server
|
|
340
299
|
|
|
341
300
|
### Local development
|
|
@@ -352,6 +311,14 @@ The mirror database (`./data/ecfr-mirror.sqlite`) is a local artifact — it is
|
|
|
352
311
|
bun run start:http
|
|
353
312
|
```
|
|
354
313
|
|
|
314
|
+
- **Populate/refresh the eCFR mirror** (out-of-band, idempotent — the codified-text tools work against the live eCFR API until this completes, so it's a latency optimization, not a hard dependency):
|
|
315
|
+
|
|
316
|
+
```sh
|
|
317
|
+
bun run mirror:init # full build across all 50 titles, resumable (scope with ECFR_MIRROR_TITLES)
|
|
318
|
+
bun run mirror:refresh # incremental refresh against the latest eCFR issues
|
|
319
|
+
bun run mirror:verify # report row counts and the last-synced issue date
|
|
320
|
+
```
|
|
321
|
+
|
|
355
322
|
- **Run checks and tests:**
|
|
356
323
|
|
|
357
324
|
```sh
|
|
@@ -393,21 +360,19 @@ See [`CLAUDE.md`](./CLAUDE.md) for development guidelines and architectural rule
|
|
|
393
360
|
- Register new tools and resources via the barrels in `src/mcp-server/*/definitions/index.ts`
|
|
394
361
|
- Wrap external API calls: validate raw → normalize to the domain type → return the output schema; never fabricate missing upstream fields
|
|
395
362
|
|
|
363
|
+
## Data disclaimer
|
|
364
|
+
|
|
365
|
+
eCFR content is "authoritative but unofficial" per the Office of the Federal Register — not the official legal edition of the Code of Federal Regulations; verify against [govinfo.gov](https://www.govinfo.gov/app/collection/cfr) for legal research. This server is not affiliated with or endorsed by the Office of the Federal Register, the Government Publishing Office, or the General Services Administration.
|
|
366
|
+
|
|
396
367
|
## Contributing
|
|
397
368
|
|
|
398
|
-
Issues
|
|
369
|
+
Issues are welcome. Run checks and tests before submitting:
|
|
399
370
|
|
|
400
371
|
```sh
|
|
401
372
|
bun run devcheck
|
|
402
373
|
bun run test
|
|
403
374
|
```
|
|
404
375
|
|
|
405
|
-
## Data disclaimer
|
|
406
|
-
|
|
407
|
-
eCFR content is "authoritative but unofficial" per the Office of the Federal Register — it is not the official legal edition of the Code of Federal Regulations. The official CFR is published annually in print and digitally at [govinfo.gov](https://www.govinfo.gov/app/collection/cfr). For legal research requiring the official text, verify against govinfo.gov or the annual printed CFR.
|
|
408
|
-
|
|
409
|
-
This server is not affiliated with or endorsed by the Office of the Federal Register (OFR), the Government Publishing Office (GPO), or the General Services Administration (GSA).
|
|
410
|
-
|
|
411
376
|
## License
|
|
412
377
|
|
|
413
378
|
Apache-2.0 — see [LICENSE](LICENSE) for details.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "HTTP deployments now declare stateless session mode explicitly, and markdown table cells with a backslash render correctly."
|
|
3
|
+
breaking: false
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 0.2.5 — 2026-09-20
|
|
8
|
+
|
|
9
|
+
## Changed
|
|
10
|
+
|
|
11
|
+
- **Skill tree** moved `skills/` → `framework-skills/`, so a plugin host no longer auto-loads these development skills into every installing agent.
|
|
12
|
+
- A handler that unwinds after its request's abort signal fired now classifies `RequestCancelled` (`-32011`) instead of `InternalError` (`-32603`), picked up from the framework bump below.
|
|
13
|
+
|
|
14
|
+
## Fixed
|
|
15
|
+
|
|
16
|
+
- **Session mode** — the server now declares `sessionMode: 'stateless'` explicitly instead of relying on the schema default (`auto`, which resolves to `stateful`). An HTTP deployment with no `MCP_SESSION_MODE` set previously resolved `stateful` and issued an `Mcp-Session-Id` header; it now resolves `stateless` and does not. `teardown()` also drains an in-flight mirror refresh before closing the store.
|
|
17
|
+
- **Markdown table cells** — `escapePipes` now escapes backslashes before pipes, so a value carrying its own backslash (e.g. `x\|y`) no longer produces a malformed row or an eaten character. Also resolves the open CodeQL alert `js/incomplete-sanitization`.
|
|
18
|
+
|
|
19
|
+
## Dependencies
|
|
20
|
+
|
|
21
|
+
- `@cyanheads/mcp-ts-core` ^0.12.3 → ^0.13.6 (error-contract `severity`/`thrownBy` fields, plugin-manifest `userConfig` wiring checks, README version-badge parity check)
|
|
22
|
+
- `zod` ^4.4.3 → ^4.6.5, `vitest` ^4.1.11 → ^5.0.1
|
|
23
|
+
- dev: `@biomejs/biome`, `@types/node`, `ignore`, `tsc-alias` refreshed; bun engine floor raised to `>=1.4.0`
|