{
  "markdown": "# render-mcp\n\n**Gives an AI agent a real browser.** Screenshots, PDFs, and the HTML a page produces *after*\nJavaScript has run.\n\nNo API key. No signup. No account. Point your client at a URL and it works.\n\n```\nhttps://render.makermargins.com/mcp\n```\n\n---\n\n## Why this exists\n\nAn agent can fetch a URL. It cannot *see* one.\n\nAsk an assistant to check whether a page looks right, or to read a site built with React, and it\nhits a wall: a plain HTTP fetch returns an empty shell and a loading spinner. The content is\nbuilt by JavaScript that never runs.\n\nThe usual answer is a rendering API — but every one of them requires you to sign up, verify an\nemail, and paste an API key. **An agent working on its own can't do any of that.** It has no\ninbox and no card. A free tier it cannot register for is worth nothing to it.\n\nThis server needs none of it. It runs on Cloudflare's network, launches a real headless browser,\nand hands back what the page actually looks like.\n\n## Install\n\n**Claude Code — as a plugin** (one command, and updates arrive automatically)\n\n```\n/plugin marketplace add RodRomer/render-mcp\n/plugin install render@render-mcp\n```\n\n**Claude Code — directly**\n\n```bash\nclaude mcp add --transport http render https://render.makermargins.com/mcp\n```\n\n**Claude Desktop, Cursor, and other clients** — add to your MCP config:\n\n```json\n{\n  \"mcpServers\": {\n    \"render\": {\n      \"type\": \"http\",\n      \"url\": \"https://render.makermargins.com/mcp\"\n    }\n  }\n}\n```\n\nThat's the whole setup. Nothing to install, nothing to configure, no credentials.\n\n## Tools\n\n### `screenshot_url`\n\nSee a page as a person would. Returns a PNG.\n\n| Argument | Type | Notes |\n|---|---|---|\n| `url` | string | **Required.** Absolute, including `https://` |\n| `full_page` | boolean | Capture the whole scrollable page. Default `false` |\n| `width` | number | Viewport width, 320–2560. Default `1280` |\n| `height` | number | Viewport height, 240–2000. Default `800` |\n\nGood for: confirming a deployment looks right, checking a layout, seeing what a user sees.\n\n### `rendered_html`\n\nThe DOM *after* JavaScript has executed. Returns HTML text.\n\n| Argument | Type | Notes |\n|---|---|---|\n| `url` | string | **Required.** Absolute, including `https://` |\n| `wait_for` | string | CSS selector to wait for, if content loads late |\n| `max_chars` | number | Truncation limit, 1,000–500,000. Default `100000` |\n\nGood for: single-page apps, anything where a plain fetch returns a shell.\n\n### `page_diagnostics`\n\nLoad a page and report what went wrong: JavaScript console errors, failed network requests, and\nany 4xx/5xx responses. Returns a readable summary.\n\n| Argument | Type | Notes |\n|---|---|---|\n| `url` | string | **Required.** Absolute, including `https://` |\n| `include_warnings` | boolean | Include warnings and info, not just errors. Default `false` |\n| `width` | number | Viewport width, 320–2560. Default `1280` |\n| `height` | number | Viewport height, 240–2000. Default `800` |\n\nGood for: a deployment that might have shipped a bug, a page that loads blank, a site that\n\"looks broken\" and you need to know why.\n\n**Stated honestly:** this is not a rare capability. Microsoft's `@playwright/mcp` returns console\nmessages, and Google's `chrome-devtools-mcp` returns them with source-mapped stack traces across\n29 tools. Both are excellent, both are better resourced than this, and between them they are\ndownloaded around **34 million times a month**. If you can run a local process, use one of them.\n\nThe one thing neither can do is run where nothing can be installed. They need `npx`, Node, and\nbrowser binaries, or a local Chrome. This needs a URL. That is the whole of the difference, and\nit only matters if you are in that situation.\n\n### `inspect_element`\n\nAnswers *\"why isn't this element showing where I expect?\"* for a CSS selector.\n\n| Argument | Type | Notes |\n|---|---|---|\n| `url` | string | **Required.** Absolute, including `https://` |\n| `selector` | string | **Required.** CSS selector, e.g. `.buy-button` |\n| `max_matches` | number | How many matches to report, 1–10. Default `3` |\n| `width` | number | Viewport width, 320–2560. Default `1280` |\n| `height` | number | Viewport height, 240–2000. Default `800` |\n\nLeads with a diagnosis, then the numbers: resolved box model, computed\n`display` / `visibility` / `opacity` / `position` / `z-index`, colours, whether it's inside the\nviewport, and whether **another element is covering it**.\n\nThe useful part is that it walks *up* the tree. The usual reason an element is missing is not the\nelement — it's an ancestor, and the answer you want is *which* one:\n\n```\n--- match 1: button#buy\n  HIDDEN BY AN ANCESTOR — div.modal.panel has display:none. The element itself is fine.\n```\n\nIt also catches the case no single property reveals. Content inside a closed `<details>` keeps a\nnormal box and reports `display:block`, `visibility:visible`, `opacity:1` — everything looks fine,\nand it still doesn't paint:\n\n```\n  NOT RENDERED — it sits inside details.fees, which is not displaying its\n  contents because of a closed <details>.\n  Box: 70x27 ...  display:block  visibility:visible  opacity:1\n```\n\nGood for: an element that \"should be there\", a click landing on the wrong thing, verifying a CSS\nchange actually applied. **Cascade resolution and layout cannot be derived from reading HTML and\nCSS** — this is the one thing a browser is strictly required for.\n\n### `url_to_pdf`\n\nRender a page to PDF as a browser would print it.\n\n| Argument | Type | Notes |\n|---|---|---|\n| `url` | string | **Required.** Absolute, including `https://` |\n| `landscape` | boolean | Default `false` |\n\nGood for: archiving a page, turning a rendered report into a document.\n\n## What it won't do\n\nStated plainly, so an agent doesn't waste calls discovering them:\n\n- **No private networks.** Loopback, RFC1918 ranges, `169.254.x.x`, `.internal` and `.local`\n  hostnames are refused. This server runs inside Cloudflare's network and an unvalidated URL\n  would be a server-side request forgery.\n- **No logins.** There's no session, so anything behind authentication renders as its login page.\n- **20 second navigation limit.** Very slow pages will time out.\n- **No JavaScript injection.** It renders pages; it doesn't run your code on them.\n\nFailures come back as readable text explaining what went wrong, not as protocol errors — so an\nagent can route around them rather than crashing.\n\n## Privacy\n\n**URLs and page content are never stored or logged.** Each call launches a browser, does the\nwork, hands back the result, and closes it. Nothing about *what* you asked for is retained.\n\nOne thing is counted, and it's worth stating precisely rather than hiding behind \"anonymised\":\n\n| Recorded | Not recorded |\n|---|---|\n| Which tool ran (`screenshot_url`, …) | The URL, or any part of it |\n| How it ended (`ok`, `timeout`, `capacity`, …) | Your IP address |\n| How long it took, in milliseconds | Any header, cookie or credential |\n| | Any page content, image or PDF |\n\nThat's three fields with no way to tie them to a request, a person or a site. The function that\nwrites them is never handed the URL in the first place, so it cannot record one by accident —\nsee `count()` in [`src/index.js`](src/index.js).\n\nIt exists for one reason: this server is free, and the only way to decide whether it's worth\nkeeping alive is knowing whether anything calls it.\n\nThe counts are public — no login, no dashboard:\n\n```\nhttps://render.makermargins.com/stats\n```\n\n## Development\n\n```bash\nnpm install\nnpm test          # 279 tests, no network or browser needed\nnpm run test:dom  # 39 DOM tests, headless Edge/Chrome, no network\nnpm run dev       # local worker\nnpm run deploy    # to Cloudflare\n```\n\nTwo layers, both tested outside their host:\n\n- **`src/protocol.js`** — the MCP request/response surface as pure functions, with no Cloudflare\n  or browser dependency. Every URL validation and SSRF rule is proven under plain Node before\n  anything deploys.\n- **`src/inspect-page.js`** — the half of `inspect_element` that runs *inside* the page. It closes\n  over nothing, so any real DOM can execute it. Its tests build fixture pages in headless Edge\n  and assert on real computed styles and real geometry. A mocked DOM would prove nothing here:\n  the entire premise of the tool is that these values only exist once a browser has resolved the\n  cascade and run layout.\n\n`src/index.js` is a thin shell — transport in, browser work out, prose formatting on the way back.\n\nIf Node isn't installed, `npm run test:nonode` runs the protocol suite in headless Edge instead.\nIt strips only the `export`/`import` keywords and executes the same source.\n\n### When testing against a live page, use a neutral URL\n\nUse `https://example.com` — it is reserved by IANA for exactly this — or `httpstat.us` for error\npaths. **Do not point live tests at `makermargins.com`.**\n\nThe reason is not politeness. That site has Cloudflare Web Analytics, whose beacon is injected\ninto HTML responses for browser-like requests. This server drives a real headless browser, so\nevery screenshot or audit of that site **executes the beacon and registers as a visitor** —\ninflating the traffic figures of the very asset the numbers are meant to measure. An instrument\nthat counts its own operator measures nothing.\n\n## Status\n\nEarly and free. Built to find out whether MCP registry discovery actually works. If it gets\nused, it'll be maintained; if it doesn't, that's a useful answer too.\n\nIssues and pull requests welcome.\n\n## How this was built\n\nWritten by Claude, directed by a human, and stated here rather than left to be inferred.\n\nThat is worth knowing when judging it, so the relevant facts are these: **318 tests** cover the\nprotocol surface, the routing table, the usage counter, the landing page and the plugin manifests,\nand the parts that need a real browser are tested against real fixture pages rather than a mock\nDOM. Every platform claim in this README was checked against a primary source, and several\nwidely-repeated ones turned out to be wrong.\n\nNone of that makes it good on its own — but it is checkable, which is more useful than a promise.\n\n## Licence\n\nMIT\n",
  "bytes": 10141,
  "sha": "85c2333e233b6fddf55c814dfaa823ed330273b09f0a0000209383a874dea09e",
  "repo_slug": "rodromer/render-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_rodromer_render_mcp_e7888758/readme"
}