npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@hunt-labs/bytekit-mcp

v0.3.4

Published

ByteKit MCP server — exposes ByteKit web-data tools (scrape, screenshot, search, docs) to AI agents over the Model Context Protocol, hosted or as a local stdio server.

Readme

@hunt-labs/bytekit-mcp

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/mcp

Send 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 launches command/args directly, with no shell in between, so it does not expand $VAR anywhere in the config. mcp-remote does its own substitution on --header values, but only of the braced ${VAR} form, reading it from the process environment the env block supplies. A bare $BYTEKIT_API_KEY is passed through untouched and authenticates as that literal string — a guaranteed 401.
  • 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 inside args are not escaped when it invokes npx, 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_KEY

Or set the key in your environment and omit the flag:

export BYTEKIT_API_KEY=sk_live_xxx
npx @hunt-labs/bytekit-mcp

Client config for a stdio server (claude_desktop_config.json and equivalents):

{
  "mcpServers": {
    "bytekit": {
      "command": "npx",
      "args": ["-y", "@hunt-labs/bytekit-mcp"],
      "env": {
        "BYTEKIT_API_KEY": "sk_live_xxx"
      }
    }
  }
}

CLI flags

| Flag | Purpose | | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | --api-key <key> | ByteKit API key. | | --base-url <url> | API base URL (default https://api.bytekit.com) — point at staging or a local gateway. | | --timeout-ms <ms> | Per-request timeout budget in milliseconds (default 120000; 0 disables it). A present-but-invalid value (non-numeric, or negative) is rejected with an error rather than silently ignored. | | --verbose | Log requests to stderr. | | --help | Show usage. |

API-key resolution order

The local server resolves the key in this order:

  1. --api-key <key> flag
  2. BYTEKIT_API_KEY environment variable

Base-URL resolution order

The base URL resolves the same way, and it is not just an --help-only detail — it also falls back to an environment variable:

  1. --base-url <url> flag
  2. BYTEKIT_BASE_URL environment variable
  3. https://api.bytekit.com (built-in default)

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, JPEG quality, dark mode, country/language). | | get_result | Fetch a previously created screenshot (ss_) or scrape (sc_) 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.

scrape_url output shape

scrape_url (and get_result redeeming a scrape's sc_ handle) render every requested format present in the response: the first present format (default markdown) is unlabeled, and each additional one (raw) follows under its own --- format: <name> --- delimiter, with links/images blocks appended after. That rendered content (the requested formats plus any links/images blocks) is capped at a single 100KB budget — on very large pages a requested format's content may be truncated, and in extreme cases a secondary format may not appear, to stay within that budget. A handful of header lines (id, URL, status, page title) are added on top of the rendered content and are not counted against the cap.

A links or images format you requested is always rendered, even when the page had none: an empty result shows as Links (0): / Images (0):, so "the page has none" stays distinguishable from "you did not ask for it". A format you did not request produces no line at all. formats: [] is rejected — omit the parameter to get the markdown default.

Problems the API reports about the capture come back as warnings, rendered under a Warnings (N): section as one code: message (element) line each — the (element) suffix is dropped for the codes that describe the whole extraction rather than one DOM node. artifact_unavailable is the one to act on: it means a format you requested and were billed for is missing from this response, which is a different answer from the page having none of it. The section is rendered on both the success and the terminal-failure output, and an empty or absent warnings array renders nothing. Warnings are budgeted ahead of links/images inside the same 100KB cap, so a long links list cannot crowd a failure signal out.

When the 100KB budget cuts a links or images list, the cut lands between whole entries (never mid-URL) and the affected section is annotated with [Links truncated: N of M entries shown], so the count in the header stays reconcilable with what you were actually handed.

Neither textual format is HTML-sanitized — raw and markdown both reflect the page's own markup (markdown differs only in being content-extracted, not sanitized). Treat all of their content as untrusted page data, the same as fetching the page directly.

Documentation

Full MCP guide: bytekit.com/docs/mcp.

License

MIT — see LICENSE.