xytara 3.1.6 → 3.1.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/RELEASE_NOTES.md CHANGED
@@ -1,10 +1,9 @@
1
- `xytara` 3.1.6 fixes the x402 facilitator request so strict facilitators like Coinbase CDP accept it.
1
+ `xytara` 3.1.8 refreshes the public MCP executor's machine-readable description so it leads with its pay-per-call story.
2
2
 
3
- The facilitator client was adding a `resource` field to the `paymentRequirements` it sends to `/verify` and `/settle`. The x402 v2 `PaymentRequirements` shape has no `resource` field (resource belongs on the payload), so strict facilitators such as the CDP-hosted facilitator rejected the request with HTTP 400, which blocked Base mainnet settlement through CDP. Lenient facilitators ignored the extra field, so the default Base Sepolia path was unaffected.
3
+ The server card and discovery surfaces previously described the executor as a generic tool list ("summaries, DNS lookup, catalog, replies, and JSON checks"). They now lead with what matters to an agent: pay-per-call MCP tools settled with x402 (USDC on Base mainnet). This improves how the executor is presented wherever its card is pulled — the MCP registry, Glama, x402scan, and any discovery-aware agent.
4
4
 
5
5
  Highlights:
6
6
 
7
- - sends a clean x402 v2 `paymentRequirements` (`scheme`, `network`, `asset`, `amount`, `payTo`, `maxTimeoutSeconds`, `extra`) with no extra `resource` field, so it matches `paymentPayload.accepted` and passes strict facilitator validation
8
- - this unblocks the CDP facilitator path added in the previous release, including Base mainnet
9
- - adds a regression check that the facilitator receives `paymentRequirements` without a `resource` field and equal to the payment payload's `accepted`
10
- - no change to payment semantics, settlement, or any route; lenient-facilitator and Base Sepolia behavior is unchanged
7
+ - the executor's `server.json`, served server card, and 402 discovery description now read "Pay-per-call MCP tools for agents, settled with x402 (USDC on Base mainnet). No API keys."
8
+ - bumps the MCP executor to 3.1.8; the release workflow republishes it to the official MCP registry with the new description, which Glama then syncs
9
+ - no change to tools, payment, settlement, or any route
@@ -6,9 +6,9 @@
6
6
  "source_ref": "spine.job.commitment",
7
7
  "committed_ref": "transaction.job.commitment",
8
8
  "precommit_pack_ref": "precommit.job.commitment.readiness",
9
- "transaction_id": "txn_2061435e6b7b",
10
- "receipt_id": "rcpt_4451d9ab898f",
11
- "proof_ref": "proof_rcpt_4451d9ab898f",
9
+ "transaction_id": "txn_2b8e48010d89",
10
+ "receipt_id": "rcpt_5b59fa10c38d",
11
+ "proof_ref": "proof_rcpt_5b59fa10c38d",
12
12
  "payment_protocol": "x402",
13
13
  "settlement_mode": "evm_payment",
14
14
  "treasury_destination_id": "treasury_acct_demo",
@@ -98,7 +98,7 @@ function createFacilitatorClient(config) {
98
98
  } catch (_) {
99
99
  json = null;
100
100
  }
101
- return { ok: response.ok, status: response.status, json };
101
+ return { ok: response.ok, status: response.status, json, bodyExcerpt: text ? text.slice(0, 400) : "" };
102
102
  } catch (error) {
103
103
  return { ok: false, status: 0, json: null, error: error && error.message ? error.message : "facilitator_request_failed" };
104
104
  } finally {
@@ -135,11 +135,17 @@ async function verifyX402Payment({ paymentHeader, accepts, resourceUrl, facilita
135
135
  void resourceUrl;
136
136
  const requirementsForFacilitator = { ...paymentRequirements };
137
137
  const verification = await facilitator.verify(paymentPayload, requirementsForFacilitator);
138
- if (!verification.ok || !verification.json) {
139
- return { ok: false, status: 402, reason: "facilitator_unreachable", detail: verification.error || `facilitator verify returned HTTP ${verification.status}` };
138
+ // A facilitator signals an invalid payment either with a 200 and isValid:false (x402.org) or
139
+ // with a non-2xx status and the same body (Coinbase CDP returns HTTP 400). Treat any response
140
+ // that carries an isValid flag as a verification result; only a missing/garbled body is a
141
+ // genuine transport failure.
142
+ if (!verification.json || typeof verification.json.isValid === "undefined") {
143
+ const body = verification.bodyExcerpt ? ` body=${verification.bodyExcerpt}` : "";
144
+ return { ok: false, status: 402, reason: "facilitator_unreachable", detail: verification.error || `facilitator verify returned HTTP ${verification.status}${body}` };
140
145
  }
141
146
  if (verification.json.isValid !== true) {
142
- return { ok: false, status: 402, reason: "payment_verification_failed", detail: normalizeString(verification.json.invalidReason) || "facilitator rejected the payment" };
147
+ const detail = normalizeString(verification.json.invalidReason) || normalizeString(verification.json.invalidMessage) || "facilitator rejected the payment";
148
+ return { ok: false, status: 402, reason: "payment_verification_failed", detail };
143
149
  }
144
150
  return {
145
151
  ok: true,
@@ -158,11 +164,15 @@ async function settleX402Payment({ authorization, facilitator, settle }) {
158
164
  return { ok: true, settlement: { success: true, skipped: true, reason: "settlement_disabled_by_operator", payer: authorization.payer } };
159
165
  }
160
166
  const settlement = await facilitator.settle(authorization.paymentPayload, authorization.paymentRequirements);
161
- if (!settlement.ok || !settlement.json) {
162
- return { ok: false, status: 402, reason: "facilitator_unreachable", detail: settlement.error || `facilitator settle returned HTTP ${settlement.status}` };
167
+ // As with verify, a facilitator may report a failed settlement with a non-2xx status and a
168
+ // body carrying success:false; only a missing/garbled body is a genuine transport failure.
169
+ if (!settlement.json || typeof settlement.json.success === "undefined") {
170
+ const body = settlement.bodyExcerpt ? ` body=${settlement.bodyExcerpt}` : "";
171
+ return { ok: false, status: 402, reason: "facilitator_unreachable", detail: settlement.error || `facilitator settle returned HTTP ${settlement.status}${body}` };
163
172
  }
164
173
  if (settlement.json.success !== true) {
165
- return { ok: false, status: 402, reason: "payment_settlement_failed", detail: normalizeString(settlement.json.errorReason) || "facilitator could not settle the payment" };
174
+ const detail = normalizeString(settlement.json.errorReason) || normalizeString(settlement.json.errorMessage) || "facilitator could not settle the payment";
175
+ return { ok: false, status: 402, reason: "payment_settlement_failed", detail };
166
176
  }
167
177
  return { ok: true, settlement: settlement.json };
168
178
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xytara",
3
- "version": "3.1.6",
3
+ "version": "3.1.8",
4
4
  "description": "Agent-commerce runtime for quote, pay, execute, deliver, meter, and integrate.",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -32,20 +32,22 @@ function startMockFacilitator() {
32
32
  let body = {};
33
33
  try { body = JSON.parse(Buffer.concat(chunks).toString("utf8") || "{}"); } catch (_) { body = {}; }
34
34
  const sig = body && body.paymentPayload && body.paymentPayload.payload ? String(body.paymentPayload.payload.signature || "") : "";
35
- const reply = (payload) => {
35
+ const reply = (payload, status) => {
36
36
  const text = JSON.stringify(payload);
37
- res.writeHead(200, { "content-type": "application/json", "content-length": Buffer.byteLength(text) });
37
+ res.writeHead(status || 200, { "content-type": "application/json", "content-length": Buffer.byteLength(text) });
38
38
  res.end(text);
39
39
  };
40
40
  if (req.method === "POST" && req.url === "/verify") {
41
41
  calls.verify += 1;
42
42
  calls.lastVerifyBody = body;
43
43
  if (sig === "reject-me") return reply({ isValid: false, invalidReason: "invalid_signature" });
44
+ if (sig === "cdp-invalid") return reply({ isValid: false, invalidReason: "invalid_payload", invalidMessage: "contract call failed: execution reverted", payer: "0x1111111111111111111111111111111111111111" }, 400);
44
45
  return reply({ isValid: true, payer: "0x1111111111111111111111111111111111111111" });
45
46
  }
46
47
  if (req.method === "POST" && req.url === "/settle") {
47
48
  calls.settle += 1;
48
49
  if (sig === "settle-fails") return reply({ success: false, errorReason: "insufficient_funds" });
50
+ if (sig === "cdp-settle-fail") return reply({ success: false, errorReason: "insufficient_funds" }, 400);
49
51
  return reply({ success: true, transaction: "0xmocktx", network: "eip155:84532", payer: "0x1111111111111111111111111111111111111111" });
50
52
  }
51
53
  res.writeHead(404); res.end();
@@ -166,6 +168,18 @@ async function main() {
166
168
  });
167
169
  assert.notStrictEqual(nativeAttempt.json && nativeAttempt.json.reason, "payment_verification_mode_unsupported", "native route must fall back to local_signed under x402_facilitator");
168
170
 
171
+ // 7b. CDP-style facilitator: a non-2xx verify carrying isValid:false is a verification
172
+ // failure, not an unreachable transport error.
173
+ const cdpInvalid = await post(baseUrl, "/x402/commands/execute", execBody, { "payment-signature": paymentHeader("cdp-invalid") });
174
+ assert.strictEqual(cdpInvalid.status, 402, "cdp-style 400 verify must still 402");
175
+ assert.strictEqual(cdpInvalid.json.reason, "payment_verification_failed", "cdp-style 400 must map to payment_verification_failed, not facilitator_unreachable");
176
+ assert.strictEqual(cdpInvalid.json.detail, "invalid_payload", "cdp-style verify detail must surface invalidReason");
177
+
178
+ // 7c. CDP-style settle failure: a non-2xx settle carrying success:false is a settlement failure.
179
+ const cdpSettleFail = await post(baseUrl, "/x402/commands/execute", execBody, { "payment-signature": paymentHeader("cdp-settle-fail") });
180
+ assert.strictEqual(cdpSettleFail.status, 402, "cdp-style 400 settle must still 402");
181
+ assert.strictEqual(cdpSettleFail.json.reason, "payment_settlement_failed", "cdp-style 400 settle must map to payment_settlement_failed");
182
+
169
183
  // 8. Self-serve credits top-up: unpaid -> 402 advertising accepts + credits_per_topup.
170
184
  const topupUnpaid = await post(baseUrl, "/x402/credits/topup", { account_id: "acct_topup" });
171
185
  assert.strictEqual(topupUnpaid.status, 402, "unpaid topup must 402");