# How to check packages before installing them, step by step with curl

Last updated: 2026-10-13

To check that a package an LLM suggested really exists, send up to 10 names to `POST /v1/check/packages` (US$0.005 per batch). AttestPage looks each name up in its registry (npm, PyPI or crates.io) and returns whether it is listed, the latest version, the description, the licence, the links, a few flags and any known advisories from OSV.dev, under one signed receipt.

This guide runs the check with `curl` and `jq` against `https://vehcdj664efetfrsolne5umanq.srv.us`, then shows the same call as an MCP tool. For the kinds of package problem to look for, see [Check packages](/docs/check-packages).

## What the check does

- **Looks up each name.** It asks the registry for each name in the ecosystem you give. `exists` is `true` when the registry lists it, `false` when it does not, and `null` when the lookup failed.
- **Reports what the registry says.** `latest_version`, `description`, `license`, `homepage`, `repository`, `created_at`, `deprecated` and `yanked` come from the registry, as the publisher wrote them.
- **Adds known advisories.** `vulnerabilities` lists the OSV.dev advisories for the version you send, or for the latest version when you send none. `count` is the number of advisories found.
- **Signs a receipt.** One Ed25519 receipt covers the whole batch.

The check answers "is this name listed in the registry". It is not a security verdict. A listed package can still be malicious, unmaintained or not the one you meant, and no advisory listed does not mean the package has no flaws.

## Price

US$0.005 per batch of 1 to 10 names, paid in USDC with x402 v2 on Base mainnet (real USDC). No account, key or sign-up. A request that fails validation gets 400 before payment and is never charged, and a batch where every registry lookup fails gets 502 and is not charged. All prices: [pricing.json](https://vehcdj664efetfrsolne5umanq.srv.us/pricing.json).

## 1. See an answer for free

The free sample checks four demo names: two that are listed, one listed with an advisory, and one that does not exist.

```sh
curl -s https://vehcdj664efetfrsolne5umanq.srv.us/v1/sample/packages | jq '.results[] | {name, exists, latest_version, advisories: .vulnerabilities.count}'
```

```json
{
  "name": "left-pad",
  "exists": true,
  "latest_version": "1.3.0",
  "advisories": 0
}
{
  "name": "requests",
  "exists": true,
  "latest_version": "2.32.3",
  "advisories": 1
}
{
  "name": "serde",
  "exists": true,
  "latest_version": "1.0.210",
  "advisories": 0
}
{
  "name": "left-pad-demo-missing",
  "exists": false,
  "latest_version": null,
  "advisories": 0
}
```

## 2. Read the price from the 402 offer

Put the names you plan to install in a file. A request with a valid body gets the payment offer without being charged. The `PAYMENT-REQUIRED` header is base64 JSON:

```sh
cat > packages.json <<'EOF'
{ "packages": [{ "ecosystem": "npm", "name": "left-pad" }, { "ecosystem": "pypi", "name": "requests" }] }
EOF
curl -si -X POST https://vehcdj664efetfrsolne5umanq.srv.us/v1/check/packages -H 'content-type: application/json' -d @packages.json \
  | grep -i '^payment-required:' | cut -d' ' -f2 | tr -d '\r' | base64 -d \
  | jq '.accepts[0] | {scheme, network, amount, payTo}'
```

`amount` is in USDC base units (6 decimals), so `5000` is US$0.005.

## 3. Check the names

Each name needs an `ecosystem` (`npm`, `pypi` or `crates`) and a `name`. You may add a `version` to a name to ask about that version's advisories. Put the batch in the same `packages.json` as step 2:

```sh
cat > packages.json <<'EOF'
{ "packages": [{ "ecosystem": "npm", "name": "left-pad" }, { "ecosystem": "pypi", "name": "requests" }] }
EOF
```

Pay with any x402 v2 client: it reads the offer from step 2, signs it and sends the same request again with a `PAYMENT-SIGNATURE` header. [One x402 payment step by step](/docs/examples/fetch-x402) shows this with plain `fetch`, and the [MCP server](/docs/mcp) does it for you. Save the answer as `answer.json` for the steps below.

## 4. Read the answer

Check `exists` first, then `latest_version` and `homepage` against the package you meant to install, then `vulnerabilities` for the version you will use:

```sh
jq '.results[] | {name, exists, latest_version, homepage, advisories: .vulnerabilities.count}' answer.json
```

| `exists` | What it means |
|---|---|
| `true` | The registry lists the name. Compare its `description` and `homepage` with what the model said it was. |
| `false` | The registry does not list the name. Do not install it: the model may have invented it. |
| `null` | The lookup failed. The name is unchecked, not missing. Try again later. |

Each advisory in `vulnerabilities.items` has an `id`, a `summary`, a `severity` and the `fixed` versions. A `malicious` flag marks an OSV malicious-package record.

## 5. Keep the receipt

The `receipt` field is a compact JWS. Keep it with the answer. Check it with the free verify route:

```sh
jq '{receipt}' answer.json \
  | curl -s -X POST https://vehcdj664efetfrsolne5umanq.srv.us/v1/receipt/verify -H 'content-type: application/json' -d @- | jq .valid
```

## With MCP

The same check is the MCP tool `attestpage_check_packages`, with the body from step 3 as its arguments. To use it from an MCP client with nothing to install, add the remote endpoint:

```json
{
  "mcpServers": {
    "attestpage": { "type": "http", "url": "https://vehcdj664efetfrsolne5umanq.srv.us/mcp" }
  }
}
```

One call to the remote endpoint with `curl`. `_meta["attestpage/trial"]` set to `false` asks for the payment offer rather than a trial call, so nothing is charged:

```sh
curl -s -X POST https://vehcdj664efetfrsolne5umanq.srv.us/mcp -H 'content-type: application/json' -d @- <<'EOF' \
  | jq '.result.structuredContent.accepts[0] | {scheme, network, amount}'
{ "jsonrpc": "2.0", "id": 1, "method": "tools/call",
  "params": { "name": "attestpage_check_packages",
    "arguments": { "packages": [{ "ecosystem": "npm", "name": "left-pad" }, { "ecosystem": "pypi", "name": "requests" }] },
    "_meta": { "attestpage/trial": false } } }
EOF
```

```json
{
  "scheme": "exact",
  "network": "eip155:8453",
  "amount": "5000"
}
```

The result has `isError: true` and the x402 payment requirements in `structuredContent`, with the same `amount` as step 2. Sign one of `accepts`, put the payload in `_meta["x402/payment"]` and call again: `structuredContent` is then the same JSON as `answer.json`, and `_meta["x402/payment-response"]` holds the settlement.

## Questions

### How much does a check cost?

US$0.005 per batch of up to 10 names. Checking 23 names takes 3 batches, so 3 calls.

### Is a batch charged if a lookup fails?

Yes, when the batch is checked. A failed lookup is a result, and `exists` is `null` for it. A request that fails validation, and a batch where every registry lookup failed, are never charged.

### Does `exists: true` mean the package is fine to install?

No. It means the registry listed the name when we looked. Check the publisher, the homepage and the advisories yourself before you install it.
