From 73d2e127cf1b83e47ef179829d7cf52525892962 Mon Sep 17 00:00:00 2001 From: Rodolphe Marques Date: Thu, 9 Jul 2026 17:59:43 +0200 Subject: [PATCH] chore(weather-mcp): bump payments SDK to ^1.10.0, fix stale token API docs, add Fleet smoke-test guide - Bump @nevermined-io/payments ^1.9.0 -> ^1.10.0 (latest release; keeps the x402 v2 in-band MCP transport). Verified: `yarn build` (tsc) compiles and the server boots to Payments-client init on 1.10.0. - Fix docs referencing the removed `payments.agents.getAgentAccessToken()` -> `payments.x402.getX402AccessToken(planId, agentId, { delegationConfig })` (create the delegation first via `payments.delegation.createDelegation`). - Drop the deprecated `environment` option (1.10.0 derives it from the API-key prefix). - Add FLEET-SMOKE-TEST.md: run this MCP server behind LangSmith Fleet via a static `Authorization: Bearer` header (the x402 v2 header fallback). Co-Authored-By: Claude Opus 4.8 (1M context) --- mcp-examples/weather-mcp/CLAUDE.md | 2 +- mcp-examples/weather-mcp/FLEET-SMOKE-TEST.md | 74 ++++++++++++++++++++ mcp-examples/weather-mcp/README.md | 19 +++-- mcp-examples/weather-mcp/RUN.md | 19 +++-- mcp-examples/weather-mcp/package.json | 2 +- mcp-examples/weather-mcp/yarn.lock | 8 +-- 6 files changed, 110 insertions(+), 14 deletions(-) create mode 100644 mcp-examples/weather-mcp/FLEET-SMOKE-TEST.md diff --git a/mcp-examples/weather-mcp/CLAUDE.md b/mcp-examples/weather-mcp/CLAUDE.md index f1113449..e1255176 100644 --- a/mcp-examples/weather-mcp/CLAUDE.md +++ b/mcp-examples/weather-mcp/CLAUDE.md @@ -190,7 +190,7 @@ Credits can be: ### Testing Without Auth -The MCP Inspector (`yarn inspector`) doesn't send auth headers. For testing with authentication, create a client script using `payments.agents.getAgentAccessToken()`. +The MCP Inspector (`yarn inspector`) doesn't send auth headers. For testing with authentication, create a client script that mints an x402 access token with `payments.x402.getX402AccessToken(planId, agentId, { delegationConfig: { delegationId } })` — create the delegation first via `payments.delegation.createDelegation(...)`. See [`FLEET-SMOKE-TEST.md`](./FLEET-SMOKE-TEST.md) for a full runnable flow. ## Dependencies diff --git a/mcp-examples/weather-mcp/FLEET-SMOKE-TEST.md b/mcp-examples/weather-mcp/FLEET-SMOKE-TEST.md new file mode 100644 index 00000000..011ed325 --- /dev/null +++ b/mcp-examples/weather-mcp/FLEET-SMOKE-TEST.md @@ -0,0 +1,74 @@ +# Testing weather-mcp with LangSmith Fleet (static bearer header) + +[LangSmith Fleet](https://docs.langchain.com/langsmith/fleet) is LangChain's no-code agent builder. It can connect to a **remote MCP server by URL** and attach a **static `Authorization` header** to every request. This runbook proves, with the least setup, that a Fleet agent can pay this Nevermined-gated MCP server through that static-header path. + +> **What this proves:** a Fleet agent pays a Nevermined-gated MCP tool with an operator-provisioned, spend-capped token in a static header. +> **What it does not prove:** per-user, agent-driven delegation setup (the card-enroll popup) — that's a separate, OAuth-based flow. + +## How the payment travels + +This server implements the **x402 v2 MCP transport**: it prefers the in-band payment payload in `_meta["x402/payment"]`, and **falls back to the `Authorization: Bearer ` header** when `_meta` is absent (a one-time deprecation warning, not an error). Fleet injects static headers but not per-call `_meta`, so the smoke test rides that header fallback. + +> ⚠️ The header fallback is **deprecated**. For a Fleet-backed deployment, pin an exact SDK version (this tutorial uses `@nevermined-io/payments@^1.10.0`) and track the fallback's removal. A first-class OAuth path is the durable integration. + +## Prerequisites + +- A **sandbox NVM API key** (subscriber) — its account auto-has an `erc4337` smart-account wallet (no card needed). The key prefix now selects the environment (`sandbox:…`), so the `environment` option is no longer required. +- `node` + `yarn`, and **`ngrok`** (Fleet needs an HTTPS URL). +- A Fleet workspace with the **"MCP Server Create"** permission. +- Optional `OPENAI_API_KEY` for richer forecasts. + +## Steps + +### 1. Install (already on the latest SDK) +```bash +cd mcp-examples/weather-mcp +yarn install # resolves @nevermined-io/payments@1.10.0 +``` + +### 2. Register a plan + agent (sandbox, crypto, fixed-credits) +Use the SDK's `payments.agents.registerAgentAndPlan(...)` (a crypto ERC-20 price config + a fixed-credits config) to get an `{ agentId, planId }`. The `nvm-deploy-e2e` skill in the `nevermined-io/nvm-monorepo` repo (`register-test.ts`) is a ready-made script for this. + +### 3. Run the server + expose it over HTTPS +```bash +# .env: NVM_API_KEY=sandbox:... NVM_AGENT_ID= PORT=3002 [OPENAI_API_KEY=...] +yarn dev # tsx src/main.ts (use `yarn dev`, not `yarn start`) +# second terminal: +ngrok http 3002 # copy the https URL +``` +Set `BASE_URL=https://` in `.env` and restart — `BASE_URL` is what the server's OAuth metadata advertises. Transport is streamable-HTTP at `POST /mcp`. + +### 4. Mint a token (crypto delegation — no card) +```ts +const { delegationId } = await payments.delegation.createDelegation({ + provider: "erc4337", spendingLimitCents: 10000, durationSecs: 604800, currency: "usdc", +}); +const { accessToken } = await payments.x402.getX402AccessToken( + planId, agentId, { delegationConfig: { delegationId } }, +); +``` +(`getX402AccessToken` **requires** a `delegationConfig`; there is no delegation-free mint. `erc4337` is headless — no popup.) + +### 5. Gating check — confirm the header path works *before* touching Fleet +```bash +curl -X POST https:///mcp \ + -H "Content-Type: application/json" \ + -H "Accept: application/json, text/event-stream" \ + -H "Authorization: Bearer " \ + -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"weather.today","arguments":{"city":"London"}}}' +``` +Expect a forecast, a `_meta["x402/payment-response"]` settlement receipt, and a one-time `[x402] … falling back to the Authorization header` warning in the server log. **If the weather comes back, the static-header contract Fleet needs is proven.** + +### 6. Wire it into Fleet +Add a **remote MCP server**: URL = `https:///mcp`, Auth = Header `Authorization` = `Bearer `. Then chat *"what's the weather in London?"* — the agent calls the tool and the server charges against the delegation. + +## Troubleshooting + +| Symptom | Fix | +|---|---| +| `401 … Authorization header required` | The header must be on **every** call including discovery (`initialize`/`tools/list`). Ensure Fleet attaches it to both. | +| `getAgentAccessToken is not a function` | Removed method — use `payments.x402.getX402AccessToken(planId, agentId, { delegationConfig })`. | +| `delegationConfig is required …` | Pass `delegationConfig: { delegationId }` (create the delegation first). | +| `Invalid NVM API Key` at startup | A real sandbox key is required; the server constructs the Payments client on boot. | +| Header path breaks after an SDK upgrade | The deprecated header fallback may have been removed — pin the SDK version, or move to the OAuth path. | +| `[DEPRECATED] The 'environment' option …` | 1.10.0 derives the environment from the API-key prefix; drop the `environment` option. | diff --git a/mcp-examples/weather-mcp/README.md b/mcp-examples/weather-mcp/README.md index 22054525..16fc5943 100644 --- a/mcp-examples/weather-mcp/README.md +++ b/mcp-examples/weather-mcp/README.md @@ -192,12 +192,23 @@ import { Payments } from "@nevermined-io/payments"; const payments = Payments.getInstance({ nvmApiKey: process.env.NVM_API_KEY!, - environment: "sandbox", }); -// agentId is optional under the plan-centric model — the plan id is all you need -const { accessToken } = await payments.agents.getAgentAccessToken( - process.env.NVM_PLAN_ID! +// 1) Create a delegation (a spending mandate). `erc4337` = crypto, headless, no card. +// For card payments, create the delegation via the Nevermined embed flow and reuse its `delegationId`. +const { delegationId } = await payments.delegation.createDelegation({ + provider: "erc4337", + spendingLimitCents: 10000, + durationSecs: 604800, + currency: "usdc", +}); + +// 2) Mint the x402 access token against the plan. +// agentId is optional under the plan-centric model — the plan id is all you need. +const { accessToken } = await payments.x402.getX402AccessToken( + process.env.NVM_PLAN_ID!, + undefined, + { delegationConfig: { delegationId } } ); ``` diff --git a/mcp-examples/weather-mcp/RUN.md b/mcp-examples/weather-mcp/RUN.md index f99b1a6a..4ad14ebd 100644 --- a/mcp-examples/weather-mcp/RUN.md +++ b/mcp-examples/weather-mcp/RUN.md @@ -136,12 +136,23 @@ import { Payments } from "@nevermined-io/payments"; const payments = Payments.getInstance({ nvmApiKey: process.env.NVM_API_KEY!, - environment: "sandbox", }); -// agentId is optional under the plan-centric model — the plan id is all you need -const { accessToken } = await payments.agents.getAgentAccessToken( - process.env.NVM_PLAN_ID! +// 1) Create a delegation (a spending mandate). `erc4337` = crypto, headless, no card. +// For card payments, create the delegation via the Nevermined embed flow and reuse its `delegationId`. +const { delegationId } = await payments.delegation.createDelegation({ + provider: "erc4337", + spendingLimitCents: 10000, + durationSecs: 604800, + currency: "usdc", +}); + +// 2) Mint the x402 access token against the plan. +// agentId is optional under the plan-centric model — the plan id is all you need. +const { accessToken } = await payments.x402.getX402AccessToken( + process.env.NVM_PLAN_ID!, + undefined, + { delegationConfig: { delegationId } } ); console.log("Access Token:", accessToken); diff --git a/mcp-examples/weather-mcp/package.json b/mcp-examples/weather-mcp/package.json index 443e4182..0adb4dfb 100644 --- a/mcp-examples/weather-mcp/package.json +++ b/mcp-examples/weather-mcp/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@modelcontextprotocol/sdk": "^1.25.2", - "@nevermined-io/payments": "^1.9.0", + "@nevermined-io/payments": "^1.10.0", "dotenv": "^17.2.1", "express": "^5.0.1", "openai": "^6.15.0", diff --git a/mcp-examples/weather-mcp/yarn.lock b/mcp-examples/weather-mcp/yarn.lock index 8d9fcc5f..da948559 100644 --- a/mcp-examples/weather-mcp/yarn.lock +++ b/mcp-examples/weather-mcp/yarn.lock @@ -248,10 +248,10 @@ zod "^3.25 || ^4.0" zod-to-json-schema "^3.25.0" -"@nevermined-io/payments@^1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@nevermined-io/payments/-/payments-1.9.0.tgz#01984f0a2bec136588ff1f27c0d7425f9dc57746" - integrity sha512-POjzDfovZj1DmgtNlgUusF2qGaHzTTkNf0xExxAjo7ZyT72oyIKOanFxtP6pCeg766QThNnzO3ovcVfN7UkyRw== +"@nevermined-io/payments@^1.10.0": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@nevermined-io/payments/-/payments-1.10.0.tgz#08ad29f4be186c2b2a7a85680f6ee1463b5a4916" + integrity sha512-9VuonZSWhnUrSnOAYQVkEpSJy3peLr0ES2rFAv0/pCnpNh0coEg09Q+QKmpH1A3KL55SUzb7vp62xzZ8RXvWjg== dependencies: "@a2a-js/sdk" "^0.3.13" "@helicone/helpers" "^1.6.0"