Any URL to clean, LLM-ready Markdown for RAG. Strips ads, nav, and boilerplate.
Runs on your machine from its package; the steps are below.
At most: only reads or looks up information.
Odel charges nothing per call. The publisher may charge for its own service.
Runs locally over stdio
This server isn't hosted — your MCP client launches it from a package registry. Use one of the commands below, or drop the config into your client (e.g. Claude Desktop).
Run
npx -y clean-markdown-mcp
MCP client config
{
"mcpServers": {
"clean-markdown": {
"command": "npx",
"args": [
"-y",
"clean-markdown-mcp"
]
}
}
}An MCP server that turns any URL into clean, LLM-ready Markdown — no ads, no nav bars, no cookie banners, no scripts.
Built for RAG pipelines and AI agents, where junk in the input means junk in the output.
Give it: https://en.wikipedia.org/wiki/Markdown
Get back: # Markdown
Markdown is a lightweight markup language for creating
formatted text using a plain-text editor...
npm install -g clean-markdown-mcp
Add this to your claude_desktop_config.json:
~/Library/Application Support/Claude/claude_desktop_config.json%APPDATA%\Claude\claude_desktop_config.json{
"mcpServers": {
"clean-markdown": {
"command": "npx",
"args": ["-y", "clean-markdown-mcp"]
}
}
}
Restart Claude Desktop, then ask it to "scrape https://example.com to Markdown."
Works the same way in Cursor, Windsurf, or any other MCP client — point it at the
clean-markdown-mcp command.
scrape_url
| Parameter | Type | Description |
|---|---|---|
url | string | The page to scrape. Must be http:// or https://. |
includeLinks | boolean | Keep hyperlinks in the Markdown. Default true. Set false for cleaner prose. |
renderJs | boolean | Load the page in a real browser first, for JavaScript-built sites. Default false. Requires Playwright — see below. |
Returns the page title, source, word count, and the clean Markdown.
If a page isn't article-shaped, it falls back to a cleaned <body> rather than
failing, so you still get usable text.
Some sites build their content with JavaScript after loading. A plain fetch returns almost nothing for those. To handle them, install Playwright once:
npm install playwright && npx playwright install chromium
Then pass renderJs: true. The difference on such a page is dramatic:
| Mode | Result |
|---|---|
| Default | ~3 words — just navigation links |
renderJs: true | ~190 words — the full content |
Playwright is not a dependency of this package, so a normal install stays
small and fast. Without it, renderJs returns a clear message telling you how to
enable it.
The scraper refuses private, loopback, and link-local addresses — including cloud
metadata endpoints like 169.254.169.254 — and re-validates every redirect hop.
This matters because an MCP server runs on your machine, with access to your
local network.
This package handles one page at a time, locally. For batch scraping, hosted JavaScript rendering, and a pay-per-page API with no infrastructure to run, the same engine is available as a hosted Actor:
https://apify.com/perforated_hummingbird/url-to-markdown
robots.txt enforcement — you are responsible for how you use it.npm install
npm run build
node dist/try.js https://example.com # quick test
node dist/try.js https://example.com --render # with JS rendering
MIT