From 6d7cd2c664070c5c412e85ebe51d7855bf0c10c3 Mon Sep 17 00:00:00 2001 From: Rodolphe Marques Date: Tue, 11 Aug 2026 16:37:09 +0200 Subject: [PATCH 01/10] feat(skills): distribute both skills as Claude Code plugins (#2595) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds .claude-plugin/marketplace.json so the two published skills install with '/plugin marketplace add nevermined-io/docs' plus one '/plugin install' each, and auto-update thereafter — replacing a six-line sparse-checkout incantation. The marketplace is named 'nevermined', so users see and type '@nevermined' rather than the repository name. Entries use source './skills' with an explicit per-plugin skills array (the shape Anthropic's own amd-skills entry uses): 'source: "./"' also works but copies the entire 33 MB docs repo into the plugin cache for a 64 KB skill. Verified with 'claude plugin validate', then by adding the marketplace and installing both plugins locally. Co-Authored-By: Claude Opus 5 (1M context) --- .claude-plugin/marketplace.json | 59 +++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 .claude-plugin/marketplace.json diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json new file mode 100644 index 00000000..9f4a33c7 --- /dev/null +++ b/.claude-plugin/marketplace.json @@ -0,0 +1,59 @@ +{ + "$schema": "https://anthropic.com/claude-code/marketplace.schema.json", + "name": "nevermined", + "description": "Nevermined's AI coding skills: monetize an agent with the Payments SDK, or let one buy from external x402 / MPP services through the Nevermined Router.", + "owner": { + "name": "Nevermined", + "url": "https://nevermined.ai" + }, + "plugins": [ + { + "name": "nevermined-payments", + "displayName": "Nevermined Payments", + "description": "Charge for your AI agent or API. Covers the Nevermined Payments SDK (TypeScript and Python), the x402 protocol, payment plans and credits, and middleware for Express, FastAPI, Strands, MCP and Google A2A.", + "author": { + "name": "Nevermined", + "url": "https://nevermined.ai" + }, + "homepage": "https://nevermined.ai/docs/development-guide/build-using-nvm-skill", + "license": "MIT-0", + "category": "development", + "keywords": [ + "payments", + "x402", + "agents", + "monetization", + "mcp" + ], + "source": "./skills", + "strict": false, + "skills": [ + "./nevermined-payments" + ] + }, + { + "name": "nevermined-router", + "displayName": "Nevermined Router", + "description": "Let your agent pay external services it has no account with. Covers discovering services in the Agent Services Catalog, creating a capped spending Delegation, paying any x402 or MPP endpoint through the Nevermined Router, reading the payment ledger, and the guardrails an autonomous buyer must respect.", + "author": { + "name": "Nevermined", + "url": "https://nevermined.ai" + }, + "homepage": "https://nevermined.ai/docs/products/router/overview", + "license": "MIT-0", + "category": "development", + "keywords": [ + "payments", + "x402", + "mpp", + "agents", + "autonomous-buying" + ], + "source": "./skills", + "strict": false, + "skills": [ + "./nevermined-router" + ] + } + ] +} From e2ea41403d7cf029fa75cdb93073cf9125431f9b Mon Sep 17 00:00:00 2001 From: Rodolphe Marques Date: Tue, 11 Aug 2026 16:37:24 +0200 Subject: [PATCH 02/10] feat(skills): add condensed Router guidance to the Copilot and Codex files (#2595) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit .github/copilot-instructions.md and AGENTS.md are single files with no per-skill segment, so until now they carried the payments half only — an assistant reading either could help you charge for an agent but not buy from one. Each now gains a Nevermined Router section: the buy loop, the two money traps (requestId is an idempotency key not a counter; targetUrl is a complete URL not a base), whole-cent rounding, and the full BCK.ROUTER.* guardrail table with 'never widen a Delegation to get past a refusal'. Condensed rather than complete — these files are always-on context, so every line competes with the payments content. Co-Authored-By: Claude Opus 5 (1M context) --- .github/copilot-instructions.md | 74 ++++++++++++++++++++++++++++++++- AGENTS.md | 71 ++++++++++++++++++++++++++++++- 2 files changed, 141 insertions(+), 4 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 3ffe90e9..a7aa1c09 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,6 +1,6 @@ -# Nevermined Payments — GitHub Copilot Instructions +# Nevermined — GitHub Copilot Instructions -This repository contains documentation for Nevermined, an AI payment infrastructure platform. When assisting with code that integrates Nevermined payments, follow these patterns. +This repository contains documentation for Nevermined, an AI payment infrastructure platform. It covers two halves: **receiving** payments (the SDK and REST patterns below) and **spending** at external x402 / MPP services through the **Nevermined Router** (last section). When assisting with code that integrates Nevermined, follow these patterns. ## SDK Packages @@ -95,6 +95,76 @@ Card payments: `scheme: "nvm:card-delegation"`, `network: "stripe"`. A human is - **Delegation first:** create a spending delegation (`POST /delegation/create` with `provider` + `currency` + `spendingLimitCents` + `durationSecs`, or SDK `createDelegation`) and reuse its `delegationId`. The inline create-on-the-fly `delegationConfig` (passing limits without a `delegationId`) is **deprecated since `@nevermined-io/payments` 1.8.0** — it only emits a runtime warning now; always pass an explicit `delegationId`. - **Seller analytics:** discover your `orgId` from `.orgId` on `GET /protocol/plans` / `/protocol/agents` records (no separate lookup endpoint). A malformed/placeholder `orgId` returns a silent 200-of-zeros; a non-Premium org returns `403 BCK.ORGANIZATIONS.0022`; a non-admin org returns `403 BCK.AUTH.0004`. +## Nevermined Router — paying external services + +Use the Router when the agent must **pay** a service it has no account with — any x402 agent or MPP merchant. Everything above is the other half: *receiving* payments and buying Nevermined plans. Plain HTTP, no SDK: `Authorization: Bearer $NVM_API_KEY` against `$NVM_API_URL` (`https://api.sandbox.nevermined.app` sandbox, `https://api.live.nevermined.app` live). **Never send `NVM_API_KEY` to the merchant** — it authenticates you to Nevermined only; the merchant's own auth goes in `headers`. + +The Router pays a price quoted **on the wire**. A service answering `401`/`403` rather than `402` wants authentication, not payment — say so, don't route it. + +### 1. Create a Delegation (the budget) + +```bash +curl -sX POST "$NVM_API_URL/api/v1/delegation/create" \ + -H "Authorization: Bearer $NVM_API_KEY" -H "Content-Type: application/json" \ + -d '{"provider":"erc4337","currency":"usdc","spendingLimitCents":500,"durationSecs":604800}' +# → { "delegationId": "5e7481c3-…" } +``` + +All four fields are required — no defaults. `erc4337` is the crypto-funded Delegation both stablecoin rails need. Create it once and reuse the id. + +### 2. Fund the buyer wallet + +Both rails **pull** from your own custodial wallet: a Delegation authorizes a spend, it does not supply funds. Read the address off the live Delegation every time — `GET /api/v1/delegation/{id}` → `providerPaymentMethodId` — and fund it with the payment asset on the network you intend to pay on. **Never reuse a cached address**: a stale one is the most common cause of `402 BCK.ROUTER.0009`, and that error deliberately does not echo the address it checked. + +### 3. Discover a service (public catalog, no API key) + +```bash +curl -s "$NVM_API_URL/api/v1/catalog/services?protocol=x402&search=web+search" +``` + +- Only `protocol` of `x402` or `mpp` is routable — filter for them. +- **`targetUrl` is the default endpoint's complete URL, not a base** (it may already carry the path), so concatenating yields `/search/search`. Resolve instead: + +```ts +const url = endpoint ? new URL(endpoint.path, service.targetUrl).toString() : service.targetUrl +``` + +### 4. Pay + +```bash +curl -sX POST "$NVM_API_URL/api/v1/router/route" \ + -H "Authorization: Bearer $NVM_API_KEY" -H "Content-Type: application/json" \ + -d '{"delegationId":"'"$NVM_DELEGATION_ID"'","url":"https://service.example/api/resource", + "method":"POST","body":{"query":"…"},"requestId":"search-nevermined-router-v1"}' +# → { "status": 200, "body": {…}, "paid": true, +# "payment": { "paymentId": "…", "settlement": { "approxCents": "1" }, "status": "Settled" } } +``` + +The Router probes the merchant, auto-detects the protocol from the 402, pays and relays. `status`/`body` are the merchant's own; `paid: false` with no `payment` block means the resource was free — handle that. For streaming use `ALL /api/v1/router/proxy` with `X-Router-Target-Url`, `X-Router-Delegation-Id` and `X-Router-Request-Id` headers. + +- **`requestId` is an idempotency key, not a request counter.** Use one stable id per logical purchase, reused across retries of that purchase: the same id returns the original payment, a fresh id buys again. **A fresh `uuid4()` per HTTP attempt is how an agent double-spends.** +- Budget is debited in **whole cents, rounded up** — 1000 calls at $0.001 costs $10.00, not $1.00. `settlement.approxCents` is what was actually reserved; trust it over any catalog `priceLabel`. + +### 5. Read what you spent + +`GET /api/v1/router/payments` (filters `delegationId`, `from`, `to`, `format=csv`) and `/api/v1/router/payments/summary`. `amount` is the asset's smallest unit, not cents. A record at `Issued` is **not** an error — the money moved; do not retry it. + +### Guardrails — a refusal is the system working + +| Code | Status | Meaning | Retry? | +| --- | --- | --- | --- | +| `BCK.ROUTER.0001` | 400 | Bad input / no fundable option / non-allowlisted asset; `details` names it | No | +| `BCK.ROUTER.0002` | 409 | `requestId` already used; the original `paymentId` is in the response | No | +| `BCK.ROUTER.0003` | 402 | Delegation over cap, expired, exhausted or revoked | No — **stop** | +| `BCK.ROUTER.0006` | 500 | Transient failure building the payments summary | **Yes** | +| `BCK.ROUTER.0007` | 429 | Too many concurrent routed requests in flight | **Yes**, after backoff | +| `BCK.ROUTER.0008` | 403 | Legacy API key — create a new one | No | +| `BCK.ROUTER.0009` | 402 | Wallet short on the target network; nothing was signed | No — **stop** | + +**Never widen a Delegation, and never create a second one, to get past a refusal.** The cap is the user's decision, not a runtime obstacle; minting a fresh Delegation to escape an exhausted one defeats the whole mechanism. Report and stop. Only `0006` and `0007` are retryable — everything else is a decision, and retrying it unchanged gives the same answer. Delegations also expire silently, so check `expiresAt` before diagnosing a `0003` as anything else. + ## Full Reference See `skills/nevermined-payments/SKILL.md` for complete integration patterns (Track A = operate autonomously via REST; Track B = add payments to your code via SDK). + +See `skills/nevermined-router/SKILL.md` and its `references/` for the Router half — discovery, mode A vs mode B, the ledger, and every guardrail. diff --git a/AGENTS.md b/AGENTS.md index 8eba8eda..074292a3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,6 @@ -# Nevermined Payments SDK +# Nevermined — Payments SDK and Router -This repository contains documentation for Nevermined, an AI payment infrastructure platform. When assisting with code that integrates Nevermined payments, follow these patterns. +This repository contains documentation for Nevermined, an AI payment infrastructure platform. It covers two halves: **receiving** payments (the SDK and REST patterns below) and **spending** at external x402 / MPP services through the **Nevermined Router** (last section). When assisting with code that integrates Nevermined, follow these patterns. ## SDK Packages @@ -82,6 +82,73 @@ await payments.a2a.start({ port: 3005, basePath: '/a2a/', agentCard, executor }) When an agent must act on its own behalf at runtime (buy a plan, enroll a card, check credits/revenue), call the REST API directly with `Authorization: Bearer $NVM_API_KEY` against `https://api.sandbox.nevermined.app` (sandbox) or `https://api.live.nevermined.app` (live). Buy in two calls — `POST /api/v1/x402/permissions` (→ `accessToken`) then `POST /api/v1/x402/settle` (→ `creditsRedeemed`, `remainingBalance`). Crypto uses `scheme: "nvm:erc4337"` / `network: "eip155:84532"`; cards use `scheme: "nvm:card-delegation"` / `network: "stripe"`. A human is needed only for one-time setup — the first API key, plus card enrollment if paying by card (the stablecoin path needs neither). Full runbook: `skills/nevermined-payments/references/autonomous-operations.md`. +## Nevermined Router — paying external services + +Use the Router when the agent must **pay** a service it has no account with — any x402 agent or MPP merchant. Everything above is the other half: *receiving* payments and buying Nevermined plans. Plain HTTP, no SDK: `Authorization: Bearer $NVM_API_KEY` against `$NVM_API_URL` (`https://api.sandbox.nevermined.app` sandbox, `https://api.live.nevermined.app` live). **Never send `NVM_API_KEY` to the merchant** — it authenticates you to Nevermined only; the merchant's own auth goes in `headers`. The Router pays a price quoted **on the wire**, so a service answering `401`/`403` rather than `402` wants authentication, not payment — say so, don't route it. + +### 1. Create a Delegation (the budget) + +```bash +curl -sX POST "$NVM_API_URL/api/v1/delegation/create" \ + -H "Authorization: Bearer $NVM_API_KEY" -H "Content-Type: application/json" \ + -d '{"provider":"erc4337","currency":"usdc","spendingLimitCents":500,"durationSecs":604800}' +# → { "delegationId": "5e7481c3-…" } +``` + +All four fields are required — no defaults. `erc4337` is the crypto-funded Delegation both stablecoin rails need. Create it once and reuse the id. + +### 2. Fund the buyer wallet + +Both rails **pull** from your own custodial wallet: a Delegation authorizes a spend, it does not supply funds. Read the address off the live Delegation every time — `GET /api/v1/delegation/{id}` → `providerPaymentMethodId` — and fund it with the payment asset on the network you intend to pay on. **Never reuse a cached address**: a stale one is the most common cause of `402 BCK.ROUTER.0009`, and that error deliberately does not echo the address it checked. + +### 3. Discover a service (public catalog, no API key) + +```bash +curl -s "$NVM_API_URL/api/v1/catalog/services?protocol=x402&search=web+search" +``` + +Only `protocol` of `x402` or `mpp` is routable — filter for them. **`targetUrl` is the default endpoint's complete URL, not a base** (it may already carry the path), so concatenating yields `/search/search`. Resolve instead: + +```ts +const url = endpoint ? new URL(endpoint.path, service.targetUrl).toString() : service.targetUrl +``` + +### 4. Pay + +```bash +curl -sX POST "$NVM_API_URL/api/v1/router/route" \ + -H "Authorization: Bearer $NVM_API_KEY" -H "Content-Type: application/json" \ + -d '{"delegationId":"'"$NVM_DELEGATION_ID"'","url":"https://service.example/api/resource", + "method":"POST","body":{"query":"…"},"requestId":"search-nevermined-router-v1"}' +# → { "status": 200, "body": {…}, "paid": true, +# "payment": { "paymentId": "…", "settlement": { "approxCents": "1" }, "status": "Settled" } } +``` + +The Router probes the merchant, auto-detects the protocol from the 402, pays and relays. `status`/`body` are the merchant's own; `paid: false` with no `payment` block means the resource was free — handle that. For streaming use `ALL /api/v1/router/proxy` with `X-Router-Target-Url`, `X-Router-Delegation-Id` and `X-Router-Request-Id` headers. + +- **`requestId` is an idempotency key, not a request counter.** Use one stable id per logical purchase, reused across retries of that purchase: the same id returns the original payment, a fresh id buys again. **A fresh `uuid4()` per HTTP attempt is how an agent double-spends.** +- Budget is debited in **whole cents, rounded up** — 1000 calls at $0.001 costs $10.00, not $1.00. `settlement.approxCents` is what was actually reserved; trust it over any catalog `priceLabel`. + +### 5. Read what you spent + +`GET /api/v1/router/payments` (filters `delegationId`, `from`, `to`, `format=csv`) and `/api/v1/router/payments/summary`. `amount` is the asset's smallest unit, not cents. A record at `Issued` is **not** an error — the money moved; do not retry it. + +### Guardrails — a refusal is the system working + +| Code | Status | Meaning | Retry? | +| --- | --- | --- | --- | +| `BCK.ROUTER.0001` | 400 | Bad input / no fundable option / non-allowlisted asset; `details` names it | No | +| `BCK.ROUTER.0002` | 409 | `requestId` already used; the original `paymentId` is in the response | No | +| `BCK.ROUTER.0003` | 402 | Delegation over cap, expired, exhausted or revoked | No — **stop** | +| `BCK.ROUTER.0006` | 500 | Transient failure building the payments summary | **Yes** | +| `BCK.ROUTER.0007` | 429 | Too many concurrent routed requests in flight | **Yes**, after backoff | +| `BCK.ROUTER.0008` | 403 | Legacy API key — create a new one | No | +| `BCK.ROUTER.0009` | 402 | Wallet short on the target network; nothing was signed | No — **stop** | + +**Never widen a Delegation, and never create a second one, to get past a refusal.** The cap is the user's decision, not a runtime obstacle; minting a fresh Delegation to escape an exhausted one defeats the whole mechanism. Report and stop. Only `0006` and `0007` are retryable — everything else is a decision, and retrying it unchanged gives the same answer. Delegations also expire silently, so check `expiresAt` before diagnosing a `0003` as anything else. + ## Full Reference See `skills/nevermined-payments/SKILL.md` for complete integration patterns (Track A = operate autonomously via REST; Track B = add payments to your code via SDK) and its reference files. + +See `skills/nevermined-router/SKILL.md` and its `references/` for the Router half — discovery, mode A vs mode B, the ledger, and every guardrail. From c04a80581bd80eeba0b74d4a7976c528aa08d3d0 Mon Sep 17 00:00:00 2001 From: Rodolphe Marques Date: Tue, 11 Aug 2026 16:37:24 +0200 Subject: [PATCH 03/10] docs(skills): lead the Claude Code install with the plugin, and document ClawHub (#2595) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The install page told Claude Code users to sparse-checkout git, and never mentioned ClawHub at all — despite a CI job publishing both skills there on every push to skills/**. The registry we pay to publish to was invisible to readers. - Claude Code tab now leads with the plugin; the file copy stays as the pin-a-version fallback. - New ClawHub tab. Notes that --dir is a global flag (it must precede the subcommand) and that the payments skill's slug is 'nevermined', not 'nevermined-payments'. - 'Supported Tools at a Glance' no longer says Router is unavailable on Copilot and Codex, and gains a ClawHub row. - CLAUDE.md records both distribution channels and why source must stay './skills'. Co-Authored-By: Claude Opus 5 (1M context) --- CLAUDE.md | 6 +- development-guide/build-using-nvm-skill.mdx | 76 ++++++++++++++------- 2 files changed, 57 insertions(+), 25 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index a8a3dc45..7e52a585 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -43,8 +43,10 @@ They give AI coding assistants (Claude Code, Cursor, Copilot, Codex, Windsurf, C | `.windsurf/rules/.md` | payments, router (**6,000-char limit per file**) | | `.clinerules/.md` | payments, router | | `.amazonq/rules/.md` | payments, router | -| `.github/copilot-instructions.md` | payments only — single file, no skill segment | -| `AGENTS.md` | payments only — single file, no skill segment | +| `.github/copilot-instructions.md` | payments + a condensed Router section — single file, no per-skill split | +| `AGENTS.md` | payments + a condensed Router section — single file, no per-skill split | + +Both skills are also distributed as **Claude Code plugins** via `.claude-plugin/marketplace.json` at the repo root (`/plugin marketplace add nevermined-io/docs`), and to the **ClawHub** registry by `.github/workflows/publish-skill-clawhub.yml`. The marketplace entries use `source: "./skills"` with an explicit per-plugin `skills` array, so each plugin ships only `skills/` and not the whole 33 MB docs repo — do not change `source` to `"./"`. Validate any edit with `claude plugin validate .` before committing. `.cursor/` is otherwise gitignored; `.gitignore` re-includes `.cursor/rules/` specifically, because those files are published — users `curl` them from `main`, so one that fails to commit becomes a 404 in their editor. Both `development-guide/build-using-nvm-skill.mdx` (the install page) and its "Supported Tools at a Glance" table must be updated when this set changes. diff --git a/development-guide/build-using-nvm-skill.mdx b/development-guide/build-using-nvm-skill.mdx index 7abfc489..aeda5c26 100644 --- a/development-guide/build-using-nvm-skill.mdx +++ b/development-guide/build-using-nvm-skill.mdx @@ -17,11 +17,11 @@ There are two, and which one you want depends on which side of the payment you'r | **`nevermined-payments`** | **receiving** money, or buying a Nevermined plan | SDK integration, middleware, plans, credits — the rest of this page | | **`nevermined-router`** | **spending** money at external services | Discovering services in the catalog, creating a spending Delegation, paying any x402 or MPP endpoint through the [Nevermined Router](/products/router/overview) | -Installing both is fine — they don't overlap. How you install the Router one depends on your tool: +Installing both is fine — they don't overlap. Every supported tool can now get either skill: -- **Claude Code, Cursor, Windsurf, Cline, Amazon Q** — swap `nevermined-payments` for `nevermined-router` in the paths below, or run the extra command shown in each tab. -- **Codex CLI** — its `AGENTS.md` route is a *single* file with no skill name in it, and that file carries the **payments** guidance only. Use the tab's **"install as a Codex skill"** alternative instead: `.agents/skills/` takes a skill directory, so `nevermined-router` works there. -- **GitHub Copilot** — `.github/copilot-instructions.md` is a single file with nothing to swap, and it carries the **payments** guidance only. Until that changes, use the [full skill on GitHub](https://github.com/nevermined-io/docs/tree/main/skills/nevermined-router) or paste it in manually. +- **Claude Code** — install them as plugins, one command each. See the tab below. +- **Cursor, Windsurf, Cline, Amazon Q** — swap `nevermined-payments` for `nevermined-router` in the paths below, or run the extra command shown in each tab. +- **Codex CLI and GitHub Copilot** — `AGENTS.md` and `.github/copilot-instructions.md` are *single* files with no skill name to swap, so each one carries **both** halves: the payments guidance and a condensed Router section. Take the whole file. Codex can also install `nevermined-router` as a proper skill directory under `.agents/skills/` — see its tab. ## What's Included @@ -34,6 +34,13 @@ The skill provides your coding assistant with: - **Client-side** integration — ordering plans, generating tokens, calling protected APIs - **Troubleshooting** for common errors (402, `-32003`, `-32002`) +And, from the Router skill: + +- **Service discovery** in the public Agent Services Catalog +- **Spending Delegations** — creating a capped budget and funding the buyer wallet +- **Paid calls** over x402 and MPP through one endpoint, with the protocol auto-detected +- **Guardrails** — every `BCK.ROUTER.*` code, what's retryable, and why a refusal must not be worked around + ## Install the Skill Choose your AI coding tool below for step-by-step installation instructions. @@ -42,7 +49,20 @@ Choose your AI coding tool below for step-by-step installation instructions. ### Claude Code - **Commands to install** from the Nevermined repository: + Install from the Nevermined plugin marketplace — two commands, and Claude Code keeps the skills up to date for you: + + ```bash + /plugin marketplace add nevermined-io/docs + + /plugin install nevermined-payments@nevermined # charging for your agent + /plugin install nevermined-router@nevermined # buying from external services + ``` + + Install either or both. The marketplace is named `nevermined` (that's the `@nevermined` suffix); `nevermined-io/docs` is just the repository it's served from. + + Claude Code loads each skill when it's relevant to your work, and refreshes it in the background when we publish changes. To update by hand, run `/plugin marketplace update nevermined`. + + **Alternative — copy the files directly.** Use this if you'd rather pin a version, or you're on a Claude Code build without plugin support: ```bash mkdir -p ~/.claude/skills @@ -57,27 +77,32 @@ Choose your AI coding tool below for step-by-step installation instructions. cd - rm -rf "$tmpdir" + ``` + Claude Code discovers skills automatically from `~/.claude/skills/` (personal) and `.claude/skills/` (project). Copied this way they're a snapshot — you re-run the command to update. + + + + ### ClawHub + + Both skills are published to the [ClawHub](https://clawhub.ai) registry under the `nevermined-io` publisher, so any tool that reads a `skills/` directory can pull them with one command: + + ```bash + npx clawhub install nevermined-router # buying through the Router + npx clawhub install nevermined # the payments skill ``` - This downloads the `SKILL.md` and all reference files into your local Claude Code skills directory. Claude Code automatically loads the skill when it's relevant to your work. + This writes to `skills//` in the current directory. `--dir` sets a different target — it's a global flag, so it goes **before** the subcommand: - **Alternative — manual installation:** + ```bash + npx clawhub --dir ~/.claude/skills install nevermined-router + ``` - 1. Clone or download the skill files: - ```bash - git clone https://github.com/nevermined-io/docs.git /tmp/nvm-docs - ``` - 2. Copy to your personal skills directory: - ```bash - cp -r /tmp/nvm-docs/skills/nevermined-payments ~/.claude/skills/ - ``` - 3. Or copy to a project-specific location: - ```bash - cp -r /tmp/nvm-docs/skills/nevermined-payments .claude/skills/ - ``` + No account or login is needed to install. Run `npx clawhub update` to pull the latest published version, and `npx clawhub inspect nevermined-router` to see what you'd get without installing it. - Claude Code discovers skills automatically from `~/.claude/skills/` (personal) and `.claude/skills/` (project). + + ClawHub publishes the payments skill under the slug **`nevermined`**, not `nevermined-payments`. The Router skill's slug matches its name. + @@ -238,13 +263,18 @@ Choose your AI coding tool below for step-by-step installation instructions. | Tool | Config File | Router available? | Auto-Activates | |---|---|---|---| -| **Claude Code** | `~/.claude/skills//SKILL.md` | Yes | Yes — when relevant | +| **Claude Code** | plugin, or `~/.claude/skills//SKILL.md` | Yes | Yes — when relevant | | **Cursor** | `.cursor/rules/.mdc` | Yes | Yes — on `.ts`, `.js`, `.py` files | | **Windsurf** | `.windsurf/rules/.md` | Yes | Yes — model decides | -| **GitHub Copilot** | `.github/copilot-instructions.md` | **No** — single file, payments only | Yes — always | -| **Codex CLI** | `AGENTS.md` or `.agents/skills//` | Yes — via `.agents/skills/`; **not** via `AGENTS.md` (single file, payments only) | Yes — always or when relevant | +| **GitHub Copilot** | `.github/copilot-instructions.md` | Yes — condensed, in the same single file | Yes — always | +| **Codex CLI** | `AGENTS.md` or `.agents/skills//` | Yes — condensed in `AGENTS.md`, in full via `.agents/skills/` | Yes — always or when relevant | | **Cline** | `.clinerules/.md` | Yes | Yes — on `.ts`, `.js`, `.py` files | | **Amazon Q** | `.amazonq/rules/.md` | Yes | Yes — always | +| **Any** (ClawHub) | `skills//SKILL.md` | Yes | Depends on the tool | + + +The Copilot and Codex files are **condensed** — they carry the buy loop, the money traps and every guardrail, but not the full reference set. For the complete Router skill, install it as a directory (Claude Code plugin, ClawHub, or `.agents/skills/`). + ## Example Prompts From fda4b5269e03f47e2e3bc1f58dc76a6a5a1aae74 Mon Sep 17 00:00:00 2001 From: Rodolphe Marques Date: Tue, 11 Aug 2026 16:47:16 +0200 Subject: [PATCH 04/10] fix(skills): document BCK.ROUTER.0010 in the new Router sections (#2595) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 0010 landed in nvm-monorepo on 2026-08-07 (fdf057ffd, the #2191 fee accrual) and is documented nowhere — not in the skill, not on the docs site, not in any IDE rule file. Adding a code table to two more files without it would ship a new artifact already known to be incomplete. It is the most retry-dangerous code in the set and the reason is not obvious: it is a 500, so an agent applying 'retry 5xx' — or reading our own 'only 0006 (500) and 0007 (429) are retryable' too loosely — will retry it. But a payment credential was already minted before it failed, and because no payment record was written the requestId will NOT suppress the retry. So each attempt mints a fresh credential and fails identically. The pre-existing surfaces (SKILL.md, references/errors.md, guardrails.mdx and the four IDE rule files) are fixed separately, off main — they don't depend on this branch. The generated codes.mdx is already covered by #287. Co-Authored-By: Claude Opus 5 (1M context) --- .github/copilot-instructions.md | 3 +++ AGENTS.md | 3 +++ 2 files changed, 6 insertions(+) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index a7aa1c09..f617d07d 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -160,6 +160,9 @@ The Router probes the merchant, auto-detects the protocol from the 402, pays and | `BCK.ROUTER.0007` | 429 | Too many concurrent routed requests in flight | **Yes**, after backoff | | `BCK.ROUTER.0008` | 403 | Legacy API key — create a new one | No | | `BCK.ROUTER.0009` | 402 | Wallet short on the target network; nothing was signed | No — **stop** | +| `BCK.ROUTER.0010` | 500 | Internal: the rail reported an unusable charge amount | **No — never blind-retry** | + +**`0010` is the one 500 you must not retry.** Unlike `0006`, a payment credential **was already minted** before it failed, and because no payment record was written your `requestId` will *not* suppress a retry — so retrying re-mints a fresh credential and fails identically. Report it instead. **Never widen a Delegation, and never create a second one, to get past a refusal.** The cap is the user's decision, not a runtime obstacle; minting a fresh Delegation to escape an exhausted one defeats the whole mechanism. Report and stop. Only `0006` and `0007` are retryable — everything else is a decision, and retrying it unchanged gives the same answer. Delegations also expire silently, so check `expiresAt` before diagnosing a `0003` as anything else. diff --git a/AGENTS.md b/AGENTS.md index 074292a3..26d9aea0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -144,6 +144,9 @@ The Router probes the merchant, auto-detects the protocol from the 402, pays and | `BCK.ROUTER.0007` | 429 | Too many concurrent routed requests in flight | **Yes**, after backoff | | `BCK.ROUTER.0008` | 403 | Legacy API key — create a new one | No | | `BCK.ROUTER.0009` | 402 | Wallet short on the target network; nothing was signed | No — **stop** | +| `BCK.ROUTER.0010` | 500 | Internal: the rail reported an unusable charge amount | **No — never blind-retry** | + +**`0010` is the one 500 you must not retry.** Unlike `0006`, a payment credential **was already minted** before it failed, and because no payment record was written your `requestId` will *not* suppress a retry — so retrying re-mints a fresh credential and fails identically. Report it instead. **Never widen a Delegation, and never create a second one, to get past a refusal.** The cap is the user's decision, not a runtime obstacle; minting a fresh Delegation to escape an exhausted one defeats the whole mechanism. Report and stop. Only `0006` and `0007` are retryable — everything else is a decision, and retrying it unchanged gives the same answer. Delegations also expire silently, so check `expiresAt` before diagnosing a `0003` as anything else. From 586e9a4cf2e4f633c82eb003ece6f6d2938d84b0 Mon Sep 17 00:00:00 2001 From: Rodolphe Marques Date: Tue, 11 Aug 2026 17:47:09 +0200 Subject: [PATCH 05/10] docs(skills): scope the 0006 retry comparison in the condensed Router sections MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Carrying across a finding from the sibling PR #291's review: 'Unlike 0006' was an apples-to-oranges comparison. BCK.ROUTER.0006 has exactly one throw site (router.service.ts:928, a read-only summary method), so it is never raised by a payment — meaning on the paying path 0007 is the only retryable code at all. Fixed in #291's three surfaces; these two carried the same wording and would otherwise have disagreed with them. Co-Authored-By: Claude Opus 5 (1M context) --- .github/copilot-instructions.md | 2 +- AGENTS.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index f617d07d..93257f91 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -162,7 +162,7 @@ The Router probes the merchant, auto-detects the protocol from the 402, pays and | `BCK.ROUTER.0009` | 402 | Wallet short on the target network; nothing was signed | No — **stop** | | `BCK.ROUTER.0010` | 500 | Internal: the rail reported an unusable charge amount | **No — never blind-retry** | -**`0010` is the one 500 you must not retry.** Unlike `0006`, a payment credential **was already minted** before it failed, and because no payment record was written your `requestId` will *not* suppress a retry — so retrying re-mints a fresh credential and fails identically. Report it instead. +**`0010` is the one 500 you must not retry.** A payment credential **was already minted** before it failed, and because no payment record was written your `requestId` will *not* suppress a retry — so retrying re-mints a fresh credential and fails identically. Report it instead. (`0006`, the retryable 500, is only ever raised by the payments *summary* read — never by a payment. On the paying path `0007` is the only code worth retrying at all.) **Never widen a Delegation, and never create a second one, to get past a refusal.** The cap is the user's decision, not a runtime obstacle; minting a fresh Delegation to escape an exhausted one defeats the whole mechanism. Report and stop. Only `0006` and `0007` are retryable — everything else is a decision, and retrying it unchanged gives the same answer. Delegations also expire silently, so check `expiresAt` before diagnosing a `0003` as anything else. diff --git a/AGENTS.md b/AGENTS.md index 26d9aea0..82106dae 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -146,7 +146,7 @@ The Router probes the merchant, auto-detects the protocol from the 402, pays and | `BCK.ROUTER.0009` | 402 | Wallet short on the target network; nothing was signed | No — **stop** | | `BCK.ROUTER.0010` | 500 | Internal: the rail reported an unusable charge amount | **No — never blind-retry** | -**`0010` is the one 500 you must not retry.** Unlike `0006`, a payment credential **was already minted** before it failed, and because no payment record was written your `requestId` will *not* suppress a retry — so retrying re-mints a fresh credential and fails identically. Report it instead. +**`0010` is the one 500 you must not retry.** A payment credential **was already minted** before it failed, and because no payment record was written your `requestId` will *not* suppress a retry — so retrying re-mints a fresh credential and fails identically. Report it instead. (`0006`, the retryable 500, is only ever raised by the payments *summary* read — never by a payment. On the paying path `0007` is the only code worth retrying at all.) **Never widen a Delegation, and never create a second one, to get past a refusal.** The cap is the user's decision, not a runtime obstacle; minting a fresh Delegation to escape an exhausted one defeats the whole mechanism. Report and stop. Only `0006` and `0007` are retryable — everything else is a decision, and retrying it unchanged gives the same answer. Delegations also expire silently, so check `expiresAt` before diagnosing a `0003` as anything else. From 52fc1f6f9ae11b8c25d2b0d5f8018f958364ca6e Mon Sep 17 00:00:00 2001 From: Rodolphe Marques Date: Tue, 11 Aug 2026 17:50:25 +0200 Subject: [PATCH 06/10] fix(skills): fold the review panel's findings on #289 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two panel agents reviewed this; between them one blocker, four mediums and seven lows. All verified by execution or against nvm-monorepo origin/main before folding — several of my own 'verified' claims did not survive. BLOCKER - `npx clawhub update` as documented simply fails: 'Error: Provide or --all'. I had tested `install` and assumed `update`. Now `update --all`, verified against a real install, plus a note that the lock is per working directory so a `--dir` install must be updated from that same directory. MEDIUM - The requestId bullet said a same-id retry 'returns the original payment', which reads as the retry SUCCEEDING. It returns 409 BCK.ROUTER.0002 with the original paymentId and no resource (router.service.ts:156-166, no status filter). The hazard is the recovery path: an agent hits an unexpected 409 and the obvious escape is a fresh id — the exact double-spend the bullet exists to prevent. Now says so, and says never to answer that 409 with a new id. - 'every guardrail' / 'every BCK.ROUTER.* code' were false in both directions. The condensed files omit 0004/0005, the refuses-outright set and the relay limits. Scoped honestly. - 'Use this if you'd rather pin a version' — the command below it clones --depth 1 off main and the repo has zero tags. Now 'take a snapshot'. LOW - CLAUDE.md implied `claude plugin validate` guards the source/skills invariant. Mutation-tested: it passes source './', a removed strict, AND a skills path pointing at a directory that does not exist. Now says what it actually checks and that the invariant is on the reviewer, with the round-trip test that does catch it. Also explains what strict: false is for. - $schema pointed at anthropic.com/claude-code/marketplace.schema.json, which 404s (it is what Anthropic's own marketplace uses). Switched to schemastore, which resolves and knows these fields. - Named both update commands: marketplace update refreshes the catalogue, plugin update updates the installed plugin. - allowedRecipients is optional and omitting it means ANY merchant — worth one clause in a file teaching an agent to mint its own budget. - /plugin blocks were in a bash fence; they are REPL commands, not shell. - ClawHub's registry version for the payments skill does not match its own SKILL.md header; the tab now says which to trust. - 'Every supported tool can now get either skill' overclaimed for Copilot, which gets a condensed section, not the skill. - Page frontmatter and lead-in still described payments only. Co-Authored-By: Claude Opus 5 (1M context) --- .claude-plugin/marketplace.json | 2 +- .github/copilot-instructions.md | 4 ++-- AGENTS.md | 4 ++-- CLAUDE.md | 12 ++++++++-- development-guide/build-using-nvm-skill.mdx | 26 ++++++++++++--------- 5 files changed, 30 insertions(+), 18 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 9f4a33c7..493c7c04 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -1,5 +1,5 @@ { - "$schema": "https://anthropic.com/claude-code/marketplace.schema.json", + "$schema": "https://json.schemastore.org/claude-code-marketplace.json", "name": "nevermined", "description": "Nevermined's AI coding skills: monetize an agent with the Payments SDK, or let one buy from external x402 / MPP services through the Nevermined Router.", "owner": { diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 93257f91..94acaa7f 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -110,7 +110,7 @@ curl -sX POST "$NVM_API_URL/api/v1/delegation/create" \ # → { "delegationId": "5e7481c3-…" } ``` -All four fields are required — no defaults. `erc4337` is the crypto-funded Delegation both stablecoin rails need. Create it once and reuse the id. +All four fields are required — no defaults. `erc4337` is the crypto-funded Delegation both stablecoin rails need. Create it once and reuse the id. `allowedRecipients` is optional and **omitting it means no recipient restriction at all** — the budget can pay any merchant the Router can reach, bounded only by the cap and expiry. ### 2. Fund the buyer wallet @@ -142,7 +142,7 @@ curl -sX POST "$NVM_API_URL/api/v1/router/route" \ The Router probes the merchant, auto-detects the protocol from the 402, pays and relays. `status`/`body` are the merchant's own; `paid: false` with no `payment` block means the resource was free — handle that. For streaming use `ALL /api/v1/router/proxy` with `X-Router-Target-Url`, `X-Router-Delegation-Id` and `X-Router-Request-Id` headers. -- **`requestId` is an idempotency key, not a request counter.** Use one stable id per logical purchase, reused across retries of that purchase: the same id returns the original payment, a fresh id buys again. **A fresh `uuid4()` per HTTP attempt is how an agent double-spends.** +- **`requestId` is an idempotency key, not a request counter.** Use one stable id per logical purchase, reused across retries of that purchase. **A fresh `uuid4()` per HTTP attempt is how an agent double-spends.** Note what a same-id retry actually returns: `409 BCK.ROUTER.0002` carrying the original `paymentId` — **not the resource**. That is the protection working. **Never answer that 409 by minting a fresh id**, which is exactly the double-spend you avoided a moment ago; if the purchase genuinely failed, report it. - Budget is debited in **whole cents, rounded up** — 1000 calls at $0.001 costs $10.00, not $1.00. `settlement.approxCents` is what was actually reserved; trust it over any catalog `priceLabel`. ### 5. Read what you spent diff --git a/AGENTS.md b/AGENTS.md index 82106dae..f77834d6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -95,7 +95,7 @@ curl -sX POST "$NVM_API_URL/api/v1/delegation/create" \ # → { "delegationId": "5e7481c3-…" } ``` -All four fields are required — no defaults. `erc4337` is the crypto-funded Delegation both stablecoin rails need. Create it once and reuse the id. +All four fields are required — no defaults. `erc4337` is the crypto-funded Delegation both stablecoin rails need. Create it once and reuse the id. `allowedRecipients` is optional and **omitting it means no recipient restriction at all** — the budget can pay any merchant the Router can reach, bounded only by the cap and expiry. ### 2. Fund the buyer wallet @@ -126,7 +126,7 @@ curl -sX POST "$NVM_API_URL/api/v1/router/route" \ The Router probes the merchant, auto-detects the protocol from the 402, pays and relays. `status`/`body` are the merchant's own; `paid: false` with no `payment` block means the resource was free — handle that. For streaming use `ALL /api/v1/router/proxy` with `X-Router-Target-Url`, `X-Router-Delegation-Id` and `X-Router-Request-Id` headers. -- **`requestId` is an idempotency key, not a request counter.** Use one stable id per logical purchase, reused across retries of that purchase: the same id returns the original payment, a fresh id buys again. **A fresh `uuid4()` per HTTP attempt is how an agent double-spends.** +- **`requestId` is an idempotency key, not a request counter.** Use one stable id per logical purchase, reused across retries of that purchase. **A fresh `uuid4()` per HTTP attempt is how an agent double-spends.** Note what a same-id retry actually returns: `409 BCK.ROUTER.0002` carrying the original `paymentId` — **not the resource**. That is the protection working. **Never answer that 409 by minting a fresh id**, which is exactly the double-spend you avoided a moment ago; if the purchase genuinely failed, report it. - Budget is debited in **whole cents, rounded up** — 1000 calls at $0.001 costs $10.00, not $1.00. `settlement.approxCents` is what was actually reserved; trust it over any catalog `priceLabel`. ### 5. Read what you spent diff --git a/CLAUDE.md b/CLAUDE.md index 7e52a585..b4e644d0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -46,10 +46,18 @@ They give AI coding assistants (Claude Code, Cursor, Copilot, Codex, Windsurf, C | `.github/copilot-instructions.md` | payments + a condensed Router section — single file, no per-skill split | | `AGENTS.md` | payments + a condensed Router section — single file, no per-skill split | -Both skills are also distributed as **Claude Code plugins** via `.claude-plugin/marketplace.json` at the repo root (`/plugin marketplace add nevermined-io/docs`), and to the **ClawHub** registry by `.github/workflows/publish-skill-clawhub.yml`. The marketplace entries use `source: "./skills"` with an explicit per-plugin `skills` array, so each plugin ships only `skills/` and not the whole 33 MB docs repo — do not change `source` to `"./"`. Validate any edit with `claude plugin validate .` before committing. - `.cursor/` is otherwise gitignored; `.gitignore` re-includes `.cursor/rules/` specifically, because those files are published — users `curl` them from `main`, so one that fails to commit becomes a 404 in their editor. Both `development-guide/build-using-nvm-skill.mdx` (the install page) and its "Supported Tools at a Glance" table must be updated when this set changes. +### Plugin and registry distribution + +Both skills are also distributed as **Claude Code plugins** via `.claude-plugin/marketplace.json` at the repo root (`/plugin marketplace add nevermined-io/docs`), and to the **ClawHub** registry by `.github/workflows/publish-skill-clawhub.yml`. The install page must be updated for these surfaces too. + +The marketplace entries use `source: "./skills"` with an explicit per-plugin `skills` array. **`source` is a copy boundary, not just a lookup path**: it is the directory copied into `~/.claude/plugins/cache/`, so `source: "./"` would ship the entire docs repo — tens of megabytes, for a skill measured in kilobytes (33 MB vs 236 KB when this was written), and again per plugin if a user installs both. Do not change it to `"./"`. + +⚠️ **Nothing enforces that.** `claude plugin validate` checks the manifest's *shape* only — verified by mutation, it passes a `source` of `"./"`, a removed `strict`, and even a `skills` entry pointing at a directory that does not exist. Run it to catch malformed JSON, but the `source`/`skills` invariant is on the reviewer. The honest test is a round trip: add the marketplace from a local path, install, and check the footprint with `du -sh ~/.claude/plugins/cache//*/*/`. + +`strict: false` is load-bearing too: it tells the marketplace entry — rather than a `plugin.json` inside the skill directory — to define which skills the plugin exposes. The skill directories have no `plugin.json`, so removing `strict` would leave the entries claiming components nothing declares. + --- ## Content Tone & Voice diff --git a/development-guide/build-using-nvm-skill.mdx b/development-guide/build-using-nvm-skill.mdx index aeda5c26..b0099354 100644 --- a/development-guide/build-using-nvm-skill.mdx +++ b/development-guide/build-using-nvm-skill.mdx @@ -1,6 +1,6 @@ --- title: "AI Coding Skill" -description: "Import Nevermined payment knowledge into your AI coding assistant and start building monetizable agents in minutes." +description: "Import Nevermined payment and Router knowledge into your AI coding assistant — build agents that charge, and agents that buy." icon: "wand-magic-sparkles" --- @@ -15,9 +15,9 @@ There are two, and which one you want depends on which side of the payment you'r | Skill | Your agent is… | Covers | | --- | --- | --- | | **`nevermined-payments`** | **receiving** money, or buying a Nevermined plan | SDK integration, middleware, plans, credits — the rest of this page | -| **`nevermined-router`** | **spending** money at external services | Discovering services in the catalog, creating a spending Delegation, paying any x402 or MPP endpoint through the [Nevermined Router](/products/router/overview) | +| **`nevermined-router`** | **spending** money at external services | Discovering services in the catalog, creating a spending Delegation, paying any x402 or MPP endpoint through the [Nevermined Router](/products/router/overview), and the guardrails an autonomous buyer must respect | -Installing both is fine — they don't overlap. Every supported tool can now get either skill: +Installing both is fine — they don't overlap. Every supported tool can now reach the Router: - **Claude Code** — install them as plugins, one command each. See the tab below. - **Cursor, Windsurf, Cline, Amazon Q** — swap `nevermined-payments` for `nevermined-router` in the paths below, or run the extra command shown in each tab. @@ -25,7 +25,7 @@ Installing both is fine — they don't overlap. Every supported tool can now get ## What's Included -The skill provides your coding assistant with: +The skills provide your coding assistant with: - **SDK initialization** patterns for TypeScript and Python - **Framework-specific middleware** (Express.js, FastAPI, Strands, MCP, Google A2A) @@ -51,18 +51,20 @@ Choose your AI coding tool below for step-by-step installation instructions. Install from the Nevermined plugin marketplace — two commands, and Claude Code keeps the skills up to date for you: - ```bash + ```text /plugin marketplace add nevermined-io/docs /plugin install nevermined-payments@nevermined # charging for your agent /plugin install nevermined-router@nevermined # buying from external services ``` + Those are Claude Code commands — type them in Claude Code, not in a shell. + Install either or both. The marketplace is named `nevermined` (that's the `@nevermined` suffix); `nevermined-io/docs` is just the repository it's served from. - Claude Code loads each skill when it's relevant to your work, and refreshes it in the background when we publish changes. To update by hand, run `/plugin marketplace update nevermined`. + Claude Code loads each skill when it's relevant to your work, and refreshes it in the background when we publish changes. To update by hand it takes two steps — `/plugin marketplace update nevermined` refreshes the catalogue, then `/plugin update nevermined-router@nevermined` updates the installed plugin itself (restart to apply). - **Alternative — copy the files directly.** Use this if you'd rather pin a version, or you're on a Claude Code build without plugin support: + **Alternative — copy the files directly.** Use this if you'd rather take a snapshot you control, or you're on a Claude Code build without plugin support: ```bash mkdir -p ~/.claude/skills @@ -88,8 +90,8 @@ Choose your AI coding tool below for step-by-step installation instructions. Both skills are published to the [ClawHub](https://clawhub.ai) registry under the `nevermined-io` publisher, so any tool that reads a `skills/` directory can pull them with one command: ```bash - npx clawhub install nevermined-router # buying through the Router npx clawhub install nevermined # the payments skill + npx clawhub install nevermined-router # buying through the Router ``` This writes to `skills//` in the current directory. `--dir` sets a different target — it's a global flag, so it goes **before** the subcommand: @@ -98,10 +100,12 @@ Choose your AI coding tool below for step-by-step installation instructions. npx clawhub --dir ~/.claude/skills install nevermined-router ``` - No account or login is needed to install. Run `npx clawhub update` to pull the latest published version, and `npx clawhub inspect nevermined-router` to see what you'd get without installing it. + You don't need an account to install. `npx clawhub update --all` pulls the latest published versions (the bare `update` errors — it wants a slug or `--all`), and `npx clawhub inspect nevermined-router` shows what you'd get without installing it. + + The install lock is per working directory, so if you installed with `--dir`, run the update from that same directory with the same flag: `npx clawhub --dir ~/.claude/skills update --all`. - ClawHub publishes the payments skill under the slug **`nevermined`**, not `nevermined-payments`. The Router skill's slug matches its name. + Two ClawHub surprises worth knowing. The payments skill is published under the slug **`nevermined`**, not `nevermined-payments` (the Router skill's slug matches its name). And the version `clawhub inspect` reports for the payments skill won't match the `Skill version` in its own `SKILL.md` header — the registry assigns its own numbering. Trust the registry's version for "is my copy current", and the file's for what changed. @@ -273,7 +277,7 @@ Choose your AI coding tool below for step-by-step installation instructions. | **Any** (ClawHub) | `skills//SKILL.md` | Yes | Depends on the tool | -The Copilot and Codex files are **condensed** — they carry the buy loop, the money traps and every guardrail, but not the full reference set. For the complete Router skill, install it as a directory (Claude Code plugin, ClawHub, or `.agents/skills/`). +The Copilot and Codex files are **condensed** — they carry the buy loop, the money traps and every `BCK.ROUTER.*` retry decision, but not the full reference set: they omit `0004`/`0005`, everything the Router refuses outright (MPP splits, internal targets, redirects, forged `X-Router-*`), and the relay limits. For the complete Router skill, install it as a directory (Claude Code plugin, ClawHub, or `.agents/skills/`). ## Example Prompts From 0496cdfdf2b02b2b2e533691555170aea622be3d Mon Sep 17 00:00:00 2001 From: Rodolphe Marques Date: Tue, 11 Aug 2026 17:57:52 +0200 Subject: [PATCH 07/10] fix(skills): scope the retry rule to the paying path, and correct the source rationale MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Second round of panel findings, both verified before folding. - The Guardrails section still closed with an unqualified 'Only 0006 and 0007 are retryable', sitting directly under a table of PAY-path errors. Both throw sites traced on origin/main: 0006 comes only from router.service.ts:928 (the payments-summary aggregation) and 0007 only from router-concurrency.service.ts:31. So on the paying path 0007 is the only retryable code and every other 500 is a stop — which is the opposite of what an agent writing a retry loop around /route would have taken from that sentence. The 0010 paragraph already said this; the summarising sentence undid it. - CLAUDE.md justified 'source: ./skills' by download size, and that rationale is wrong. Verified against the installed anthropics/claude-plugins-official: '/plugin marketplace add' materializes a FULL-content snapshot of the repo's default branch into ~/.claude/plugins/marketplaces// — every top-level dir, no .git — regardless of the source field. The repo lands on disk either way. What source actually controls is what each installed PLUGIN copies and loads on top of that. Left as-is, a maintainer would add the marketplace, see ~32 MiB arrive anyway, conclude the trick does nothing, and simplify it back to './' — reaching the right conclusion from the wrong claim. Rewritten to argue from plugin content instead. Sizes corrected while there: 'du -sh' reports block usage, ~19% high on a tree of small files. skills/ is 203,675 bytes apparent (~200 KB, not 236 KB) and the repo is 33,619,640 bytes (~32 MiB), of which video/ and images/ are ~94%. Co-Authored-By: Claude Opus 5 (1M context) --- .github/copilot-instructions.md | 2 +- AGENTS.md | 2 +- CLAUDE.md | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 94acaa7f..f2f09bf7 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -164,7 +164,7 @@ The Router probes the merchant, auto-detects the protocol from the 402, pays and **`0010` is the one 500 you must not retry.** A payment credential **was already minted** before it failed, and because no payment record was written your `requestId` will *not* suppress a retry — so retrying re-mints a fresh credential and fails identically. Report it instead. (`0006`, the retryable 500, is only ever raised by the payments *summary* read — never by a payment. On the paying path `0007` is the only code worth retrying at all.) -**Never widen a Delegation, and never create a second one, to get past a refusal.** The cap is the user's decision, not a runtime obstacle; minting a fresh Delegation to escape an exhausted one defeats the whole mechanism. Report and stop. Only `0006` and `0007` are retryable — everything else is a decision, and retrying it unchanged gives the same answer. Delegations also expire silently, so check `expiresAt` before diagnosing a `0003` as anything else. +**Never widen a Delegation, and never create a second one, to get past a refusal.** The cap is the user's decision, not a runtime obstacle; minting a fresh Delegation to escape an exhausted one defeats the whole mechanism. Report and stop. **On the paying path `0007` is the only retryable code** — `0006` can only come from the summary read, so any *other* 500 from `/route` is a stop. Everything else is a decision, and retrying it unchanged gives the same answer. Delegations also expire silently, so check `expiresAt` before diagnosing a `0003` as anything else. ## Full Reference diff --git a/AGENTS.md b/AGENTS.md index f77834d6..9154899b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -148,7 +148,7 @@ The Router probes the merchant, auto-detects the protocol from the 402, pays and **`0010` is the one 500 you must not retry.** A payment credential **was already minted** before it failed, and because no payment record was written your `requestId` will *not* suppress a retry — so retrying re-mints a fresh credential and fails identically. Report it instead. (`0006`, the retryable 500, is only ever raised by the payments *summary* read — never by a payment. On the paying path `0007` is the only code worth retrying at all.) -**Never widen a Delegation, and never create a second one, to get past a refusal.** The cap is the user's decision, not a runtime obstacle; minting a fresh Delegation to escape an exhausted one defeats the whole mechanism. Report and stop. Only `0006` and `0007` are retryable — everything else is a decision, and retrying it unchanged gives the same answer. Delegations also expire silently, so check `expiresAt` before diagnosing a `0003` as anything else. +**Never widen a Delegation, and never create a second one, to get past a refusal.** The cap is the user's decision, not a runtime obstacle; minting a fresh Delegation to escape an exhausted one defeats the whole mechanism. Report and stop. **On the paying path `0007` is the only retryable code** — `0006` can only come from the summary read, so any *other* 500 from `/route` is a stop. Everything else is a decision, and retrying it unchanged gives the same answer. Delegations also expire silently, so check `expiresAt` before diagnosing a `0003` as anything else. ## Full Reference diff --git a/CLAUDE.md b/CLAUDE.md index b4e644d0..4cb4b326 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -52,7 +52,9 @@ They give AI coding assistants (Claude Code, Cursor, Copilot, Codex, Windsurf, C Both skills are also distributed as **Claude Code plugins** via `.claude-plugin/marketplace.json` at the repo root (`/plugin marketplace add nevermined-io/docs`), and to the **ClawHub** registry by `.github/workflows/publish-skill-clawhub.yml`. The install page must be updated for these surfaces too. -The marketplace entries use `source: "./skills"` with an explicit per-plugin `skills` array. **`source` is a copy boundary, not just a lookup path**: it is the directory copied into `~/.claude/plugins/cache/`, so `source: "./"` would ship the entire docs repo — tens of megabytes, for a skill measured in kilobytes (33 MB vs 236 KB when this was written), and again per plugin if a user installs both. Do not change it to `"./"`. +The marketplace entries use `source: "./skills"` with an explicit per-plugin `skills` array. **`source` is a copy boundary, not just a lookup path**: it is the directory copied into `~/.claude/plugins/cache///`, and it becomes that plugin's root. With `source: "./"` every plugin would carry the entire repo — `video/` and `images/` are ~94% of it — and would carry it *again* for the second plugin. Scoped to `./skills`, each plugin carries the skills tree only (~200 KB against ~32 MiB for the repo). Do not change it to `"./"`. + +⚠️ **Do not justify this by the download size, and do not expect `marketplace add` to get smaller.** `/plugin marketplace add` materializes a **full-content snapshot of the repo's default branch** into `~/.claude/plugins/marketplaces//` — every top-level directory, no `.git` — *regardless of what `source` says* (verified against the installed `anthropics/claude-plugins-official`). So the repo lands on disk either way; what `source` controls is what each installed **plugin** copies and loads on top of that. A maintainer who adds the marketplace, watches ~32 MiB arrive anyway, and concludes "the `source` trick does nothing" would be drawing the right conclusion from the wrong claim — which is why the argument here is about plugin content, not bandwidth. ⚠️ **Nothing enforces that.** `claude plugin validate` checks the manifest's *shape* only — verified by mutation, it passes a `source` of `"./"`, a removed `strict`, and even a `skills` entry pointing at a directory that does not exist. Run it to catch malformed JSON, but the `source`/`skills` invariant is on the reviewer. The honest test is a round trip: add the marketplace from a local path, install, and check the footprint with `du -sh ~/.claude/plugins/cache//*/*/`. From a6525efb497f0d013b725692a6fd82b79f02f6e1 Mon Sep 17 00:00:00 2001 From: Rodolphe Marques Date: Wed, 12 Aug 2026 10:57:42 +0200 Subject: [PATCH 08/10] fix(skills): add BCK.ROUTER.0011 to the condensed Router sections MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keeps these in step with #291, which documents 0011 across the canonical skill, guardrails.mdx and the four IDE rule files after aaitor caught it missing. Also carries across the reframing his review prompted: the point is not that 0010 is 'the exception to retry the 5xx' but that the HTTP status does not tell you whether to retry at all — 0010 is a 500 you must not retry, 0011 a 402 that reads like an ordinary payment error and equally must not be. Co-Authored-By: Claude Opus 5 (1M context) --- .github/copilot-instructions.md | 5 +++++ AGENTS.md | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index f2f09bf7..9c36327f 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -161,6 +161,11 @@ The Router probes the merchant, auto-detects the protocol from the 402, pays and | `BCK.ROUTER.0008` | 403 | Legacy API key — create a new one | No | | `BCK.ROUTER.0009` | 402 | Wallet short on the target network; nothing was signed | No — **stop** | | `BCK.ROUTER.0010` | 500 | Internal: the rail reported an unusable charge amount | **No — never blind-retry** | +| `BCK.ROUTER.0011` | 402 | Card rail: needs cardholder 3-D Secure, which an agent can't complete. Nothing charged, no usable credential | No — **needs a human** | + +**Neither `0010` nor `0011` may be auto-retried, and the HTTP status won't tell you that** — one is a 500, the other a 402 that reads like a routine payment error. Read the code, not the status. + +**`0011`** means the card issuer wants 3-D Secure and the agent has no browser to answer it. Nothing was charged. Don't loop: 3DS is often mandated per charge, so each attempt re-demands it and strands a fresh single-use card credential. Surface it to a human. **`0010` is the one 500 you must not retry.** A payment credential **was already minted** before it failed, and because no payment record was written your `requestId` will *not* suppress a retry — so retrying re-mints a fresh credential and fails identically. Report it instead. (`0006`, the retryable 500, is only ever raised by the payments *summary* read — never by a payment. On the paying path `0007` is the only code worth retrying at all.) diff --git a/AGENTS.md b/AGENTS.md index 9154899b..b53d700d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -145,6 +145,11 @@ The Router probes the merchant, auto-detects the protocol from the 402, pays and | `BCK.ROUTER.0008` | 403 | Legacy API key — create a new one | No | | `BCK.ROUTER.0009` | 402 | Wallet short on the target network; nothing was signed | No — **stop** | | `BCK.ROUTER.0010` | 500 | Internal: the rail reported an unusable charge amount | **No — never blind-retry** | +| `BCK.ROUTER.0011` | 402 | Card rail: needs cardholder 3-D Secure, which an agent can't complete. Nothing charged, no usable credential | No — **needs a human** | + +**Neither `0010` nor `0011` may be auto-retried, and the HTTP status won't tell you that** — one is a 500, the other a 402 that reads like a routine payment error. Read the code, not the status. + +**`0011`** means the card issuer wants 3-D Secure and the agent has no browser to answer it. Nothing was charged. Don't loop: 3DS is often mandated per charge, so each attempt re-demands it and strands a fresh single-use card credential. Surface it to a human. **`0010` is the one 500 you must not retry.** A payment credential **was already minted** before it failed, and because no payment record was written your `requestId` will *not* suppress a retry — so retrying re-mints a fresh credential and fails identically. Report it instead. (`0006`, the retryable 500, is only ever raised by the payments *summary* read — never by a payment. On the paying path `0007` is the only code worth retrying at all.) From 6abc41e58713af0ba1022a0f36ef8ae960cfebc0 Mon Sep 17 00:00:00 2001 From: Rodolphe Marques Date: Wed, 12 Aug 2026 12:57:58 +0200 Subject: [PATCH 09/10] docs(skills): mark the two always-on Router sections as keep-in-sync MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit aaitor's LOW. The zero-divergence between .github/copilot-instructions.md and AGENTS.md is the load-bearing property of this change, and nothing in-file preserved it — the next editor could touch one and forget the other. An HTML comment atop each Router section names its twin explicitly. Co-Authored-By: Claude Opus 5 (1M context) --- .github/copilot-instructions.md | 1 + AGENTS.md | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 9c36327f..6e624f75 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -96,6 +96,7 @@ Card payments: `scheme: "nvm:card-delegation"`, `network: "stripe"`. A human is - **Seller analytics:** discover your `orgId` from `.orgId` on `GET /protocol/plans` / `/protocol/agents` records (no separate lookup endpoint). A malformed/placeholder `orgId` returns a silent 200-of-zeros; a non-Premium org returns `403 BCK.ORGANIZATIONS.0022`; a non-admin org returns `403 BCK.AUTH.0004`. ## Nevermined Router — paying external services + Use the Router when the agent must **pay** a service it has no account with — any x402 agent or MPP merchant. Everything above is the other half: *receiving* payments and buying Nevermined plans. Plain HTTP, no SDK: `Authorization: Bearer $NVM_API_KEY` against `$NVM_API_URL` (`https://api.sandbox.nevermined.app` sandbox, `https://api.live.nevermined.app` live). **Never send `NVM_API_KEY` to the merchant** — it authenticates you to Nevermined only; the merchant's own auth goes in `headers`. diff --git a/AGENTS.md b/AGENTS.md index b53d700d..60651005 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -83,6 +83,7 @@ await payments.a2a.start({ port: 3005, basePath: '/a2a/', agentCard, executor }) When an agent must act on its own behalf at runtime (buy a plan, enroll a card, check credits/revenue), call the REST API directly with `Authorization: Bearer $NVM_API_KEY` against `https://api.sandbox.nevermined.app` (sandbox) or `https://api.live.nevermined.app` (live). Buy in two calls — `POST /api/v1/x402/permissions` (→ `accessToken`) then `POST /api/v1/x402/settle` (→ `creditsRedeemed`, `remainingBalance`). Crypto uses `scheme: "nvm:erc4337"` / `network: "eip155:84532"`; cards use `scheme: "nvm:card-delegation"` / `network: "stripe"`. A human is needed only for one-time setup — the first API key, plus card enrollment if paying by card (the stablecoin path needs neither). Full runbook: `skills/nevermined-payments/references/autonomous-operations.md`. ## Nevermined Router — paying external services + Use the Router when the agent must **pay** a service it has no account with — any x402 agent or MPP merchant. Everything above is the other half: *receiving* payments and buying Nevermined plans. Plain HTTP, no SDK: `Authorization: Bearer $NVM_API_KEY` against `$NVM_API_URL` (`https://api.sandbox.nevermined.app` sandbox, `https://api.live.nevermined.app` live). **Never send `NVM_API_KEY` to the merchant** — it authenticates you to Nevermined only; the merchant's own auth goes in `headers`. The Router pays a price quoted **on the wire**, so a service answering `401`/`403` rather than `402` wants authentication, not payment — say so, don't route it. From c681960e12bb219eaab25784d348a8e67dc350c8 Mon Sep 17 00:00:00 2001 From: Rodolphe Marques Date: Wed, 12 Aug 2026 13:00:24 +0200 Subject: [PATCH 10/10] =?UTF-8?q?fix(skills):=20use=20MDX=20comment=20synt?= =?UTF-8?q?ax=20in=20AGENTS.md=20=E2=80=94=20HTML=20comments=20break=20the?= =?UTF-8?q?=20deploy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The keep-in-sync marker from the review broke Mintlify Deployment: Failed to parse page content at path AGENTS.md: Unexpected character `!` (U+0021) before name ... (note: to create a comment in MDX, use {/* text */}) AGENTS.md IS in Mintlify's content set and is parsed as MDX, so an HTML comment is a syntax error there. The review's premise that 'HTML comments are invisible to the tools that consume these files' holds for Copilot and Codex but not for Mintlify, which publishes this one. .github/copilot-instructions.md is NOT in that set — it appears in no docs.json route and there is no .mintignore — so its HTML comment is fine and stays. That is why the two files now differ on this single line despite being kept in sync on everything else. Recorded in CLAUDE.md so the next person adding a marker here doesn't rediscover it through a red deploy. Co-Authored-By: Claude Opus 5 (1M context) --- AGENTS.md | 2 +- CLAUDE.md | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 60651005..f3e73f54 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -83,7 +83,7 @@ await payments.a2a.start({ port: 3005, basePath: '/a2a/', agentCard, executor }) When an agent must act on its own behalf at runtime (buy a plan, enroll a card, check credits/revenue), call the REST API directly with `Authorization: Bearer $NVM_API_KEY` against `https://api.sandbox.nevermined.app` (sandbox) or `https://api.live.nevermined.app` (live). Buy in two calls — `POST /api/v1/x402/permissions` (→ `accessToken`) then `POST /api/v1/x402/settle` (→ `creditsRedeemed`, `remainingBalance`). Crypto uses `scheme: "nvm:erc4337"` / `network: "eip155:84532"`; cards use `scheme: "nvm:card-delegation"` / `network: "stripe"`. A human is needed only for one-time setup — the first API key, plus card enrollment if paying by card (the stablecoin path needs neither). Full runbook: `skills/nevermined-payments/references/autonomous-operations.md`. ## Nevermined Router — paying external services - +{/* Keep this Router section in sync with the identical one in .github/copilot-instructions.md (and vice-versa). MDX comment syntax is deliberate — Mintlify parses this file and rejects HTML comments. */} Use the Router when the agent must **pay** a service it has no account with — any x402 agent or MPP merchant. Everything above is the other half: *receiving* payments and buying Nevermined plans. Plain HTTP, no SDK: `Authorization: Bearer $NVM_API_KEY` against `$NVM_API_URL` (`https://api.sandbox.nevermined.app` sandbox, `https://api.live.nevermined.app` live). **Never send `NVM_API_KEY` to the merchant** — it authenticates you to Nevermined only; the merchant's own auth goes in `headers`. The Router pays a price quoted **on the wire**, so a service answering `401`/`403` rather than `402` wants authentication, not payment — say so, don't route it. diff --git a/CLAUDE.md b/CLAUDE.md index 4cb4b326..01df71da 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -46,6 +46,8 @@ They give AI coding assistants (Claude Code, Cursor, Copilot, Codex, Windsurf, C | `.github/copilot-instructions.md` | payments + a condensed Router section — single file, no per-skill split | | `AGENTS.md` | payments + a condensed Router section — single file, no per-skill split | +⚠️ **`AGENTS.md` is parsed by Mintlify as MDX, so it must not contain HTML comments.** A `` there fails the Mintlify Deployment check with *"Unexpected character `!` … to create a comment in MDX, use `{/* text */}`"* — verified on docs#289. Use `{/* … */}` in `AGENTS.md`. `.github/copilot-instructions.md` is **not** in Mintlify's content set (it appears in no `docs.json` route and there is no `.mintignore`), so plain HTML comments are fine there — which is why the two files legitimately differ on this one point. + `.cursor/` is otherwise gitignored; `.gitignore` re-includes `.cursor/rules/` specifically, because those files are published — users `curl` them from `main`, so one that fails to commit becomes a 404 in their editor. Both `development-guide/build-using-nvm-skill.mdx` (the install page) and its "Supported Tools at a Glance" table must be updated when this set changes. ### Plugin and registry distribution