A Model Context Protocol (MCP) server for structured understanding of RFC documents.
Unlike existing text-based RFC MCP servers, this server leverages the semantic structure of RFCXML to enable:
- Normative requirements extraction (MUST/SHOULD/MAY) with structured output
- RFC dependency graph construction
- Definition scope management
- Implementation checklist generation
┌─────────────────────────┐
│ Markdown / PDF │ Display & Sharing
├─────────────────────────┤
│ Translation │ Explanation & Verification
├─────────────────────────┤
│ RFCXML MCP │ Common Understanding for AI & Humans
├─────────────────────────┤
│ RFCXML │ Single Source of Truth
└─────────────────────────┘
| Feature | Existing mcp-rfc | RFCXML MCP |
|---|---|---|
| RFC text retrieval | ✅ | ✅ |
| Section extraction | ✅ (text-based) | ✅ (structure-based) |
| MUST/SHOULD/MAY extraction | ❌ | ✅ |
| Condition/exception structuring | ❌ | ✅ |
| RFC dependency graph | ❌ | ✅ |
| Definition scope management | ❌ | ✅ |
| Implementation checklist | ❌ | ✅ |
Add the following to your MCP configuration file:
{
"mcpServers": {
"rfcxml": {
"command": "npx",
"args": ["-y", "@shuji-bonji/rfcxml-mcp"]
}
}
}To pin a version (0.6.x ships patch releases frequently), write the version into the package spec:
{
"mcpServers": {
"rfcxml": {
"command": "npx",
"args": ["-y", "@shuji-bonji/rfcxml-mcp@0.6.53"]
}
}
}To keep fetched RFCs on disk across restarts, pass RFCXML_CACHE_DIR (see Disk cache and rfcxml-prefetch):
{
"mcpServers": {
"rfcxml": {
"command": "npx",
"args": ["-y", "@shuji-bonji/rfcxml-mcp@0.6.53"],
"env": { "RFCXML_CACHE_DIR": "/home/you/.cache/rfcxml-mcp" }
}
}
}Configuration file locations:
- Claude Desktop (macOS):
~/Library/Application Support/Claude/claude_desktop_config.json - Claude Desktop (Windows):
%APPDATA%\Claude\claude_desktop_config.json - Claude Code (project scope):
.mcp.jsonat the project root - Claude Code (user scope):
~/.claude.json - Claude Code (CLI):
claude mcp add rfcxml -- npx -y @shuji-bonji/rfcxml-mcp
For global installation:
npm install -g @shuji-bonji/rfcxml-mcpThen configure MCP:
{
"mcpServers": {
"rfcxml": {
"command": "rfcxml-mcp"
}
}
}get_rfc_structure- Get section hierarchy and metadataget_requirements- Extract normative requirements (MUST/SHOULD/MAY) with structureget_definitions- Get term definitions and their scope
get_rfc_dependencies- Get referenced RFCs (normative/informative)get_related_sections- Get related sections within the same RFC
validate_statement- Find the RFC requirements that bear on a statement and report detected contradictions. This is not a conformance judgment:isValidis three-valued (null= nothing matched strongly enough to judge,false= a contradiction was detected,true= no contradiction was detected among the matches). The verdict is yours.generate_checklist- Generate implementation checklist
RFCs published after RFC 8650 (December 2019) are available in official RFCXML v3 format. Earlier RFCs may not have XML available.
This server includes automatic fallback functionality - when XML is unavailable, it parses the text format instead.
All responses include source information:
{
"rfc": 6455,
"sections": [...],
"_source": "text",
"_sourceNote": "Warning: Parsed from text format. Accuracy may be limited."
}_source |
Description |
|---|---|
xml |
Parsed from RFCXML (high accuracy) |
text |
Parsed from text (medium accuracy) |
| RFC | Format | Notes |
|---|---|---|
| RFC 8650+ | XML | Official RFCXML v3 support |
| Before RFC 8650 | Text | Automatic fallback |
Every sample below is trimmed from the actual output of the current build (npm run build, then the listed tool call through an MCP client). Field names and values are verbatim; long arrays are cut with ....
get_rfc_structure { "rfc": 9293 }
{
"metadata": {
"title": "Transmission Control Protocol (TCP)",
"docName": "draft-ietf-tcpm-rfc793bis-28",
"number": 9293,
"date": "2022-08",
"category": "std",
"stream": "IETF",
"abstract": "This document specifies the Transmission Control Protocol (TCP). ..."
},
"sections": [
{ "number": "1", "title": "Purpose and Scope" },
{ "number": "2", "title": "Introduction" },
{
"number": "3",
"title": "Functional Specification",
"subsections": [
{ "number": "3.1", "title": "Header Format" },
{
"number": "3.5",
"title": "Establishing a Connection",
"subsections": [
{ "number": "3.5.1", "title": "Half-Open Connections and Other Anomalies" },
{ "number": "3.5.2", "title": "Reset Generation" },
{ "number": "3.5.3", "title": "Reset Processing" }
]
}
]
}
],
"referenceCount": { "normative": 15, "informative": 85 },
"_source": "xml"
}number is the section number as printed in the RFC (3.5, A.2), not the RFCXML pn (section-3.5). category / stream / abstract come from the IETF Datatracker API and are omitted when the API is unreachable or reports a value outside the mapping (e.g. RFC 1 is unkn / legacy); in that case _sourceNote says so.
get_requirements { "rfc": 9293, "level": "MUST" }
{
"rfc": 9293,
"filter": { "section": "all", "level": "MUST" },
"stats": { "total": 55, "byLevel": { "MUST": 55 } },
"requirements": [
{
"id": "R-3.5-1",
"level": "MUST",
"text": "A TCP implementation MUST support simultaneous open attempts (MUST-10).",
"section": "3.5",
"sectionTitle": "Establishing a Connection",
"fullContext": "A TCP implementation MUST support simultaneous open attempts (MUST-10).",
"subject": "tcp implementation",
"action": "support simultaneous open attempts (MUST-10)"
},
{
"id": "R-3.7.1-1",
"level": "MUST",
"text": "TCP endpoints MUST implement both sending and receiving the MSS Option (MUST-14).",
"section": "3.7.1",
"sectionTitle": "Maximum Segment Size Option",
"fullContext": "TCP endpoints MUST implement both sending and receiving the MSS Option (MUST-14).",
"subject": "tcp endpoints",
"action": "implement both sending and receiving the MSS Option (MUST-14)"
}
],
"_source": "xml"
}id is R-<section>-<n> with n numbered per section, so the identifier stays stable when requirements are added elsewhere in the document.
get_rfc_dependencies { "rfc": 9293 }
{
"rfc": 9293,
"normative": [
{ "rfcNumber": 791, "title": "Internet Protocol", "anchor": "RFC0791" },
{ "rfcNumber": 1191, "title": "Path MTU discovery", "anchor": "RFC1191" },
{
"rfcNumber": 2119,
"title": "Key words for use in RFCs to Indicate Requirement Levels",
"anchor": "RFC2119"
}
],
"informative": [
{ "rfcNumber": 793, "title": "Transmission Control Protocol", "anchor": "RFC0793" },
{ "rfcNumber": 896, "title": "Congestion Control in IP/TCP Internetworks", "anchor": "RFC0896" }
],
"_source": "xml",
"_referencesSource": "xml"
}_referencesSource tells where the reference list came from: xml (RFCXML <references>), text (the References section of the plain text), or api (Datatracker relateddocument, with placeholder titles).
generate_checklist { "rfc": 9293, "role": "client", "sections": ["3.5", "3.7.1"] } — the markdown field:
# RFC 9293 Implementation Checklist
**Transmission Control Protocol (TCP)**
Role: client
Generated: 2026-09-04T17:09:37.833Z
## Mandatory Requirements (MUST / REQUIRED / SHALL)
- [ ] **MUST** A TCP implementation MUST support simultaneous open attempts (MUST-10). (§3.5)
- [ ] **MUST** TCP endpoints MUST implement both sending and receiving the MSS Option (MUST-14). (§3.7.1)
- [ ] **MUST** If an MSS Option is not received at connection setup, TCP implementations MUST assume a default send MSS of 536 (576 - 40) for IPv4 or 1220 (1280 - 60) for IPv6 (MUST-15). (§3.7.1)
## Recommended Requirements (SHOULD / RECOMMENDED)
- [ ] **SHOULD** TCP implementations SHOULD allow a received RST segment to include data (SHLD-2). (§3.5.3)The same call also returns "stats": { "must": 6, "should": 2, "may": 1, "total": 9 }.
validate_statement { "rfc": 6455, "statement": "The client MUST mask all frames sent to the server." }
{
"rfc": 6455,
"statement": "The client MUST mask all frames sent to the server.",
"analysis": { "detectedLevel": "MUST", "detectedSubject": "client" },
"isValid": true,
"matchingRequirements": [
{
"id": "R-5.3-2",
"level": "MUST",
"text": "When preparing a masked frame, the client MUST pick a fresh masking key from the set of allowed 32-bit values.",
"section": "5.3",
"sectionTitle": "Client-to-Server Masking",
"_matchScore": 17,
"_matchedKeywords": ["client", "mask", "frames"],
"_subjectMatch": true,
"_levelMatch": true
}
],
"conflicts": [],
"_source": "text",
"_sourceNote": "Warning: Parsed from text format. Validation accuracy may be limited."
}isValid: true means only that no contradiction was detected among the matched requirements. When nothing matches strongly enough, isValid is null and _verdictNote explains why.
get_rfc_structure { "rfc": 6455 } — RFC 6455 predates RFCXML v3, so the plain text is parsed:
{
"metadata": {
"title": "The WebSocket Protocol",
"number": 6455,
"date": "2011-12",
"category": "std",
"stream": "IETF",
"abstract": "The WebSocket Protocol enables two-way communication ..."
},
"sections": [
{ "number": "1", "title": "Introduction" },
{ "number": "2", "title": "Conformance Requirements" },
{ "number": "5", "title": "Data Framing" }
],
"referenceCount": { "normative": 18, "informative": 9 },
"_source": "text",
"_sourceNote": "Warning: Parsed from text format. Accuracy may be limited."
}For RFCs 8650 and later, the XML is tried first. If every XML source returns 404 the call fails ("No RFC with that number is published"). If the XML fetch fails for another reason (5xx, timeout), the text is used and _sourceNote says that the XML fetch failed and may be temporary.
By default, fetched RFCs live only in an in-memory LRU cache and are re-fetched after every restart. Set RFCXML_CACHE_DIR to keep them on disk:
$RFCXML_CACHE_DIR/
├── xml/rfc9293.xml # RFCXML (RFC 8650 and later)
└── text/rfc6455.txt # plain text (older RFCs, or XML fetch failures)
The package also ships a rfcxml-prefetch CLI that fills the same layout ahead of time, for offline or CI use:
# Fetch a range into $RFCXML_CACHE_DIR (or ~/.cache/rfcxml-mcp when unset)
npx -y -p @shuji-bonji/rfcxml-mcp rfcxml-prefetch --range 9110-9114
# Individual RFCs, explicit directory, re-download even if cached
npx -y -p @shuji-bonji/rfcxml-mcp rfcxml-prefetch --rfc 6455 --rfc 9293 --cache-dir ./rfc-cache --forceOptions: --range A-B, --rfc N (repeatable), --cache-dir DIR, --concurrency N (default 3), --force. RFCs already on disk (XML or text) are skipped unless --force is given. RFC numbers must be digits only; --rfc 9110abc exits with code 1.
See the examples/ directory for complete checklist samples:
| RFC | Protocol | Source |
|---|---|---|
| RFC 6455 | WebSocket | Text (fallback) |
| RFC 9293 | TCP | RFCXML |
| RFC 7540 | HTTP/2 | Text (fallback) |
Example prompt for Claude:
Generate an implementation checklist for RFC 9293 (TCP).
src/
├── index.ts # MCP server entry point
├── config.ts # Centralized configuration
├── constants.ts # BCP 14 keyword definitions + RFC number limits
├── services/
│ ├── rfc-fetcher.ts # RFC fetching (parallel)
│ ├── rfc-service.ts # RFC parse & cache management
│ ├── rfcxml-parser.ts # RFCXML parser
│ ├── rfc-text-parser.ts # Text fallback parser
│ └── checklist-generator.ts # Checklist generation service
├── tools/
│ ├── definitions.ts # MCP tool definitions
│ └── handlers.ts # Tool handlers (toolHandlers map)
├── types/
│ └── index.ts # Type definitions
└── utils/
├── cache.ts # LRU cache
├── fetch.ts # Parallel fetch utility
├── logger.ts # Logger abstraction
├── requirement-extractor.ts # Shared requirement extraction
├── section.ts # Section search utilities
├── statement-matcher.ts # Weighted statement matching
├── text.ts # Text processing utility
└── validation.ts # Input validation
Sends parallel requests to the two XML sources (RFC Editor, Datatracker) and uses the first successful response. tools.ietf.org was retired in 2021 and is not used. There is no retry; the parallel race is the only redundancy.
┌─────────────────┐
│ fetchRFCXML() │
└────────┬────────┘
│ Parallel requests
┌────┴─────────┐
▼ ▼
┌────────┐ ┌────────┐
│RFC │ │Data- │
│Editor │ │tracker │
└────┬───┘ └────┬───┘
│ │
└──────┬──────┘
│ Promise.any (first success)
▼
┌───────────┐
│ Successful│ → Cancel other requests via AbortController
│ Response │
└───────────┘
Concurrent calls for the same RFC (e.g. get_rfc_structure and get_requirements issued in parallel) share one in-flight fetch and one parse.
LRU (Least Recently Used) cache with memory limits, plus the optional disk cache (RFCXML_CACHE_DIR) below the XML / text caches:
| Cache | Max Entries | Content |
|---|---|---|
| XML Cache | 20 | Raw RFCXML |
| Text Cache | 20 | Raw text |
| Metadata Cache | 100 | RFC metadata |
| Parse Cache | 50 | Parsed structure |
# Install dependencies
npm install
# Development mode
npm run dev
# Build
npm run build
# Unit tests (single run) / watch mode
npm test
npm run test:watch
# E2E test (MCP client integration; fetches a few real RFCs)
npm run test:e2e
# Audit against real RFCs, tool-to-tool crosscheck, output snapshots
# (see tests/audit/README.md; run weekly by .github/workflows/audit.yml)
npm run audit
npm run crosscheck
npm run snapshot
# Lint
npm run lint
# Format
npm run formatMIT
- mjpitz/mcp-rfc - Text-based RFC MCP
- ietf-tools/RFCXML - RFCXML schema
- xml2rfc - IETF official tool