@cyanheads/federal-regulations-mcp-server 0.4.0 → 0.5.0
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 +1 -1
- package/CLAUDE.md +1 -1
- package/README.md +59 -93
- package/changelog/0.5.x/0.5.0.md +44 -0
- package/dist/mcp-server/resources/definitions/document.resource.d.ts +1 -1
- package/dist/mcp-server/resources/definitions/document.resource.d.ts.map +1 -1
- package/dist/mcp-server/resources/definitions/document.resource.js +9 -6
- package/dist/mcp-server/resources/definitions/document.resource.js.map +1 -1
- package/dist/mcp-server/resources/definitions/index.d.ts +12 -12
- package/dist/mcp-server/tools/definitions/document-number.d.ts +24 -0
- package/dist/mcp-server/tools/definitions/document-number.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/document-number.js +28 -0
- package/dist/mcp-server/tools/definitions/document-number.js.map +1 -0
- package/dist/mcp-server/tools/definitions/find-comments.tool.d.ts +26 -4
- package/dist/mcp-server/tools/definitions/find-comments.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/find-comments.tool.js +229 -46
- package/dist/mcp-server/tools/definitions/find-comments.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/format-utils.d.ts +3 -1
- package/dist/mcp-server/tools/definitions/format-utils.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/format-utils.js +5 -1
- package/dist/mcp-server/tools/definitions/format-utils.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 +59 -13
- package/dist/mcp-server/tools/definitions/get-docket.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/get-document.tool.d.ts +1 -1
- package/dist/mcp-server/tools/definitions/get-document.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/get-document.tool.js +5 -7
- package/dist/mcp-server/tools/definitions/get-document.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/index.d.ts +35 -4
- package/dist/mcp-server/tools/definitions/index.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/paging.d.ts +48 -0
- package/dist/mcp-server/tools/definitions/paging.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/paging.js +48 -0
- package/dist/mcp-server/tools/definitions/paging.js.map +1 -0
- package/dist/mcp-server/tools/definitions/search-rules.tool.d.ts +9 -0
- package/dist/mcp-server/tools/definitions/search-rules.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/search-rules.tool.js +76 -18
- package/dist/mcp-server/tools/definitions/search-rules.tool.js.map +1 -1
- package/dist/services/character-references.d.ts +29 -0
- package/dist/services/character-references.d.ts.map +1 -0
- package/dist/services/character-references.js +49 -0
- package/dist/services/character-references.js.map +1 -0
- package/dist/services/ecfr/ecfr-service.d.ts.map +1 -1
- package/dist/services/ecfr/ecfr-service.js +8 -28
- package/dist/services/ecfr/ecfr-service.js.map +1 -1
- package/dist/services/ecfr/xml.d.ts +2 -2
- package/dist/services/ecfr/xml.d.ts.map +1 -1
- package/dist/services/ecfr/xml.js +6 -37
- package/dist/services/ecfr/xml.js.map +1 -1
- package/dist/services/federal-register/federal-register-service.d.ts.map +1 -1
- package/dist/services/federal-register/federal-register-service.js +16 -14
- package/dist/services/federal-register/federal-register-service.js.map +1 -1
- package/dist/services/regulations-gov/regulations-gov-service.d.ts +22 -6
- package/dist/services/regulations-gov/regulations-gov-service.d.ts.map +1 -1
- package/dist/services/regulations-gov/regulations-gov-service.js +70 -42
- package/dist/services/regulations-gov/regulations-gov-service.js.map +1 -1
- package/dist/services/regulations-gov/types.d.ts +19 -2
- package/dist/services/regulations-gov/types.d.ts.map +1 -1
- package/package.json +3 -2
- 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
|
+
**Version:** 0.5.0
|
|
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
|
+
**Version:** 0.5.0
|
|
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
|
-
[](./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
|
|
|
@@ -29,123 +29,94 @@
|
|
|
29
29
|
|
|
30
30
|
## Overview
|
|
31
31
|
|
|
32
|
-
US federal regulatory law
|
|
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
|
|
38
|
+
| `regulations_search_rules` | Search Federal Register proposed rules, final rules, notices, and presidential documents by query, type, agency, and date range |
|
|
39
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
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
|
|
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}` |
|
|
51
|
-
| `regulations://cfr/{title}/{part}/{section}` | Codified text of a current 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
|
-
|
|
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
|
-
-
|
|
60
|
-
- `
|
|
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), and `published_after` / `published_before`; `per_page` 2–100 (default 20), `page` 1–50
|
|
60
|
+
- Each result carries `documentNumber`, `agencies[].slug`, `docketIds`, `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
61
|
|
|
65
62
|
---
|
|
66
63
|
|
|
67
64
|
### `regulations_get_document` <sub>tool</sub>
|
|
68
65
|
|
|
69
|
-
-
|
|
70
|
-
-
|
|
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
|
|
66
|
+
- `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
|
|
67
|
+
- Returns metadata plus the handles other tools take: `docketId`, `regulationsGovDocumentId`, `commentCount`, and `cfrReferences`. With text, `fullTextLength` and `fullTextNextOffset` (present while text remains) page the body; a major final rule runs past a million characters
|
|
75
68
|
|
|
76
69
|
---
|
|
77
70
|
|
|
78
71
|
### `regulations_browse_cfr` <sub>tool</sub>
|
|
79
72
|
|
|
80
|
-
-
|
|
81
|
-
- `
|
|
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
|
|
73
|
+
- `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
|
|
74
|
+
- 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
75
|
|
|
88
76
|
---
|
|
89
77
|
|
|
90
78
|
### `regulations_get_cfr_section` <sub>tool</sub>
|
|
91
79
|
|
|
92
|
-
-
|
|
93
|
-
-
|
|
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
|
|
80
|
+
- 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
|
|
81
|
+
- 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
82
|
|
|
102
83
|
---
|
|
103
84
|
|
|
104
85
|
### `regulations_get_docket` <sub>tool</sub> · key required
|
|
105
86
|
|
|
106
|
-
-
|
|
107
|
-
- `
|
|
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
|
|
87
|
+
- `docket_id` (e.g. `EPA-HQ-OAR-2025-0194`); `document_types` filters to `Proposed Rule`, `Rule`, `Notice`, `Supporting & Related Material`, or `Other`; `per_page` 5–250 (default 25), `page` 1–40
|
|
88
|
+
- Returns the docket's metadata, `documentCount`, and `documents[]`, each with an `objectId` for `regulations_find_comments` and a `frDocNum` back to `regulations_get_document`. `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
89
|
|
|
112
90
|
---
|
|
113
91
|
|
|
114
92
|
### `regulations_find_comments` <sub>tool</sub> · key required
|
|
115
93
|
|
|
116
|
-
-
|
|
117
|
-
- `
|
|
118
|
-
-
|
|
119
|
-
- `
|
|
120
|
-
- `auth_required` / `not_found` / `rate_limited` (429, 1,000 req/hr) / `upstream_unavailable` round out the errors
|
|
94
|
+
- 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
|
|
95
|
+
- `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
|
|
96
|
+
- 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`
|
|
97
|
+
- `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
98
|
|
|
122
99
|
---
|
|
123
100
|
|
|
124
101
|
### `regulations_list_open_comments` <sub>tool</sub>
|
|
125
102
|
|
|
126
|
-
-
|
|
127
|
-
-
|
|
128
|
-
-
|
|
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
|
|
103
|
+
- Optional `query`, `type` (`PRORULE`, `RULE`, `NOTICE`; default `["PRORULE", "RULE"]`), `agencies` (Federal Register slugs), and `closing_before`; `per_page` 1–100 (default 20)
|
|
104
|
+
- Rows sort by closing date, soonest first, and carry `commentsCloseOn`, `daysRemaining`, `documentNumber`, and `docketIds`. `totalPages` and `nextPage` page the window; `truncated` means the Federal Register's 10,000-document limit was reached
|
|
105
|
+
- Keyless. `keyed` reports whether `commentCount` was filled in, which happens only when `REGULATIONS_GOV_API_KEY` is set
|
|
132
106
|
|
|
133
107
|
---
|
|
134
108
|
|
|
135
109
|
### `regulations://document/{documentNumber}` <sub>resource</sub>
|
|
136
110
|
|
|
137
|
-
-
|
|
138
|
-
- `
|
|
139
|
-
- `not_found` / `upstream_unavailable` mirror the tool's errors
|
|
111
|
+
- `documentNumber` in any Federal Register form (`2024-07773`, `98-1572`, `E9-25990`); the payload is `regulations_get_document`'s without the body text
|
|
112
|
+
- `docketId`, `cfrReferences`, and `commentCount` chain into the comment and CFR tools
|
|
140
113
|
|
|
141
114
|
---
|
|
142
115
|
|
|
143
116
|
### `regulations://cfr/{title}/{part}/{section}` <sub>resource</sub>
|
|
144
117
|
|
|
145
|
-
-
|
|
146
|
-
-
|
|
147
|
-
- Mirror-backed with a live eCFR fallback; `source` (`mirror`/`live`) reports provenance
|
|
148
|
-
- `not_found` / `upstream_unavailable` mirror the tool's errors
|
|
118
|
+
- 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"`)
|
|
119
|
+
- `source` (`mirror` / `live`) reports provenance. `bodyTextNextOffset` marks a section longer than the window; read the rest through `regulations_get_cfr_section` with `offset`
|
|
149
120
|
|
|
150
121
|
## Features
|
|
151
122
|
|
|
@@ -153,18 +124,17 @@ Built on [`@cyanheads/mcp-ts-core`](https://github.com/cyanheads/mcp-ts-core): s
|
|
|
153
124
|
|
|
154
125
|
Federal Register / eCFR / Regulations.gov-specific:
|
|
155
126
|
|
|
156
|
-
- One workflow over three official sources
|
|
157
|
-
- Cross-source stitching
|
|
158
|
-
- Keyless core
|
|
159
|
-
- Locally mirrored codified CFR
|
|
160
|
-
- A 45-second
|
|
127
|
+
- One workflow over three official sources: the agent calls regulatory verbs (`search_rules`, `get_cfr_section`, `find_comments`) rather than three API clients
|
|
128
|
+
- 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
|
|
129
|
+
- 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
|
|
130
|
+
- 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
|
|
131
|
+
- 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
132
|
|
|
162
133
|
Agent-friendly output:
|
|
163
134
|
|
|
164
|
-
- Provenance
|
|
165
|
-
- Honest
|
|
166
|
-
- Attachment-aware comments
|
|
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
|
|
135
|
+
- Provenance: `source: "mirror" | "live"` on every CFR read, plus a `sourceScope` line on search naming what the corpus covers
|
|
136
|
+
- 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
|
|
137
|
+
- Attachment-aware comments: `attachmentOnly` flags a comment whose substance is a file, with the download URLs
|
|
168
138
|
|
|
169
139
|
## Getting started
|
|
170
140
|
|
|
@@ -185,7 +155,7 @@ A public instance is available at `https://federal-regulations.caseyjhand.com/mc
|
|
|
185
155
|
|
|
186
156
|
### Self-Hosted / Local
|
|
187
157
|
|
|
188
|
-
Add the following to your MCP client configuration file.
|
|
158
|
+
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
159
|
|
|
190
160
|
```json
|
|
191
161
|
{
|
|
@@ -242,8 +212,6 @@ Or with Docker:
|
|
|
242
212
|
}
|
|
243
213
|
```
|
|
244
214
|
|
|
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
215
|
For Streamable HTTP, set the transport and start the server:
|
|
248
216
|
|
|
249
217
|
```sh
|
|
@@ -253,8 +221,8 @@ MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
|
|
|
253
221
|
|
|
254
222
|
### Prerequisites
|
|
255
223
|
|
|
256
|
-
- [Bun v1.
|
|
257
|
-
- Optional: a free [api.data.gov key](https://api.data.gov/signup/) for the Regulations.gov tools
|
|
224
|
+
- [Bun v1.4.0](https://bun.sh/) or higher (or Node.js v24+).
|
|
225
|
+
- Optional: a free [api.data.gov key](https://api.data.gov/signup/) for the Regulations.gov tools and comment counts.
|
|
258
226
|
|
|
259
227
|
### Installation
|
|
260
228
|
|
|
@@ -285,23 +253,21 @@ cp .env.example .env
|
|
|
285
253
|
|
|
286
254
|
## Configuration
|
|
287
255
|
|
|
288
|
-
All configuration is validated at startup via Zod schemas in `src/config/server-config.ts`. Key environment variables:
|
|
289
|
-
|
|
290
256
|
| Variable | Description | Default |
|
|
291
257
|
|:---|:---|:---|
|
|
292
|
-
| `REGULATIONS_GOV_API_KEY` |
|
|
258
|
+
| `REGULATIONS_GOV_API_KEY` | api.data.gov key for `regulations_get_docket`, `regulations_find_comments`, and comment counts in `regulations_list_open_comments`. | none |
|
|
293
259
|
| `FEDERAL_REGISTER_BASE_URL` | Federal Register API v1 base URL. | `https://www.federalregister.gov/api/v1` |
|
|
294
260
|
| `ECFR_BASE_URL` | eCFR API base URL. | `https://www.ecfr.gov/api` |
|
|
295
261
|
| `REGULATIONS_GOV_BASE_URL` | Regulations.gov API v4 base URL. | `https://api.regulations.gov/v4` |
|
|
296
|
-
| `ECFR_MIRROR_PATH` |
|
|
297
|
-
| `ECFR_MIRROR_REFRESH_CRON` | Cron expression for an in-process mirror refresh (HTTP transport only), e.g. `0 4 * * 0`.
|
|
298
|
-
| `ECFR_MIRROR_TITLES` | Comma-separated CFR
|
|
262
|
+
| `ECFR_MIRROR_PATH` | Path to the eCFR SQLite mirror database. | `./data/ecfr-mirror.sqlite` |
|
|
263
|
+
| `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) |
|
|
264
|
+
| `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
265
|
| `MCP_TRANSPORT_TYPE` | Transport: `stdio` or `http`. | `stdio` |
|
|
300
|
-
| `MCP_HTTP_PORT` |
|
|
301
|
-
| `MCP_AUTH_MODE` |
|
|
302
|
-
| `MCP_LOG_LEVEL` | Log level (
|
|
303
|
-
| `STORAGE_PROVIDER_TYPE` | Storage backend
|
|
304
|
-
| `OTEL_ENABLED` | Enable [OpenTelemetry
|
|
266
|
+
| `MCP_HTTP_PORT` | HTTP server port. | `3010` |
|
|
267
|
+
| `MCP_AUTH_MODE` | Authentication: `none`, `jwt`, or `oauth`. | `none` |
|
|
268
|
+
| `MCP_LOG_LEVEL` | Log level (`debug`, `info`, `warning`, `error`, etc.). | `info` |
|
|
269
|
+
| `STORAGE_PROVIDER_TYPE` | Storage backend: `in-memory`, `filesystem`, `supabase`, `cloudflare-kv/r2/d1`. | `in-memory` |
|
|
270
|
+
| `OTEL_ENABLED` | Enable [OpenTelemetry](https://github.com/cyanheads/mcp-ts-core/tree/main/docs/telemetry). | `false` |
|
|
305
271
|
|
|
306
272
|
See [`.env.example`](./.env.example) for the full list of optional overrides.
|
|
307
273
|
|
|
@@ -321,10 +287,10 @@ See [`.env.example`](./.env.example) for the full list of optional overrides.
|
|
|
321
287
|
bun run start:http
|
|
322
288
|
```
|
|
323
289
|
|
|
324
|
-
- **
|
|
290
|
+
- **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
291
|
|
|
326
292
|
```sh
|
|
327
|
-
bun run mirror:init # full build
|
|
293
|
+
bun run mirror:init # full build, resumable (scope with ECFR_MIRROR_TITLES)
|
|
328
294
|
bun run mirror:refresh # re-harvest every configured title against the latest eCFR issues
|
|
329
295
|
bun run mirror:verify # report row counts and the last-synced issue date
|
|
330
296
|
```
|
|
@@ -334,7 +300,6 @@ See [`.env.example`](./.env.example) for the full list of optional overrides.
|
|
|
334
300
|
```sh
|
|
335
301
|
bun run devcheck # Lint, format, typecheck, security, changelog sync
|
|
336
302
|
bun run test # Vitest test suite
|
|
337
|
-
bun run lint:mcp # Validate MCP definitions against the linter rules
|
|
338
303
|
```
|
|
339
304
|
|
|
340
305
|
### Docker
|
|
@@ -344,20 +309,21 @@ docker build -t federal-regulations-mcp-server .
|
|
|
344
309
|
docker run --rm -e REGULATIONS_GOV_API_KEY=your-key -p 3010:3010 federal-regulations-mcp-server
|
|
345
310
|
```
|
|
346
311
|
|
|
347
|
-
The Dockerfile defaults to HTTP transport, stateless session mode, and logs to `/var/log/federal-regulations-mcp-server`.
|
|
312
|
+
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
313
|
|
|
349
314
|
## Project structure
|
|
350
315
|
|
|
351
316
|
| Directory | Purpose |
|
|
352
317
|
|:---|:---|
|
|
353
|
-
| `src/index.ts` | `createApp()` entry point
|
|
318
|
+
| `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
319
|
| `src/config` | Server-specific environment variable parsing and validation with Zod. |
|
|
355
|
-
| `src/mcp-server/tools` | Tool definitions (`*.tool.ts`)
|
|
356
|
-
| `src/mcp-server/resources` | Resource definitions (`*.resource.ts`)
|
|
320
|
+
| `src/mcp-server/tools` | Tool definitions (`*.tool.ts`), the seven `regulations_*` tools. |
|
|
321
|
+
| `src/mcp-server/resources` | Resource definitions (`*.resource.ts`), the document and CFR-section resources. |
|
|
357
322
|
| `src/services/federal-register` | Federal Register API v1 client (keyless). |
|
|
358
|
-
| `src/services/ecfr` | eCFR API client (keyless)
|
|
359
|
-
| `src/services/ecfr-mirror` | eCFR codified-text mirror (
|
|
360
|
-
| `src/services/regulations-gov` | Regulations.gov v4 client (`X-Api-Key`)
|
|
323
|
+
| `src/services/ecfr` | eCFR API client (keyless): versioner, structure, search, section XML parsing, and cite resolution. |
|
|
324
|
+
| `src/services/ecfr-mirror` | eCFR codified-text mirror (SQLite + FTS5) and the opt-in refresh job. |
|
|
325
|
+
| `src/services/regulations-gov` | Regulations.gov v4 client (`X-Api-Key`): dockets and comments. |
|
|
326
|
+
| `src/services` | Shared request budget, text windowing, character-reference decoding, and upstream-failure handling. |
|
|
361
327
|
| `scripts/ecfr-mirror-*.ts` | Out-of-band mirror lifecycle: `init`, `refresh`, `verify`. |
|
|
362
328
|
| `tests/` | Unit and integration tests mirroring `src/`. |
|
|
363
329
|
|
|
@@ -372,7 +338,7 @@ See [`CLAUDE.md`](./CLAUDE.md) for development guidelines and architectural rule
|
|
|
372
338
|
|
|
373
339
|
## Data disclaimer
|
|
374
340
|
|
|
375
|
-
eCFR content is "authoritative but unofficial" per the Office of the Federal Register
|
|
341
|
+
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
342
|
|
|
377
343
|
## Contributing
|
|
378
344
|
|
|
@@ -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).
|
|
@@ -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 \"
|
|
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;
|
|
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
15
|
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
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:
|
|
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 "
|
|
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,
|
|
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,mPAAmP;IACrP,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
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Input schema for a Federal Register document number, shared by
|
|
3
|
+
* regulations_get_document, the regulations://document resource, and
|
|
4
|
+
* regulations_find_comments' fr_document_number.
|
|
5
|
+
* @module mcp-server/tools/definitions/document-number
|
|
6
|
+
*/
|
|
7
|
+
import { z } from '@cyanheads/mcp-ts-core';
|
|
8
|
+
/**
|
|
9
|
+
* Every document-number shape the Federal Register serves, 1994 on: `2024-07773`
|
|
10
|
+
* (2010 onward), `98-1572` (1994–2008), `94-31556-2` (1994–1999), a letter and
|
|
11
|
+
* one or two digits before the sequence for corrections and republications
|
|
12
|
+
* (`E9-25990`, `X94-100621`), and a letter, a digit, and a year for corrections
|
|
13
|
+
* from 2010 on (`C1-2009-30484`). The letter is matched in either case;
|
|
14
|
+
* {@link normalizeFrDocumentNumber} fixes it before a request goes out.
|
|
15
|
+
*/
|
|
16
|
+
export declare const FR_DOCUMENT_NUMBER_PATTERN: RegExp;
|
|
17
|
+
/** A Federal Register document number. A fresh schema per call keeps each field's JSON Schema inline. */
|
|
18
|
+
export declare function frDocumentNumber(): z.ZodString;
|
|
19
|
+
/**
|
|
20
|
+
* The form the Federal Register and Regulations.gov both serve: a letter prefix
|
|
21
|
+
* uppercased. Both answer `e9-25990` with nothing (a 404, zero hits).
|
|
22
|
+
*/
|
|
23
|
+
export declare function normalizeFrDocumentNumber(documentNumber: string): string;
|
|
24
|
+
//# sourceMappingURL=document-number.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document-number.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/document-number.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAE3C;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,QAAmD,CAAC;AAE3F,yGAAyG;AACzG,wBAAgB,gBAAgB,gBAE/B;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAExE"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Input schema for a Federal Register document number, shared by
|
|
3
|
+
* regulations_get_document, the regulations://document resource, and
|
|
4
|
+
* regulations_find_comments' fr_document_number.
|
|
5
|
+
* @module mcp-server/tools/definitions/document-number
|
|
6
|
+
*/
|
|
7
|
+
import { z } from '@cyanheads/mcp-ts-core';
|
|
8
|
+
/**
|
|
9
|
+
* Every document-number shape the Federal Register serves, 1994 on: `2024-07773`
|
|
10
|
+
* (2010 onward), `98-1572` (1994–2008), `94-31556-2` (1994–1999), a letter and
|
|
11
|
+
* one or two digits before the sequence for corrections and republications
|
|
12
|
+
* (`E9-25990`, `X94-100621`), and a letter, a digit, and a year for corrections
|
|
13
|
+
* from 2010 on (`C1-2009-30484`). The letter is matched in either case;
|
|
14
|
+
* {@link normalizeFrDocumentNumber} fixes it before a request goes out.
|
|
15
|
+
*/
|
|
16
|
+
export const FR_DOCUMENT_NUMBER_PATTERN = /^[A-Za-z]?[0-9]{1,4}-[0-9]{1,6}(-[0-9]{1,6})?$/;
|
|
17
|
+
/** A Federal Register document number. A fresh schema per call keeps each field's JSON Schema inline. */
|
|
18
|
+
export function frDocumentNumber() {
|
|
19
|
+
return z.string().regex(FR_DOCUMENT_NUMBER_PATTERN);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The form the Federal Register and Regulations.gov both serve: a letter prefix
|
|
23
|
+
* uppercased. Both answer `e9-25990` with nothing (a 404, zero hits).
|
|
24
|
+
*/
|
|
25
|
+
export function normalizeFrDocumentNumber(documentNumber) {
|
|
26
|
+
return documentNumber.toUpperCase();
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=document-number.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document-number.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/document-number.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAE3C;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,gDAAgD,CAAC;AAE3F,yGAAyG;AACzG,MAAM,UAAU,gBAAgB;IAC9B,OAAO,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;AACtD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,yBAAyB,CAAC,cAAsB;IAC9D,OAAO,cAAc,CAAC,WAAW,EAAE,CAAC;AACtC,CAAC"}
|