inspect_url
Inspect URL (free)
Free, lightweight check of a public URL: reachability, HTTP status, final URL after redirects, content type, page title, and whether extraction is likely to work. Downloads at most a small prefix of the page.
url必填
io.github.omarhassanatef由 GitHub 帳號 omarhassanatef 發布v0.2.0更新於 Oct 27, 2026
Web pages to clean Markdown for AI agents. 5 free calls/day, then pay per call via x402 on Base.
遠端 MCP 伺服器,抓取公開網頁並回傳乾淨的 Markdown 或結構化內容,提供免費 URL 檢查與按次 USDC 微支付。
Web executable,透過 Streamable HTTP。 遠端服務在工作區中設定後即可從網頁執行環境執行。
把這段提示詞貼上到 Claude Code、Codex、Cursor 等代理程式,或 SourceWeft 對話中。代理程式會讀取該伺服器的安裝指南,先向你展示發布者、端點和登入方式,經你同意後再把它新增到你的客戶端。金鑰不會寫進任何命令。
閱讀 https://sourceweft.com/mcp/io.github.omarhassanatef%2Fagentread/install.md,並按其中說明把這個 MCP 伺服器接入我的客戶端。修改任何設定之前,先告訴我它的發布者、端點和登入方式,等我確認。不要把 API 金鑰寫進命令或我會提交的檔案;問我金鑰應從哪裡取得。把它新增到你客戶端的 mcpServers 設定中。
{
"mcpServers": {
"agentread": {
"type": "http",
"url": "https://agentread.omar-cv.workers.dev/mcp"
}
}
}Clean web content for AI agents, paid per request.
AgentRead is a Cloudflare Worker that turns public web pages into clean, structured Markdown for LLMs and autonomous agents. It is exposed as both an HTTP API and a remote MCP server. Paid operations are charged per successful request with x402 micropayments (USDC on Base). There are no accounts, API keys or subscriptions.
Free allowance: the first 5 successful extractions per day per client are free; just call without payment. Only successful extractions count, and the x-free-tier-remaining response header (MCP: _meta["agentread/free-tier"]) shows what's left. A global cap of 1,000 free calls per day protects the service. Prices and allowances are configuration, not code (see Configuration).
Service: https://agentread.omar-cv.workers.dev · MCP: https://agentread.omar-cv.workers.dev/mcp · Listed in the official MCP Registry as io.github.omarhassanatef/agentread and on x402scan
Try the free inspection:
Add it to an MCP client (inspect_url works in any client; the paid tools need an x402-capable client such as @x402/mcp):
Pay per call from code with an x402 client: see Paying with an x402 client and MCP. Agent-readable docs: /llms.txt, /openapi.json, /.well-known/x402.
Status: MVP, live.
- Production (Base mainnet, real USDC): https://agentread.omar-cv.workers.dev. Payments settle via the CDP facilitator to
0xb866dD8C8401438Efb29b538ec4df11b6b407Ce6.- Testnet: no hosted testnet.
npm run devruns locally against Base Sepolia and the free x402.org facilitator.What has been verified:
- Automated tests: all pass (237 tests, run inside the Workers runtime).
- Directory checks: both paid endpoints pass the CDP Bazaar validator (all preflight checks) and the AgentCash/x402scan discovery audit, and are registered on x402scan.
- Local smoke test (9/9): run against
wrangler devusing the live x402.org facilitator. A genuinely signed payment from an unfunded wallet passes signature and requirement checks and is rejected forinsufficient_balance, so the live wire path is proven up to the transfer itself.- Production deployment: passes all smoke checks from GitHub Actions (
gh workflow run CI). The live CDP facilitator authenticates AgentRead and verifies signed payments. The development network blocks theworkers.devIP range, hence the CI vantage point.- Runs on the Workers Free plan. The deployed
liteengine had no CPU-limit kills in about 180 benchmark requests on Cloudflare's edge (Free account); thereadabilityengine was killed in up to 84% of them. See Extraction engines.- Not yet exercised on the deployed edge: a paid extraction through the product Worker. The same extraction code was benchmarked on the edge, but no funded payment has gone through.
A funded payment has not yet been made (see Testnet payment demo). Read Limitations before relying on it.
Raw HTML is mostly noise: navigation, cookie banners, ads, scripts, styling. An agent that fetches pages itself wastes tokens and has to maintain HTML cleaning, SSRF protection, encoding detection, redirect handling and size limits. AgentRead does that once and returns predictable, versioned JSON.
x402 lets an agent pay a fraction of a cent per call from its own wallet. There is no sign-up and no stored credentials. Failed extractions are not charged.
That is a hypothesis to validate, not a proven market. The service logs the numbers needed to test it (see Observability).
@x402/hono for HTTP, @x402/mcp for MCP, and @x402/evm for the exact EVM scheme. No x402 protocol logic is reimplemented here.More detail: docs/architecture.md · docs/payments.md · docs/security.md · docs/demo.md
Machine-readable docs: GET / (discovery: operations, prices, limits) and GET /openapi.json (OpenAPI 3.1, generated from the zod schemas the service validates with).
All errors use one shape:
GET /healthPOST /v1/inspect: freeChecks reachability and extractability before you pay. It downloads at most 64 KB and builds no DOM. Upstream problems (timeouts, 404s, non-HTML) are reported in the 200 body as reachable: false or a limitation. Only invalid or blocked URLs return 4xx.
POST /v1/extract: paidRequest body (JSON, max 8 KB; unknown fields are rejected):
Without payment, the response is HTTP 402. The PAYMENT-REQUIRED header carries the base64 x402 PaymentRequired object; the JSON body is a human/agent-readable summary:
Decoded PAYMENT-REQUIRED (accepts[0]): {"scheme":"exact","network":"eip155:84532","amount":"3000","asset":"0x036CbD53842c5426634e7929541eC2318f3dCF7e","payTo":"<your address>","maxTimeoutSeconds":120,...}. 3000 is $0.003 in 6-decimal USDC.
With a valid PAYMENT-SIGNATURE header, the response is 200 with a PAYMENT-RESPONSE header containing the settlement receipt:
Real output for https://example.com/ (captured Oct 2026 by running the operation code directly):
extraction.method is one of readability, main-content-fallback, body-fallback, plain-text.
POST /v1/extract/structured: paidSame request body as /v1/extract. Returns metadata, Markdown, headings and links separately:
Missing metadata is null; nothing is guessed. includeLinks affects only the Markdown; links is always returned (max 200, deduplicated, absolute http(s) only).
The official @x402/fetch wrapper handles the 402 → sign → retry loop. This is scripts/testnet-smoke.mjs, trimmed:
Endpoint: POST /mcp, MCP Streamable HTTP, stateless (no sessions, JSON responses). GET/DELETE return 405, as the transport spec allows for servers without a server-initiated stream.
Payment over MCP follows the @x402/mcp transport convention, not HTTP headers:
isError: true. Its structuredContent is the x402 PaymentRequired object, also JSON-encoded in content[0].text.params._meta["x402/payment"]._meta["x402/payment-response"].Invalid input and blocked URLs are rejected before a payment challenge. Tool results that fail (isError) are not settled.
Compatibility note: paid tools intentionally do not declare an outputSchema. MCP SDK clients validate structuredContent against outputSchema even on error results. That would reject the x402 payment challenge, which we observed with the official TypeScript client. Their output shape is described in the tool description and in /openapi.json. inspect_url does declare an outputSchema.
Connect and pay with the official client (scripts/mcp-client-example.mjs):
Paid MCP tools also carry x402 Bazaar discovery metadata (input and output schemas) in their payment challenge, so Bazaar-enabled facilitators can catalogue them. HTTP routes do not: @x402/hono 2.28 validates Bazaar route metadata with ajv, which compiles code at runtime. Workers forbids that, so it would log errors on every request. HTTP discovery is GET / + /openapi.json.
Generic MCP clients (IDEs, desktop apps) can connect to /mcp and use inspect_url. Paid tools work only with clients that implement the x402 MCP payment flow, such as @x402/mcp. Other clients see the payment challenge as a tool error. Universal agent-framework compatibility is not claimed.
Requirements: Node.js ≥ 20 and npm.
Non-secret configuration lives in wrangler.jsonc (vars, per environment). Secrets go in wrangler secret put (deployed) or .dev.vars (local). Client-script settings are documented in .env.example.
Invalid payment configuration fails closed. Paid endpoints return 503, the reason is logged (payments.misconfigured), and free endpoints keep working. Rate limits use Workers Rate Limiting bindings (INSPECT_RATE_LIMITER 30/min and PAID_RATE_LIMITER 120/min per client IP per Cloudflare location). Adjust them in wrangler.jsonc.
All tests run inside workerd (the Workers runtime) via @cloudflare/vitest-plugin. They never touch the internet, a blockchain, or a real facilitator.
What is mocked, and what is real. Payment tests use the real x402 SDKs end to end:
@x402/fetch and @x402/mcp with genuine EIP-712 signatures from throwaway wallets.@x402/hono and @x402/mcp.x402Facilitator with the @x402/evm exact scheme, so signature, amount, recipient, expiry and nonce checks are SDK code.Only the blockchain is simulated (tests/helpers/facilitator.ts), along with upstream web pages (tests/helpers/upstream.ts). Live settlement on a real network is not covered by npm test. Use npm run smoke:testnet for that.
Prerequisites:
EXTRACTION_ENGINE=lite, the configured default. Switch to readability only on Workers Paid ($5/month); see Extraction engines.npx wrangler login.Logs: npx wrangler tail --env production or Workers Observability in the dashboard (enabled in wrangler.jsonc).
Add the domain to your Cloudflare account, then add "routes": [{ "pattern": "read.example.com", "custom_domain": true }] under env.production and redeploy. Payment requirements use the request URL as the x402 resource, so no other change is needed.
Testnet runs locally with npm run dev (Base Sepolia, x402.org facilitator). Two testnet wallets were generated: the receiver 0xDE20133E787A3cc64565b7AF1cF2bE3bEA3298Ea and a payer 0x7816FC23017c86aa917AdbD44367312696421322 was generated too. Both private keys are in .secrets/testnet-wallets.json, which is gitignored and owner-only. Testnet only: never send mainnet funds to these addresses.
Structured JSON logs (console.log), with no secrets, payment payloads, signatures or page content:
These answer: request volume, success rate, failure reasons, latency, unique and repeat payers (payer hash), and verify/settle failures. Query them in Workers Observability.
The same API, schemas and payment flow run on either engine (EXTRACTION_ENGINE):
The measurements come from the temporary src/bench.ts Worker driven by .github/workflows/bench.yml, with CPU percentiles from Cloudflare's GraphQL analytics (Oct 2026). Free-plan CPU enforcement isn't a simple per-request 10 ms cutoff: lite's measured CPU exceeds 10 ms but it was never killed. Enforcement is opaque and could change, so re-run the benchmark if you see 1102 errors. Once revenue justifies $5/month, switch to Workers Paid and EXTRACTION_ENGINE=readability.
Default prices: $0.003 (extract) and $0.005 (structured). Before changing them, account for these costs (Oct 2026, verify current prices):
lite engine: 100k requests/day, $0.readability) includes 10M requests and 30M CPU-ms, then $0.30 per million requests and $0.02 per million CPU-ms. At about 80 CPU-ms per Readability extraction, compute is about $0.000002 per request after the allowance.toMarkdown: documented as free for HTML. AgentRead never enables image conversion, which would use paid AI models.exact EIP-3009 flow the payer only signs an authorization. The facilitator submits the settlement transaction, so the payer needs no ETH. CDP states that gas is paid on-chain separately from its fee. Check the facilitator's current terms for whether it passes gas costs on.toMarkdown staying free for HTML.javascript_required (422, not charged). inspect_url flags likely cases with mayRequireJavaScript.text/plain and text/markdown only. PDFs, images and JSON are rejected with unsupported_content_type.exact scheme are configured.npm run smoke:testnet (see above).來源:README.md,提交 a08a887
Inspect URL (free)
Free, lightweight check of a public URL: reachability, HTTP status, final URL after redirects, content type, page title, and whether extraction is likely to work. Downloads at most a small prefix of the page.
url必填Extract URL (clean Markdown)
Fetch a public web page and return its main content as clean Markdown with title, description, headings and extraction metadata. Removes navigation, ads, cookie banners and scripts. Does not execute JavaScript. Paid via x402 ($0.003 per successful call; the first 5 successful calls per day are free). Returns JSON (structuredContent + text) with schemaVersion, operation, requestedUrl, finalUrl, title, description, markdown, headings[{level,text}], truncated, extraction{method,statusCode,contentType,charset,redirects,bytesFetched,characters,totalCharacters,words,warnings,durationMs}.
url必填includeLinksmaxCharactersExtract URL (structured)
Fetch a public web page and return structured data: page metadata (title, description, canonical URL, language, site name, byline), main content Markdown, ordered headings with levels, and links (text + URL). Deterministic HTML processing, no LLM. Paid via x402 ($0.005 per successful call; the first 5 successful calls per day are free). Returns JSON with schemaVersion, operation, requestedUrl, finalUrl, metadata{title,description,canonicalUrl,language,siteName,byline,publishedTime}, markdown, headings, headingsTruncated, links[{text,url}], linksTruncated, truncated, extraction{...}.
url必填includeLinksmaxCharacters已滿足適用檢查項的 100%
該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。
curl -s -X POST https://agentread.omar-cv.workers.dev/v1/inspect \ -H 'content-type: application/json' -d '{"url":"https://example.com/"}'claude mcp add --transport http agentread https://agentread.omar-cv.workers.dev/mcp{ "mcpServers": { "agentread": { "url": "https://agentread.omar-cv.workers.dev/mcp" } } }AI agent ──HTTP (x402 headers)──┐ ┌── POST /v1/inspect ──────────────┐ ▼ │ │ Cloudflare Worker (Hono) ▼AI agent ──MCP Streamable HTTP──► /mcp ────┤ validate input + URL policy + DNS guard (x402 in _meta) │ (rejects bad input BEFORE any 402) │ │ ├── POST /v1/extract[/structured] │ │ └─ @x402/hono paymentMiddleware ──► facilitator /verify │ │ └── MCP paid tools ▼ └─ @x402/mcp wrapper shared operations (inspectUrl / extractUrl / extractUrlStructured) │ bounded fetch (manual redirects, re-validated each hop, timeout, byte cap) → linkedom → boilerplate removal → Readability → Turndown │ handler OK (2xx) → facilitator /settle → result + PAYMENT-RESPONSE handler failed → settlement cancelled → error (not charged){ "error": { "code": "blocked_destination", "message": "Internal, reserved, or loopback hostnames are not allowed.", "details": {} } }curl -s http://localhost:8787/health# {"status":"ok","service":"agentread","version":"0.1.0","environment":"development",# "payments":{"enabled":true,"network":"eip155:84532","networkName":"Base Sepolia"}}curl -s -X POST http://localhost:8787/v1/inspect \ -H 'content-type: application/json' \ -d '{"url":"https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/402"}'{ "schemaVersion": "1.0", "operation": "inspect_url", "requestedUrl": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/402", "normalizedUrl": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/402", "hostname": "developer.mozilla.org", "reachable": true, "statusCode": 200, "finalUrl": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/402", "redirects": 1, "contentType": "text/html", "contentLength": 209180, "isHtml": true, "title": "402 Payment Required - HTTP | MDN", "mayRequireJavaScript": false, "extractionSupported": true, "limitation": null}curl -si -X POST http://localhost:8787/v1/extract \ -H 'content-type: application/json' -d '{"url":"https://example.com/"}'# HTTP/1.1 402 Payment Required# payment-required: eyJ4NDAyVmVyc2lvbiI6Miwi...# {"error":{"code":"payment_required","message":"This operation costs $0.003 (USDC on Base Sepolia). ...",# "details":{"operation":"extract_url","price":"$0.003","network":"eip155:84532","x402Version":2}}}{ "schemaVersion": "1.0", "operation": "extract_url", "requestedUrl": "https://example.com/", "finalUrl": "https://example.com/", "title": "Example Domain", "description": null, "markdown": "This domain is for use in documentation examples without needing permission. This is not a service; avoid relying on it for testing and monitoring purposes.", "headings": [], "truncated": false, "extraction": { "method": "readability", "statusCode": 200, "contentType": "text/html", "charset": "utf-8", "redirects": 0, "bytesFetched": 577, "characters": 156, "totalCharacters": 156, "words": 25, "warnings": [], "durationMs": 854 }}{ "schemaVersion": "1.0", "operation": "extract_url_structured", "requestedUrl": "...", "finalUrl": "...", "metadata": { "title": "...", "description": "...", "canonicalUrl": "...", "language": "en-GB", "siteName": "...", "byline": "...", "publishedTime": "2026-03-14T09:00:00Z" }, "markdown": "...", "headings": [{ "level": 2, "text": "Zones of the intertidal" }], "headingsTruncated": false, "links": [{ "text": "official tide tables", "url": "https://example.org/tide-tables" }], "linksTruncated": false, "truncated": false, "extraction": { "...": "same as extract_url" }}import { wrapFetchWithPayment, x402Client, x402HTTPClient } from "@x402/fetch";import { ExactEvmScheme } from "@x402/evm/exact/client";import { toClientEvmSigner } from "@x402/evm";import { privateKeyToAccount } from "viem/accounts";
const account = privateKeyToAccount(process.env.EVM_PRIVATE_KEY); // TESTNET-ONLY walletconst client = new x402Client().register("eip155:84532", new ExactEvmScheme(toClientEvmSigner(account)));client.registerPolicy((_v, reqs) => reqs.filter((r) => BigInt(r.amount) <= 10_000n)); // never pay > $0.01
const payFetch = wrapFetchWithPayment(fetch, client);const res = await payFetch("http://localhost:8787/v1/extract", { // `npm run dev` (Base Sepolia) method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ url: "https://example.com/" }),});const result = await res.json();const receipt = new x402HTTPClient(client).getPaymentSettleResponse((n) => res.headers.get(n));console.log(result.markdown, receipt.transaction);import { Client } from "@modelcontextprotocol/sdk/client/index.js";import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";import { wrapMCPClientWithPayment } from "@x402/mcp";import { x402Client } from "@x402/core/client";import { ExactEvmScheme } from "@x402/evm/exact/client";import { toClientEvmSigner } from "@x402/evm";import { privateKeyToAccount } from "viem/accounts";
const account = privateKeyToAccount(process.env.EVM_PRIVATE_KEY);const payments = new x402Client().register("eip155:84532", new ExactEvmScheme(toClientEvmSigner(account)));const mcp = wrapMCPClientWithPayment(new Client({ name: "my-agent", version: "1.0.0" }), payments, { autoPayment: true, onPaymentRequested: async ({ paymentRequired }) => BigInt(paymentRequired.accepts[0].amount) <= 10_000n,});await mcp.connect(new StreamableHTTPClientTransport(new URL("http://localhost:8787/mcp")));
console.log((await mcp.listTools()).tools.map((t) => t.name)); // inspect_url, extract_url, extract_url_structuredconst r = await mcp.callTool("extract_url", { url: "https://example.com/" });console.log(r.paymentMade, r.paymentResponse?.transaction, r.content[0].text);npm install # see Troubleshooting if npm 10 fails with "edgesOut"cp .dev.vars.example .dev.vars # set X402_PAY_TO to your testnet receiving addressnpm run dev # wrangler dev on http://localhost:8787 (Base Sepolia, x402.org facilitator)npm run smoke:local # health, inspect, 402, SSRF rejection, MCP discovery (no payment)npm test# 1. Create a CDP API key: https://portal.cdp.coinbase.com (needed for the CDP facilitator)npx wrangler secret put CDP_API_KEY_ID --env productionnpx wrangler secret put CDP_API_KEY_SECRET --env production# 2. Set your mainnet receiving address in wrangler.jsonc → env.production.vars.X402_PAY_TO# (an address you control on Base; consider a dedicated wallet)# 3. Deploynpm run deploy:production # → https://agentread.<your-subdomain>.workers.dev# 4. Verify health shows {"network":"eip155:8453"} and an unpaid request returns 402 with amount "3000"echo "X402_PAY_TO=0xDE20133E787A3cc64565b7AF1cF2bE3bEA3298Ea" > .dev.varsnpm run dev # in one terminalexport EVM_PRIVATE_KEY=$(node -p 'require("./.secrets/testnet-wallets.json").payer.privateKey')npm run smoke:testnet && npm run smoke:mcp # in another (AGENTREAD_URL defaults to localhost:8787)src/ index.ts Worker entry api/ Hono app, per-request context, discovery, OpenAPI mcp/server.ts Stateless MCP server + x402 payment wrapper payments/x402.ts Facilitator client, resource server, route pricing, logging hooks extraction/ Bounded fetch, HTML cleaning, Readability, Markdown, operations security/ URL policy, IP classification, DoH guard schemas/ Versioned zod schemas (shared by HTTP, MCP, OpenAPI, tests) config/env.ts Config loading/validation (fails closed) utils/ Errors, structured loggingtests/{unit,integration,helpers,fixtures}scripts/ Smoke tests and example clients (Node)docs/ Architecture, payments, security, demo script