About federal-regulations-mcp-server
federal-regulations-mcp-server helps policy researchers, legal teams, and rulemaking participants search US federal regulations across the Federal Register, eCFR, and Regulations.gov. Its cross-source identifiers support tracing a rule from proposal and comments to codified text.
Key features
- 01Search rules, notices, and documents
- 02Read current or historical CFR text
- 03Browse and search CFR structure
- 04Retrieve dockets and public comments
- 05List documents open for comment
Use cases
- 01Trace a proposed rule to codified text
- 02Find public comments on a docket
- 03Read a CFR section as of a past date
- 04Identify rulemaking documents with open comment periods
MCP support
Exposes seven tools and two resources to MCP clients over STDIO or Streamable HTTP. Five Federal Register and eCFR tools are keyless; docket and comment tools require a Regulations.gov API key.
Configuration
Remote servers need no local install. Pick your client, then copy the config or install in one click.
https://federal-regulations.caseyjhand.com/mcpclaude mcp add --transport http cyanheads-federal-regulations https://federal-regulations.caseyjhand.com/mcp{"mcpServers":{"cyanheads-federal-regulations":{"type":"http","url":"https://federal-regulations.caseyjhand.com/mcp"}}}Open in Cursor{"servers":{"cyanheads-federal-regulations":{"type":"http","url":"https://federal-regulations.caseyjhand.com/mcp"}}}Open in VS Code{"mcpServers":{"cyanheads-federal-regulations":{"type":"http","url":"https://federal-regulations.caseyjhand.com/mcp"}}}{"mcpServers":{"cyanheads-federal-regulations":{"serverUrl":"https://federal-regulations.caseyjhand.com/mcp"}}}Run commands in a terminal; paste JSON into the client’s MCP config and restart. Servers that need sign-in will prompt on first connect.
Tools
7| Parameter | Type | Required | Description |
|---|---|---|---|
| rin | string | No | Only documents carrying this Regulation Identifier Number — a regulationIdNumbers entry such as "2040-AG18", which follows one rulemaking from proposal to final rule. Pass the complete RIN: a fragment ("2040") matches every RIN that contains it. |
| page | integer | No | Page number (1–50, default 1). The Federal Register serves 50 pages, so a query reaches 50 × per_page matches (5,000 at per_page 100); totalPages and nextPage in the response say how far this one goes. To reach past that, narrow with published_after/published_before rather than paging deeper. |
| type | array | No | Document types to include. PRORULE=Proposed Rule, RULE=Final Rule, NOTICE=Notice, PRESDOCU=Presidential Document. Omit for all types. |
| order | enum | No | Result order. Defaults to relevance with a query, newest without one. relevance without a query falls back to newest first; oldest lists the earliest publications first. Ignored with citation, whose matches come in page order.relevance | newest | oldest |
| query | string | No | Full-text search across document title and body. Omit to browse by filters alone (e.g. all EPA proposed rules in a date range). |
| agencies | array | No | Filter to one or more agencies by Federal Register agency slug (e.g. "environmental-protection-agency", "securities-and-exchange-commission") — lowercase kebab-case, not a name or acronym. Every result lists its agencies with their slugs; if unsure, search by query and read agencies[].slug off a result. One unrecognized slug fails the whole request. |
| cfr_part | string | No | Only documents whose cfrReferences list this part of cfr_title: a part number ("141") or a range ("140-143"), such as the part regulations_get_cfr_section just read; a "Part" or "pt." prefix is dropped. Requires cfr_title. A part with a letter in it ("1203a") cannot be filtered on. Unlike a query of "40 CFR 141", which matches documents that mention the part, this matches the documents whose CFR references list it. |
| citation | any | No | A Federal Register page cite, "89 FR 49102" (volume FR page) — such as one from the source note that ends a regulations_get_cfr_section bodyText. Returns the documents printed on that page, usually one and sometimes a few sharing it, ordered by start page. Requires citation_date and replaces published_after/published_before. Volume 59 (1994) onward. |
| per_page | integer | No | Results per page (2–100, default 20). The Federal Register API treats exactly 1 as its default page size instead of returning one result. |
| cfr_title | integer | No | Only documents whose affected CFR parts (cfrReferences) fall in this title (1–50) — such as the title regulations_get_cfr_section just read. Alone it filters to the title; with cfr_part, to that part. |
| docket_id | string | No | Only documents carrying this docket number as the Federal Register prints it — a docketIds entry from a result here or from regulations_get_document, with or without a wrapper such as "Docket No.". Lists a docket's Federal Register documents with no REGULATIONS_GOV_API_KEY. A Regulations.gov docket ID matches only where the Federal Register prints it. Pass the complete number: a fragment ("FAA-2026") matches every docket that contains it. |
| citation_date | any | No | The publication date printed beside the cite in the source note (YYYY-MM-DD) — "89 FR 49102, June 11, 2024" is 2024-06-11. Required with citation; its year is the volume + 1935. |
| published_after | any | No | Earliest publication date, ISO 8601 (YYYY-MM-DD), a real calendar day. Combine with published_before to window large result sets — the FR caps navigation at 50 pages. |
| published_before | any | No | Latest publication date, ISO 8601 (YYYY-MM-DD), a real calendar day. |
Details
From the project README@cyanheads/federal-regulations-mcp-server
Search and trace US federal rules across the Federal Register (proposed/final rules and notices), the eCFR (codified, point-in-time CFR full text, locally mirrored), and Regulations.gov (rulemaking dockets and public comments) via MCP. STDIO or Streamable HTTP.
Public Hosted Server: https://federal-regulations.caseyjhand.com/mcp
Overview
US federal regulations from the Federal Register, the eCFR, and Regulations.gov. Search proposed and final rules, trace a rule from proposal through public comments to codified text, and read any CFR section as it stands today or stood on a past date.
Tools
| Tool | Description |
|---|---|
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 |
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 |
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 |
regulations_get_cfr_section | Read codified CFR text for a section, whole part, or appendix, current or as of a past date |
regulations_get_docket | Pull a rulemaking docket and its filed documents from Regulations.gov (key required) |
regulations_find_comments | Fetch public comments on a document or docket, or one comment's full body and attachments (key required) |
regulations_list_open_comments | List documents currently open for public comment, soonest closing first |
Resources
| Resource | Description |
|---|---|
regulations://document/{documentNumber} | One Federal Register document's metadata and cross-source handles |
regulations://cfr/{title}/{part}/{section} | Codified text of a current CFR section |
Both resources mirror a tool (regulations_get_document, regulations_get_cfr_section), so tool-only clients lose nothing.
Capability reference
regulations_search_rules tool
- Optional full-text
query, filtered bytype(PRORULE/RULE/NOTICE/PRESDOCU),agencies(Federal Register slugs such asenvironmental-protection-agency, not names or acronyms),published_after/published_before,cfr_title+cfr_part(a part number or range such as140-143;cfr_partneedscfr_title, elsetitle_required_for_part),docket_id(a docket number as the Federal Register prints it), andrin, all combined;per_page2–100 (default 20),page1–50 citation("89 FR 49102") withcitation_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 withcitation_incomplete; a volume before 59 or a date from another year fails withcitation_out_of_range. When no document spans the page, the notice gives that day's page range- Each result carries
documentNumber,citation/startPage/endPage,agencies[].slug, the printeddocketIds,regulationsGovDocketIdandregulationsGovDocumentId,commentCount,commentUrl,commentsCloseOnwithcommentPeriodOpen(open through 11:59 PM Eastern on the close date),regulationIdNumbers, andcfrReferencesfor the follow-up tools.totalPagesandnextPagepage the results. The Federal Register serves 50 pages, sotruncatedmarks a set larger than 50 ×per_page(1,000 at the default, 5,000 at 100); narrow the date window rather than paging deeper.totalCountstops at 10,000, which means at least that many. A window whose start falls after its end fails withdate_range_inverted, andinvalid_filternames a rejected parameter
regulations_get_document tool
document_numberin any form the Federal Register issues:2024-07773from 2010 on, and older and correction numbers such as98-1572,E9-25990, orC1-2009-30484;include_full_textadds the plain-text body as one window ofmax_chars(default 64,000, max 200,000) starting atoffset, and passing either of those implies it- Returns metadata plus the handles other tools take: the Regulations.gov
docketIdandregulationsGovDocumentId, the printeddocketIds,citation/startPage/endPage,commentCount,commentUrl,commentPeriodOpen, andcfrReferences. With text,fullTextLengthandfullTextNextOffset(present while text remains) page the body; a major final rule runs past a million characters
regulations_browse_cfr tool
mode: "structure"lists the 50 titles, a title's top-level divisions, or, withtitle+part, every section and appendix in the part with itssubpartandsubjectGroup;mode: "search"full-text-searches the codified CFR and requiresquery.title,part(needstitle), and a point-in-timedatescope both modes, andper_page1–50 (default 20) pages search results and part listings- Search rows carry
cfrCite,heading,hierarchyPath, andexcerpt, one per section.source(mirror/live) andsourceScopename the corpus that answered and what it covers, andcountBasissays whethertotalCountcountssectionsorsection_versions; live search reaches eCFR's first 10,000 hits only (page_out_of_windowpast them)
regulations_get_cfr_section tool
- One section (
title+part+section), a whole part (sectionomitted), or one appendix (appendix, verbatim asregulations_browse_cfremits it, e.g.Appendix A-1 to Part 50).sectionalso takes cites as people write them ("61","§ 141.61","141.61(c)") and thecfrCitethe server returns ("40 CFR 141.61","14 CFR 241 § 25"; a cite naming another title fails withconflicting_title),parttakes"Part 141", anddatereads text from 2017-01-01 through the title's up-to-date date - Text comes back as one
bodyTextwindow (max_charsdefault 64,000, max 200,000, fromoffset), paged bybodyTextLengthandbodyTextNextOffset. A whole-part read adds asections[]index with each section'soffsetand names itsappendiceswithout their text;source(mirror/live) reports provenance - A whole-part read also returns the part's
heading,authority,sourceNote, andnotes, and eachsections[]entry carries the Authority or Source its subpart or subject group states for it - Superscripts read
^xand subscripts_x(3 × 10^−8,CO_{2}e), footnote markers[n], and diacritics and overlines are combining marks (x̄)
regulations_get_docket tool · key required
docket_id, the Regulations.gov docket ID (e.g.EPA-HQ-OAR-2025-0194) —regulationsGovDocketIdon a search or open-comments row,docketIdfromregulations_get_document, not a printeddocketIdsentry;document_typesfilters toProposed Rule,Rule,Notice,Supporting & Related Material, orOther;per_page5–250 (default 25),page1–40- Returns the docket's metadata,
documentCount, anddocuments[], each with anobjectIdforregulations_find_comments, afrDocNumback toregulations_get_document, andcommentPeriodOpenas Regulations.gov reports it.totalPagesandnextPagepage the documents; Regulations.gov serves 40 pages, sotruncatedmarks a docket larger than 40 ×per_page(10,000 at 250)
regulations_find_comments tool · key required
- Exactly one of
docket_id,document_object_id,fr_document_number, orcomment_id; list scopes takeper_page5–250 (default 25) andpage1–40 document_object_idtakes an object ID (0900006485883ec6) or a document ID (EPA-HQ-OW-2022-0114-0027).fr_document_numberresolves to the Regulations.gov document carrying that exact number and answersnot_foundwhen none does- Lists narrow by comment text with
search_term, and each hit then carrieshighlightedContent, the matched passages.posted_after/posted_beforeset an inclusive posted-date window. A backwards window fails withdate_range_inverted, and filters passed withcomment_idfail withfilter_requires_list_mode modeislistordetail. Lists return comment summaries without body text.comment_idreturns the body, submitter,receivedDate,postmarkDate,duplicateComments(above 1 marks a mass-mail campaign record), andattachments, withattachmentOnly: truewhen the substance is in a PDF/DOCX file. Lists carrytotalPagesandnextPage;truncatedmarks 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
regulations_list_open_comments tool
- Optional
query,type(PRORULE,RULE,NOTICE; default["PRORULE", "RULE"]),agencies(Federal Register slugs), andclosing_before;per_page1–100 (default 20) - Rows sort by closing date, soonest first, and carry
commentsCloseOn,daysRemaining,documentNumber, the printeddocketIds,regulationsGovDocketIdandregulationsGovDocumentId,commentCount, andcommentUrl.asOfis today's date in Eastern time, so a document stays listed through its close day.totalPagesandnextPagepage the window;truncatedmeans the Federal Register's 10,000-document limit was reached - Keyless.
keyedreports whetherREGULATIONS_GOV_API_KEYis set, whichregulations_get_docketandregulations_find_commentsneed to follow up on a row
regulations://document/{documentNumber} resource
documentNumberin any Federal Register form (2024-07773,98-1572,E9-25990); the payload isregulations_get_document's without the body textdocketId,regulationsGovDocumentId, the printeddocketIds,cfrReferences,citation,commentCount, andcommentUrlchain into the search, comment, and CFR tools
regulations://cfr/{title}/{part}/{section} resource
- Sections only, at the current date and the tool's default 64,000-character window; the
partandsectionsegments resolve the way the tool resolves them ("Part 141";"61","§ 141.61","40 CFR 141.61") source(mirror/live) reports provenance.bodyTextNextOffsetmarks a section longer than the window; read the rest throughregulations_get_cfr_sectionwithoffset
Features
Built on @cyanheads/mcp-ts-core: stdio and Streamable HTTP transports, pluggable auth (none / jwt / oauth), swappable storage (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), structured logging with optional OpenTelemetry tracing.
Federal Register / eCFR / Regulations.gov-specific:
- One workflow over three official sources: the agent calls regulatory verbs (
search_rules,get_cfr_section,find_comments) rather than three API clients - 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
- Keyless core: the five Federal Register and eCFR tools need no key.
regulations_get_docketandregulations_find_commentsneedREGULATIONS_GOV_API_KEY(free at api.data.gov/signup, 1,000 requests/hour) and fail withauth_required, naming the variable and signup URL, when it is missing or rejected - 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, titles outside the mirror, and titles eCFR has re-issued since the mirror last synced them
- A 45-second budget per request, shared by every upstream call, retry, and backoff, so a stalled source answers with
upstream_unavailableinside a client's timeout
Agent-friendly output:
- Provenance:
source: "mirror" | "live"on every CFR read, plus asourceScopeline on search naming what the corpus covers - Honest paging:
totalPagesandnextPagesay 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 throughtruncatedandnotice, never as silently dropped rows - Attachment-aware comments:
attachmentOnlyflags a comment whose substance is a file, with the download URLs
Getting started
Public Hosted Instance
A public instance is available at https://federal-regulations.caseyjhand.com/mcp — no installation required. It serves Streamable HTTP; each client takes its own config shape.
For Claude Code:
claude mcp add --transport http federal-regulations-mcp-server https://federal-regulations.caseyjhand.com/mcp
For Cline (cline_mcp_settings.json):
{
"mcpServers": {
"federal-regulations-mcp-server": {
"type": "streamableHttp",
"url": "https://federal-regulations.caseyjhand.com/mcp"
}
}
}
For Cursor (mcp.json):
{
"mcpServers": {
"federal-regulations-mcp-server": {
"url": "https://federal-regulations.caseyjhand.com/mcp"
}
}
}
Claude Desktop adds remote servers under Settings → Connectors.
Self-Hosted / Local
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.
{
"mcpServers": {
"federal-regulations-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["@cyanheads/federal-regulations-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info",
"REGULATIONS_GOV_API_KEY": "your-key-here"
}
}
}
}
Or with npx (no Bun required):
{
"mcpServers": {
"federal-regulations-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@cyanheads/federal-regulations-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info",
"REGULATIONS_GOV_API_KEY": "your-key-here"
}
}
}
}
Or with Docker:
{
"mcpServers": {
"federal-regulations-mcp-server": {
"type": "stdio",
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "MCP_TRANSPORT_TYPE=stdio",
"-e", "REGULATIONS_GOV_API_KEY=your-key-here",
"ghcr.io/cyanheads/federal-regulations-mcp-server:latest"
]
}
}
}
For Streamable HTTP, set the transport and start the server:
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
# Server listens at http://localhost:3010/mcp
Prerequisites
- Bun v1.4.0 or higher (or Node.js v24+).
- Optional: a free api.data.gov key for the Regulations.gov tools.
Installation
- Clone the repository:
git clone https://github.com/cyanheads/federal-regulations-mcp-server.git
- Navigate into the directory:
cd federal-regulations-mcp-server
- Install dependencies:
bun install
- Configure environment:
cp .env.example .env
# optionally set REGULATIONS_GOV_API_KEY
Configuration
| Variable | Description | Default |
|---|---|---|
REGULATIONS_GOV_API_KEY | api.data.gov key for regulations_get_docket and regulations_find_comments. | none |
FEDERAL_REGISTER_BASE_URL | Federal Register API v1 base URL. | https://www.federalregister.gov/api/v1 |
ECFR_BASE_URL | eCFR API base URL. | https://www.ecfr.gov/api |
REGULATIONS_GOV_BASE_URL | Regulations.gov API v4 base URL. | https://api.regulations.gov/v4 |
ECFR_MIRROR_PATH | Path to the eCFR SQLite mirror database. | ./data/ecfr-mirror.sqlite |
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) |
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 |
MCP_TRANSPORT_TYPE | Transport: stdio or http. | stdio |
MCP_HTTP_PORT | HTTP server port. | 3010 |
MCP_AUTH_MODE | Authentication: none, jwt, or oauth. | none |
MCP_LOG_LEVEL | Log level (debug, info, warning, error, etc.). | info |
STORAGE_PROVIDER_TYPE | Storage backend: in-memory, filesystem, supabase, cloudflare-kv/r2/d1. | in-memory |
OTEL_ENABLED | Enable OpenTelemetry. | false |
See .env.example for the full list of optional overrides.
Running the server
Local development
-
Build and run:
# One-time build bun run rebuild # Run the built server bun run start:stdio # or bun run start:http -
Build or refresh the eCFR mirror (out of band and idempotent; the CFR tools use the live eCFR API until the first build completes):
bun run mirror:init # full build, resumable (scope with ECFR_MIRROR_TITLES) bun run mirror:refresh # re-harvest every configured title against the latest eCFR issues bun run mirror:verify # report row counts and the last-synced issue date -
Run checks and tests:
bun run devcheck # Lint, format, typecheck, security, changelog sync bun run test # Vitest test suite
Docker
docker build -t federal-regulations-mcp-server .
docker run --rm -e REGULATIONS_GOV_API_KEY=your-key -p 3010:3010 federal-regulations-mcp-server
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.
Project structure
| Directory | Purpose |
|---|---|
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. |
src/config | Server-specific environment variable parsing and validation with Zod. |
src/mcp-server/tools | Tool definitions (*.tool.ts), the seven regulations_* tools. |
src/mcp-server/resources | Resource definitions (*.resource.ts), the document and CFR-section resources. |
src/services/federal-register | Federal Register API v1 client (keyless). |
src/services/ecfr | eCFR API client (keyless): versioner, structure, search, section XML parsing, and cite resolution. |
src/services/ecfr-mirror | eCFR codified-text mirror (SQLite + FTS5) and the opt-in refresh job. |
src/services/regulations-gov | Regulations.gov v4 client (X-Api-Key): dockets and comments. |
src/services | Shared request budget, text windowing, character-reference decoding, and upstream-failure handling. |
scripts/ecfr-mirror-*.ts | Out-of-band mirror lifecycle: init, refresh, verify. |
tests/ | Unit and integration tests mirroring src/. |
Development guide
See CLAUDE.md for development guidelines and architectural rules. The short version:
- Handlers throw, framework catches — no
try/catchin tool logic - Use
ctx.logfor request-scoped logging,ctx.statefor tenant-scoped storage - Register new tools and resources via the barrels in
src/mcp-server/*/definitions/index.ts - Wrap external API calls: validate raw → normalize to the domain type → return the output schema; never fabricate missing upstream fields
Data disclaimer
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 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.
Contributing
Issues are welcome. Run checks and tests before submitting:
bun run devcheck
bun run test
License
Apache-2.0 — see LICENSE for details.
Comments
New to MCP?
From what the protocol is to installing your first server, these answers cover most first questions.
An open protocol that defines how AI applications connect to external tools and data in a uniform way. Clients talk to the model; MCP servers provide tools, resources and prompt templates. They exchange standard messages, so one server can be reused by many clients.