@goplausible/algorand-mcp 4.3.2 → 4.3.3

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.
@@ -45,8 +45,29 @@ const x402ToolSchemas = {
45
45
  headers: { type: 'object', description: 'Optional additional request headers', additionalProperties: { type: 'string' } },
46
46
  correlationId: { type: 'string', description: 'Optional correlation id; forwarded as X-Correlation-ID header' },
47
47
  maxAmountPerRequest: { type: 'integer', description: 'Maximum payment in USDC atomic units (1,000,000 = $1.00). If a payment requirement exceeds this, the call is refused.' },
48
- paymentRequirements: { type: 'array', description: 'Pre-fetched accepts[] array from a prior x402_discover_payment_requirements call. If omitted, this tool discovers internally.' },
49
- preferredNetwork: { type: 'string', enum: ['mainnet', 'testnet', 'localnet'], description: 'Preferred Algorand network. When multiple accepts entries match, this network wins. If omitted, the active wallet network is used.' },
48
+ paymentRequirements: {
49
+ type: 'array',
50
+ description: 'Pre-fetched accepts[] array from a prior x402_discover_payment_requirements call. If omitted, this tool discovers internally. Each element must be an OBJECT with at least { scheme, network, payTo, asset, amount }. Common mistake: passing `preferredNetwork` or `maxAmountPerRequest` as array elements instead of sibling top-level fields.',
51
+ items: {
52
+ type: 'object',
53
+ required: ['scheme', 'network', 'payTo', 'asset'],
54
+ properties: {
55
+ scheme: { type: 'string' },
56
+ network: { type: 'string', description: 'CAIP-2 network identifier, e.g. "algorand:SGO1…" for testnet.' },
57
+ amount: { type: 'string', description: 'x402 V2 canonical amount field (atomic units as decimal string).' },
58
+ maxAmountRequired: { type: 'string', description: 'x402 V1 legacy amount field. Either `amount` or `maxAmountRequired` must be present.' },
59
+ payTo: { type: 'string' },
60
+ asset: { type: 'string' },
61
+ maxTimeoutSeconds: { type: 'integer' },
62
+ extra: { type: 'object' }
63
+ }
64
+ }
65
+ },
66
+ preferredNetwork: {
67
+ type: 'string',
68
+ enum: ['mainnet', 'testnet', 'localnet'],
69
+ description: 'TOP-LEVEL sibling of paymentRequirements (NOT an array element). Preferred Algorand network when multiple accepts entries match. If omitted, the cheapest affordable Algorand entry is chosen.'
70
+ },
50
71
  extensions: { type: 'object', description: 'Optional pre-fetched PaymentRequired extensions (e.g. Bazaar resource details). Passed through to the response under `extensions`.' },
51
72
  },
52
73
  required: ['baseURL', 'path', 'method'],
@@ -86,7 +107,35 @@ export class X402Manager {
86
107
  const preferredNetwork = typeof args.preferredNetwork === 'string' ? args.preferredNetwork : undefined;
87
108
  const extensions = args.extensions ?? undefined;
88
109
  let accepts = Array.isArray(args.paymentRequirements) ? args.paymentRequirements : undefined;
89
- // 1. Discover if needed
110
+ // 1a. Defensive validation: every paymentRequirements entry must be an
111
+ // object with at least { network, payTo, asset } and an amount field
112
+ // (either x402 V2 `amount` or V1 `maxAmountRequired`). LLMs frequently
113
+ // malform JSON by appending sibling field names (e.g. "preferredNetwork")
114
+ // as bare strings inside this array — catch that here with an actionable
115
+ // message rather than crashing later in selectRequirement.
116
+ if (accepts) {
117
+ for (let i = 0; i < accepts.length; i++) {
118
+ const entry = accepts[i];
119
+ const isObject = !!entry && typeof entry === 'object' && !Array.isArray(entry);
120
+ if (!isObject) {
121
+ throw new McpError(ErrorCode.InvalidParams, `paymentRequirements[${i}] must be an OBJECT (got ${entry === null ? 'null' : Array.isArray(entry) ? 'array' : typeof entry}: ${JSON.stringify(entry)}). Tip: pass the accepts[] array verbatim from x402_discover_payment_requirements. Other arguments like preferredNetwork and maxAmountPerRequest are TOP-LEVEL siblings of paymentRequirements, not array members.`);
122
+ }
123
+ const e = entry;
124
+ const missing = [];
125
+ if (typeof e.network !== 'string')
126
+ missing.push('network');
127
+ if (typeof e.payTo !== 'string')
128
+ missing.push('payTo');
129
+ if (typeof e.asset !== 'string')
130
+ missing.push('asset');
131
+ if (typeof e.amount !== 'string' && typeof e.maxAmountRequired !== 'string')
132
+ missing.push('amount (or maxAmountRequired)');
133
+ if (missing.length > 0) {
134
+ throw new McpError(ErrorCode.InvalidParams, `paymentRequirements[${i}] is missing required string field(s): ${missing.join(', ')}. Expected shape: { scheme, network, payTo, asset, amount, extra: { feePayer, ... } }. Pass the accepts[] entry verbatim from x402_discover_payment_requirements.`);
135
+ }
136
+ }
137
+ }
138
+ // 1b. Discover if needed
90
139
  if (!accepts || accepts.length === 0) {
91
140
  const discovered = await X402Manager.discover({ baseURL, path, method, queryParams, body });
92
141
  if (!discovered.x402 || !Array.isArray(discovered.accepts) || discovered.accepts.length === 0) {
package/dist/types.d.ts CHANGED
@@ -47,16 +47,16 @@ export declare const AccountDetailsSchema: z.ZodObject<{
47
47
  }>, "many">;
48
48
  authAddress: z.ZodOptional<z.ZodString>;
49
49
  }, "strip", z.ZodTypeAny, {
50
- address: string;
51
50
  amount: number | bigint;
51
+ address: string;
52
52
  assets: {
53
53
  assetId: number | bigint;
54
54
  amount: number | bigint;
55
55
  }[];
56
56
  authAddress?: string | undefined;
57
57
  }, {
58
- address: string;
59
58
  amount: number | bigint;
59
+ address: string;
60
60
  assets: {
61
61
  assetId: number | bigint;
62
62
  amount: number | bigint;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goplausible/algorand-mcp",
3
- "version": "4.3.2",
3
+ "version": "4.3.3",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },