verify402-mcp 1.0.7 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -4
- package/client-id.mjs +17 -0
- package/index.mjs +10 -10
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ dedicated hot wallet funded with **Base Sepolia testnet USDC** (`eip155:84532`).
|
|
|
10
10
|
The analysis reads mainnet chain data, but the demo never settles mainnet
|
|
11
11
|
payments. The bridge never signs or sends a trade.
|
|
12
12
|
|
|
13
|
-
> **Published package note:** `verify402-mcp@1.0.
|
|
13
|
+
> **Published package note:** `verify402-mcp@1.0.8` is published to npm and
|
|
14
14
|
> defaults paid calls to Base Sepolia testnet (`eip155:84532`) for the public
|
|
15
15
|
> demo. Base mainnet (`eip155:8453`) requires an explicit `VERIFY402_NETWORKS`
|
|
16
16
|
> opt-in in a separate deployment and is not enabled by this release. The
|
|
@@ -46,9 +46,13 @@ Two optional caller-supplied checks are forwarded as-is:
|
|
|
46
46
|
- `payment.idempotencyKey` + `policy.obligation.settledKeys` rejects a key the
|
|
47
47
|
caller already settled; `policy.obligation.spentUsd` + `grantUsd` rejects
|
|
48
48
|
cumulative spend over the grant. The bridge does **not** persist this state.
|
|
49
|
-
- `policy.sellerPayoutWallets`
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
- `policy.sellerPayoutWallets` is a caller-supplied payout allowlist. A
|
|
50
|
+
mismatch is BLOCK, but a match is not provenance and never adds
|
|
51
|
+
`payee_payout_binding`.
|
|
52
|
+
- `policy.sellerPayoutManifest` fetches a seller-signed manifest from a
|
|
53
|
+
caller-pinned HTTPS origin outside the 402. Only a verified manifest whose
|
|
54
|
+
`payTo` list matches adds `payee_payout_binding`. The response includes
|
|
55
|
+
`payeeBinding` with the source, origin, signer, expiry, and manifest hash.
|
|
52
56
|
|
|
53
57
|
The gate is advisory at this layer: the bridge does not hold the agent's
|
|
54
58
|
transaction key and cannot stop an agent that ignores the answer. Enforcement
|
|
@@ -208,6 +212,10 @@ Once configured, your agent automatically sees the tools in its context. You can
|
|
|
208
212
|
- `MAX_PAYMENT_USD` is enforced inside the x402 client, before a signature
|
|
209
213
|
exists; removing it is a security regression. The caps limit what the bridge
|
|
210
214
|
can authorize; they do **not** protect a compromised private key.
|
|
215
|
+
- The bridge sends `X-Verify402-Client: verify402-mcp/<version>` and the same
|
|
216
|
+
version in its User-Agent. This is an anonymous label for `/usage`, not an
|
|
217
|
+
account: it contains no wallet, key, or user id, and the server treats it as
|
|
218
|
+
self-declared and spoofable.
|
|
211
219
|
- `preflight_gate` is a check, not a guarantee. Read `notChecked`: it does not
|
|
212
220
|
screen recipient reputation or sanctions, audit contracts, simulate full
|
|
213
221
|
balance deltas, audit the counterparty, independently prove payee ownership,
|
package/client-id.mjs
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// One shared, anonymous client label for every request the bridge makes.
|
|
2
|
+
//
|
|
3
|
+
// This is not an identity: it contains no wallet, key, account, or user id, and
|
|
4
|
+
// the server treats it as a self-declared hint. Its only job is to let /usage
|
|
5
|
+
// separate bridge traffic from generic HTTP probes. The version comes from the
|
|
6
|
+
// package manifest so a release bump cannot leave the label stale.
|
|
7
|
+
|
|
8
|
+
import { readFileSync } from "node:fs";
|
|
9
|
+
|
|
10
|
+
const pkg = JSON.parse(readFileSync(new URL("./package.json", import.meta.url), "utf8"));
|
|
11
|
+
|
|
12
|
+
export const CLIENT_ID = "verify402-mcp";
|
|
13
|
+
export const VERSION = pkg.version;
|
|
14
|
+
export const CLIENT_HEADERS = Object.freeze({
|
|
15
|
+
"User-Agent": `${CLIENT_ID}/${VERSION}`,
|
|
16
|
+
"X-Verify402-Client": `${CLIENT_ID}/${VERSION}`,
|
|
17
|
+
});
|
package/index.mjs
CHANGED
|
@@ -31,6 +31,7 @@ import { config } from "dotenv";
|
|
|
31
31
|
import { z } from "zod";
|
|
32
32
|
import { installConsoleRedaction, logInfo, logError } from "./log.mjs";
|
|
33
33
|
import { paidPost } from "./paid-call.mjs";
|
|
34
|
+
import { CLIENT_HEADERS, VERSION } from "./client-id.mjs";
|
|
34
35
|
import { fileURLToPath } from "node:url";
|
|
35
36
|
import { realpathSync } from "node:fs";
|
|
36
37
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
@@ -42,20 +43,14 @@ installConsoleRedaction();
|
|
|
42
43
|
const IS_MAIN = isMainModule();
|
|
43
44
|
if (IS_MAIN) config();
|
|
44
45
|
|
|
45
|
-
const VERSION = "1.0.7";
|
|
46
46
|
const EVM_PRIVATE_KEY = process.env.EVM_PRIVATE_KEY;
|
|
47
47
|
// Normalise once so the axios base URL, the payment policy prefix, and the
|
|
48
48
|
// per-tool expected resource are all the same string (no trailing slash).
|
|
49
49
|
const SERVER_URL = (process.env.VERIFY402_URL || "https://verify402.onrender.com")
|
|
50
50
|
.trim()
|
|
51
51
|
.replace(/\/+$/, "");
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
// wallet addresses.
|
|
55
|
-
const CLIENT_HEADERS = {
|
|
56
|
-
"User-Agent": `verify402-mcp/${VERSION}`,
|
|
57
|
-
"X-Verify402-Client": `mcp/${VERSION}`,
|
|
58
|
-
};
|
|
52
|
+
// CLIENT_HEADERS is a label, not an identity: it helps /usage separate MCP
|
|
53
|
+
// calls from generic HTTP probes without collecting keys or wallet addresses.
|
|
59
54
|
// Preflight is free, so it does not go through the paying client. Keeping the
|
|
60
55
|
// two clients separate means a misconfigured 402 on a free route can never
|
|
61
56
|
// turn into a signature.
|
|
@@ -223,7 +218,8 @@ const DESCRIPTIONS = {
|
|
|
223
218
|
"Free. Pass `from` (the wallet that will sign) to enable simulation and balance checks. For " +
|
|
224
219
|
"kind=transaction, chainId is REQUIRED (400 missing_chain without it). For payment intents " +
|
|
225
220
|
"with no `from` or no proof, the response is PASS with structural_only:true and names what was not checked. " +
|
|
226
|
-
"Optional caller-supplied checks: policy.sellerPayoutWallets
|
|
221
|
+
"Optional caller-supplied checks: policy.sellerPayoutWallets is a payout allowlist, not provenance; " +
|
|
222
|
+
"policy.sellerPayoutManifest fetches a seller-signed manifest from a caller-pinned HTTPS origin and is the only source of payee_payout_binding; " +
|
|
227
223
|
"policy.obligation checks a local settled-key ledger and cumulative spend against a grant. The gate does not persist either state.",
|
|
228
224
|
verify_arbitrage:
|
|
229
225
|
"Ask verify402 whether a cross-venue arbitrage opportunity is REAL, right now. " +
|
|
@@ -293,7 +289,11 @@ async function main() {
|
|
|
293
289
|
allowedFacilitators: z.array(z.string()).optional().describe("Allowlist of x402 facilitators"),
|
|
294
290
|
expectedResource: z.string().optional().describe("The exact resource URL the agent intends to pay for"),
|
|
295
291
|
allowedResourceHosts: z.array(z.string()).optional().describe("Bare hostnames allowed in payment.resource; a foreign host is BLOCK"),
|
|
296
|
-
sellerPayoutWallets: z.array(z.string()).optional().describe("Caller-supplied seller payout
|
|
292
|
+
sellerPayoutWallets: z.array(z.string()).optional().describe("Caller-supplied seller payout allowlist; a payTo mismatch is BLOCK. This is not provenance and never adds payee_payout_binding"),
|
|
293
|
+
sellerPayoutManifest: z.object({
|
|
294
|
+
url: z.string().url().describe("HTTPS URL of a seller-signed manifest outside the 402"),
|
|
295
|
+
expectedSigner: z.string().optional().describe("Optional EVM address pin for the manifest signer"),
|
|
296
|
+
}).optional().describe("Seller-signed payout manifest; the only source of payee_payout_binding"),
|
|
297
297
|
obligation: z.object({
|
|
298
298
|
settledKeys: z.array(z.string()).optional().describe("Payment idempotency keys already settled"),
|
|
299
299
|
spentUsd: z.number().nonnegative().optional().describe("Already spent under the grant, in USD"),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "verify402-mcp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"mcpName": "io.github.a32116150-ctrl/verify402-mcp",
|
|
5
5
|
"description": "MCP server for verify402: a free, fail-closed preflight gate before signing, plus five paid on-chain checks paid per call via x402.",
|
|
6
6
|
"type": "module",
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"payment-policy.mjs",
|
|
15
15
|
"paid-call.mjs",
|
|
16
16
|
"log.mjs",
|
|
17
|
+
"client-id.mjs",
|
|
17
18
|
"README.md",
|
|
18
19
|
".env.example",
|
|
19
20
|
"LICENSE",
|