Quick answer
How do I add a web page reader to Claude Desktop or Cursor with MCP?
Run a small MCP server that exposes a read_url tool calling WebReaderAPI, then register it in your client's MCP settings with your key in env. The model can then read any public page as clean Markdown. Each page read costs 1 credit, 2 with a browser, from $0.07/1K pages as of October 2026. No KYC; pay in crypto.
The MCP server at a glance
| Language | Python 3.10+, with the mcp and httpx packages |
|---|---|
| Transport | stdio: the client starts the server as a local process |
| Tool | read_url(url, max_chars) → title, source URL, then the page's Markdown |
| Why not web_fetch | Claude Code and OpenClaw already ship a built-in tool with that name |
| Key | Read from WEBREADER_API_KEY in the server's environment |
| Billing | 1 credit = 1 page read without a browser; 2 credits when the page needs a browser to render JavaScript. A request that fails, or that a site's robots.txt refuses, is not charged |
Why a read_url tool and not web_fetch?
Because two tools with the same name confuse both the client and the model. Claude Code and OpenClaw already include a tool called web_fetch. A distinct name, read_url, and a clear description let the model pick ours when it needs the main content of a page, JavaScript pages and PDFs included.
The tool returns text, not JSON. Models handle a short header and Markdown better than nested fields, and the header keeps the source URL in view so the answer can cite it.
import os
import httpx
from mcp.server.fastmcp import FastMCP
API = "https://webreaderapi.com/api/v1/read"
KEY = os.environ["WEBREADER_API_KEY"]
mcp = FastMCP("webreaderapi")
@mcp.tool()
def read_url(url: str, max_chars: int = 20000) -> str:
"""Read a public web page and return its main content as Markdown.
Use it when the user gives a link or when a page must be read to answer.
max_chars caps the length of the returned text (1,000 to 500,000).
"""
params = {"url": url, "max_chars": max(1000, min(int(max_chars), 500000))}
try:
resp = httpx.get(API, params=params, headers={"Authorization": "Bearer " + KEY}, timeout=75)
except httpx.HTTPError as exc:
return "Network error while reading the page: " + str(exc)
body = resp.json()
if resp.status_code != 200:
err = body.get("error", {})
return "Could not read the page: " + err.get("code", str(resp.status_code)) + ". " + err.get("message", "")
title = body.get("title") or url
return "# " + title + "\nSource: " + body.get("final_url", url) + "\n\n" + body.get("markdown", "")
if __name__ == "__main__":
mcp.run()
How do I install and test the server?
Create a virtual environment, install mcp and httpx, and save the file above. The mcp package is the official Python SDK of the Model Context Protocol; FastMCP turns a typed Python function into a tool, using the docstring as its description.
Before wiring it into a client, run the file once from a terminal with your key set. It waits silently for a client on stdin, which means it started correctly; stop it with Ctrl+C.
python -m venv .venv
. .venv/bin/activate
pip install "mcp[cli]" httpx
WEBREADER_API_KEY="your-key" python webreader_mcp.pyHow do I register it in Claude Desktop?
Open Claude Desktop's developer settings, edit claude_desktop_config.json, and add an entry under mcpServers. Use absolute paths: the Python of your virtual environment and the server file. Put the key in env, so it never appears in the tool's arguments or in the conversation. Restart Claude Desktop, and read_url appears in the tools list.
Cursor reads the same mcpServers structure from its own MCP configuration file, and Claude Code can register the same command with its claude mcp add command. For OpenClaw, see the OpenClaw page.
{
"mcpServers": {
"webreaderapi": {
"command": "/home/you/webreader/.venv/bin/python",
"args": ["/home/you/webreader/webreader_mcp.py"],
"env": { "WEBREADER_API_KEY": "your-key" }
}
}
}How do I keep pages within the model's context?
Lower max_chars. The default of 20,000 characters is enough for most articles and keeps a long page from filling the context. A model reading several pages in one answer does better with 8,000 to 12,000 per page.
Ask the model to read the pages it needs, not every link it sees. A short sentence in your project instructions, such as "read at most three pages per question", keeps both cost and context in check.
What happens when a site refuses the read?
The tool returns a short message with the error code, such as blocked_by_robots or target_refused, and nothing is billed. We respect robots.txt: our crawler identifies itself as WebReaderAPI, follows the rules written for it or for all robots, and does not read a page that a site disallows. Refused pages are not charged. If a site refuses our request, we return its answer: we never solve CAPTCHAs, log in to accounts, or try to get around a block or a paywall. The model can then say the page is not available to automated readers and use another source.
MCP server: common questions
Is there an official WebReaderAPI MCP package?
No. The file on this page is the integration: 40 lines you can read, change and run yourself. Nothing to install from us, and your key never leaves your machine except in calls to our API.
Can the model search the web with this server?
No. read_url reads a page you or the model already have the address of. Pair it with any search tool your client offers; the model searches, then reads the pages that matter.
How much does an assistant cost to run with it?
One credit per page read, two when a browser is needed. An assistant that reads 50 pages a day uses about 1,500 credits a month, and 10,000 pages ($3.75) covers that for about six months.
Does the server work offline?
No. It calls https://webreaderapi.com/api/v1/read for every page, so it needs internet access and a valid key.