@cyanheads/federal-regulations-mcp-server 0.4.0 → 0.5.1

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.
Files changed (88) hide show
  1. package/AGENTS.md +1 -1
  2. package/CLAUDE.md +1 -1
  3. package/README.md +61 -94
  4. package/changelog/0.5.x/0.5.0.md +44 -0
  5. package/changelog/0.5.x/0.5.1.md +27 -0
  6. package/dist/index.js +1 -1
  7. package/dist/index.js.map +1 -1
  8. package/dist/mcp-server/resources/definitions/document.resource.d.ts +1 -1
  9. package/dist/mcp-server/resources/definitions/document.resource.d.ts.map +1 -1
  10. package/dist/mcp-server/resources/definitions/document.resource.js +10 -7
  11. package/dist/mcp-server/resources/definitions/document.resource.js.map +1 -1
  12. package/dist/mcp-server/resources/definitions/index.d.ts +12 -12
  13. package/dist/mcp-server/tools/definitions/browse-cfr.tool.d.ts.map +1 -1
  14. package/dist/mcp-server/tools/definitions/browse-cfr.tool.js +1 -20
  15. package/dist/mcp-server/tools/definitions/browse-cfr.tool.js.map +1 -1
  16. package/dist/mcp-server/tools/definitions/cfr-part.d.ts +22 -0
  17. package/dist/mcp-server/tools/definitions/cfr-part.d.ts.map +1 -0
  18. package/dist/mcp-server/tools/definitions/cfr-part.js +27 -0
  19. package/dist/mcp-server/tools/definitions/cfr-part.js.map +1 -0
  20. package/dist/mcp-server/tools/definitions/document-number.d.ts +24 -0
  21. package/dist/mcp-server/tools/definitions/document-number.d.ts.map +1 -0
  22. package/dist/mcp-server/tools/definitions/document-number.js +28 -0
  23. package/dist/mcp-server/tools/definitions/document-number.js.map +1 -0
  24. package/dist/mcp-server/tools/definitions/find-comments.tool.d.ts +26 -4
  25. package/dist/mcp-server/tools/definitions/find-comments.tool.d.ts.map +1 -1
  26. package/dist/mcp-server/tools/definitions/find-comments.tool.js +230 -47
  27. package/dist/mcp-server/tools/definitions/find-comments.tool.js.map +1 -1
  28. package/dist/mcp-server/tools/definitions/format-utils.d.ts +18 -1
  29. package/dist/mcp-server/tools/definitions/format-utils.d.ts.map +1 -1
  30. package/dist/mcp-server/tools/definitions/format-utils.js +24 -1
  31. package/dist/mcp-server/tools/definitions/format-utils.js.map +1 -1
  32. package/dist/mcp-server/tools/definitions/fr-citation.d.ts +24 -0
  33. package/dist/mcp-server/tools/definitions/fr-citation.d.ts.map +1 -0
  34. package/dist/mcp-server/tools/definitions/fr-citation.js +29 -0
  35. package/dist/mcp-server/tools/definitions/fr-citation.js.map +1 -0
  36. package/dist/mcp-server/tools/definitions/get-cfr-section.tool.js +1 -1
  37. package/dist/mcp-server/tools/definitions/get-cfr-section.tool.js.map +1 -1
  38. package/dist/mcp-server/tools/definitions/get-docket.tool.d.ts +5 -1
  39. package/dist/mcp-server/tools/definitions/get-docket.tool.d.ts.map +1 -1
  40. package/dist/mcp-server/tools/definitions/get-docket.tool.js +67 -17
  41. package/dist/mcp-server/tools/definitions/get-docket.tool.js.map +1 -1
  42. package/dist/mcp-server/tools/definitions/get-document.tool.d.ts +12 -6
  43. package/dist/mcp-server/tools/definitions/get-document.tool.d.ts.map +1 -1
  44. package/dist/mcp-server/tools/definitions/get-document.tool.js +51 -21
  45. package/dist/mcp-server/tools/definitions/get-document.tool.js.map +1 -1
  46. package/dist/mcp-server/tools/definitions/index.d.ts +240 -170
  47. package/dist/mcp-server/tools/definitions/index.d.ts.map +1 -1
  48. package/dist/mcp-server/tools/definitions/list-open-comments.tool.d.ts +6 -3
  49. package/dist/mcp-server/tools/definitions/list-open-comments.tool.d.ts.map +1 -1
  50. package/dist/mcp-server/tools/definitions/list-open-comments.tool.js +41 -31
  51. package/dist/mcp-server/tools/definitions/list-open-comments.tool.js.map +1 -1
  52. package/dist/mcp-server/tools/definitions/paging.d.ts +48 -0
  53. package/dist/mcp-server/tools/definitions/paging.d.ts.map +1 -0
  54. package/dist/mcp-server/tools/definitions/paging.js +48 -0
  55. package/dist/mcp-server/tools/definitions/paging.js.map +1 -0
  56. package/dist/mcp-server/tools/definitions/search-rules.tool.d.ts +43 -3
  57. package/dist/mcp-server/tools/definitions/search-rules.tool.d.ts.map +1 -1
  58. package/dist/mcp-server/tools/definitions/search-rules.tool.js +274 -33
  59. package/dist/mcp-server/tools/definitions/search-rules.tool.js.map +1 -1
  60. package/dist/services/character-references.d.ts +29 -0
  61. package/dist/services/character-references.d.ts.map +1 -0
  62. package/dist/services/character-references.js +49 -0
  63. package/dist/services/character-references.js.map +1 -0
  64. package/dist/services/ecfr/ecfr-service.d.ts.map +1 -1
  65. package/dist/services/ecfr/ecfr-service.js +8 -28
  66. package/dist/services/ecfr/ecfr-service.js.map +1 -1
  67. package/dist/services/ecfr/xml.d.ts +2 -2
  68. package/dist/services/ecfr/xml.d.ts.map +1 -1
  69. package/dist/services/ecfr/xml.js +6 -37
  70. package/dist/services/ecfr/xml.js.map +1 -1
  71. package/dist/services/federal-register/comment-period.d.ts +17 -0
  72. package/dist/services/federal-register/comment-period.d.ts.map +1 -0
  73. package/dist/services/federal-register/comment-period.js +28 -0
  74. package/dist/services/federal-register/comment-period.js.map +1 -0
  75. package/dist/services/federal-register/federal-register-service.d.ts +20 -6
  76. package/dist/services/federal-register/federal-register-service.d.ts.map +1 -1
  77. package/dist/services/federal-register/federal-register-service.js +164 -49
  78. package/dist/services/federal-register/federal-register-service.js.map +1 -1
  79. package/dist/services/federal-register/types.d.ts +79 -15
  80. package/dist/services/federal-register/types.d.ts.map +1 -1
  81. package/dist/services/regulations-gov/regulations-gov-service.d.ts +22 -6
  82. package/dist/services/regulations-gov/regulations-gov-service.d.ts.map +1 -1
  83. package/dist/services/regulations-gov/regulations-gov-service.js +71 -42
  84. package/dist/services/regulations-gov/regulations-gov-service.js.map +1 -1
  85. package/dist/services/regulations-gov/types.d.ts +21 -2
  86. package/dist/services/regulations-gov/types.d.ts.map +1 -1
  87. package/package.json +3 -2
  88. package/server.json +3 -3
package/AGENTS.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Developer Protocol
2
2
 
3
3
  **Server:** federal-regulations-mcp-server
4
- **Version:** 0.4.0
4
+ **Version:** 0.5.1
5
5
  **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `0.13.6`
6
6
  **Engines:** Bun ≥1.4.0, Node ≥24.0.0
7
7
  **MCP SDK:** `@modelcontextprotocol/server` ^2.0.0
package/CLAUDE.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Developer Protocol
2
2
 
3
3
  **Server:** federal-regulations-mcp-server
4
- **Version:** 0.4.0
4
+ **Version:** 0.5.1
5
5
  **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `0.13.6`
6
6
  **Engines:** Bun ≥1.4.0, Node ≥24.0.0
7
7
  **MCP SDK:** `@modelcontextprotocol/server` ^2.0.0
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
  <div align="center">
9
9
 
10
- [![Version](https://img.shields.io/badge/Version-0.4.0-blue.svg?style=flat-square)](./CHANGELOG.md) [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![Docker](https://img.shields.io/badge/Docker-ghcr.io-2496ED?style=flat-square&logo=docker&logoColor=white)](https://github.com/users/cyanheads/packages/container/package/federal-regulations-mcp-server) [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^2.0.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/) [![npm](https://img.shields.io/npm/v/@cyanheads/federal-regulations-mcp-server?style=flat-square&logo=npm&logoColor=white)](https://www.npmjs.com/package/@cyanheads/federal-regulations-mcp-server) [![TypeScript](https://img.shields.io/badge/TypeScript-^7.0.2-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun-v1.4.0-blueviolet.svg?style=flat-square)](https://bun.sh/)
10
+ [![Version](https://img.shields.io/badge/Version-0.5.1-blue.svg?style=flat-square)](./CHANGELOG.md) [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![Docker](https://img.shields.io/badge/Docker-ghcr.io-2496ED?style=flat-square&logo=docker&logoColor=white)](https://github.com/users/cyanheads/packages/container/package/federal-regulations-mcp-server) [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^2.0.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/) [![npm](https://img.shields.io/npm/v/@cyanheads/federal-regulations-mcp-server?style=flat-square&logo=npm&logoColor=white)](https://www.npmjs.com/package/@cyanheads/federal-regulations-mcp-server) [![TypeScript](https://img.shields.io/badge/TypeScript-^7.0.2-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun-v1.4.0-blueviolet.svg?style=flat-square)](https://bun.sh/)
11
11
 
12
12
  </div>
13
13
 
@@ -29,123 +29,95 @@
29
29
 
30
30
  ## Overview
31
31
 
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.
32
+ US federal regulatory law from three official sources: the Federal Register (proposed rules, final rules, and notices), the eCFR (codified CFR text, current or as of a past date), and Regulations.gov (rulemaking dockets and public comments). Search rules, trace a document from proposal through its comments to the codified text, and read CFR sections. 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
37
  |:---|:---|
38
- | `regulations_search_rules` | Search Federal Register proposed rules, final rules, notices, and presidential documents by query, type, agency, and date range, ranked by relevance or date |
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 |
38
+ | `regulations_search_rules` | Search Federal Register proposed rules, final rules, notices, and presidential documents by query, type, agency, date range, CFR part, docket number, and RIN, or resolve a page cite to its document |
39
+ | `regulations_get_document` | Fetch one Federal Register document by number, with the docket IDs, CFR parts, citation, comment count, and comment URL that chain into other tools |
40
40
  | `regulations_browse_cfr` | List CFR titles, a title's chapters, or every section and appendix in a part, or full-text-search the codified CFR |
41
41
  | `regulations_get_cfr_section` | Read codified CFR text for a section, whole part, or appendix, current or as of a past date |
42
42
  | `regulations_get_docket` | Pull a rulemaking docket and its filed documents from Regulations.gov (key required) |
43
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 proposed rules, comment-requesting final rules, and optionally notices currently open for public comment, soonest closing first |
44
+ | `regulations_list_open_comments` | List documents currently open for public comment, soonest closing first |
45
45
 
46
46
  ### Resources
47
47
 
48
48
  | Resource | Description |
49
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`) |
50
+ | `regulations://document/{documentNumber}` | One Federal Register document's metadata and cross-source handles |
51
+ | `regulations://cfr/{title}/{part}/{section}` | Codified text of a current CFR section |
52
52
 
53
- All resource data is also reachable via the tool surface — tool-only MCP clients lose nothing.
53
+ Both resources mirror a tool (`regulations_get_document`, `regulations_get_cfr_section`), so tool-only clients lose nothing.
54
54
 
55
55
  ## Capability reference
56
56
 
57
57
  ### `regulations_search_rules` <sub>tool</sub>
58
58
 
59
- - Keyless; full-text `query` optional, or browse by `type` (`PRORULE`/`RULE`/`NOTICE`/`PRESDOCU`), `agencies` (Federal Register slug, e.g. `environmental-protection-agency`), and `published_after`/`published_before` (real calendar days, `YYYY-MM-DD`)
60
- - `order` is `relevance`, `newest`, or `oldest`; omitted, it is `relevance` with a `query` and `newest` without one
61
- - `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
62
- - Each result carries `documentNumber` (→ `regulations_get_document`), `agencies` as `{ name, slug }` (the slug feeds back into `agencies`; null when the Federal Register lists an agency by raw name only), `docketIds` (→ `regulations_get_docket` / `find_comments`), `regulationIdNumbers`, and `cfrReferences` (→ `regulations_get_cfr_section`)
63
- - `invalid_filter` when the Federal Register rejects a filter value (an agency name or acronym instead of a slug), naming the parameter; `upstream_unavailable` on a 5xx/timeout, retryable after a brief wait
59
+ - Optional full-text `query`, filtered by `type` (`PRORULE` / `RULE` / `NOTICE` / `PRESDOCU`), `agencies` (Federal Register slugs such as `environmental-protection-agency`, not names or acronyms), `published_after` / `published_before`, `cfr_title` + `cfr_part` (a part number or range such as `140-143`; `cfr_part` needs `cfr_title`, else `title_required_for_part`), `docket_id` (a docket number as the Federal Register prints it), and `rin`, all combined; `per_page` 2–100 (default 20), `page` 1–50
60
+ - `citation` (`"89 FR 49102"`) with `citation_date` (the date its source note prints beside it) returns the documents printed on that page, in page order, volume 59 (1994) onward. A cite missing its date, or combined with the date window, fails with `citation_incomplete`; a volume before 59 or a date from another year fails with `citation_out_of_range`. When no document spans the page, the notice gives that day's page range
61
+ - Each result carries `documentNumber`, `citation` / `startPage` / `endPage`, `agencies[].slug`, the printed `docketIds`, `regulationsGovDocketId` and `regulationsGovDocumentId`, `commentCount`, `commentUrl`, `commentsCloseOn` with `commentPeriodOpen` (open through 11:59 PM Eastern on the close date), `regulationIdNumbers`, and `cfrReferences` for the follow-up tools. `totalPages` and `nextPage` page the results. The Federal Register serves 50 pages, so `truncated` marks a set larger than 50 × `per_page` (1,000 at the default, 5,000 at 100); narrow the date window rather than paging deeper. `totalCount` stops at 10,000, which means at least that many. A window whose start falls after its end fails with `date_range_inverted`, and `invalid_filter` names a rejected parameter
64
62
 
65
63
  ---
66
64
 
67
65
  ### `regulations_get_document` <sub>tool</sub>
68
66
 
69
- - Keyless; fetch one document by `document_number` (format `\d{4}-\d+`, e.g. `2025-14555`)
70
- - Full metadata (title, type, agencies as `{ name, slug }`, abstract, action, effective/comment dates, RINs) plus cross-source handles: `docketId`, `regulationsGovDocumentId`, `commentCount`, and `cfrReferences`
71
- - `include_full_text` inlines the plain-text body as a character window: `max_chars` (default 64,000, up to 200,000) from `offset` (default 0). `fullTextLength` reports the whole body's length and `fullTextNextOffset` the offset to resume from while text remains; documents of about ten printed pages or fewer come back whole, while a major final rule runs past a million characters
72
- - Passing `offset` or `max_chars` implies `include_full_text`; pairing either with an explicit `include_full_text: false` fails as `full_text_disabled`. An offset at or past the end returns empty `fullText` with the length and a notice
73
- - `fullText` is plain text: links reduce to their text, and email addresses the published body obfuscates are decoded
74
- - `not_found` when the FR number doesn't exist; `upstream_unavailable` on a 5xx/timeout
67
+ - `document_number` in any form the Federal Register issues: `2024-07773` from 2010 on, and older and correction numbers such as `98-1572`, `E9-25990`, or `C1-2009-30484`; `include_full_text` adds the plain-text body as one window of `max_chars` (default 64,000, max 200,000) starting at `offset`, and passing either of those implies it
68
+ - Returns metadata plus the handles other tools take: the Regulations.gov `docketId` and `regulationsGovDocumentId`, the printed `docketIds`, `citation` / `startPage` / `endPage`, `commentCount`, `commentUrl`, `commentPeriodOpen`, and `cfrReferences`. With text, `fullTextLength` and `fullTextNextOffset` (present while text remains) page the body; a major final rule runs past a million characters
75
69
 
76
70
  ---
77
71
 
78
72
  ### `regulations_browse_cfr` <sub>tool</sub>
79
73
 
80
- - Keyless; `mode: "structure"` lists all 50 titles, a title's top-level divisions (chapters or subtitles), or — with `title` + `part` — every section and appendix in the part, flattened, each carrying its `subpart` and `subjectGroup`; `mode: "search"` full-text-searches the codified CFR
81
- - `title` (1–50) and `part` scope both modes; a `part` without `title` is rejected (`title_required_for_part`) since part numbers repeat across titles
82
- - `date` is point-in-time in both modes and must be a real calendar day; structure mode rejects a date past the title's up-to-date date (`date_out_of_range`), and search indexes 2017-01-03 onward
83
- - `page` (1-based, default 1) and `per_page` (1–50, default 20) page search results and a part's listing; `page` and `totalCount` come back with a notice naming the next page, and a page past the end returns no rows plus the last page. Labels are plain text (eCFR's inline markup stripped)
84
- - Search returns one row per section: eCFR's live index answers one hit per section *version*, so repeats are collapsed. `countBasis` says what `totalCount` counts — `sections`, or eCFR's own `section_versions` count until the whole hit list has been read. Live search pages through eCFR's first 10,000 hits only; a page past them fails as `page_out_of_window`
85
- - 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
86
- - `query_required` when `mode="search"` has no query; `title_not_found` / `date_out_of_range` / `page_out_of_window` / `upstream_unavailable` round out the errors
74
+ - `mode: "structure"` lists the 50 titles, a title's top-level divisions, or, with `title` + `part`, every section and appendix in the part with its `subpart` and `subjectGroup`; `mode: "search"` full-text-searches the codified CFR and requires `query`. `title`, `part` (needs `title`), and a point-in-time `date` scope both modes, and `per_page` 1–50 (default 20) pages search results and part listings
75
+ - Search rows carry `cfrCite`, `heading`, `hierarchyPath`, and `excerpt`, one per section. `source` (`mirror` / `live`) and `sourceScope` name the corpus that answered and what it covers, and `countBasis` says whether `totalCount` counts `sections` or `section_versions`; live search reaches eCFR's first 10,000 hits only (`page_out_of_window` past them)
87
76
 
88
77
  ---
89
78
 
90
79
  ### `regulations_get_cfr_section` <sub>tool</sub>
91
80
 
92
- - 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`)
93
- - `section` accepts the cite the way people write it: `"61"` in part 141, `"§ 141.61"`, `"Sec. 141.61"`, and `"141.61(c)"` (paragraph designators dropped; the whole section comes back) all read 40 CFR 141.61. An identifier that resolves as given is never rewritten (14 CFR 241 `"25"`, 26 CFR `"48.4061(a)"`); a rewrite returns the identifier read in `section` / `cfrCite` plus a `notice`
94
- - Text is one window of `bodyText`: `offset` (default 0) and `max_chars` (default 64,000, max 200,000) select it, and `bodyTextOffset` / `bodyTextLength` / `bodyTextNextOffset` (present while text remains) page through it — the same contract as `regulations_get_document`'s full text. An offset past the end is an empty window with a `notice`
95
- - A whole-part fetch adds `sections[]`, a text-free index of the sections in the window (`section`, `heading`, `cfrCite`, and the `offset` each starts at in the part's text — pass it as `offset` to jump there)
96
- - `date` for point-in-time text, 2017-01-01 through the title's up-to-date date; anything outside that is `date_out_of_range`, checked before any text request, and a date that is not a real calendar day is rejected at input validation
97
- - `appendix` must be passed verbatim as eCFR / `regulations_browse_cfr` emits it (e.g. `Appendix A-1 to Part 50`), not a short form
98
- - A whole-part fetch lists its appendices' identifiers and headings without inlining their text — call again with `appendix` to read one
99
- - `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
100
- - `not_found` / `location_required` / `upstream_unavailable` round out the errors
81
+ - One section (`title` + `part` + `section`), a whole part (`section` omitted), or one appendix (`appendix`, verbatim as `regulations_browse_cfr` emits it, e.g. `Appendix A-1 to Part 50`). `section` also takes cites as people write them (`"61"`, `"§ 141.61"`, `"141.61(c)"`), and `date` reads text from 2017-01-01 through the title's up-to-date date
82
+ - Text comes back as one `bodyText` window (`max_chars` default 64,000, max 200,000, from `offset`), paged by `bodyTextLength` and `bodyTextNextOffset`. A whole-part read adds a `sections[]` index with each section's `offset` and names its `appendices` without their text; `source` (`mirror` / `live`) reports provenance
101
83
 
102
84
  ---
103
85
 
104
86
  ### `regulations_get_docket` <sub>tool</sub> · key required
105
87
 
106
- - Requires `REGULATIONS_GOV_API_KEY`; fetch a docket by `docket_id` (e.g. `EPA-HQ-OAR-2025-0194`)
107
- - `document_types` filters to `Proposed Rule` / `Rule` / `Notice` / `Supporting & Related Material` / `Other` — a docket often holds hundreds of supporting materials
108
- - `per_page` 5–250 (default 25), `page` 1–20 — Regulations.gov caps a query at 5,000 records
109
- - Each document's `objectId` chains into `regulations_find_comments`; `frDocNum` chains back to `regulations_get_document`
110
- - `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
88
+ - `docket_id`, the Regulations.gov docket ID (e.g. `EPA-HQ-OAR-2025-0194`) — `regulationsGovDocketId` on a search or open-comments row, `docketId` from `regulations_get_document`, not a printed `docketIds` entry; `document_types` filters to `Proposed Rule`, `Rule`, `Notice`, `Supporting & Related Material`, or `Other`; `per_page` 5–250 (default 25), `page` 1–40
89
+ - Returns the docket's metadata, `documentCount`, and `documents[]`, each with an `objectId` for `regulations_find_comments`, a `frDocNum` back to `regulations_get_document`, and `commentPeriodOpen` as Regulations.gov reports it. `totalPages` and `nextPage` page the documents; Regulations.gov serves 40 pages, so `truncated` marks a docket larger than 40 × `per_page` (10,000 at 250)
111
90
 
112
91
  ---
113
92
 
114
93
  ### `regulations_find_comments` <sub>tool</sub> · key required
115
94
 
116
- - 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
117
- - `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`
118
- - When a comment's substance is a PDF/DOCX attachment, `bodyText` is a stub and `attachmentOnly` is `true`, with the attachment download URLs
119
- - `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`
120
- - `auth_required` / `not_found` / `rate_limited` (429, 1,000 req/hr) / `upstream_unavailable` round out the errors
95
+ - Exactly one of `docket_id`, `document_object_id`, `fr_document_number`, or `comment_id`; list scopes take `per_page` 5–250 (default 25) and `page` 1–40
96
+ - `document_object_id` takes an object ID (`0900006485883ec6`) or a document ID (`EPA-HQ-OW-2022-0114-0027`). `fr_document_number` resolves to the Regulations.gov document carrying that exact number and answers `not_found` when none does
97
+ - Lists narrow by comment text with `search_term`, and each hit then carries `highlightedContent`, the matched passages. `posted_after` / `posted_before` set an inclusive posted-date window. A backwards window fails with `date_range_inverted`, and filters passed with `comment_id` fail with `filter_requires_list_mode`
98
+ - `mode` is `list` or `detail`. Lists return comment summaries without body text. `comment_id` returns the body, submitter, `receivedDate`, `postmarkDate`, `duplicateComments` (above 1 marks a mass-mail campaign record), and `attachments`, with `attachmentOnly: true` when the substance is in a PDF/DOCX file. Lists carry `totalPages` and `nextPage`; `truncated` marks a set larger than the 40 pages Regulations.gov serves reach (40 × `per_page`, 10,000 at 250), and posted-date windows take it one slice at a time
121
99
 
122
100
  ---
123
101
 
124
102
  ### `regulations_list_open_comments` <sub>tool</sub>
125
103
 
126
- - Keyless; lists documents currently open for public comment, sorted by closing date soonest first (same-day closes by document number) across the whole open window — filter by `type`, `query`, `agencies` (Federal Register slug), and `closing_before` (a real calendar day, `YYYY-MM-DD`)
127
- - `type` takes `PRORULE`, `RULE`, and `NOTICE`; the default (also used for an empty list) is `["PRORULE", "RULE"]` — proposed rules plus the direct final and interim final rules that take comment. `NOTICE` adds several hundred information-collection and other notices
128
- - `per_page` 1–100 (default 20), `page` from 1; `totalCount`, `totalPages`, and `nextPage` describe the window. The Federal Register can't sort by comment date, so the window is fetched whole (one request up to 2,000 documents, then 2,000 per request) and paged locally; past the Federal Register's 10,000-document limit the response is `truncated` and holds the 10,000 most recently published matches
129
- - Each row carries `daysRemaining`, `documentNumber` (→ `regulations_get_document`), `agencies` as `{ name, slug }`, and `docketIds` (→ `regulations_find_comments`)
130
- - 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
131
- - `invalid_filter` when the Federal Register rejects a filter value, naming the parameter; `upstream_unavailable` on a 5xx/timeout
104
+ - Optional `query`, `type` (`PRORULE`, `RULE`, `NOTICE`; default `["PRORULE", "RULE"]`), `agencies` (Federal Register slugs), and `closing_before`; `per_page` 1–100 (default 20)
105
+ - Rows sort by closing date, soonest first, and carry `commentsCloseOn`, `daysRemaining`, `documentNumber`, the printed `docketIds`, `regulationsGovDocketId` and `regulationsGovDocumentId`, `commentCount`, and `commentUrl`. `asOf` is today's date in Eastern time, so a document stays listed through its close day. `totalPages` and `nextPage` page the window; `truncated` means the Federal Register's 10,000-document limit was reached
106
+ - Keyless. `keyed` reports whether `REGULATIONS_GOV_API_KEY` is set, which `regulations_get_docket` and `regulations_find_comments` need to follow up on a row
132
107
 
133
108
  ---
134
109
 
135
110
  ### `regulations://document/{documentNumber}` <sub>resource</sub>
136
111
 
137
- - Same payload as `regulations_get_document` without full text — metadata plus cross-source handles, the body never inlined
138
- - `documentNumber` format `\d{4}-\d+` (e.g. `2025-14555`)
139
- - `not_found` / `upstream_unavailable` mirror the tool's errors
112
+ - `documentNumber` in any Federal Register form (`2024-07773`, `98-1572`, `E9-25990`); the payload is `regulations_get_document`'s without the body text
113
+ - `docketId`, `regulationsGovDocumentId`, the printed `docketIds`, `cfrReferences`, `citation`, `commentCount`, and `commentUrl` chain into the search, comment, and CFR tools
140
114
 
141
115
  ---
142
116
 
143
117
  ### `regulations://cfr/{title}/{part}/{section}` <sub>resource</sub>
144
118
 
145
- - Same payload as `regulations_get_cfr_section` at the current date and its default 64,000-character window — sections only; read an appendix via the tool's `appendix` input instead
146
- - The section resolves as the tool resolves it (`"61"`, `"§ 141.61"`, `"141.61(c)"`), with a `notice` naming the rewrite; a section longer than one window carries `bodyTextNextOffset`, and the rest is read through the tool's `offset`
147
- - Mirror-backed with a live eCFR fallback; `source` (`mirror`/`live`) reports provenance
148
- - `not_found` / `upstream_unavailable` mirror the tool's errors
119
+ - Sections only, at the current date and the tool's default 64,000-character window; the `section` segment resolves the way the tool resolves it (`"61"`, `"§ 141.61"`)
120
+ - `source` (`mirror` / `live`) reports provenance. `bodyTextNextOffset` marks a section longer than the window; read the rest through `regulations_get_cfr_section` with `offset`
149
121
 
150
122
  ## Features
151
123
 
@@ -153,18 +125,17 @@ Built on [`@cyanheads/mcp-ts-core`](https://github.com/cyanheads/mcp-ts-core): s
153
125
 
154
126
  Federal Register / eCFR / Regulations.gov-specific:
155
127
 
156
- - One workflow over three official sources — the agent sees regulatory verbs (`search_rules`, `get_cfr_section`, `find_comments`), not three API clients
157
- - 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 — 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
159
- - 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
160
- - 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
128
+ - One workflow over three official sources: the agent calls regulatory verbs (`search_rules`, `get_cfr_section`, `find_comments`) rather than three API clients
129
+ - Cross-source stitching: every Federal Register document surfaces its docket ID and CFR-part handles, which feed the proposal → comments → final rule → codified text trace
130
+ - Keyless core: the five Federal Register and eCFR tools need no key. `regulations_get_docket` and `regulations_find_comments` need `REGULATIONS_GOV_API_KEY` (free at [api.data.gov/signup](https://api.data.gov/signup/), 1,000 requests/hour) and fail with `auth_required`, naming the variable and signup URL, when it is missing or rejected
131
+ - Locally mirrored codified CFR: the eCFR syncs into embedded SQLite + FTS5 for exact-cite reads and full-text search, and falls back to the live API for historical dates, whole parts, appendices, and titles outside the mirror
132
+ - A 45-second budget per request, shared by every upstream call, retry, and backoff, so a stalled source answers with `upstream_unavailable` inside a client's timeout
161
133
 
162
134
  Agent-friendly output:
163
135
 
164
- - Provenance — `source: "mirror" | "live"` on every CFR read, and a `sourceScope` line on search naming what that corpus covers
165
- - 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
166
- - 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
167
- - Actionable `auth_required` errors — the two keyed tools name the env var and free signup URL rather than passing through a raw 401/403
136
+ - Provenance: `source: "mirror" | "live"` on every CFR read, plus a `sourceScope` line on search naming what the corpus covers
137
+ - Honest paging: `totalPages` and `nextPage` say how far a list goes, a page past the end names the last page instead of reading as "nothing matched", and matches beyond the Federal Register's 50 pages or Regulations.gov's 40 surface through `truncated` and `notice`, never as silently dropped rows
138
+ - Attachment-aware comments: `attachmentOnly` flags a comment whose substance is a file, with the download URLs
168
139
 
169
140
  ## Getting started
170
141
 
@@ -185,7 +156,7 @@ A public instance is available at `https://federal-regulations.caseyjhand.com/mc
185
156
 
186
157
  ### Self-Hosted / Local
187
158
 
188
- Add the following to your MCP client configuration file. The Federal Register and eCFR tools work with no key; set `REGULATIONS_GOV_API_KEY` (free at [api.data.gov/signup](https://api.data.gov/signup/)) to enable the Regulations.gov docket and comment tools.
159
+ Add the following to your MCP client configuration file. `REGULATIONS_GOV_API_KEY` enables the docket and comment tools; omit it to run the keyless Federal Register and eCFR tools alone.
189
160
 
190
161
  ```json
191
162
  {
@@ -242,8 +213,6 @@ Or with Docker:
242
213
  }
243
214
  ```
244
215
 
245
- > Omit the `REGULATIONS_GOV_API_KEY` line entirely to run the keyless core (Federal Register + eCFR). The two Regulations.gov tools then return an actionable `auth_required` error, and `regulations_list_open_comments` runs without comment counts.
246
-
247
216
  For Streamable HTTP, set the transport and start the server:
248
217
 
249
218
  ```sh
@@ -253,8 +222,8 @@ MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
253
222
 
254
223
  ### Prerequisites
255
224
 
256
- - [Bun v1.3](https://bun.sh/) or higher (or Node.js v24+).
257
- - Optional: a free [api.data.gov key](https://api.data.gov/signup/) for the Regulations.gov tools (`regulations_get_docket`, `regulations_find_comments`, and comment counts in `regulations_list_open_comments`). The Federal Register and eCFR tools need no key. The shared key allows 1,000 requests/hour.
225
+ - [Bun v1.4.0](https://bun.sh/) or higher (or Node.js v24+).
226
+ - Optional: a free [api.data.gov key](https://api.data.gov/signup/) for the Regulations.gov tools.
258
227
 
259
228
  ### Installation
260
229
 
@@ -285,23 +254,21 @@ cp .env.example .env
285
254
 
286
255
  ## Configuration
287
256
 
288
- All configuration is validated at startup via Zod schemas in `src/config/server-config.ts`. Key environment variables:
289
-
290
257
  | Variable | Description | Default |
291
258
  |:---|:---|:---|
292
- | `REGULATIONS_GOV_API_KEY` | `api.data.gov` key for the Regulations.gov tools (`get_docket`, `find_comments`, and comment-count enrichment in `list_open_comments`). Optional — the Federal Register and eCFR tools work without it. | — |
259
+ | `REGULATIONS_GOV_API_KEY` | api.data.gov key for `regulations_get_docket` and `regulations_find_comments`. | none |
293
260
  | `FEDERAL_REGISTER_BASE_URL` | Federal Register API v1 base URL. | `https://www.federalregister.gov/api/v1` |
294
261
  | `ECFR_BASE_URL` | eCFR API base URL. | `https://www.ecfr.gov/api` |
295
262
  | `REGULATIONS_GOV_BASE_URL` | Regulations.gov API v4 base URL. | `https://api.regulations.gov/v4` |
296
- | `ECFR_MIRROR_PATH` | Filesystem path for the eCFR SQLite mirror database. | `./data/ecfr-mirror.sqlite` |
297
- | `ECFR_MIRROR_REFRESH_CRON` | Cron expression for an in-process mirror refresh (HTTP transport only), e.g. `0 4 * * 0`. Unset registers no job. Each run re-harvests every configured title in full, and a tick is skipped until `mirror:init` has completed once. | — (no job) |
298
- | `ECFR_MIRROR_TITLES` | Comma-separated CFR title numbers to scope the mirror to (e.g. `21,40`). Omit to mirror all 50 titles. Cites and searches outside the set fall through to the live eCFR API, as does any all-titles search while this is set. | — (all titles) |
263
+ | `ECFR_MIRROR_PATH` | Path to the eCFR SQLite mirror database. | `./data/ecfr-mirror.sqlite` |
264
+ | `ECFR_MIRROR_REFRESH_CRON` | Cron expression for an in-process mirror refresh (HTTP transport only), e.g. `0 4 * * 0`. Each run re-harvests every configured title in full. | none (no job) |
265
+ | `ECFR_MIRROR_TITLES` | Comma-separated CFR titles to mirror (e.g. `21,40`). Reads and searches outside the set, and all-titles searches, go to the live eCFR API. | all 50 titles |
299
266
  | `MCP_TRANSPORT_TYPE` | Transport: `stdio` or `http`. | `stdio` |
300
- | `MCP_HTTP_PORT` | Port for the HTTP server. | `3010` |
301
- | `MCP_AUTH_MODE` | Auth mode: `none`, `jwt`, or `oauth`. | `none` |
302
- | `MCP_LOG_LEVEL` | Log level (RFC 5424). | `info` |
303
- | `STORAGE_PROVIDER_TYPE` | Storage backend. | `in-memory` |
304
- | `OTEL_ENABLED` | Enable [OpenTelemetry instrumentation](https://github.com/cyanheads/mcp-ts-core/tree/main/docs/telemetry). | `false` |
267
+ | `MCP_HTTP_PORT` | HTTP server port. | `3010` |
268
+ | `MCP_AUTH_MODE` | Authentication: `none`, `jwt`, or `oauth`. | `none` |
269
+ | `MCP_LOG_LEVEL` | Log level (`debug`, `info`, `warning`, `error`, etc.). | `info` |
270
+ | `STORAGE_PROVIDER_TYPE` | Storage backend: `in-memory`, `filesystem`, `supabase`, `cloudflare-kv/r2/d1`. | `in-memory` |
271
+ | `OTEL_ENABLED` | Enable [OpenTelemetry](https://github.com/cyanheads/mcp-ts-core/tree/main/docs/telemetry). | `false` |
305
272
 
306
273
  See [`.env.example`](./.env.example) for the full list of optional overrides.
307
274
 
@@ -321,10 +288,10 @@ See [`.env.example`](./.env.example) for the full list of optional overrides.
321
288
  bun run start:http
322
289
  ```
323
290
 
324
- - **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):
291
+ - **Build or refresh the eCFR mirror** (out of band and idempotent; the CFR tools use the live eCFR API until the first build completes):
325
292
 
326
293
  ```sh
327
- bun run mirror:init # full build across all 50 titles, resumable (scope with ECFR_MIRROR_TITLES)
294
+ bun run mirror:init # full build, resumable (scope with ECFR_MIRROR_TITLES)
328
295
  bun run mirror:refresh # re-harvest every configured title against the latest eCFR issues
329
296
  bun run mirror:verify # report row counts and the last-synced issue date
330
297
  ```
@@ -334,7 +301,6 @@ See [`.env.example`](./.env.example) for the full list of optional overrides.
334
301
  ```sh
335
302
  bun run devcheck # Lint, format, typecheck, security, changelog sync
336
303
  bun run test # Vitest test suite
337
- bun run lint:mcp # Validate MCP definitions against the linter rules
338
304
  ```
339
305
 
340
306
  ### Docker
@@ -344,20 +310,21 @@ docker build -t federal-regulations-mcp-server .
344
310
  docker run --rm -e REGULATIONS_GOV_API_KEY=your-key -p 3010:3010 federal-regulations-mcp-server
345
311
  ```
346
312
 
347
- The Dockerfile defaults to HTTP transport, stateless session mode, and logs to `/var/log/federal-regulations-mcp-server`. The build stage installs dependencies with `--ignore-scripts` — `better-sqlite3` is a build/ingest-only dependency whose native compile is skipped, and the runtime reads the mirror through Bun's built-in `bun:sqlite`. Populate the mirror in a running container with `docker exec <container> bun run mirror:init`; mount a volume over `/usr/src/app/data` so the synced index survives container recreation. OpenTelemetry peer dependencies are installed by default — build with `--build-arg OTEL_ENABLED=false` to omit them.
313
+ The Dockerfile defaults to HTTP transport, stateless session mode, and logs to `/var/log/federal-regulations-mcp-server`. Build the mirror inside a running container with `docker exec <container> bun run mirror:init`, and mount a volume over `/usr/src/app/data` so it survives recreation. OpenTelemetry peer dependencies are installed by default; build with `--build-arg OTEL_ENABLED=false` to omit them.
348
314
 
349
315
  ## Project structure
350
316
 
351
317
  | Directory | Purpose |
352
318
  |:---|:---|
353
- | `src/index.ts` | `createApp()` entry point — registers tools/resources, inits the three services, and schedules the mirror refresh on HTTP when `ECFR_MIRROR_REFRESH_CRON` is set. |
319
+ | `src/index.ts` | `createApp()` entry point: registers tools and resources, inits the three services, and schedules the mirror refresh when `ECFR_MIRROR_REFRESH_CRON` is set. |
354
320
  | `src/config` | Server-specific environment variable parsing and validation with Zod. |
355
- | `src/mcp-server/tools` | Tool definitions (`*.tool.ts`) — the seven `regulations_*` tools. |
356
- | `src/mcp-server/resources` | Resource definitions (`*.resource.ts`) — the document and CFR-section resources. |
321
+ | `src/mcp-server/tools` | Tool definitions (`*.tool.ts`), the seven `regulations_*` tools. |
322
+ | `src/mcp-server/resources` | Resource definitions (`*.resource.ts`), the document and CFR-section resources. |
357
323
  | `src/services/federal-register` | Federal Register API v1 client (keyless). |
358
- | `src/services/ecfr` | eCFR API client (keyless) — versioner, structure, search, and section XML parsing. |
359
- | `src/services/ecfr-mirror` | eCFR codified-text mirror (MirrorService — SQLite + FTS5), its read path, and the opt-in refresh job. |
360
- | `src/services/regulations-gov` | Regulations.gov v4 client (`X-Api-Key`) — dockets and comments. |
324
+ | `src/services/ecfr` | eCFR API client (keyless): versioner, structure, search, section XML parsing, and cite resolution. |
325
+ | `src/services/ecfr-mirror` | eCFR codified-text mirror (SQLite + FTS5) and the opt-in refresh job. |
326
+ | `src/services/regulations-gov` | Regulations.gov v4 client (`X-Api-Key`): dockets and comments. |
327
+ | `src/services` | Shared request budget, text windowing, character-reference decoding, and upstream-failure handling. |
361
328
  | `scripts/ecfr-mirror-*.ts` | Out-of-band mirror lifecycle: `init`, `refresh`, `verify`. |
362
329
  | `tests/` | Unit and integration tests mirroring `src/`. |
363
330
 
@@ -372,7 +339,7 @@ See [`CLAUDE.md`](./CLAUDE.md) for development guidelines and architectural rule
372
339
 
373
340
  ## Data disclaimer
374
341
 
375
- 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.
342
+ 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.
376
343
 
377
344
  ## Contributing
378
345
 
@@ -0,0 +1,44 @@
1
+ ---
2
+ summary: "regulations_find_comments gains text and posted-date filters and resolves documents by exact FR number or document ID; list tools share one paging contract to each upstream's real page ceiling; older FR document numbers are accepted."
3
+ breaking: true
4
+ security: false
5
+ ---
6
+
7
+ # 0.5.0 — 2026-09-25
8
+
9
+ Breaking: five inputs that previously succeeded now fail.
10
+
11
+ 1. `regulations_search_rules` with `published_after` later than `published_before` is `date_range_inverted` (previously an empty success).
12
+ 2. `regulations_find_comments` `fr_document_number` must match the Federal Register number pattern. A Regulations.gov document ID, which previously worked through a text-search fallback, belongs in `document_object_id`.
13
+ 3. An `fr_document_number` that no Regulations.gov document carries is `not_found` (previously resolved to the first document whose text quoted the number, and listed that document's comments).
14
+ 4. A `document_object_id` that is neither a 16-hex object ID nor a resolvable Regulations.gov document ID is `not_found` (previously a 0-comment success).
15
+ 5. A `docket_id` with a character no Regulations.gov ID contains (anything but letters, digits, `-`, and `_`) is `not_found` (previously a 0-comment success).
16
+
17
+ ## Added
18
+
19
+ - **`regulations_find_comments` list filters**: `search_term` (`filter[searchTerm]`, stemmed) and inclusive `posted_after` / `posted_before` (`filter[postedDate][ge|le]`). Search hits carry `highlightedContent`, the matched passages as plain text [#39](https://github.com/cyanheads/federal-regulations-mcp-server/issues/39).
20
+ - **`filter_requires_list_mode`** and **`date_range_inverted`** on `regulations_find_comments`: a filter alongside `comment_id`, or an inverted posted window, fails before any request [#39](https://github.com/cyanheads/federal-regulations-mcp-server/issues/39).
21
+ - **`regulations_find_comments` detail mode** adds `postmarkDate` and `duplicateComments`; a count above 1 renders as a mass-mail campaign record in `content[]` [#33](https://github.com/cyanheads/federal-regulations-mcp-server/issues/33).
22
+ - **`totalPages`, `nextPage`, and `cap`** enrichment on `regulations_search_rules`, `regulations_get_docket`, and `regulations_find_comments` list mode, matching `regulations_list_open_comments` [#48](https://github.com/cyanheads/federal-regulations-mcp-server/issues/48).
23
+ - **`date_range_inverted`** on `regulations_search_rules` for an inverted `published_after` / `published_before` window [#48](https://github.com/cyanheads/federal-regulations-mcp-server/issues/48).
24
+
25
+ ## Changed
26
+
27
+ - **`page`** on `regulations_get_docket` and `regulations_find_comments` accepts 1–40, the last page Regulations.gov serves (was 1–20) [#43](https://github.com/cyanheads/federal-regulations-mcp-server/issues/43).
28
+ - **`truncated`** now means matches lie past the last reachable page (`50 × per_page` on the Federal Register, `40 × per_page` on Regulations.gov), set on every page of such a set; the notice names a `per_page` that reaches them when one exists [#48](https://github.com/cyanheads/federal-regulations-mcp-server/issues/48).
29
+ - **A page past the end** returns a notice naming the last page instead of "nothing matched" advice [#48](https://github.com/cyanheads/federal-regulations-mcp-server/issues/48).
30
+ - **`regulations_find_comments` `document_object_id`** takes a document ID (`EPA-HQ-OW-2022-0114-0027`) as well as an object ID, resolving it through `/documents/{id}`; an object ID is matched in either case and sent lowercase [#34](https://github.com/cyanheads/federal-regulations-mcp-server/issues/34).
31
+ - **`regulations_find_comments` `fr_document_number`** resolves through the exact-match `filter[frDocNum]` and answers `not_found` when no document carries the number; an empty result on a resolved document names its docket to widen to [#34](https://github.com/cyanheads/federal-regulations-mcp-server/issues/34).
32
+ - **`regulations_find_comments` targets** are trimmed, and a value with characters no Regulations.gov ID contains answers `not_found` without a request [#34](https://github.com/cyanheads/federal-regulations-mcp-server/issues/34).
33
+
34
+ ## Fixed
35
+
36
+ - **`regulations_find_comments` `receivedDate`** reads Regulations.gov's `receiveDate` and is no longer always null [#33](https://github.com/cyanheads/federal-regulations-mcp-server/issues/33).
37
+ - **`regulations_get_document` and `regulations://document/{documentNumber}`** accept every Federal Register number shape (`98-1572`, `94-31556-2`, `E9-25990`, `C1-2009-30484`), uppercasing a letter prefix before the request [#40](https://github.com/cyanheads/federal-regulations-mcp-server/issues/40).
38
+ - **Character references in comment bodies and eCFR text** decode through one shared decoder: every HTML-standard name, numeric references outside Unicode scalar values left as written instead of throwing `RangeError`, and `&constructor;`-style names kept literal [#54](https://github.com/cyanheads/federal-regulations-mcp-server/issues/54), [#56](https://github.com/cyanheads/federal-regulations-mcp-server/issues/56).
39
+ - **Comment HTML stripping** removes only what HTML reads as a tag (`<` then a letter, `/`, `!`, or `?`, closed before the next `<`), so text like "levels < 4 ppt and > 2 ppt" is kept instead of eaten up to the next `>`; a body of unclosed `<` runs in linear time; non-breaking spaces collapse like other spaces [#54](https://github.com/cyanheads/federal-regulations-mcp-server/issues/54).
40
+
41
+ ## Dependencies
42
+
43
+ - `entities` added: `^8.1.0` (character-reference decoding).
44
+ - `@socketsecurity/bun-security-scanner` `^1.1.2 → ^1.1.3` (dev).
@@ -0,0 +1,27 @@
1
+ ---
2
+ summary: "regulations_search_rules filters by CFR part, printed docket number, and RIN and resolves a CFR source-note cite to its document; Federal Register rows carry keyless Regulations.gov IDs, comment counts, and comment URLs, plus whether the comment period is open."
3
+ breaking: false
4
+ security: false
5
+ ---
6
+
7
+ # 0.5.1 — 2026-09-25
8
+
9
+ ## Added
10
+
11
+ - **`regulations_search_rules` filters `cfr_title`, `cfr_part`, `docket_id`, and `rin`**, ANDed with the rest. `cfr_part` takes a part or a range (`"140-143"`) and needs `cfr_title` (`title_required_for_part`); `docket_id` matches the docket numbers the Federal Register prints [#47](https://github.com/cyanheads/federal-regulations-mcp-server/issues/47).
12
+ - **`citation` + `citation_date` on `regulations_search_rules`** resolve a page cite (`"89 FR 49102"`, `2024-06-11`) to the documents printed on that page, volume 59 (1994) on. `citation_incomplete` and `citation_out_of_range` reject a bad pairing before any request; an empty result reports that day's page span [#46](https://github.com/cyanheads/federal-regulations-mcp-server/issues/46).
13
+ - **`citation`, `startPage`, `endPage`** on `regulations_search_rules` rows and `regulations_get_document`, null where the Federal Register records no pages [#46](https://github.com/cyanheads/federal-regulations-mcp-server/issues/46).
14
+ - **`regulationsGovDocketId`, `regulationsGovDocumentId`, `commentCount`, `commentUrl`** on `regulations_search_rules` and `regulations_list_open_comments` rows, read from the Federal Register with no key; `regulations_get_document` adds `commentUrl` and the printed `docketIds` [#49](https://github.com/cyanheads/federal-regulations-mcp-server/issues/49).
15
+ - **`commentPeriodOpen`** on `regulations_search_rules` rows and `regulations_get_document` (true through 11:59 PM Eastern on `commentsCloseOn`, null with no close date), and on `regulations_get_docket` documents, relayed from Regulations.gov's `openForComment` [#50](https://github.com/cyanheads/federal-regulations-mcp-server/issues/50).
16
+
17
+ ## Changed
18
+
19
+ - **`regulations_list_open_comments`** fills `commentCount` without `REGULATIONS_GOV_API_KEY` and drops the "counts unavailable" notice; `keyed` now reports whether `regulations_get_docket` / `regulations_find_comments` can follow up on a row [#49](https://github.com/cyanheads/federal-regulations-mcp-server/issues/49).
20
+ - **`docketIds`** is described as the Federal Register's printed docket numbers. `regulations_get_docket`, `regulations_find_comments`, and the `find_comments` empty-docket notice point at `regulationsGovDocketId` instead [#49](https://github.com/cyanheads/federal-regulations-mcp-server/issues/49).
21
+ - **`regulations_get_cfr_section` `bodyText`** names `citation` + `citation_date` as the path from a source-note cite to its rulemaking [#46](https://github.com/cyanheads/federal-regulations-mcp-server/issues/46).
22
+
23
+ ## Fixed
24
+
25
+ - **`commentsCloseOn` and `commentEndDate`** no longer read as "still open for comment": the first is the inclusive Eastern close date, the second the UTC instant the period ends [#50](https://github.com/cyanheads/federal-regulations-mcp-server/issues/50).
26
+ - **`regulations_list_open_comments` `asOf`** is today's Eastern date, so documents closing today stay listed after 8 PM EDT [#50](https://github.com/cyanheads/federal-regulations-mcp-server/issues/50).
27
+ - **`cfrReferences`** keeps parts the Federal Register serves as numbers, which most documents before 2020 do; they were dropped [#47](https://github.com/cyanheads/federal-regulations-mcp-server/issues/47).
package/dist/index.js CHANGED
@@ -31,7 +31,7 @@ await createApp({
31
31
  tools: allToolDefinitions,
32
32
  resources: allResourceDefinitions,
33
33
  prompts: [],
34
- instructions: 'US federal regulatory law over three official sources. The Federal Register (regulations_search_rules, regulations_get_document, regulations_list_open_comments) and eCFR (regulations_browse_cfr, regulations_get_cfr_section) tools are keyless. The Regulations.gov tools (regulations_get_docket, regulations_find_comments) need REGULATIONS_GOV_API_KEY (free at https://api.data.gov/signup/) and return an actionable auth_required error without it; regulations_list_open_comments runs keyless and only adds comment counts when the key is present. Trace a rule end to end: search_rules → get_document (yields docket ID + CFR parts) → find_comments → get_cfr_section.',
34
+ instructions: "US federal regulatory law over three official sources. The Federal Register (regulations_search_rules, regulations_get_document, regulations_list_open_comments) and eCFR (regulations_browse_cfr, regulations_get_cfr_section) tools are keyless. The Regulations.gov tools (regulations_get_docket, regulations_find_comments) need REGULATIONS_GOV_API_KEY (free at https://api.data.gov/signup/) and return an actionable auth_required error without it; comment counts and Regulations.gov IDs on the Federal Register tools need no key. Trace a rule end to end: search_rules → get_document (yields docket ID + CFR parts) → find_comments → get_cfr_section, and back from a section's source-note cite with search_rules citation + citation_date.",
35
35
  setup(core) {
36
36
  initFederalRegisterService(core.config, core.storage);
37
37
  initEcfrService(core.config, core.storage);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,sBAAsB,EAAE,MAAM,6CAA6C,CAAC;AACrF,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,MAAM,uCAAuC,CAAC;AACnE,OAAO,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AACjG,OAAO,EAAE,0BAA0B,EAAE,MAAM,yDAAyD,CAAC;AACrG,OAAO,EAAE,yBAAyB,EAAE,MAAM,uDAAuD,CAAC;AAElG,MAAM,SAAS,CAAC;IACd,IAAI,EAAE,gCAAgC;IACtC,KAAK,EAAE,gCAAgC;IACvC,+EAA+E;IAC/E,wEAAwE;IACxE,yEAAyE;IACzE,2EAA2E;IAC3E,wEAAwE;IACxE,WAAW,EAAE,WAAW;IACxB,KAAK,EAAE,kBAAkB;IACzB,SAAS,EAAE,sBAAsB;IACjC,OAAO,EAAE,EAAE;IACX,YAAY,EACV,wpBAAwpB;IAC1pB,KAAK,CAAC,IAAI;QACR,0BAA0B,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACtD,eAAe,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAC3C,yBAAyB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACrD,uEAAuE;QACvE,KAAK,qBAAqB,CACxB,IAAI,CAAC,MAAM,CAAC,gBAAgB,EAC5B,eAAe,EAAE,CAAC,qBAAqB,CACxC,CAAC;IACJ,CAAC;IAED;;;;;;;;OAQG;IACH,KAAK,CAAC,QAAQ;QACZ,MAAM,iBAAiB,EAAE,CAAC;QAC1B,MAAM,UAAU,CAAC,KAAK,EAAE,CAAC;IAC3B,CAAC;CACF,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,sBAAsB,EAAE,MAAM,6CAA6C,CAAC;AACrF,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,MAAM,uCAAuC,CAAC;AACnE,OAAO,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AACjG,OAAO,EAAE,0BAA0B,EAAE,MAAM,yDAAyD,CAAC;AACrG,OAAO,EAAE,yBAAyB,EAAE,MAAM,uDAAuD,CAAC;AAElG,MAAM,SAAS,CAAC;IACd,IAAI,EAAE,gCAAgC;IACtC,KAAK,EAAE,gCAAgC;IACvC,+EAA+E;IAC/E,wEAAwE;IACxE,yEAAyE;IACzE,2EAA2E;IAC3E,wEAAwE;IACxE,WAAW,EAAE,WAAW;IACxB,KAAK,EAAE,kBAAkB;IACzB,SAAS,EAAE,sBAAsB;IACjC,OAAO,EAAE,EAAE;IACX,YAAY,EACV,+tBAA+tB;IACjuB,KAAK,CAAC,IAAI;QACR,0BAA0B,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACtD,eAAe,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAC3C,yBAAyB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACrD,uEAAuE;QACvE,KAAK,qBAAqB,CACxB,IAAI,CAAC,MAAM,CAAC,gBAAgB,EAC5B,eAAe,EAAE,CAAC,qBAAqB,CACxC,CAAC;IACJ,CAAC;IAED;;;;;;;;OAQG;IACH,KAAK,CAAC,QAAQ;QACZ,MAAM,iBAAiB,EAAE,CAAC;QAC1B,MAAM,UAAU,CAAC,KAAK,EAAE,CAAC;IAC3B,CAAC;CACF,CAAC,CAAC"}
@@ -13,7 +13,7 @@ export declare const documentResource: import("@cyanheads/mcp-ts-core").Resource
13
13
  readonly reason: "not_found";
14
14
  readonly code: JsonRpcErrorCode.NotFound;
15
15
  readonly when: "No Federal Register document exists with that number.";
16
- readonly recovery: "Verify the number via regulations_search_rules; FR numbers look like \"2025-14555\".";
16
+ readonly recovery: "Verify the number via regulations_search_rules; FR numbers look like \"2024-07773\", or \"98-1572\" and \"E9-25990\" before 2010.";
17
17
  }, {
18
18
  readonly reason: "upstream_unavailable";
19
19
  readonly code: JsonRpcErrorCode.ServiceUnavailable;
@@ -1 +1 @@
1
- {"version":3,"file":"document.resource.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/document.resource.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAY,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAGjE,eAAO,MAAM,gBAAgB;;;;;;;;;;;;GA6C3B,CAAC"}
1
+ {"version":3,"file":"document.resource.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/document.resource.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAY,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAOjE,eAAO,MAAM,gBAAgB;;;;;;;;;;;;GAqD3B,CAAC"}
@@ -7,24 +7,27 @@
7
7
  */
8
8
  import { resource, z } from '@cyanheads/mcp-ts-core';
9
9
  import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
10
+ import { frDocumentNumber, normalizeFrDocumentNumber, } from '../../tools/definitions/document-number.js';
10
11
  import { getFederalRegisterService } from '../../../services/federal-register/federal-register-service.js';
11
12
  export const documentResource = resource('regulations://document/{documentNumber}', {
12
13
  name: 'federal-register-document',
13
14
  title: 'Federal Register document',
14
- description: 'A single Federal Register document by FR number: metadata plus the cross-source handles (docket ID, affected CFR parts, comment count) that chain into the comment and codified-text tools. Mirrors regulations_get_document (full text omitted).',
15
+ description: 'A single Federal Register document by FR number: metadata, citation and pages, and the cross-source handles (Regulations.gov docket and document IDs, printed docket numbers, affected CFR parts, comment count and comment URL) that chain into the search, comment, and codified-text tools. Mirrors regulations_get_document (full text omitted).',
15
16
  mimeType: 'application/json',
17
+ /**
18
+ * Validated as the template hands it over: the pattern admits only unreserved
19
+ * URI characters, which RFC 6570 expansion never percent-encodes, so a valid
20
+ * number arrives exactly as written.
21
+ */
16
22
  params: z.object({
17
- documentNumber: z
18
- .string()
19
- .regex(/^[0-9]{4}-[0-9]+$/)
20
- .describe('Federal Register document number (e.g. "2025-14555").'),
23
+ documentNumber: frDocumentNumber().describe('Federal Register document number: "2024-07773" from 2010 on, older and correction numbers like "98-1572", "E9-25990", or "C1-2009-30484".'),
21
24
  }),
22
25
  errors: [
23
26
  {
24
27
  reason: 'not_found',
25
28
  code: JsonRpcErrorCode.NotFound,
26
29
  when: 'No Federal Register document exists with that number.',
27
- recovery: 'Verify the number via regulations_search_rules; FR numbers look like "2025-14555".',
30
+ recovery: 'Verify the number via regulations_search_rules; FR numbers look like "2024-07773", or "98-1572" and "E9-25990" before 2010.',
28
31
  },
29
32
  {
30
33
  reason: 'upstream_unavailable',
@@ -35,7 +38,7 @@ export const documentResource = resource('regulations://document/{documentNumber
35
38
  ],
36
39
  async handler(params, ctx) {
37
40
  const service = getFederalRegisterService();
38
- const detail = await service.getDocument(params.documentNumber, undefined, ctx);
41
+ const detail = await service.getDocument(normalizeFrDocumentNumber(params.documentNumber), undefined, ctx);
39
42
  // Drop the (absent) fullText field; the resource omits the body by contract.
40
43
  const { fullText: _omit, ...rest } = detail;
41
44
  return rest;
@@ -1 +1 @@
1
- {"version":3,"file":"document.resource.js","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/document.resource.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,yBAAyB,EAAE,MAAM,yDAAyD,CAAC;AAEpG,MAAM,CAAC,MAAM,gBAAgB,GAAG,QAAQ,CAAC,yCAAyC,EAAE;IAClF,IAAI,EAAE,2BAA2B;IACjC,KAAK,EAAE,2BAA2B;IAClC,WAAW,EACT,mPAAmP;IACrP,QAAQ,EAAE,kBAAkB;IAC5B,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,cAAc,EAAE,CAAC;aACd,MAAM,EAAE;aACR,KAAK,CAAC,mBAAmB,CAAC;aAC1B,QAAQ,CAAC,uDAAuD,CAAC;KACrE,CAAC;IACF,MAAM,EAAE;QACN;YACE,MAAM,EAAE,WAAW;YACnB,IAAI,EAAE,gBAAgB,CAAC,QAAQ;YAC/B,IAAI,EAAE,uDAAuD;YAC7D,QAAQ,EACN,oFAAoF;SACvF;QACD;YACE,MAAM,EAAE,sBAAsB;YAC9B,IAAI,EAAE,gBAAgB,CAAC,kBAAkB;YACzC,IAAI,EAAE,2EAA2E;YACjF,QAAQ,EAAE,6EAA6E;SACxF;KACF;IAED,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG;QACvB,MAAM,OAAO,GAAG,yBAAyB,EAAE,CAAC;QAC5C,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,cAAc,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC;QAChF,6EAA6E;QAC7E,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,IAAI,EAAE,GAAG,MAAM,CAAC;QAC5C,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;QACX,SAAS,EAAE;YACT;gBACE,GAAG,EAAE,mCAAmC;gBACxC,IAAI,EAAE,mCAAmC;gBACzC,QAAQ,EAAE,kBAAkB;aAC7B;SACF;KACF,CAAC;CACH,CAAC,CAAC"}
1
+ {"version":3,"file":"document.resource.js","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/document.resource.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EACL,gBAAgB,EAChB,yBAAyB,GAC1B,MAAM,mDAAmD,CAAC;AAC3D,OAAO,EAAE,yBAAyB,EAAE,MAAM,yDAAyD,CAAC;AAEpG,MAAM,CAAC,MAAM,gBAAgB,GAAG,QAAQ,CAAC,yCAAyC,EAAE;IAClF,IAAI,EAAE,2BAA2B;IACjC,KAAK,EAAE,2BAA2B;IAClC,WAAW,EACT,sVAAsV;IACxV,QAAQ,EAAE,kBAAkB;IAC5B;;;;OAIG;IACH,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,cAAc,EAAE,gBAAgB,EAAE,CAAC,QAAQ,CACzC,2IAA2I,CAC5I;KACF,CAAC;IACF,MAAM,EAAE;QACN;YACE,MAAM,EAAE,WAAW;YACnB,IAAI,EAAE,gBAAgB,CAAC,QAAQ;YAC/B,IAAI,EAAE,uDAAuD;YAC7D,QAAQ,EACN,6HAA6H;SAChI;QACD;YACE,MAAM,EAAE,sBAAsB;YAC9B,IAAI,EAAE,gBAAgB,CAAC,kBAAkB;YACzC,IAAI,EAAE,2EAA2E;YACjF,QAAQ,EAAE,6EAA6E;SACxF;KACF;IAED,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG;QACvB,MAAM,OAAO,GAAG,yBAAyB,EAAE,CAAC;QAC5C,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,WAAW,CACtC,yBAAyB,CAAC,MAAM,CAAC,cAAc,CAAC,EAChD,SAAS,EACT,GAAG,CACJ,CAAC;QACF,6EAA6E;QAC7E,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,IAAI,EAAE,GAAG,MAAM,CAAC;QAC5C,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;QACX,SAAS,EAAE;YACT;gBACE,GAAG,EAAE,mCAAmC;gBACxC,IAAI,EAAE,mCAAmC;gBACzC,QAAQ,EAAE,kBAAkB;aAC7B;SACF;KACF,CAAC;CACH,CAAC,CAAC"}
@@ -3,6 +3,18 @@
3
3
  * @module mcp-server/resources/definitions/index
4
4
  */
5
5
  export declare const allResourceDefinitions: (import("@cyanheads/mcp-ts-core").ResourceDefinition<import("zod").ZodObject<{
6
+ documentNumber: import("zod").ZodString;
7
+ }, import("zod/v4/core").$strip>, undefined, readonly [{
8
+ readonly reason: "not_found";
9
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.NotFound;
10
+ readonly when: "No Federal Register document exists with that number.";
11
+ readonly recovery: "Verify the number via regulations_search_rules; FR numbers look like \"2024-07773\", or \"98-1572\" and \"E9-25990\" before 2010.";
12
+ }, {
13
+ readonly reason: "upstream_unavailable";
14
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ServiceUnavailable;
15
+ readonly when: "Federal Register returned a 5xx, timed out, or served an HTML error page.";
16
+ readonly recovery: "Retry after a brief wait; the Federal Register API may be momentarily down.";
17
+ }]> | import("@cyanheads/mcp-ts-core").ResourceDefinition<import("zod").ZodObject<{
6
18
  title: import("zod").ZodString;
7
19
  part: import("zod").ZodString;
8
20
  section: import("zod").ZodString;
@@ -17,17 +29,5 @@ export declare const allResourceDefinitions: (import("@cyanheads/mcp-ts-core").R
17
29
  readonly when: "eCFR returned a 5xx, timed out, or served an HTML error page (live path, mirror not ready or missing the section).";
18
30
  readonly recovery: "Retry after a brief wait; the eCFR API may be momentarily unavailable.";
19
31
  readonly thrownBy: "service";
20
- }]> | import("@cyanheads/mcp-ts-core").ResourceDefinition<import("zod").ZodObject<{
21
- documentNumber: import("zod").ZodString;
22
- }, import("zod/v4/core").$strip>, undefined, readonly [{
23
- readonly reason: "not_found";
24
- readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.NotFound;
25
- readonly when: "No Federal Register document exists with that number.";
26
- readonly recovery: "Verify the number via regulations_search_rules; FR numbers look like \"2025-14555\".";
27
- }, {
28
- readonly reason: "upstream_unavailable";
29
- readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ServiceUnavailable;
30
- readonly when: "Federal Register returned a 5xx, timed out, or served an HTML error page.";
31
- readonly recovery: "Retry after a brief wait; the Federal Register API may be momentarily down.";
32
32
  }]>)[];
33
33
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"browse-cfr.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/browse-cfr.tool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAmJjE,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyXxB,CAAC"}
1
+ {"version":3,"file":"browse-cfr.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/browse-cfr.tool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AA6HjE,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyXxB,CAAC"}