kitesurf-bridge
Allows agents to drive Cloudflare's Kitesurf browser service, enabling navigation, extraction of Markdown, HTML, text, links, screenshots, and accessibility audits of live web pages.
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., "@kitesurf-bridgeextract the main content from https://example.com as markdown"
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.
kitesurf-bridge
Drive Cloudflare Kitesurf — the agent-first browser that runs in V8 isolates on Cloudflare Workers — from anywhere. Zero dependencies, no local Chrome.
Ships four ways to use one engine:
Surface | Install | Use it for |
MCP server |
| Claude Code, Cursor, Codex, any MCP client |
CLI |
| shells, scripts, CI |
Library |
| your own Node code |
DSH / Cordis plugin | composition row | native tools in a DSH harness |
Install commands below use the GitHub spec, which works today with no registry account. Once published to npm as
@truenix/kitesurf-bridge, everygithub:TrueNix/kitesurf-bridgeshortens to@truenix/kitesurf-bridge.
npx -y github:TrueNix/kitesurf-bridge markdown https://news.ycombinator.comThat renders a real page in a real browser engine, on Cloudflare's network, with no browser installed locally and no API token.
Why this exists
Kitesurf is not open source and cannot run on your machine. Cloudflare says they intend to open source it "once we're ready", and even then the stated goal is for customers to "deploy their own version of Kitesurf on their own accounts" — still on Workers.
There is also no local Kitesurf in the dev loop: wrangler dev launches your local Chrome, not Kitesurf. Kitesurf only exists behind browser=kitesurf on remote endpoints.
So the practical question is not "can I run it locally" but "can I drive it from local code". This package is that bridge.
Install
As an MCP server
claude mcp add kitesurf -- npx -y github:TrueNix/kitesurf-bridge mcp{
"mcpServers": {
"kitesurf": {
"command": "npx",
"args": ["-y", "github:TrueNix/kitesurf-bridge", "mcp"]
}
}
}{
"mcpServers": {
"kitesurf": {
"command": "npx",
"args": ["-y", "github:TrueNix/kitesurf-bridge", "mcp"],
"env": {
"CLOUDFLARE_ACCOUNT_ID": "your-account-id",
"CLOUDFLARE_API_TOKEN": "your-browser-run-token"
}
}
}
}Tools exposed: kitesurf_markdown, kitesurf_text, kitesurf_html, kitesurf_links, kitesurf_screenshot, kitesurf_evaluate, kitesurf_accessibility_tree, kitesurf_probe.
As a DSH / Cordis plugin
# in an agent preset composition
- '@truenix/kitesurf-bridge/cordis':
cli: npx -y github:TrueNix/kitesurf-bridge
timeoutMs: 120000The plugin registers the same tools on the host. It deliberately shells out to the CLI: a dynamic Cordis host half has no WebSocket, fetch or node:* access, so CDP cannot be opened inside the sandbox. See cordis/plugin.mjs.
As a library
npm install github:TrueNix/kitesurf-bridgeimport { withSession } from '@truenix/kitesurf-bridge';
const md = await withSession({}, async (session) => {
await session.navigate('https://example.com');
return session.markdown();
});CLI
kitesurf-bridge <command> [options]
markdown <url> Extract the page as Markdown (main content by default)
text <url> Visible text only
html <url> Full serialized DOM after JS runs
links <url> Every anchor as JSON
screenshot <url> PNG/JPEG (-o file, --full)
pdf <url> PDF (-o file)
a11y <url> Filtered accessibility tree
eval <url> <expr> Evaluate JS in the page
probe Endpoint + engine capability report
mcp Run as an MCP server on stdioUseful options: --main, --raw, --full, --width, --height, --json, --endpoint, --account, --token, --timeout.
Endpoints
Playground (default) | Account | |
URL |
|
|
Auth | none |
|
Target | page | browser (a page is created + attached automatically) |
Suitable for | evaluation | production |
Set CLOUDFLARE_ACCOUNT_ID + CLOUDFLARE_API_TOKEN (or CF_*) to switch. Supplying an account id without a token is a hard error rather than a silent downgrade to the shared playground.
The playground is a free, shared, unauthenticated resource with no SLA. Fine for evaluation and local agent work — do not build production on it.
Things worth knowing about Kitesurf
These are verified against the live service, not copied from docs. kitesurf-bridge probe reproduces them.
Kitesurf does not run V8 for page script — it runs Boa, a Rust JS engine. Boa enforces a much lower recursion limit and throws RuntimeLimit: exceeded maximum number of recursive calls. A natural recursive DOM walk dies on any large page (Wikipedia, docs sites). This package's Markdown converter therefore walks the DOM with an explicit stack, keeping JS call depth at O(1). If you use kitesurf_evaluate, prefer iterative expressions.
Navigation failures arrive as Cloudflare edge status codes, not CDP errors. Page.navigate returns a normal frameId/loaderId even for a nonexistent host, and no Network.loadingFailed fires. A missing domain shows up as HTTP 530, a broken origin as 520, leaving a ~16-character placeholder document. Trusting Page.navigate hands an agent a blank page and calls it success — so this package classifies outcomes from the Network domain and throws when a >=400 status comes with an empty document, while still returning real error pages (with status) that have readable content.
Capability flags (verified):
✅ canvas2d, WebAssembly, shadow DOM, localStorage, cookies, | |
❌ WebGL, ServiceWorker, video/audio playback, real TLS-fingerprint bot-challenge handshakes, long-lived authenticated sessions |
For those, use Browser Run's default Chromium browser instead.
Performance trade (Cloudflare's own figures): Kitesurf uses 3–7× less CPU and memory than warm Chromium, but is 1.7–1.8× slower in wall time. That win is on Cloudflare's bill for bursty cloud agent workloads — it saves nothing on your own hardware. If you just want local browser automation and already have Chrome, local Playwright is faster and does WebGL and video.
Zero dependencies
package.json has an empty dependencies block, including for the WebSocket transport.
Node's global WebSocket (WHATWG) cannot send request headers, and the account endpoint needs Authorization: Bearer …. undici is not importable as a standalone module. So src/ws.mjs implements the RFC 6455 client directly over node:http(s) — handshake, masking, continuation fragments, 64-bit lengths, ping/pong, close — which is everything CDP needs, with header support.
Tests
npm test # live tests against the playground
KITESURF_SKIP_NETWORK=1 npm test # offline onlyThe suite hits the real service on purpose: the interesting failures (Boa's recursion limit, pipe truncation, edge status codes) only appear against the real thing.
Requirements
Node ≥ 18. No browser, no API token, no build step.
License
MIT
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
MCP server for AI agents to plan, verify, and deploy Cloudflare-native apps.
Hosted real Google Chrome MCP with per-user persistent state. Navigate, click, type, screenshot.
Browser MCP for logged-in tasks. Uses your Chrome — credentials stay local. Zero-token replay.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- 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/TrueNix/kitesurf-bridge'
If you have feedback or need assistance with the MCP directory API, please join our Discord server