cf-package-check
Optional enrichment that pulls repository facts from the GitHub API (archived status, last push, open issues, stars) when the registry metadata links a repository, and reports why it was skipped if unavailable.
Checks npm packages for abandonment risk: reads packument metadata (newest publish of any version), deprecation messages, unpublished packages, and last-month download counts. Streams large packuments rather than parsing them whole.
Checks PyPI projects for abandonment risk: reads project JSON (PEP 503-normalised names), the newest file upload to any release, and flags projects whose releases have all been yanked.
Checks WordPress.org plugins for abandonment risk: reads plugin info (last_updated, 'tested up to' version vs. current WordPress core), support thread counts and resolution rate, and detects plugins closed for security or other reasons. Returns a 0–100 abandonment score with the signals behind it.
Click on "Deploy 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., "@cf-package-checkis the npm package request still maintained?"
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.
CF Package Check
Is this WordPress plugin, npm package or PyPI project abandoned?
A pay-per-call answer computed live from the public registries, on Cloudflare Workers. Charged per request with x402, and exposed to agents as an MCP tool. Every answer lists the signals behind its score.
Signals, not a verdict. The score is computed from public registry metadata; it is not a security or code-quality review. Check the project before deciding.
Live: https://cf-package-check.cf-exclusion-check.workers.dev
Network: Base mainnet (eip155:8453), USDC, via the Coinbase CDP
facilitator. Payments are real. Settlement happens after the handler
succeeds, so a request that cannot be answered is never charged.
This is booth #2. Its payment, discovery, MCP, rate-limiting and logging code is copied from booth #1 (cf-exclusion-check).
Endpoints
endpoint | price | notes |
| $0.02 | status, score, months since update, last release, signals |
| $0.10 | everything above + breakdown, raw registry facts, GitHub facts, version history, support/issue stats, recommendation |
| free | registry reachability, cache and payment configuration |
| free | discovery document, |
| $0.02 per | JSON-RPC; discovery and |
ecosystem is wordpress, npm or pypi. name is the npm package
(express, @types/node), the PyPI project (requests; normalised per PEP
503), or the WordPress.org plugin slug (contact-form-7, as in
wordpress.org/plugins/<slug>/).
Only GET is served on the paid paths. The query is validated after the
paywall: CDP's Bazaar probes the bare resource URL and requires a 402, so a bare
GET /v1/check answers with the payment challenge. A paid request with a
malformed query gets 400, and a response of 400 or above is never settled, so it
costs nothing.
/v1/check response
{
"ecosystem": "npm",
"name": "request",
"exists": true,
"status": "deprecated",
"score": 100,
"months_since_update": 78,
"last_release": { "version": "2.88.2", "date": "2020-02-11T16:35:36.122Z" },
"signals": [
"deprecated on npm: \"request has been deprecated, see https://github.com/request/request/issues/3142\"",
"last update 78 months ago (2020-02-11, 2.88.2)",
"50,778,952 downloads last month"
],
"as_of": "2026-09-11T07:36:46.761Z",
"disclaimer": "Signals, not a verdict. …"
}status is one of active, stale, abandoned, deprecated, closed.
score is abandonment risk, 0–100. as_of is when the registry facts were read
(up to 24 h old on a cache hit). A package that does not exist is an answer, not
an error: exists: false, with status and score null.
/v1/report adds
score_breakdown (every rule that fired and its points), recommendation,
registry (the raw facts per ecosystem), github (archived, last push, open
issues, stars — or why it was not checked), versions (count, first release,
releases in the last 12 months, the ten newest), support (WordPress support
threads and resolution rate; GitHub open issues + PRs), partial (optional
enrichment that was skipped), sources (the upstream URLs used), cached,
checked_at.
Related MCP server: address-intel-api
Scoring
Deterministic: a pure function of the registry facts and the current date
(src/score.ts, asserted rule by rule in test/score.test.ts).
rule | points |
months since last update: 0–5 | 0 |
6–11 | 25 |
12–23 | 50 |
24–35 | 70 |
36+ (or no dated release at all) | 90 |
GitHub repository archived | +20 |
WordPress "tested up to" ≥ 3 major releases behind current WordPress | +10 |
npm: < 100 downloads last month and first published > 2 years ago | +10 |
cap | 100 |
registry flag | score | status |
closed on WordPress.org, or unpublished from npm | 100 |
|
deprecated on npm, or every release yanked on PyPI | 100 |
|
Without a flag: score < 25 → active, < 50 → stale, otherwise
abandoned. A score of 100 reached by stacking rules is still abandoned;
deprecated and closed are reserved for the registry itself saying so.
Months = floor(days since last update / 30.4375), so band edges are exact
whole months.
"Last update" is chosen per ecosystem, and the report states which:
ecosystem | last update means | why not the obvious field |
npm | newest publish of any version | express's |
PyPI | newest file upload to any release | adding wheels to an old release is maintenance |
WordPress |
| the only date the API has; it also moves on readme-only commits |
WordPress majors are X.Y (6.8, 6.9, 7.0 …), so "3 majors behind" is
(10·X + Y) of current minus that of tested-up-to.
Worked examples (2026-09-11)
package | result | why |
wordpress | active, 0 | updated 2026-08-17, tested up to 7.1 = current |
wordpress | abandoned, 100 | 168 months (90) + tested up to 3.4.2, 37 majors behind 7.1 (+10) |
wordpress | closed, 100 | closed 2018-12-14, Security Issue |
npm | active, 0 | last publish 4 months ago (4.22.2) |
npm | deprecated, 100 | deprecated by its maintainers |
npm | abandoned, 100 | 41 months (90) + repo archived as facebookarchive/flux (+20), capped |
pypi | active, 0 | last upload 3 months ago |
pypi | abandoned, 90 | last upload 2014-06-20 |
Paying
An unpaid call returns 402 with the machine-readable challenge in the
payment-required header (base64 JSON, x402 v2):
{"x402Version":2,"accepts":[{"scheme":"exact","network":"eip155:8453",
"amount":"20000","asset":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"payTo":"0xCa28eb92657F8a81aFb5493b3a04A740B204d816","maxTimeoutSeconds":60,
"extra":{"name":"USD Coin","version":"2","paymentFlow":"authorization"}}],
"extensions":{"bazaar":{…}}}amount is atomic USDC (6 decimals): 20000 = $0.02, 100000 = $0.10.
curl -i "https://cf-package-check.cf-exclusion-check.workers.dev/v1/check?ecosystem=npm&name=express" # 402
node scripts/x402-client.mjs # pays $0.02 for /v1/check npm express
node scripts/x402-client.mjs /v1/report pypi requests # pays $0.10The client refuses to pay unless /v1/health answers 200 and the payer holds at
least $0.05, then waits for the receipt and checks the USDC Transfer to
PAY_TO on chain. Gas is paid by the facilitator under EIP-3009, so a payer
needs USDC only.
MCP
{
"mcpServers": {
"cf-package-check": {
"type": "http",
"url": "https://cf-package-check.cf-exclusion-check.workers.dev/mcp"
}
}
}curl -X POST https://cf-package-check.cf-exclusion-check.workers.dev/mcp \
-H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
curl -X POST https://cf-package-check.cf-exclusion-check.workers.dev/mcp \
-H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call",
"params":{"name":"package_check","arguments":{"ecosystem":"npm","name":"express"}}}'
# -> 402 until paid; an x402-capable client pays and retriestool | price | returns |
| $0.02 per call | the |
| free | upstream endpoints, the scoring table, the cache policy |
initialize, ping and tools/list are free, so an agent can learn the tool
and its price before paying. Bad arguments are answered as a free tool error
before the paywall. A registry outage is HTTP 503, not a 200 carrying an
error, so the payment is never settled for it.
How it works
Stateless. Each answer is read live from:
ecosystem | upstream |
WordPress |
|
npm |
|
PyPI |
|
GitHub (optional) |
|
npm packuments are never parsed whole. typescript's is 15.6 MB and a full
JSON.parse costs ~31 ms of CPU against the free plan's 10 ms. The packument is
streamed, searched byte-wise for "time":{, and only that object is decoded; the
stream is cancelled once it closes. docs/SOURCES.md has the measurements.
WordPress answers 404 for both "closed" and "never existed"; only the body
tells them apart. Closed plugins are reported as closed with the date and
reason, not as a typo.
Cache and free-tier discipline
Registry facts are cached in KV for 24 h — one combined record per package, because the free plan allows 1,000 KV writes a day. When optional enrichment was skipped (GitHub rate-limited, download count or current WordPress version unavailable) the TTL is 1 h so it is retried soon.
Registry failures are never cached and are answered 503, which the
authorizationpayment flow never settles.No request scans anything unbounded: npm reads are capped by
MAX_PACKUMENT_BYTES(32 MiB), PyPI by 16 MB, WordPress by 2 MB./v1/healthprobes are memoised for 60 s per isolate, because health is never rate limited.D1 holds only the request audit log.
Limits
rate limit, free | 10 / min per (salted-hash) IP — approximate: Cloudflare's rate-limit binding is eventually consistent per location, and a live burst passed 12 requests before the first 429 |
rate limit, paid (request carries a payment header) | 60 / min, same caveat |
never rate limited |
|
freshness | facts up to 24 h old; |
GitHub | unauthenticated, 60 requests/hour per egress IP shared with other Workers; when limited, the archived rule is skipped and the answer says so |
ecosystems | WordPress.org plugins, npm, PyPI only |
CPU | the largest packuments (typescript, aws-sdk, next) answered 200 on an edge preview; if production ever returns 1102 for them, lower |
D1 | free-tier D1 quotas are account-wide, shared with booth #1. Audit-log writes fail soft (logged to the console) when the account's budget is spent — which happened on launch day: see below |
Mainnet verification (2026-09-11)
check | result |
unpaid |
|
paid |
|
settlement |
|
on-chain transfer | USDC |
Bazaar, first attempt | not listed: bare |
fix + indexing payment | input now validated after the paywall (version |
Bazaar | listed about 10 minutes after that settlement: the merchant lookup shows |
audit log | the paid request's row was not written: the account's D1 free-tier quota was already spent by booth #1 (13.4M rows read in 24h), so most inserts that day failed with |
Staying listed in the Bazaar (30-day keepalive)
CDP's Bazaar indexes a route after a payment for it settles through the CDP facilitator, and removes it again:
after 30 days without a settlement. Every listed route needs at least one settled paid call inside every 30-day window. Run a keepalive at least every three weeks:
node scripts/x402-client.mjs($0.02,/v1/check), andnode scripts/x402-client.mjs /v1/report npm express($0.10) if the report route should stay listed too. A route that has never been paid for is not listed at all.when its bare URL stops answering
402 Payment Required. Keep bareGET /v1/checkandGET /v1/reportreturning the challenge; that is why input is validated after the paywall.
Ranking is recomputed every six hours. To check, with no API key and no payment:
# would CDP accept the route for indexing?
curl -s -X POST https://api.cdp.coinbase.com/platform/v2/x402/validate \
-H 'Content-Type: application/json' \
-d '{"resource":"https://cf-package-check.cf-exclusion-check.workers.dev/v1/check","method":"GET"}'
# everything the Bazaar lists for this payout address (shared with booth #1)
curl -s "https://api.cdp.coinbase.com/platform/v2/x402/discovery/merchant?payTo=0xCa28eb92657F8a81aFb5493b3a04A740B204d816"Development
npm install
npm test # 129 tests, on recorded registry fixtures
npm run typecheck
npm run fixtures:record # re-record test/fixtures from the live registries
npx wrangler dev # local; set PAY_TO="" in .dev.vars to disable payment
BASE_URL=http://127.0.0.1:8787 node scripts/live-test.mjs --open # live registries
node scripts/live-test.mjs # paywall checks against the deployed Worker (spends nothing)Deploying:
npx wrangler d1 execute cf_package_check --remote --file migrations/0001_init.sql
npx wrangler deploy
openssl rand -hex 32 | npx wrangler secret put IP_HASH_SALT
npx wrangler secret put CDP_API_KEY_ID # same CDP key as booth #1
npx wrangler secret put CDP_API_KEY_SECRETWithout the CDP secrets the paid paths answer 503 "payment facilitator unavailable" (no payment taken) — the public facilitator does not support Base mainnet.
Privacy
Raw client IPs are never stored: they are HMAC'd with
IP_HASH_SALT, and with no salt no hash is written at all.The queried package is logged only as a salted hash, matching booth #1's schema.
No analytics.
Future work
Chrome Web Store — it has no public API for extension metadata, so it is out of scope until one exists or a sanctioned data source appears.
Other ecosystems (crates.io, RubyGems, Packagist, Maven Central) would slot in as further adapters behind the same scoring.
Licence
MIT. Registry data belongs to its publishers (WordPress.org, npm, the Python Package Index, GitHub). This project is not affiliated with or endorsed by the WordPress Foundation, npm, Inc., GitHub, Inc. or the Python Software Foundation.
This server cannot be deployed
Maintenance
Related MCP Connectors
Abandonment checks for WordPress, npm and PyPI packages, priced per call.
21XRPL token rug-checks, issuer reputation & AMM data for AI agents. Pay-per-call USDC via x402.
Dependency risk + calibrated, publicly scored forecasts on OSS packages. Docs: anteproof.com/docs
Solana address risk grades and token scans for AI agents. Pay-per-call via x402 (USDC on Base).
Related MCP Servers
- AlicenseBqualityCmaintenanceAgent payments ecosystem intelligence. Scans GitHub, Hacker News, and npm for activity across AP2, ACP, x402, MPP, and UCP protocols. Returns scored and classified opportunities. Free protocol info and comparison, paid scan via x402 USDC32011Apache 2.0
- FlicenseNot gradedqualityCmaintenanceProvides counterparty risk checks for any EVM address, returning malicious-history flags, tiered risk scoring, and plain-language summaries via x402 payment.-
- AlicenseNot gradedqualityBmaintenanceProvides software supply-chain intelligence for AI agents, enabling them to query package metadata, versions, downloads, dependencies, and health signals for npm, PyPI, and crates.io packages without API keys.MIT
- FlicenseNot gradedqualityBmaintenanceProvides coding agents with 19 pay-per-call developer utilities, npm supply-chain security checks, and Base blockchain lookups, paid via USDC on Base using x402. No API key needed—payment acts as authentication.61-