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