MCP Server
Call ByteKit web capture as a native tool from Claude, Cursor, and any MCP-compatible agent — hosted over HTTP or run locally over stdio.
The ByteKit MCP server exposes scraping, search, screenshots, and account
lookups as Model Context Protocol tools. Your
agent calls scrape_url or screenshot_url the same way it calls any other
tool — no wrapper to write, no headless browser to manage.
There are two ways to connect: a hosted HTTP endpoint (nothing to install)
and a local stdio server (npx @hunt-labs/bytekit-mcp).
Authentication
Both paths authenticate with a ByteKit API key. Sign up at
app.bytekit.com and create a key (prefixed
sk_live_). The local server reads it from the BYTEKIT_API_KEY environment
variable (or the --api-key flag); the hosted endpoint takes it as a bearer
token.
Option 1 — Hosted HTTP (no install)
Point any MCP client that speaks the Streamable HTTP transport at:
https://api.bytekit.com/mcpSend your API key as a bearer token. Example client config for a host that speaks Streamable HTTP directly (Cursor and most hosts accept this shape):
{
"mcpServers": {
"bytekit": {
"url": "https://api.bytekit.com/mcp",
"headers": {
"Authorization": "Bearer sk_live_xxx"
}
}
}
}Paste your real key in place of sk_live_xxx. A JSON config file is not a
shell: most hosts read these values verbatim and do not substitute environment
variables inside headers, so writing Bearer $BYTEKIT_API_KEY here sends that
text to the API as the token, verbatim, and the API answers 401.
Claude Desktop does not accept this shape. claude_desktop_config.json
only launches local stdio subprocesses (command/args/env) — it has
no native url-based remote-server entry, so pasting the config above into
it will not connect. To reach the hosted endpoint from Claude Desktop, either:
- add ByteKit as a Connector (Settings → Connectors → Add custom connector), or
- bridge it locally with
mcp-remote:
{
"mcpServers": {
"bytekit": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://api.bytekit.com/mcp",
"--header",
"Authorization:${AUTH_HEADER}"
],
"env": {
"AUTH_HEADER": "Bearer sk_live_xxx"
}
}
}
}Two details in that snippet are load-bearing, and both are documented by
mcp-remote itself:
${AUTH_HEADER}, not$AUTH_HEADER. Claude Desktop launchescommand/argsdirectly, with no shell in between, so it does not expand$VARanywhere in the config.mcp-remotedoes its own substitution on--headervalues, but only of the braced${VAR}form, reading it from the process environment theenvblock supplies. A bare$BYTEKIT_API_KEYis passed through untouched and authenticates as that literal string — a guaranteed401.- No space after the colon in
Authorization:${AUTH_HEADER}. The header value carries the space (Bearer sk_live_xxx) instead.mcp-remote's README documents this as the workaround for a Cursor / Claude Desktop (Windows) bug where spaces insideargsare not escaped when it invokesnpx, mangling the value:"Authorization:${AUTH_HEADER}" // note no spaces around ':'with"AUTH_HEADER": "Bearer <auth-token>" // spaces OK in env vars.
Or skip the hosted endpoint entirely and run the local stdio server (Option 2 below), which Claude Desktop supports natively.
Option 2 — Local stdio (npx)
Run the server as a local subprocess over stdio. Nothing to install globally —
npx fetches it on demand:
npx @hunt-labs/bytekit-mcp --api-key $BYTEKIT_API_KEYOr set the key in your environment and omit the flag:
export BYTEKIT_API_KEY=sk_live_xxx
npx @hunt-labs/bytekit-mcpClient config for a stdio server:
{
"mcpServers": {
"bytekit": {
"command": "npx",
"args": ["-y", "@hunt-labs/bytekit-mcp"],
"env": {
"BYTEKIT_API_KEY": "sk_live_xxx"
}
}
}
}API-key resolution order
The local server resolves the key in this order:
--api-key <key>flagBYTEKIT_API_KEYenvironment variable
Tool inventory
| Tool | What it does |
|---|---|
scrape_url | Fetch a URL and return HTML, clean markdown, links, or images. |
web_search | Run a web search and return ranked organic results. |
screenshot_url | Capture a screenshot of a page (desktop/mobile, PNG/JPEG). |
get_result | Fetch a previously created screenshot by id. |
get_account | Return account details, plan, and current usage/quota. |
list_docs | List every page in the bundled ByteKit documentation. |
search_docs | Search the bundled documentation. |
get_doc | Fetch the full markdown of one documentation page. |
The server also exposes resources (bytekit://account,
bytekit://screenshot/{id}) and prompts (summarize_webpage,
extract_structured_data, research_topic) that MCP hosts can surface directly.
Limits
The hosted POST /mcp endpoint caps request bodies at 1 MB. MCP tool
arguments are small (a URL plus a few options), so this comfortably covers
every tool and prompt in the matrix above — including research_topic, the
largest legitimate call, which carries a list of URLs. An oversized request
gets a 413 with a JSON-RPC-shaped error body rather than a dropped
connection.
Every route in the table below carries its own body-size limit, sized to
that route's shape rather than reused from /mcp (a bulk endpoint's
realistic payload is far larger than an MCP tool call). This covers both the
capture/data create endpoints and the unauthenticated webhook-ingress
receivers, which need a guard for the same reason — their handlers read the
full body before any check runs:
| Endpoint | Limit | Error |
|---|---|---|
POST /v1/scrape, /v1/search, /v1/scrape/bulk, /v1/schema | 512 KB | 400 request_too_large |
POST /v1/fetch | 512 KB | 400 request_too_large |
POST /v1/screenshots | 512 KB | 400 request_too_large |
POST /v1/monitors | 512 KB | 400 request_too_large |
POST /v1/sitemap | 512 KB | 400 request_too_large |
POST /v1/bulk | 4 MB pre-auth (chunked) / 32 MB fail-safe (declared length) / per-plan up to 28.7 MB | 400 request_too_large |
POST /v1/fetch/bulk | 2 MB pre-auth (chunked) / 20 MB fail-safe (declared length) / per-plan up to 16.8 MB | 400 request_too_large |
POST /v1/webhook-deliveries/{id}/retry | 64 KB | 400 request_too_large |
POST /v1/webhooks/stripe (internal, Stripe ingress) | 256 KB | 400 (bespoke { error } body) |
POST /webhooks/clerk (internal, Clerk ingress) | 256 KB | 400 request_too_large (bespoke { error } body) |
POST /mcp | 1 MB | 413 (JSON-RPC error) |
The two bulk endpoints are guarded in a two-tier shape, not by a single
ceiling. A streamed body with no Content-Length header is capped at the
small pre-auth limit (4 MB / 2 MB) before your API key is checked; a body
that declares Content-Length is instead bounded there by a larger fail-safe
(32 MB / 20 MB) rather than the real limit, since that branch reads nothing
until your key is verified. Once your key is verified, a second check
re-gates the body against your account's own max_bulk_size — sized off the
documented custom production account's real max_bulk_size of 50,000 URLs
or per-item configs (root CLAUDE.md: "50k bulk") — which reaches 28.7 MB /
16.8 MB for that account. A standard-plan account's effective ceiling is
exactly the pre-auth limit; either tier's overrun returns
400 request_too_large.
Next steps
- Quickstart — make your first ByteKit API call.
- Authentication — key formats and management.