readability-mcp
The readability-mcp server transforms already-rendered (post-JavaScript) HTML into clean, LLM-friendly Markdown and metadata without making any outbound network requests. It provides three tools:
extract: Feeds full rendered HTML through Mozilla Readability to isolate the main article, then converts it to Markdown, HTML, plain text, or JSON. Supports:Extraction aggressiveness modes:
balanced,aggressive, orconservativeCSS selectors to include specific subtrees (e.g.
"main") or exclude boilerplate (navbars, footers)Markdown options: GFM, ATX vs. setext headings, fenced vs. indented code blocks, image handling (
keep,drop,src-only,reference)Optional YAML/JSON frontmatter metadata (title, author, date, etc.) resolved via JSON-LD → OpenGraph → Twitter →
<meta>cascadeHTML sanitization via DOMPurify (removes scripts, iframes)
Safe truncation at block boundaries (
maxChars)Relative link absolutization via an optional
urlparameter (no fetching)Structured diagnostics: readability score, fallback usage, removed node counts, truncation status
html_to_markdown: Converts arbitrary HTML snippets directly to Markdown without Readability's article-scoring step — ideal for pre-isolated content. Shares most options withextract.outline: Quickly extracts the heading hierarchy (h1–h6) with anchor IDs as a lightweight pre-check before committing to full extraction.
Click on "Install 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., "@readability-mcpExtract article from raw HTML and format as markdown with metadata"
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.
readability-mcp
Turn already-rendered HTML (captured post-JavaScript from a browser or chrome-devtools MCP) into clean, LLM-friendly Markdown + metadata, using Mozilla Readability, Turndown, and DOMPurify.
The key idea: rendering and extraction are decoupled. A real browser (chrome-devtools) owns rendering; this server only transforms the HTML it is handed. The server makes no outbound requests — there is no fetch, no SSRF surface. The optional url is origin context only, used to absolutize relative links; it is never fetched.
Install
npm install readability-mcp
# or run on demand:
npx readability-mcpRequires Node >= 22. Build from source:
git clone <repo> && cd readability-mcp
npm install
npm run build # bundles to dist/index.js
node dist/index.js # starts the stdio MCP serverRelated MCP server: readdown-mcp
The chrome-devtools handoff
The motivating flow is two hops — each tool does the one thing it is best at:
// 1. In the chrome-devtools MCP, grab the RENDERED document (post-JS):
mcp__chrome-devtools__evaluate_script({
function: () => document.documentElement.outerHTML,
});
// 2. Hand the returned HTML string to readability-mcp.
// `url` is OPTIONAL context (origin for absolutizing relative links) — never fetched.
mcp__readability__extract({ html: "<that string>", url: pageUrl });This matters most for SPAs and JS-augmented pages, where the initial HTML is an empty <div id="root"> and only the post-JS DOM has the content.
MCP client config
Add to your MCP client config (Claude Code, Claude Desktop, etc.):
{
"mcpServers": {
"readability": {
"command": "npx",
"args": ["-y", "readability-mcp"]
}
}
}Tools
All three tools return MCP structured content (schemaVersion, metadata, diagnostics) validated by a zod outputSchema, plus a human/LLM-readable payload in content[0].text. Nothing throws across the wire — failures become { "isError": true } results. Every input and output field carries a description in the tool's JSON schema, so clients can introspect each option without reading these docs.
extract — primary tool
Extracts the main article from rendered HTML and returns Markdown + metadata + diagnostics.
Option | Default | Description |
| — | Rendered HTML (post-JS), e.g. |
| — | Optional origin. Never fetched; used to absolutize relative links/images. |
|
|
|
|
|
|
|
|
|
| — | Restrict extraction to a subtree: |
| — | Strip boilerplate before Readability: |
| — | Perf/safety cap = Readability |
| — | Semantic alias for Readability |
|
| Tables, strikethrough, task lists. |
|
|
|
|
|
|
|
|
|
|
| Run DOMPurify on the article HTML. |
| — | Truncate the payload at a block boundary — never inside a fenced code block. |
|
| For |
|
| Retain all classes (default strips non-language classes). |
| — | Escape hatch — passed verbatim to |
Fallback. If Readability's parse() returns no article (e.g. an app shell or image-only page), a selector cascade salvages the first usable root — article → main → [role=main] → largest text-dense block → body — and reports diagnostics.fallbackUsed: true with extractedNode naming the root that was used.
Metadata cascade. Each metadata field is resolved by priority: JSON-LD → OpenGraph → Twitter → <meta>/<time> → Readability → <title> (first non-empty value wins).
html_to_markdown — fragment path
Converts an arbitrary HTML fragment to Markdown without Readability scoring (e.g. a snippet already isolated via chrome-devtools). Same Turndown + DOMPurify path; reports fallbackUsed: true, extractedNode: "fragment". Shares the format, gfm, headingStyle, codeBlockStyle, images, sanitize, maxChars, wordsPerMinute, selectors, and url options. Metadata is minimal (url, wordCount, readingTimeMin, and a title from the fragment's first heading).
outline — heading pre-check
Returns the document outline (h1–h6 in document order with stable anchor ids) as a cheap "is this worth reading?" / "where's the section about X?" pre-check before paying for full extraction. Runs no Readability, Turndown, or sanitization — a pure heading walk over the normalized DOM.
Option | Default | Description |
| — | Rendered HTML (post-JS), e.g. |
| — | Optional origin. Never fetched; carried through to |
Output shape: structuredContent.outline = [{level, text, anchor}] plus an indented-bullet TOC rendered into content[0].text, and metadata = {title?, url?} (title falls back from <title> to the first <h1>). Anchor precedence: the heading's own id, then a descendant permalink's #fragment, then a slug of the text (deduped -1, -2, … for generated slugs only — author ids are kept verbatim).
Diagnostics
structuredContent.diagnostics exposes: readerable, extractedNode, fallbackUsed, removedNodes (element delta vs. the document), chromeRemoved and imagesResolved (pre-conversion cleanup counts), sanitization.{scripts,iframes} (counted across the whole pipeline), and truncated.
Payload size (stdio)
A full rendered SPA can be several MB as a string, and MCP tool args travel over JSON-RPC on stdio. Mitigations:
Scoped capture (recommended) — real pages are large (hundreds of KB of
outerHTML), so capture only what you need rather than the whole document. Via chrome-devtoolsevaluate_script, grabdocument.head.outerHTML(for metadata) plus a content subtree such asdocument.querySelector('article')?.outerHTML || document.querySelector('main')?.outerHTML, and pass that toextractwithurl.urlabsolutizes relative links within whatever HTML is passed.selectors.include— scope to the article subtree, e.g."main", so only the relevant DOM is scored and serialized.maxChars— cap the returned payload; truncation lands at a block boundary and never splits a fenced code block.maxNodes— a hard cap on elements parsed (Readability.maxElemsToParse) for very large documents.
Development
npm run typecheck # tsc --noEmit
npm run build # vite build -> dist/index.js
npm run lint # eslint
npm test # vitest run
npm run test:update-goldens # UPDATE_GOLDENS=1 vitest runLicense
MIT
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Tools
Latest Blog Posts
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/v1nvn/readability-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server