mcp-server-rfc
This MCP server lets AI agents search, navigate, and read IETF RFCs efficiently while flagging obsolete specifications.
Search RFCs by title: matches all query terms against titles and always works.
Full-text search: searches every RFC using a section index (BM25-ranked sections with headings, line numbers, snippets) or a local mirror via
rg/grep; supports regex with the mirror.Obsolescence awareness: every result carries RFC status and replacement/obsoletion info; current RFCs are prioritized over superseded ones.
List RFC sections: returns numbered headings with line numbers and line counts, helping agents choose a section before reading.
Read RFCs and sections: read by section number or heading text, include subsections, or use line ranges; supports
max_lines,start_line, and full-document reads when explicitly requested.Context guardrails: refuses non-section reads over 1500 lines unless forced with
full=true, and strips page headers/footers.Pagination feedback: search results report
count,total, andtruncatedso agents can request more results instead of miscounting.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@mcp-server-rfcFind the current RFC on HTTP caching and read section 4.2 on freshness"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Let AI agents read the RFC corpus with 40x fewer tokens, and flag obsoleted specs.
What it is
Two ways to give an AI agent the IETF RFC corpus: a skill and an MCP server. Both run the same stdlib-only Python core, so they find, read and flag documents identically. No RFC text ships with either; documents come from the RFC Editor on demand.
Obsolescence on every result. A superseded RFC says so in a banner the model can't miss:
!! OBSOLETED BY: RFC 7230, 7231, ...Section reads. List an RFC's headings with line numbers, then read only the section that answers the question.
Search. By title out of the box, or across the full text of all ~9,800 RFCs once you sync a local mirror.
Clean text. Page headers, footers and form feeds are stripped.
A guard on oversized reads. A read over 1500 lines that isn't a named section is refused, with its size and how to narrow it.
RFC and section | Whole RFC | One section |
RFC 9110 HTTP Semantics, §15.5.5 404 Not Found | 125.7k | 177 |
RFC 9000 QUIC, §19.3 ACK Frames | 100.9k | 1,742 |
RFC 8446 TLS 1.3, §4.1.2 Client Hello | 78.1k | 1,865 |
RFC 5321 SMTP, §4.2 SMTP Replies | 52.8k | 4,003 |
RFC 6749 OAuth 2.0, §5.2 Error Response | 38.0k | 935 |
RFC 9111 HTTP Caching, §4.2 Freshness | 21.1k | 2,531 |
The median saving is 41x, the figure behind "40x fewer tokens".
Tokens are approximated as bytes divided by 4, measured from rfc.py 0.5.0 output.
assets/charts/context_sizes.json has the raw measurements.
Related MCP server: conceptio-search
Why
Agents cite dead specifications with confidence. RFC 2616 has been obsolete since 2014, and it's still the first thing most models reach for on HTTP. Every result here carries the RFC index's obsolescence field, so the agent follows the replacement before it quotes anything.
This project replaces mcp-server-ietf, which is unmaintained.
rfc-ai-tooling | mcp-server-ietf | Model fetches rfc-editor.org | |
Flags obsoleted RFCs | On every result | No | Only if it reads the header |
RFCs below 1000, like IP and TCP | Yes | No, its index parser expected zero-padded numbers | Yes |
Full titles | Yes | Cut at the first period, so RFC 2616 is "HTTP/1" | Yes |
Newly published RFCs | Index refreshes | Index downloaded once | Yes |
Read one section | By number or heading | Line ranges only | Whole document |
Full-text search | Ranked by section, with a downloaded index | Titles only | No |
Install | One config line, or none for the skill | Clone and | Nothing |
Pick a surface
Use the one that matches where your agent's tools run.
Your client | Use | Why |
Claude Code, Codex CLI, OpenClaw, Cursor, Zed, or anything that runs commands on your machine | The skill | The agent gets the whole shell, including |
Claude Desktop | The MCP server over stdio | Desktop has no shell, but it spawns MCP servers on your machine |
claude.ai on the web, Cowork, other cloud sessions | The MCP server over HTTP, hosted by you | A cloud session can't reach a process on your machine |
There's no public instance of the server, and none is planned.
Install
Skill
Install from this repo with skills:
npx skills add shbernal/rfc-ai-toolingThe skill is a single stdlib-only Python script, with nothing to pip install.
MCP server
Add this to your client's MCP configuration.
uvx fetches mcp-server-rfc from PyPI on first run.
{
"mcpServers": {
"rfc": {
"command": "uvx",
"args": ["mcp-server-rfc"]
}
}
}I've tested it end to end in Claude Desktop on Linux.
macOS and Windows should work, but I haven't checked them.
docs/desktop-verification.md shows how to tell whether Desktop really called the tools.
The same binary serves HTTP:
mcp-server-rfc --transport http --host 0.0.0.0 --port 8080mcp/Dockerfile and mcp/fly.toml deploy it as a container:
docker build -t mcp-server-rfc mcp/ && docker run --rm -p 8080:8080 mcp-server-rfcThen add https://<your-app>/mcp as a custom connector in claude.ai, which needs a Pro, Max, Team or Enterprise plan.
There's no authentication. A public instance is an open proxy in front of a volunteer-run mirror, so put it behind auth or keep the URL private.
Full-text search needs the section index on the server. Run
sync --indexthere, or it returns an error. Everything else needs only the 2 MB RFC index.
Quickstart
Install the skill or the MCP server.
Ask your agent a spec question, like "What does HTTP say about how long a cached response stays fresh?"
Check that it cites a current RFC by section, like
RFC 9111 §4.2, and not RFC 2616.
The agent runs the same loop you can run yourself: search, check obsolescence, list sections, read one.
Full-text search
Title search works with no setup. To search the text of every RFC, download the section index once.
For the skill, where <skills-dir> is where it was installed, like ~/.claude/skills:
python3 <skills-dir>/rfc-lookup/scripts/rfc.py sync --indexFor the MCP server:
uvx mcp-server-rfc sync --indexBoth write to the same directory, so one download serves both.
The index is a SQLite file holding every RFC, compressed, and an FTS5 index over each section. A search returns sections ranked by BM25, with the number to read next:
$ rfc search "how long can a cache serve a stale response" --fulltext
RFC 9111 — HTTP Caching [INTERNET STANDARD]
§4.2.4 Serving Stale Responses (line 784, 17 lines)
790: A cache MUST NOT generate a stale response if it is prohibited by anReads come from the index too, so nothing is fetched from the RFC Editor after that. On the retrieval eval it finds the answering section in its top 10 for more than half of the held-out questions; ripgrep over the corpus finds it for none.
Without the index, --fulltext falls back to rg or grep over a synced mirror of the corpus as plain files, which is also what --regex needs:
uvx mcp-server-rfc syncDownload | On disk | Source | |
| About 270 MB | 360 MB | This project's GitHub releases, checked against a published SHA-256 |
| 512 MB, about 5 minutes | 512 MB | The RFC Editor's public rsync mirror, capped at 2 MB/s by default ( |
Neither starts on its own, and the skill tells agents not to run them unasked.
rfc index build makes the index from a synced mirror, in about two and a half minutes.
How it works
flowchart LR
editor[("RFC Editor")]
releases[("GitHub releases")]
index[("Section index<br/>360 MB, optional")]
mirror[("Local mirror<br/>512 MB, optional")]
core["core/rfc.py<br/>stdlib only"]
skill["Skill<br/>scripts/rfc.py"]
mcp["mcp-server-rfc<br/>stdio or HTTP"]
shell["Agent with a shell"]
client["MCP client"]
editor -- "HTTPS, on demand" --> core
editor -. "rsync, when you run sync" .-> mirror
releases -. "when you run sync --index" .-> index
index --> core
mirror --> core
core -- "vendored copy" --> skill
core -- "vendored copy" --> mcp
skill --> shell
mcp --> clientcore/rfc.py is the only implementation.
The skill and the MCP server each carry an identical copy, and CI fails if one drifts.
The core parses the RFC Editor's index for titles, status and what obsoletes what, then fetches documents over HTTPS and caches them.
With the section index, it reads from that file and ranks sections with SQLite's FTS5.
With only a synced mirror, it reads from disk and searches with rg or grep.
More
skill/SKILL.md: every CLI command and how the agent is told to use themmcp/README.md: the MCP tools and their argumentsdocs/desktop-verification.md: proving Claude Desktop called the toolsCHANGELOG.md: breaking changes are listed there, so read it before upgradingCONTRIBUTING.md: layout, tests and release rulesNOTICE: attribution, and the terms that apply to RFC text
Available Tools
3 toolsget_rfcARead-only
Read an RFC. Call list_sections first and pass a section — whole RFCs average 53 KB and run past 1.6 MB, and reading one in full is almost never what the question needs. section accepts a number ('9.3.1') or heading text ('Idempotent Methods') and includes that section's subsections. Any read of more than 1500 lines that is not a named section is an error naming its size, not a silent context-filling dump; pass full=true only when the entire text is genuinely what you need. start_line and max_lines are 1 or more. max_lines caps any read, including a section, and is the way to take the first part of one that list_sections showed is long. start_line is the fallback for RFCs without numbered headings and cannot be combined with section. Page headers and footers are stripped. The response carries a banner naming the RFC's status and, if it has been superseded, what replaced it.
| Name | Required | Description | Default |
|---|---|---|---|
| full | No | ||
| number | Yes | ||
| section | No | ||
| max_lines | No | ||
| start_line | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint and openWorldHint, but the description adds substantial behavior beyond them: oversized non-section reads fail with an error naming the size rather than dumping context, page headers and footers are stripped, and the response carries a status/supersession banner. The failure mode and response shape are both disclosed, which is rare and valuable.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the primary action and the single most important directive (pass a section), then the size rationale, then parameter rules. Dense but every sentence carries operational information; nothing is padding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, and the description still covers the response banner, stripped boilerplate, and the error behavior for oversized reads. Combined with the parameter constraints, an agent has everything needed to call this correctly on the first attempt.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With 0% schema description coverage the description has to carry all five parameters, and it does for section (number or heading text, includes subsections), max_lines (caps any read, including sections), start_line (fallback, mutually exclusive with section) and full (escape hatch for whole text). The number parameter's accepted forms (integer or string) and heading-match tolerance are left implicit.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ("Read an RFC") and immediately positions it in a workflow by naming the sibling list_sections as the prerequisite step. An agent can tell this apart from list_sections and search_rfcs without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit routing rules: call list_sections first and pass a section, use full=true only when the whole text is genuinely needed, and use start_line only as a fallback for RFCs without numbered headings. It also names a concrete exclusion (start_line cannot be combined with section), which is exactly the kind of constraint that prevents bad calls.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_sectionsARead-only
List an RFC's numbered headings with their line numbers and how many lines each one runs to. Cheap, and the right first call when you need part of a specification: use it to pick a section, then pass that section to get_rfc. The line counts are there to be read — a section of a few hundred lines is a normal read, and one running to four figures wants a subsection or a max_lines cap instead. Some pre-1990 RFCs have no numbered headings, in which case this returns an empty list and get_rfc's line range is the way in.
| Name | Required | Description | Default |
|---|---|---|---|
| number | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare read-only/open-world, so the description adds the substantive behavior: the shape of each entry (heading, line number, line count), the empty-list outcome for pre-1990 RFCs, and the cost signal ('cheap'). It also interprets its own output (a few hundred lines is a normal read; four figures wants a subsection or max_lines cap), which is genuine guidance an agent could not get from annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the verb and return shape, then usage, then output interpretation, then the edge case. Every sentence carries information an agent needs; nothing repeats the schema or annotations.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description still conveys the return shape, the empty-result case, and how to proceed from the result into get_rfc. For a single-parameter listing tool this covers everything needed to call it and act on its output.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Only one parameter and schema description coverage is 0%, so the description must carry the load. 'An RFC's numbered headings' implies the parameter is an RFC number, but it never names it or resolves the anyOf ambiguity between integer and string forms (e.g. '1234' vs 'RFC 1234'). Adequate but leaves a real gap.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource (list an RFC's numbered headings) plus exactly what each entry carries: line numbers and line counts. That is immediately distinguishable from siblings get_rfc (reads content) and search_rfcs (search), and no schema inspection is needed.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says it is the right first call when you need part of a spec, names the alternative path (pass the section to get_rfc), and gives a fallback for the case it cannot serve: pre-1990 RFCs return an empty list, so use get_rfc's line range. When-to-use, when-to-skip and the alternative are all stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_rfcsARead-only
Search RFCs. scope='title' matches all query terms against RFC titles and always works. scope='fulltext' searches the text of every RFC and needs either the section index or a local mirror the user has set up; it returns an error explaining what to run if there is neither. With the section index ('tool' is 'index'), the query is words, not a phrase: each result is one section, ranked by BM25 with headings weighted, carrying its 'section' number (null for the front matter), 'heading', 'line' and how many 'lines' it runs to, and a 'snippet' quoting its best line. Pass that section straight to get_rfc. Without the index ('tool' is 'rg' or 'grep'), the query is a literal string, each result is one RFC ranked by 'matching_lines', and 'matches' quotes a few lines with the section each falls in. regex=true opts into pattern matching, which only the mirror can do, in the backend's dialect. Results carry each RFC's status and obsolescence, and title results put current RFCs before superseded ones, so a page cut short at 'limit' keeps the replacement rather than the document it replaced. A 'note' says when the index could not be used. 'count' is how many results this page holds and 'total' is how many matched; when 'truncated' is true, report 'total' and raise 'limit' (at most 200) rather than counting the results you were given. An empty query is an error.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| query | Yes | ||
| regex | No | ||
| scope | No | title |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint and openWorldHint, but the description adds substantial behavioral context: fulltext prerequisites, backend-dependent result shapes, ranking behavior, obsolescence ordering, truncation semantics, and error behavior for missing index/mirror or empty queries. This is exactly the kind of information an agent needs to invoke the tool correctly.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Although dense, every sentence adds operational detail needed for a search tool with multiple backends and no schema descriptions. Scope behavior is front-loaded, and the length is justified by the complexity of the result and pagination semantics.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema and no parameter descriptions in the schema, the description nevertheless explains result fields, ranking, snippets, status/obsolescence ordering, count/total/truncated handling, and error cases. It is complete enough for an agent to use the tool without guessing at return semantics.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must carry parameter meaning, and it does: scope values are explained, regex is tied to mirror-only pattern matching, limit is capped at 200 with truncation guidance, and query semantics differ explicitly between index word-based search and mirror literal-string search.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource ('Search RFCs') and immediately clarifies the two scopes. It also tells the agent that matching sections should be passed to get_rfc, making the search-vs-retrieve boundary clear, even though list_sections is not named.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit conditions for scope selection, fulltext prerequisites, regex limitations, and pagination/truncation handling. The main gap is that it does not explicitly contrast this tool with sibling tools like list_sections or get_rfc beyond noting that a section can be passed to get_rfc.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
3 tool updates
v0.1.0- First observed
get_rfc - First observed
list_sections - First observed
search_rfcs
TDQS
Scored across 3 tools
Each tool has a clearly distinct role: list_sections maps document structure, get_rfc reads selected content, and search_rfcs finds RFCs or sections. The descriptions explicitly describe complementary workflows and warn about overlapping usage patterns such as line ranges versus section selection.
All tools follow a consistent verb_noun snake_case pattern: list_sections, get_rfc, search_rfcs. The naming is predictable and immediately readable.
Three tools is well-scoped for an RFC reading and search server. Each tool has a distinct, necessary function: inspecting structure, retrieving content, and searching.
The core lifecycle of finding, navigating, and reading RFCs is covered, including status and obsoletion metadata. Minor gaps exist around richer metadata or bulk RFC listing, but agents can work around these using search and section navigation.
Maintenance
Related MCP Connectors
Web data for agents: YouTube transcripts, screenshots, Google News, WHOIS, jobs, tech stack, more.
Web search and page reading for AI agents, including JSON APIs, RSS feeds and YouTube.
Live web checks for AI agents: sitemaps, robots.txt, URL status, broken links, feeds, citations.
Web tools for AI agents: URL to clean Markdown, site crawl, screenshots, PDF/Office to text, OCR.
Related MCP Servers
- AlicenseAqualityAmaintenanceEnables AI agents to search and fetch high-quality information from multiple sources, including general web APIs, Wikipedia, arXiv, Hacker News, Stack Exchange, and Crossref, with optional pro-mode deep research and clean markdown page extraction.10MIT
- AlicenseAqualityBmaintenanceEnables AI agents to search 1m+ open-access papers, standards, textbooks, and legal documents, resolve identifiers such as RFCs, DOIs, and legal citations, download PDFs, and retrieve citations and metadata through MCP tools.9MIT
- AlicenseAqualityBmaintenanceEnables AI agents to read RSS/Atom feeds, extract web page content as text or Markdown, search the web, and batch-fetch multiple URLs without building custom crawlers.5MIT
- FlicenseNot gradedqualityAmaintenanceEnables AI models to search and retrieve up-to-date technical documentation for supported agent frameworks such as LangChain, LlamaIndex, AutoGen, Agno, OpenAI Agents SDK, MCP, Camel-AI, and CrewAI, returning extracted web page text over stdio or SSE.-