Package Intel
npm, PyPI & crates.io health, vulns and dependency graphs. Runs locally, no API key.
Open source Open in the app JSON README (API)
About
npm, PyPI & crates.io health, vulns and dependency graphs. Runs locally, no API key.
Details
- Kind
- MCP servers
- Topic
- Developer tools
- Publisher
- adam121393
- Origin
- official
- Category
- ferramentas
- Transport
- local
- Version
- 1.1.1
- Last push
- 2026-08-14T23:56:19Z
- Repository state
- ativo
- Language
- TypeScript
- License
- MIT
- Added
- 2026-08-29 03:02:22
- Updated
- 2026-08-29 03:02:22
- Origin id
io.github.adam121393/package-intel
README
# Package & Dependency Intelligence API (x402)
A pay-per-call API selling npm, PyPI and crates.io (Rust) package health, dependency-graph,
and vulnerability data to AI coding agents over the
[x402](https://github.com/x402-foundation/x402) payment protocol — plus an MCP server so
agents in Claude Desktop/Cursor can call it and pay automatically.
Defaults to **Base Sepolia testnet** via the free public facilitator. Going to mainnet is
an explicit config change (see [Going to mainnet](#going-to-mainnet)).
## Endpoints
Raw passthrough of the upstream sources is **free**: npm, PyPI, crates.io, OSV and deps.dev
are themselves free and unauthenticated, so charging for a relay of them prices against
zero. What gets charged for is the consolidation — the score.
| Endpoint | Method | Price | Returns |
|---|---|---|---|
| `/v1/package/:ecosystem/:name` | GET | free | Consolidated snapshot |
| `/v1/vulns/:ecosystem/:name` | GET | free | Known vulnerabilities (OSV.dev) |
| `/v1/deps/:ecosystem/:name` | GET | free | Dependency graph (deps.dev) |
| `/v1/downloads/:ecosystem/:name` | GET | free | Download counts |
| `/v1/health/:ecosystem/:name` | GET | $0.01 | Health/risk score 0-100 |
| `/v1/batch` | POST | $0.02 | Batched health scores (≤50 packages) |
`:ecosystem` is `npm`, `pypi` or `crates`. Also unpaid: `/healthz`, `/v1/sample` (canned
example response), `/.well-known/x402` (discovery manifest).
Free routes are rate limited to **60/min and 2000/day per caller** — a runaway agent loop
is how we would get our egress IP blocked by npm or OSV. Paid routes are exempt; their
price is the limiter. Exceeding a limit returns `429` with `Retry-After`.
Tier, price, description, and discovery metadata all come from `src/catalog.ts` — edit
there and the payment middleware, rate limiter, manifest, and Bazaar declarations stay in
sync. `tier` is a required discriminant, so a new endpoint cannot default into being free.
### Trusting the caller's address
The rate limiter counts per client IP, but the service sits behind a Worker proxy and a
tunnel, so every request arrives from the same address. The proxy forwards the real one as
`x-stable-ip` **signed with `PROXY_SECRET`**, and the origin honours it only when the
secret matches. Anything else — wrong secret, no secret, or a request straight to the
tunnel hostname — shares a single bucket. Without that signature a caller could forge a
fresh address per request, or skip the proxy, and get unmetered upstream fan-out.
Set the same value in both places:
```bash
# .env for the origin, plus:
npx wrangler secret put PROXY_SECRET
```
The server warns at startup if it is missing on mainnet.
## Local setup (testnet)
```bash
npm install
npm run gen-wallet
```
`gen-wallet` prints two **testnet-only** keypairs — never fund these with real assets:
- **Seller** — put its address in `.env` as `PAY_TO` (where payments land).
- **Buyer** — put its private key in `.env` as `BUYER_PRIVATE_KEY` (used by the test
script to simulate a paying agent).
Copy `.env.example` to `.env` and fill those in. Then fund the **buyer** with Base Sepolia
USDC at [faucet.circle.com](https://faucet.circle.com) (select Base Sepolia; no account
needed). No testnet ETH is required — x402's `exact` scheme uses EIP-3009, so the buyer
only signs off-chain and the facilitator pays gas.
```bash
npm run dev
```
Verify: `curl http://localhost:4021/healthz` → 200, and
`curl -i http://localhost:4021/v1/health/npm/express` → 402 with payment instructions.
## Test the payment flow
```bash
npm run test-buyer # GET /v1/health/npm/express (default)
npm run test-buyer -- /v1/deps/npm/express
npm run test-buyer -- /v1/batch
```
On Git Bash/Windows, prefix with `MSYS_NO_PATHCONV=1` so the leading `/` isn't rewritten
into a Windows path.
A request for a nonexistent package returns 404 **without charging** — the x402 middleware
skips settlement entirely on any 4xx/5xx response, so failures are free.
## MCP server (how agents consume this)
`mcp-client/` is a standalone npm package (`package-intel-mcp`) — a stdio MCP server that
runs on the *buyer's* machine. It is published separately from this server so an install
does not drag in Hono and the x402 server stack; it imports nothing from `src/`.
**It runs with no configuration at all.** With no wallet it registers the four free tools
(`package_snapshot`, `package_vulns`, `package_deps`, `package_downloads`). Requiring a
funded hot wallet before the tool did anything was the single biggest adoption blocker, so
that is now the default path, not an error.
```bash
claude mcp add package-intel -- npx -y package-intel-mcp
```
Setting `X402_PRIVATE_KEY` additionally registers `package_health` and
`package_batch_health`, which call the paid endpoints and, on a 402, sign a USDC payment
from that wallet and retry — the agent just sees data. A malformed key is a hard error
rather than a silent drop back to free mode, since that would look like the paid tools
vanishing for no reason.
```json
{
"mcpServers": {
"package-intel": {
"command": "npx",
"args": ["-y", "package-intel-mcp"],
"env": { "X402_PRIVATE_KEY": "0x..." }
}
}
}
```
`X402_PRIVATE_KEY` is the *agent operator's* wallet, funded with USDC on `NETWORK`. Use a
dedicated low-balance wallet — it is a hot key that spends automatically.
To run it from source against a local server: `npm run mcp`.
### Getting an agent to actually call it
Installing a tool does not make an agent reach for it. A line in the consuming repo's
`AGENTS.md` / `CLAUDE.md` / `.cursor/rules` does, on every relevant task — that is the
mechanism that produces recurring invocation, not registry listings. `mcp-client/README.md`
ships a copy-paste block for this.
## GitHub Action
`action.yml` publishes this repository as a dependency-review action. On a pull request that
touches `package.json`, `requirements*.txt` or `Cargo.toml`, it looks up every **newly added**
dependency and comments with advisories, deprecation, staleness and missing licences.
```yaml
name: Dependency review
on:
pull_request:
paths: ["**/package.json", "**/requirements*.txt", "**/Cargo.toml"]
permissions:
contents: read
pull-requests: write
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # needed: the action diffs base against head
- uses: adam121393/package-intel@v1
with:
fail-on: critical # none | low | moderate | high | critical
```
Uses the free endpoints only — no wallet, no API key, no signup. Zero runtime dependencies, so
adding it to a pipeline is not a supply-chain ask.
| Input | Default | Purpose |
|---|---|---|
| `fail-on` | `none` | Fail the check at this severity or above |
| `comment` | `true` | Post and update a PR comment |
| `github-token` | `${{ github.token }}` | Needs `pull-requests: write` |
| `api-url` | hosted service | Override to run against your own instance |
Two behaviours worth knowing. Only **added** dependencies are reviewed, not version bumps of
existing ones, so the comment does not become noise people learn to scroll past. And advisories
are scoped to a version: an exact pin is checked as written, while a range is checked against the
package's current release. That distinction matters — querying without a version returns every
advisory ever filed, which reports a fully patched `lodash` as critical.
A dependency that cannot be looked up is never a failure. An upstream outage must not block an
unrelated pull request.
## Coinbase CDP setup
Two **different** CDP credentials, easy to conflate:
| Credential | Needed for |
|---|---|
| `CDP_API_KEY_ID` + `CDP_API_KEY_SECRET` | The **facilitator** — verifying and settling payments |
| `CDP_WALLET_SECRET` | The **wallet SDK** — creating/controlling CDP-managed accounts |
Receiving payments needs only a public address. The server never holds key material to
get paid — `CDP_WALLET_SECRET` is only for `npm run cdp-wallet`.
```bash
# 1. Add CDP_API_KEY_ID + CDP_API_KEY_SECRET to .env, then:
npm run cdp-check # verifies keys, prints which networks CDP actually serves
# 2. Add CDP_WALLET_SECRET, then create a TEE-backed receiving account:
npm run cdp-wallet # prints an address to use as PAY_TO
npm run cdp-wallet -- --faucet # also request Base Sepolia test funds
```
`cdp-check` exists because CDP's docs list supported networks as "Base, Polygon, Arbitrum,
World, Solana" without saying whether Base *Sepolia* is included, and `/supported` requires
auth. It answers that empirically and tells you whether the testnet rehearsal below is
possible.
### Rehearsing the CDP path on testnet
If `cdp-check` reports Base Sepolia is supported, set `USE_CDP_FACILITATOR=true` while
leaving `NETWORK=eip155:84532`. You then exercise the real CDP credentials and settlement
path against **test** funds. If it isn't supported, leave the flag unset — the CDP path
will first run on mainnet, so make that first payment a small one.
## Going to mainnet
1. **Receiving wallet** — use a dedicated address (ideally from `npm run cdp-wallet`), never
a personal wallet. Only the public address goes in `PAY_TO`.
2. **Set `NETWORK=eip155:8453`.** The server switches to the CDP facilitator automatically
and refuses to boot without CDP keys, rather than silently using a testnet facilitator.
3. **Set `PUBLIC_URL`** to the real origin so the manifest advertises reachable URLs.
4. **Deploy** (below), then make 2–3 real settled payments — the CDP Bazaar only catalogs a
service after its first successful settlement.
Start small and confirm settlement on [BaseScan](https://basescan.org) against your `PAY_TO`
address before promoting the endpoint anywhere.
## Deploy (Railway)
`railway.json` is included (Nixpacks, `npm run start:api`, `/healthz` health check). Push the repo,
create a Railway project from it, and set the environment variables from `.env.example` in
Railway's variables UI — **not** in a committed file. Point uptime monitoring at `/healthz`.
## Getting listed
- **CDP Bazaar** — automatic once on mainnet via the CDP facilitator, after the first
settled payment. Each route already declares discovery metadata with a *valid* sample
input (`npm`/`express`); this matters because the Bazaar probes with that input and only
indexes endpoints that answer **402** — a placeholder ecosystem would 400 and never list.
- **`/.well-known/x402`** — already served, for agentic.market / x402scan / x402-list.
- **MCP registries** — publish to the official MCP Registry, then Glama, Smithery, PulseMCP.
## Notes
- **Caching:** in-process LRU with TTLs from 1h (vulns) to 24h (downloads/deps). On upstream
failure a stale value is served with `stale: true` rather than erroring.
- **Validation before payment:** unsupported ecosystems 400 in middleware *before* the
payment check, so they're never charged.
- **Version-scoped vulnerabilities:** health scores query OSV for the resolved current
version. Querying without a version returns every advisory in the package's history,
which badly misrepresents maintained packages.
- **pypistats rate limits** aggressively (429 after a couple of rapid calls). Download
counts are best-effort: a failure omits that field rather than failing the request. Warm
the cache for popular packages if this matters.
- The health score in `src/domain/health.ts` is a documented v1 heuristic — tune the weights
as real usage data arrives.