{
  "markdown": "# x402lint\n\n[![PyPI](https://img.shields.io/pypi/v/x402lint)](https://pypi.org/project/x402lint/)\n[![GitHub Marketplace](https://img.shields.io/badge/Marketplace-x402%20conformance%20check-2ea44f?logo=github)](https://github.com/marketplace/actions/x402-conformance-check)\n\n<!-- mcp-name: io.github.arden-instance/x402lint -->\n\nA conformance linter for the [x402](https://x402.org) agent-payments protocol.\nPoint it at an HTTP endpoint that charges for access and it tells you whether the\n`402 Payment Required` challenge it returns is well-formed — the check an agent\nruntime does before it will pay.\n\n```\n$ x402lint check https://riddlex402.vercel.app/api/riddle\nPASS  status: HTTP 402 Payment Required\nINFO  format: x402 v2 (payment-required header)\nPASS  header-decode: payment-required header is base64 JSON\nPASS  x402Version: 2\nPASS  error: 'Payment required'\nPASS  resource.url: https://riddlex402.vercel.app/api/riddle\nPASS  accepts: 1 payment option(s)\nPASS  accepts[0].required: all required fields present\nPASS  accepts[0].scheme: 'exact'\nPASS  accepts[0].network: eip155:8453 (CAIP-2)\nPASS  accepts[0].amount: 2000 atomic units\nPASS  accepts[0].asset: valid EVM address\nPASS  accepts[0].payTo: valid EVM address\nPASS  accepts[0].maxTimeoutSeconds: 300\nPASS  accepts[0].extra: EIP-712 domain: name='USD Coin' version='2'\nINFO  discovery: advertises the 'bazaar' discovery extension\n\n14 pass, 0 warn, 0 fail  (CONFORMANT)\n```\n\n## Install\n\n```\npip install x402lint\n```\n\nThe linter (`check` / `decode` / `facilitator` / `survey`) is pure standard\nlibrary, Python 3.12+. The `pay` command additionally needs an EIP-712 signer:\n`pip install 'x402lint[pay]'`.\n\n## Commands\n\n### `x402lint check <url>`\n\nFetches `<url>` with no payment header, expects a `402`, and checks the payment\nchallenge:\n\n- status is exactly `402`\n- **wire format** — v2 (`payment-required` base64 header, the common case today)\n  or v1 (`x402Version: 1` JSON body). Reports which.\n- the challenge document decodes / parses\n- `x402Version` is an integer, `error` is a human-readable string\n- `accepts` is a non-empty array, and for every entry:\n  - required fields present (`scheme`, `network`, amount, `asset`, `payTo`,\n    `maxTimeoutSeconds`)\n  - `scheme` in a known set (`exact`, `upto`, `batch-settlement`) — unknown warns\n  - `network` is CAIP-2 shaped (v2) or a recognised name (v1) — unknown warns\n  - amount is a base-10 string of a positive integer (atomic units)\n  - `asset` / `payTo` are valid `0x…` addresses on EVM networks\n  - `exact`/EVM entries carry `extra.name` + `extra.version` for the EIP-712 domain\n  - v1 entries carry an absolute `resource` URL\n- discovery metadata (`extensions.bazaar` / v1 `outputSchema`) — reported, not required\n\n`--json` emits a machine-readable report (for CI). Exit code: `0` conformant\n(warnings allowed), `1` any failure, `2` tool error.\n\nFor a POST endpoint that validates its request body before returning the `402`\n(most LLM gateways), pass a body with `--data` (implies POST; `@file` or `-`\nreads a file / stdin):\n\n```\n$ x402lint check https://x402.telnyx.com/v1/chat/completions \\\n    --data '{\"model\":\"google/gemma-2b-it\",\"messages\":[{\"role\":\"user\",\"content\":\"hi\"}]}'\n```\n\n### `x402lint decode <blob>`\n\nPretty-prints any base64 x402 header blob — `payment-required`, `X-PAYMENT`,\n`payment-response` — and labels what kind of document it is. `-` reads stdin.\n\n```\ncurl -sD - https://weather.payapi.market/current \\\n  | grep -i ^payment-required: | cut -d' ' -f2 \\\n  | x402lint decode -\n```\n\n### `x402lint facilitator [url]`\n\nFetches `GET <url>/supported` and lists every `(x402Version, scheme, network)`\ntriple the facilitator can `verify` / `settle`, plus its advertised extensions.\nWarns on unknown schemes or non-CAIP-2 v2 networks. `url` defaults to\n`https://x402.org/facilitator` (the public testnet facilitator). `--json`.\n\n```\n$ x402lint facilitator\n  v2  exact              eip155:84532\n  v2  upto               eip155:84532 +extra\n  v2  batch-settlement   eip155:84532\n  ...\n11 kind(s): schemes batch-settlement, exact, upto; 9 network(s); versions 1, 2\n```\n\n### `x402lint survey [catalogue]`\n\nPulls a discovery catalogue (`catalogue` defaults to the Coinbase CDP\n`.../x402/discovery/resources` list), takes the `--limit` busiest resources by\n30-day call volume, and runs `check` on each — a quick \"state of x402\nconformance\" snapshot. It replays each resource's advertised `bazaar` input\nmethod and example query params so the request actually reaches the paywall\n(`--no-hints` to force a plain `GET`). `--json`.\n\n```\n$ x402lint survey --limit 8\nok   v2  https://x402.twit.sh/tweets/search?from=elonmusk&minLikes=100&words=bitcoin\nFAIL v2  https://x402.tavily.com/search\n       - accepts[1].amount: 'amount' must be a base-10 string of a positive integer, got '0.016'\n...\n7/8 endpoints conformant\n```\n\nRecurring survey results — a per-host conformance table of the busiest live x402\nendpoints — are maintained in [SURVEY.md](./SURVEY.md), with dated snapshots in\n[`data/`](./data/).\n\n### `x402lint pay <url>`\n\nFetches the endpoint's 402, picks the first `exact`-scheme `accepts[]` entry\n(or `--accept-index N`), and signs an EIP-3009 `TransferWithAuthorization`\npayment **offline** — no transaction, no gas, just an EIP-712 signature. Prints\nthe `X-PAYMENT` header value a client would send back. The EIP-712 domain\n(`name`/`version`/`chainId`/`verifyingContract`) is read from the wire\n(`accepts[].extra` + `network` + `asset`), never hardcoded.\n\nThe private key comes from an env var (`X402LINT_PRIVATE_KEY` by default,\n`--key-env NAME` to change) and is never logged. Needs the `pay` extra:\n\n```\npip install 'x402lint[pay]'\nexport X402LINT_PRIVATE_KEY=0x...\n$ x402lint pay https://api.example.com/data\n# payer     0x19E7E376E7C213B7E7e7e46cc70A5dD086DAff2A\n# asset     0x036CbD53842c5426634e7929541eC2318f3dCF7e  (USDC v2, chain 84532)\n# payTo     0x209693Bc6afc0C5328bA36FaF03C514EF312287C\n# value     1000 atomic units\n# expires   validBefore=1756431600\n\nX-PAYMENT: eyJ4NDAyVmVyc2lvbiI6MSwic2NoZW1lIjoiZXhhY3Qi...\n```\n\n`--json` emits the payer, authorization tuple, signature, full `PaymentPayload`,\nand header.\n\n### `x402lint roundtrip <url>`\n\n`pay`, then resend the request with the `X-PAYMENT` header and report what the\nserver did with it. Decodes the `X-PAYMENT-RESPONSE` header (`success`,\n`transaction`, `network`); falls back to the response body's `error` string when\nthe payment is rejected. Exits `0` only if the payment settled, `1` otherwise.\n\n```\nexport X402LINT_PRIVATE_KEY=0x...\n$ x402lint roundtrip https://api.example.com/data\n# payer     0x19E7E376E7C213B7E7e7e46cc70A5dD086DAff2A\n# payTo     0x209693Bc6afc0C5328bA36FaF03C514EF312287C\n# value     1000 atomic units  (chain 84532)\n# retry     HTTP 200\n\nSETTLED  tx 0xabc123...\n```\n\nNeeds the `pay` extra and a funded key for a real settlement; without funds it\nreports `NOT SETTLED (insufficient_funds)` after exercising the full path.\n\n#### `--facilitator <url>`\n\nSettle **directly** against a facilitator's `/verify` + `/settle` rather than\nre-sending to the resource server. Useful when the resource server builds its own\n(CAIP-2) `paymentRequirements` and self-fails against a facilitator that only\naccepts v1 friendly names there. x402lint translates the challenge into the v1\nsettle envelope (`base-sepolia`, `maxAmountRequired`, `x402Version: 1`) and stops\nbefore `/settle` if `/verify` rejects the payment.\n\n```\n$ x402lint roundtrip --facilitator https://x402.org/facilitator https://x402.org/protected\n# payer        0xc838ED72fd5905C30801515DdC7B5cc13F36E88D\n# payTo        0x209693Bc6afc0C5328bA36FaF03C514EF312287C\n# value        10000 atomic units  (base-sepolia)\n# facilitator  https://x402.org/facilitator\n# verify       HTTP 200  -> valid\n\nSETTLED  tx 0x188066d0...\n```\n\n## GitHub Action\n\nRun the linter in CI so a deploy that breaks your `402` challenge fails the\nbuild. The repo ships a composite action at its root, also listed on the\n[GitHub Marketplace](https://github.com/marketplace/actions/x402-conformance-check):\n\n```yaml\n# .github/workflows/x402.yml\nname: x402 conformance\non: [push, pull_request]\njobs:\n  check:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: arden-instance/x402lint@v0.5.2\n        with:\n          url: https://your-endpoint.example/api\n          # url: |               # multiple endpoints, one per line\n          #   https://a.example/x\n          #   https://b.example/y\n          # version: 0.5.2        # pin the linter (default: latest)\n          # strict: \"true\"        # also fail on WARN-level findings\n```\n\nThe step exits non-zero (failing the job) if any endpoint returns a\nnon-conformant `402`, annotating the run with the specific findings.\n\nA runnable worked example lives at\n[`.github/workflows/x402.yml`](.github/workflows/x402.yml) in this repo — it\npoints the action at a known-good public endpoint on a weekly schedule. Copy it\nand swap in your own URL(s).\n\n## MCP server\n\n`pip install 'x402lint[mcp]'` adds an `x402lint mcp` subcommand (also installed\nas `x402lint-mcp`), a [Model Context Protocol](https://modelcontextprotocol.io)\nserver (stdio transport) so an agent or IDE assistant can lint an x402 endpoint\nwithout shelling out. It exposes three tools:\n\n| tool | what it does |\n| --- | --- |\n| `lint_endpoint` | fetch a URL unpaid, expect a `402`, return the conformance report |\n| `decode_payment` | decode + classify a base64 `X-PAYMENT` / `accepts` blob |\n| `check_facilitator` | summarise a facilitator's settleable scheme/network pairs |\n\n```jsonc\n// claude_desktop_config.json / any MCP client\n{\n  \"mcpServers\": {\n    // installed on PATH:\n    \"x402lint\": { \"command\": \"x402lint-mcp\" }\n    // or zero-install via uv:\n    // \"x402lint\": { \"command\": \"uvx\", \"args\": [\"--from\", \"x402lint[mcp]\", \"x402lint\", \"mcp\"] }\n  }\n}\n```\n\n## Protocol notes\n\nTwo wire formats exist. **v2** (`x402Version: 2`, Linux Foundation spec) is\ndominant in the wild as of 2026: the `PaymentRequired` document travels\nbase64-encoded in the `payment-required` response header, networks are CAIP-2\nids (`eip155:8453`), the amount field is `amount`. **v1** is the legacy format:\nthe document is the JSON body, networks are friendly names (`base`), the amount\nfield is `maxAmountRequired`. x402lint handles both.\n\n## License\n\nMIT\n",
  "bytes": 10295,
  "sha": "3da03da14e75328920ad25eb9e0d7a5cae99667d34bb74e4932979bd18c25920",
  "repo_slug": "arden-instance/x402lint",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_arden_instance_x402lint_70eec04e/readme"
}