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

@ooky/sdk

v0.6.2

Published

Ooky SDK — middleware for serving AI brand intelligence and capturing AI-bot analytics from your Node, Next.js, or Vercel Edge app.

Readme

@ooky/sdk

Drop-in middleware for serving AI brand intelligence and capturing AI-bot analytics from your Node, Next.js, or Vercel Edge app — without DNS changes or Cloudflare workers.

When an AI bot (GPTBot, ClaudeBot, Perplexity, etc.) hits any page on your site, the SDK fires a non-blocking event to Ooky. When a request asks for one of the well-known AI paths (/llms.txt, /.well-known/ai-manifest.json, /agents.md, …), the SDK serves the latest manifest you've published from the Ooky dashboard.

Install

npm install @ooky/sdk

You'll need a domain registered in the Ooky dashboard and an API key from Integrations → SDK → Generate API Key. The key looks like ooky_sk_<random> and is shown once — store it as an environment variable.

Reveal-once key: Ooky shows the key only at the moment of generation. After your SDK fires its first successful event, the plaintext is purged server-side (only a SHA-256 hash remains, used for ongoing authentication). If you lose the key, rotate it — there's no "show key again" path. This matches the standard set by Stripe, Linear, and GitHub.

Quickstart

Express

import express from "express";
import { ookyMiddleware } from "@ooky/sdk/express";

const app = express();
app.use(ookyMiddleware({
  apiKey: process.env.OOKY_API_KEY,
  domain: "acme.com",
}));
// your routes...

Next.js

// middleware.ts
import { ookyMiddleware } from "@ooky/sdk/next";

export default ookyMiddleware({
  apiKey: process.env.OOKY_API_KEY,
  domain: "acme.com",
});

export const config = {
  matcher: ["/llms.txt", "/llms-full.txt", "/agents.md", "/.well-known/:path*", "/(.*)"],
};

Works in both the Node and Edge runtimes — no code change.

Vercel Edge / Web Fetch

// middleware.ts (Vercel Edge)
import { ookyEdge } from "@ooky/sdk/edge";

export default ookyEdge({
  apiKey: process.env.OOKY_API_KEY,
  domain: "acme.com",
});

export const config = { matcher: "/(.*)" };

What gets intercepted

The SDK responds to these paths with the latest published manifest:

| Path | What gets served | |---|---| | /llms.txt | Markdown summary for LLM crawlers | | /llms-full.txt | Extended markdown with all page sections | | /.well-known/ai-manifest.json | Full JSON brand manifest (global + per-page) | | /ai-manifest.json | Same as above (alternate path) | | /agents.md | Markdown agent guide | | /.well-known/mcp | MCP server descriptor (GET) / tool invocation (POST) | | /mcp | Same as above (alternate path — some platforms intercept /.well-known/*) |

Every other request passes through to your app unchanged.

MCP tool invocation

POST /mcp (and /.well-known/mcp) speaks two protocols — pick whichever your client uses:

  • Standard MCP — JSON-RPC 2.0 (what real MCP clients use: Claude, MCP Inspector, ChatGPT connectors). Send initialize, tools/list, then tools/call:

    { "jsonrpc": "2.0", "id": 1, "method": "tools/call",
      "params": { "name": "get_brand_info", "arguments": { "section": "about" } } }

    The SDK answers with a single JSON response (no SSE stream required). Notifications (notifications/*) get 202 Accepted; an unparseable body returns a JSON-RPC parse error (-32700, HTTP 200 per spec).

  • Legacy Ooky protocol{ "tool": "get_brand_info", "arguments": { "section": "about" } }{ "result": … }, kept for Worker-tier compatibility.

Both answer get_brand_info from the published manifest (same cache and stale-on-error behavior as the other paths). Product tools (search_products, …) are Worker-tier only; the SDK returns a tool-not-found error for them.

What gets logged

For every request (manifest or not), the SDK checks the User-Agent against the bot registry. When a known AI bot is detected, it fires a fire-and-forget POST to /api/ingest/events with:

{
  "event_id": "<uuid>",
  "timestamp": "<ISO 8601>",
  "bot": { "name": "GPTBot", "verified": false, "ua_string": "<full UA>" },
  "request": { "page_path": "/pricing", "method": "GET" }
}

The event scope (which domain it belongs to) is determined server-side from your API key — you cannot accidentally log events for a different customer's domain.

AI referral attribution: when a human arrives from an AI platform — detected via the Referer header (chatgpt.com, perplexity.ai, claude.ai, gemini.google.com, …) or utm_source (?utm_source=chatgpt) — the SDK fires an ai_referral event instead, powering the dashboard's attribution views. Same platform list as the Worker tier.

All other human traffic produces no events.

Configuration options

ookyMiddleware({
  // Required
  apiKey: "ooky_sk_...",
  domain: "acme.com",

  // Optional — defaults are right for production
  apiBase: "https://api.ooky.ai/api",                       // Ooky API root
  cdnBase: "https://api.ooky.ai/api/public/manifest",       // Manifest source (default = apiBase + "/public/manifest")
  bots: undefined,                       // Override the bot registry; default ships with major AI bots
  autoRefreshBots: true,                 // Periodically refresh bot UA list from /api/public/bots
  fetchTimeoutMs: 10000,                 // Hard timeout on every upstream fetch
  manifestCacheTtlMs: 300000,            // In-memory manifest cache TTL (0 disables)
  maxEventsPerMinute: 300,               // Token-bucket cap on event POSTs (Infinity disables)
  onError: (err, ctx) => {},             // Surface swallowed failures (e.g. a 401 = rotated key)
  awaitEvents: "auto",                   // Express on Lambda/Netlify: await events before responding (survives the freeze)
});

| Option | Type | Default | Notes | |---|---|---|---| | apiKey | string | — | Required. Per-domain Bearer token from the dashboard. | | domain | string | — | Required. Must match the verified domain in Ooky. | | apiBase | string | https://api.ooky.ai/api | Override for self-hosted Ooky or staging. | | cdnBase | string | ${apiBase}/public/manifest | Manifest source. By default the SDK fetches from Ooky's public manifest endpoint. Override to put your own CDN (Cloudflare, CloudFront, Fastly) in front. | | bots | Array<{name, pattern, category}> | Built-in default list | Ships with the major AI bots. Override only if you have custom UA patterns. | | autoRefreshBots | boolean | true | Refresh from /api/public/bots once an hour (ETag-aware). Disable for fully offline use. | | fetchTimeoutMs | number | 10000 | Abort upstream fetches (manifest, registry, events) after this many ms so a slow Ooky API can never hang your request path. | | manifestCacheTtlMs | number | 300000 | Manifest responses are cached in-memory per process. On upstream failure (network error or 5xx), a stale copy up to 24h old is served instead of an error. Set 0 to disable. | | onError | (error, context) => void | silent | Called for every failure the SDK swallows: event POST rejections and non-2xx responses (a 401 means your key was rotated/revoked), manifest fetch failures, registry refresh failures. context is { op, status?, kind?, throttled? }. Wire it to your logger so a dead integration is visible: onError: (e, ctx) => logger.warn("ooky", ctx.op, e.message). | | maxEventsPerMinute | number | 300 | Token-bucket cap on event POSTs — a bot storm can't turn your server into an unbounded POST source. Drops are reported through onError (at most once per 10s, with a count). Pass Infinity to disable. | | awaitEvents | 'auto' \| boolean | 'auto' | Express adapter only. On a freeze-after-response serverless host (AWS Lambda, Netlify Functions, GCP Cloud Run / Cloud Functions) the process freezes the instant it responds, killing an un-awaited event POST — so events silently never arrive. 'auto' detects this (AWS_LAMBDA_FUNCTION_NAME · K_SERVICE · FUNCTION_TARGET) and awaits the event before responding (bounded by fetchTimeoutMs); on long-running servers it stays fire-and-forget. Force with true/false. The Next/Edge adapter handles this via event.waitUntil() instead. |

TypeScript declarations ship with the package (@ooky/sdk, /express, /next, /edge are all typed) — no @types/* install needed.

Performance & resilience

  • Manifest responses are cached in-memory for 5 minutes, with concurrent cold-cache requests deduped into a single upstream fetch.
  • If the Ooky API is unreachable or erroring, the SDK serves the last good copy (up to 24h old) — a transient Ooky outage never breaks your /llms.txt.
  • Every upstream fetch carries a hard 10s timeout (AbortSignal.timeout), so your request path can never hang on Ooky.
  • The manifest response also carries Cache-Control: public, max-age=300, s-maxage=600 — your CDN/edge will serve repeat requests without hitting your origin at all.
  • Event firing uses fetch(..., { keepalive: true }) so it survives the response cycle without delaying it. On Vercel Edge / Next middleware the SDK registers the event POST with event.waitUntil() automatically. On AWS Lambda (Express), keepalive isn't enough — the whole process freezes after the response — so the Express adapter awaits the event there instead (see awaitEvents).
  • Bot detection is a substring check against an in-memory list — sub-millisecond per request.

Troubleshooting

"I installed it but no events show up"

  1. Set the onError option to log swallowed failures — a repeated recordEvent error with status: 401 means the key was rotated or revoked.
  2. Confirm your domain is verified and the integration method is set to sdk (or wordpress) in the dashboard.
  3. Check that process.env.OOKY_API_KEY is actually set in your runtime — log it once at boot.
  4. Hit your site with a bot UA: curl -H "User-Agent: GPTBot/1.0" https://your-site.com/ and watch the dashboard's AI Sessions page.
  5. If your app is behind a CDN that strips User-Agent, the SDK can't see the bot. Check your CDN config.

"/llms.txt returns 404"

  • The middleware only intercepts paths the SDK knows about. Make sure your framework's matcher passes those paths to the middleware before falling through to your routes.
  • If you've published the manifest in the dashboard, also check the manifest source is reachable from your server: curl https://api.ooky.ai/api/public/manifest/<your-domain>/llms (or your cdnBase override).

"Events fail with 401 Unauthorized"

  • The API key has been revoked or rotated. Generate a new one from the dashboard and update the env var.

"Manifest is stale"

  • The HTTP cache is honoring 5 min freshness. Ooky purges the CDN on publish, but your CDN may also cache. Force a fresh fetch by clearing your edge cache for the well-known paths.

Security & key lifecycle

The SDK runs server-side only — Node middleware or the Edge runtime. The API key never reaches the browser. Key handling follows the same lifecycle as Stripe / Linear / GitHub:

| Stage | Where the key lives | |---|---| | Generated | Returned once in the dashboard. Plaintext + SHA-256 hash stored server-side. | | Customer copies it | Customer pastes into their env (OOKY_API_KEY). | | First event lands | Server drops the plaintext from the DB. Only the hash remains. | | Ongoing auth | Server hashes the incoming Bearer token and compares against the stored hash in constant time. | | Lost / leaked | Rotate via Integrations → SDK → Rotate API Key. Old key 401s immediately. |

The package is published with npm provenance attestations signed by GitHub Actions OIDC. You can verify the published tarball came from this repo's release-sdk.yml workflow:

npm view @ooky/sdk dist.attestations

If you're using a private registry mirror, mirror the provenance bundle too — most package proxies (Artifactory, Verdaccio, GitHub Packages) preserve it.

Recommended rotation cadence

  • Every 90 days for production deployments.
  • Immediately if you suspect leak (committed to a public repo, server compromise, contractor offboarding).
  • Set a calendar reminder; Ooky will surface an in-dashboard warning at 90 days from generation.

Where the key must never appear

  • ✅ Server env vars (process.env.OOKY_API_KEY) — yes.
  • next.config.js, vite.config.js, or any file shipped to the client — no.
  • ❌ Git commits — never. If you push it by accident, rotate the key first, then clean git history.
  • ❌ Logging — the SDK does not log the key. Don't log process.env blindly either.

What this SDK does NOT do

  • It does not crawl your site or generate the manifest — that happens in the dashboard.
  • It does not modify HTML responses or rewrite content for bots. (For that, use the Worker or Full DNS integration.)
  • It does not block bots. Your robots.txt and any rate limiting still apply.

License

MIT.