AttestPage · Docs · Pricing · OpenAPI · llms.txt

How to check a package's licence before installing it, step by step with curl

Last updated: 2026-10-13

To read the licence of a package an agent is about to install, 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 the license field the registry holds, with exists, the latest version, the links and any OSV.dev advisories, under one signed receipt.

This guide covers the license field and what to decide from it. For the whole call and the other fields, see How to check packages before installing them. Commands use curl and jq against https://vehcdj664efetfrsolne5umanq.srv.us, then the same call as an MCP tool.

What the licence field is

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.

1. See an answer for free

The free sample checks four demo names. Three are listed, each with a different licence form, and one does not exist:

curl -s https://vehcdj664efetfrsolne5umanq.srv.us/v1/sample/packages | jq '.results[] | {name, exists, license}'
{
  "name": "left-pad",
  "exists": true,
  "license": "WTFPL"
}
{
  "name": "requests",
  "exists": true,
  "license": "Apache-2.0"
}
{
  "name": "serde",
  "exists": true,
  "license": "MIT OR Apache-2.0"
}
{
  "name": "left-pad-demo-missing",
  "exists": false,
  "license": null
}

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:

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. Put the batch in the same packages.json as step 2:

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 shows this with plain fetch, and the MCP server does it for you. Save the answer as answer.json for the steps below.

4. Decide from the licence

List the licence next to the other fields that decide an install:

jq '.results[] | {name, exists, license, latest_version, homepage}' answer.json

Then sort each name into one of three groups. The allow list is yours.

licenseWhat to do
A form on your allow list, for example MIT, Apache-2.0 or BSD-3-ClauseInstall. Open homepage or repository once to confirm it is the package you meant.
An expression such as MIT OR Apache-2.0OR offers a choice: you may use the package under either. AND means every licence applies. Check each part against your list.
Anything else: null, free text, UNLICENSED, SEE LICENSE IN …, or a name not on your listDo not install on the agent's own say-so. Read the licence in the package's repository, or ask the person who owns the decision.

A null licence is not a permissive one: with no licence stated you have no stated permission to use the code. When exists is false there is nothing to install, and the model may have invented the name.

To print every listed name whose licence is not on your allow list, with ALLOW a JSON array of the licences you accept:

ALLOW='["MIT","Apache-2.0","BSD-3-Clause","ISC"]'
jq --argjson allow "$ALLOW" '.results[] | select(.exists != false and ((.license // "") as $l | $allow | index($l) | not)) | {name, license}' answer.json

This is an exact match: MIT OR Apache-2.0 is not in the list above, so it is printed for you to read. The check reads what the registry says. It does not detect a registry licence that differs from the one in the package's files.

5. Keep the receipt

The receipt field is a compact JWS. It signs, for each name, whether it exists and its latest version, with the hash of the registry's answer, so you can keep it with the decision. Check it with the free verify route:

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:

{
  "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:

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
{
  "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 this a licence compliance check?

No. It returns the licence text the registry holds for the latest version. It does not read the package's files, its dependencies' licences or your own policy. Use it to catch a missing or unexpected licence before an install, and make the final call yourself.

Why is license null for a package that exists?

The registry holds no licence text for the latest version, or only whitespace. Treat it as unknown and read the package's repository.

Does sending a version give that version's licence?

No. A version selects the version whose advisories are listed. license is still the latest version's (npm and PyPI) or the default version's (crates.io).