@nevermined-io/payments 1.0.3 → 1.0.4

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.
@@ -23,11 +23,22 @@ export declare class PaywallAuthenticator {
23
23
  * @returns HTTP endpoint URL or undefined if context is not available
24
24
  */
25
25
  private buildHttpUrlFromContext;
26
+ /**
27
+ * Core verification logic shared by authenticate and authenticateMeta.
28
+ * Tries logical URL first, falls back to HTTP URL if available.
29
+ */
30
+ private verifyWithFallback;
31
+ /**
32
+ * Verify permissions against a single endpoint URL.
33
+ * Resolves planId from the token or from the agent's plans as fallback.
34
+ */
35
+ private verifyWithEndpoint;
26
36
  /**
27
37
  * Authenticate an MCP request
28
38
  */
29
39
  authenticate(extra: any, options: {
30
40
  planId?: string;
41
+ maxAmount?: bigint;
31
42
  } | undefined, agentId: string, serverName: string, name: string, kind: 'tool' | 'resource' | 'prompt', argsOrVars: any): Promise<AuthResult>;
32
43
  /**
33
44
  * Authenticate generic MCP meta operations (e.g., initialize, tools/list, resources/list, prompts/list).
@@ -35,6 +46,7 @@ export declare class PaywallAuthenticator {
35
46
  */
36
47
  authenticateMeta(extra: any, options: {
37
48
  planId?: string;
49
+ maxAmount?: bigint;
38
50
  } | undefined, agentId: string, serverName: string, method: string): Promise<AuthResult>;
39
51
  }
40
52
  //# sourceMappingURL=auth.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/mcp/core/auth.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAGjD,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAA;AAMtD;;GAEG;AACH,qBAAa,oBAAoB;IACnB,OAAO,CAAC,QAAQ;gBAAR,QAAQ,EAAE,QAAQ;IAEtC;;;;;;OAMG;IACH,OAAO,CAAC,4BAA4B;IAepC;;;;OAIG;IACH,OAAO,CAAC,uBAAuB;IAuB/B;;OAEG;IACG,YAAY,CAChB,KAAK,EAAE,GAAG,EAEV,OAAO,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,YAAK,EACjC,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,QAAQ,EACpC,UAAU,EAAE,GAAG,GACd,OAAO,CAAC,UAAU,CAAC;IAuItB;;;OAGG;IACG,gBAAgB,CACpB,KAAK,EAAE,GAAG,EACV,OAAO,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,YAAK,EACjC,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,UAAU,CAAC;CA8GvB"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/mcp/core/auth.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAGjD,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAA;AAgBtD;;GAEG;AACH,qBAAa,oBAAoB;IACnB,OAAO,CAAC,QAAQ;gBAAR,QAAQ,EAAE,QAAQ;IAEtC;;;;;;OAMG;IACH,OAAO,CAAC,4BAA4B;IAepC;;;;OAIG;IACH,OAAO,CAAC,uBAAuB;IAuB/B;;;OAGG;YACW,kBAAkB;IAoEhC;;;OAGG;YACW,kBAAkB;IAoDhC;;OAEG;IACG,YAAY,CAChB,KAAK,EAAE,GAAG,EACV,OAAO,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,YAAK,EACrD,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,QAAQ,EACpC,UAAU,EAAE,GAAG,GACd,OAAO,CAAC,UAAU,CAAC;IAkBtB;;;OAGG;IACG,gBAAgB,CACpB,KAAK,EAAE,GAAG,EACV,OAAO,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,YAAK,EACrD,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,UAAU,CAAC;CAiBvB"}
@@ -56,126 +56,122 @@ export class PaywallAuthenticator {
56
56
  }
57
57
  }
58
58
  /**
59
- * Authenticate an MCP request
59
+ * Core verification logic shared by authenticate and authenticateMeta.
60
+ * Tries logical URL first, falls back to HTTP URL if available.
60
61
  */
61
- async authenticate(extra,
62
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
63
- options = {}, agentId, serverName, name, kind, argsOrVars) {
64
- const authHeader = this.extractAuthHeaderFromContext(extra);
65
- if (!authHeader) {
66
- throw createRpcError(ERROR_CODES.PaymentRequired, 'Authorization required', {
67
- reason: 'missing',
68
- });
69
- }
70
- const accessToken = stripBearer(authHeader);
71
- const logicalUrl = buildLogicalUrl({ kind, serverName, name, argsOrVars });
72
- // Validate access with Nevermined - try logical URL first
62
+ async verifyWithFallback(ctx) {
63
+ const { accessToken, logicalUrl, httpUrl, maxAmount, agentId, planIdOverride } = ctx;
64
+ // Try logical URL first
73
65
  try {
74
- const decodedAccessToken = decodeAccessToken(accessToken);
75
- if (!decodedAccessToken) {
76
- throw new Error('Invalid access token');
77
- }
78
- const planId = decodedAccessToken.accepted?.planId;
79
- // Extract subscriberAddress from payload.authorization.from per x402 spec
80
- const subscriberAddress = decodedAccessToken.payload?.authorization?.from;
81
- if (!planId || !subscriberAddress) {
82
- throw new Error('Cannot determine plan_id or subscriber_address from token (expected payload.authorization.from)');
83
- }
84
- const paymentRequired = buildPaymentRequired(planId, {
85
- endpoint: logicalUrl,
86
- agentId,
87
- httpVerb: 'POST',
88
- });
89
- const result = await this.payments.facilitator.verifyPermissions({
90
- paymentRequired,
91
- x402AccessToken: accessToken,
92
- maxAmount: 1n,
93
- });
94
- if (!result.isValid) {
95
- throw new Error('Permission verification failed');
96
- }
66
+ const result = await this.verifyWithEndpoint(accessToken, logicalUrl, agentId, maxAmount, planIdOverride);
97
67
  return {
98
68
  token: accessToken,
99
69
  agentId,
100
70
  logicalUrl,
101
- planId,
102
- subscriberAddress,
71
+ httpUrl,
72
+ planId: result.planId,
73
+ subscriberAddress: result.subscriberAddress,
103
74
  agentRequest: result.agentRequest,
104
75
  };
105
76
  }
106
- catch (e) {
107
- // If logical URL validation fails, try with HTTP endpoint
108
- const httpUrl = this.buildHttpUrlFromContext();
109
- if (httpUrl) {
110
- try {
111
- const decodedAccessToken = decodeAccessToken(accessToken);
112
- if (!decodedAccessToken) {
113
- throw new Error('Invalid access token');
114
- }
115
- // Extract planId from accepted.planId per x402 spec
116
- let planId = decodedAccessToken.accepted?.planId;
117
- // Extract subscriberAddress from payload.authorization.from per x402 spec
118
- const subscriberAddress = decodedAccessToken.payload?.authorization?.from;
119
- // If planId is not in the token, try to get it from the agent's plans
120
- if (!planId) {
121
- try {
122
- const agentPlans = await this.payments.agents.getAgentPlans(agentId);
123
- if (agentPlans && Array.isArray(agentPlans.plans) && agentPlans.plans.length > 0) {
124
- planId = agentPlans.plans[0].planId || agentPlans.plans[0].id;
125
- }
126
- }
127
- catch (planError) {
128
- // Ignore errors fetching plans
129
- }
130
- }
131
- if (!planId || !subscriberAddress) {
132
- throw new Error('Cannot determine plan_id or subscriber_address from token (expected accepted.planId and payload.authorization.from)');
133
- }
134
- const paymentRequired = buildPaymentRequired(planId, {
135
- endpoint: httpUrl,
136
- agentId,
137
- httpVerb: 'POST',
138
- });
139
- const result = await this.payments.facilitator.verifyPermissions({
140
- paymentRequired,
141
- x402AccessToken: accessToken,
142
- maxAmount: 1n,
143
- });
144
- if (!result.isValid) {
145
- throw new Error('Permission verification failed');
146
- }
147
- return {
148
- token: accessToken,
149
- agentId,
150
- logicalUrl,
151
- planId,
152
- subscriberAddress,
153
- agentRequest: result.agentRequest,
154
- };
155
- }
156
- catch (httpError) {
157
- void httpError;
158
- }
77
+ catch {
78
+ // If logical URL fails and we have an HTTP URL, try that
79
+ }
80
+ if (httpUrl) {
81
+ try {
82
+ const result = await this.verifyWithEndpoint(accessToken, httpUrl, agentId, maxAmount, planIdOverride);
83
+ return {
84
+ token: accessToken,
85
+ agentId,
86
+ logicalUrl,
87
+ httpUrl,
88
+ planId: result.planId,
89
+ subscriberAddress: result.subscriberAddress,
90
+ agentRequest: result.agentRequest,
91
+ };
92
+ }
93
+ catch {
94
+ // HTTP fallback also failed
159
95
  }
160
- // Enrich denial with suggested plans (best-effort)
161
- let plansMsg = '';
96
+ }
97
+ // Both attempts failed — enrich denial with suggested plans (best-effort)
98
+ let plansMsg = '';
99
+ try {
100
+ const plans = await this.payments.agents.getAgentPlans(agentId);
101
+ if (plans && Array.isArray(plans.plans) && plans.plans.length > 0) {
102
+ const top = plans.plans.slice(0, 3);
103
+ const summary = top
104
+ .map((p) => `${p.planId || p.id || 'plan'}${p.name ? ` (${p.name})` : ''}`)
105
+ .join(', ');
106
+ plansMsg = summary ? ` Available plans: ${summary}...` : '';
107
+ }
108
+ }
109
+ catch {
110
+ // Ignore errors fetching plans - best effort only
111
+ }
112
+ throw createRpcError(ERROR_CODES.PaymentRequired, `Payment required.${plansMsg}`, {
113
+ reason: 'invalid',
114
+ });
115
+ }
116
+ /**
117
+ * Verify permissions against a single endpoint URL.
118
+ * Resolves planId from the token or from the agent's plans as fallback.
119
+ */
120
+ async verifyWithEndpoint(accessToken, endpoint, agentId, maxAmount, planIdOverride) {
121
+ const decodedAccessToken = decodeAccessToken(accessToken);
122
+ if (!decodedAccessToken) {
123
+ throw new Error('Invalid access token');
124
+ }
125
+ let planId = planIdOverride ?? decodedAccessToken.accepted?.planId;
126
+ const subscriberAddress = decodedAccessToken.payload?.authorization?.from;
127
+ // If planId is not available, try to get it from the agent's plans
128
+ if (!planId) {
162
129
  try {
163
- const plans = await this.payments.agents.getAgentPlans(agentId);
164
- if (plans && Array.isArray(plans.plans) && plans.plans.length > 0) {
165
- const top = plans.plans.slice(0, 3);
166
- const summary = top
167
- .map((p) => `${p.planId || p.id || 'plan'}${p.name ? ` (${p.name})` : ''}`)
168
- .join(', ');
169
- plansMsg = summary ? ` Available plans: ${summary}...` : '';
130
+ const agentPlans = await this.payments.agents.getAgentPlans(agentId);
131
+ if (agentPlans && Array.isArray(agentPlans.plans) && agentPlans.plans.length > 0) {
132
+ planId = agentPlans.plans[0].planId || agentPlans.plans[0].id;
170
133
  }
171
134
  }
172
135
  catch {
173
- // Ignore errors fetching plans - best effort only
136
+ // Ignore errors fetching plans
174
137
  }
175
- throw createRpcError(ERROR_CODES.PaymentRequired, `Payment required.${plansMsg}`, {
176
- reason: 'invalid',
138
+ }
139
+ if (!planId || !subscriberAddress) {
140
+ throw new Error('Cannot determine plan_id or subscriber_address from token (expected accepted.planId and payload.authorization.from)');
141
+ }
142
+ const paymentRequired = buildPaymentRequired(planId, {
143
+ endpoint,
144
+ agentId,
145
+ httpVerb: 'POST',
146
+ });
147
+ const result = await this.payments.facilitator.verifyPermissions({
148
+ paymentRequired,
149
+ x402AccessToken: accessToken,
150
+ maxAmount,
151
+ });
152
+ if (!result.isValid) {
153
+ throw new Error('Permission verification failed');
154
+ }
155
+ return { planId, subscriberAddress, agentRequest: result.agentRequest };
156
+ }
157
+ /**
158
+ * Authenticate an MCP request
159
+ */
160
+ async authenticate(extra, options = {}, agentId, serverName, name, kind, argsOrVars) {
161
+ const authHeader = this.extractAuthHeaderFromContext(extra);
162
+ if (!authHeader) {
163
+ throw createRpcError(ERROR_CODES.PaymentRequired, 'Authorization required', {
164
+ reason: 'missing',
177
165
  });
178
166
  }
167
+ return this.verifyWithFallback({
168
+ accessToken: stripBearer(authHeader),
169
+ logicalUrl: buildLogicalUrl({ kind, serverName, name, argsOrVars }),
170
+ httpUrl: this.buildHttpUrlFromContext(),
171
+ maxAmount: options.maxAmount ?? 1n,
172
+ agentId,
173
+ planIdOverride: options.planId,
174
+ });
179
175
  }
180
176
  /**
181
177
  * Authenticate generic MCP meta operations (e.g., initialize, tools/list, resources/list, prompts/list).
@@ -188,100 +184,14 @@ export class PaywallAuthenticator {
188
184
  reason: 'missing',
189
185
  });
190
186
  }
191
- const accessToken = stripBearer(authHeader);
192
- const logicalUrl = buildLogicalMetaUrl(serverName, method);
193
- try {
194
- const decodedAccessToken = decodeAccessToken(accessToken);
195
- if (!decodedAccessToken) {
196
- throw new Error('Invalid access token');
197
- }
198
- const planId = options.planId;
199
- // Extract subscriberAddress from payload.authorization.from per x402 spec
200
- const subscriberAddress = decodedAccessToken.payload?.authorization?.from;
201
- if (!planId || !subscriberAddress) {
202
- throw new Error('Cannot determine plan_id or subscriber_address from token (expected payload.authorization.from)');
203
- }
204
- const paymentRequired = buildPaymentRequired(planId, {
205
- endpoint: logicalUrl,
206
- agentId,
207
- httpVerb: 'POST',
208
- });
209
- const result = await this.payments.facilitator.verifyPermissions({
210
- paymentRequired,
211
- x402AccessToken: accessToken,
212
- maxAmount: 1n,
213
- });
214
- if (!result.isValid) {
215
- throw new Error('Permission verification failed');
216
- }
217
- return {
218
- token: accessToken,
219
- agentId,
220
- logicalUrl,
221
- planId,
222
- subscriberAddress,
223
- agentRequest: result.agentRequest,
224
- };
225
- }
226
- catch (e) {
227
- const httpUrl = this.buildHttpUrlFromContext();
228
- if (httpUrl) {
229
- try {
230
- const decodedAccessToken = decodeAccessToken(accessToken);
231
- if (!decodedAccessToken) {
232
- throw new Error('Invalid access token');
233
- }
234
- // Extract planId from accepted.planId per x402 spec
235
- const planId = decodedAccessToken.accepted?.planId;
236
- // Extract subscriberAddress from payload.authorization.from per x402 spec
237
- const subscriberAddress = decodedAccessToken.payload?.authorization?.from;
238
- if (!planId || !subscriberAddress) {
239
- throw new Error('Cannot determine plan_id or subscriber_address from token (expected accepted.planId and payload.authorization.from)');
240
- }
241
- const paymentRequired = buildPaymentRequired(planId, {
242
- endpoint: httpUrl,
243
- agentId,
244
- httpVerb: 'POST',
245
- });
246
- const result = await this.payments.facilitator.verifyPermissions({
247
- paymentRequired,
248
- x402AccessToken: accessToken,
249
- maxAmount: 1n,
250
- });
251
- if (!result.isValid) {
252
- throw new Error('Permission verification failed');
253
- }
254
- return {
255
- token: accessToken,
256
- agentId,
257
- logicalUrl: httpUrl,
258
- planId,
259
- subscriberAddress,
260
- agentRequest: result.agentRequest,
261
- };
262
- }
263
- catch (httpError) {
264
- void httpError;
265
- }
266
- }
267
- let plansMsg = '';
268
- try {
269
- const plans = await this.payments.agents.getAgentPlans(agentId);
270
- if (plans && Array.isArray(plans.plans) && plans.plans.length > 0) {
271
- const top = plans.plans.slice(0, 3);
272
- const summary = top
273
- .map((p) => `${p.planId || p.id || 'plan'}${p.name ? ` (${p.name})` : ''}`)
274
- .join(', ');
275
- plansMsg = summary ? ` Available plans: ${summary}...` : '';
276
- }
277
- }
278
- catch (_err) {
279
- void _err;
280
- }
281
- throw createRpcError(ERROR_CODES.PaymentRequired, `Payment required.${plansMsg}`, {
282
- reason: 'invalid',
283
- });
284
- }
187
+ return this.verifyWithFallback({
188
+ accessToken: stripBearer(authHeader),
189
+ logicalUrl: buildLogicalMetaUrl(serverName, method),
190
+ httpUrl: this.buildHttpUrlFromContext(),
191
+ maxAmount: options.maxAmount ?? 1n,
192
+ agentId,
193
+ planIdOverride: options.planId,
194
+ });
285
195
  }
286
196
  }
287
197
  //# sourceMappingURL=auth.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../src/mcp/core/auth.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Authentication handler for MCP paywall using X402 tokens\n */\nimport type { Payments } from '../../payments.js'\nimport { decodeAccessToken } from '../../utils.js'\nimport { getCurrentRequestContext } from '../http/mcp-handler.js'\nimport { AuthResult } from '../types/paywall.types.js'\nimport { ERROR_CODES, createRpcError } from '../utils/errors.js'\nimport { buildLogicalMetaUrl, buildLogicalUrl } from '../utils/logical-url.js'\nimport { extractAuthHeader, stripBearer } from '../utils/request.js'\nimport { buildPaymentRequired, type X402PaymentRequired } from '../../x402/facilitator-api.js'\n\n/**\n * Handles authentication and authorization for MCP requests\n */\nexport class PaywallAuthenticator {\n constructor(private payments: Payments) {}\n\n /**\n * Extract authorization header from extra context or AsyncLocalStorage.\n * Tries SDK's extra context first, then falls back to HTTP request context.\n *\n * @param extra - MCP extra context from SDK\n * @returns Authorization header value or undefined\n */\n private extractAuthHeaderFromContext(extra: any): string | undefined {\n // Try to extract auth header from SDK's extra context first\n let authHeader = extractAuthHeader(extra)\n\n if (!authHeader) {\n const requestContext = getCurrentRequestContext()\n if (requestContext?.headers) {\n // Build an extra-like object for extractAuthHeader\n authHeader = extractAuthHeader({ requestInfo: { headers: requestContext.headers } })\n }\n }\n\n return authHeader\n }\n\n /**\n * Build HTTP endpoint URL from request context.\n *\n * @returns HTTP endpoint URL or undefined if context is not available\n */\n private buildHttpUrlFromContext(): string | undefined {\n const requestContext = getCurrentRequestContext()\n if (!requestContext) {\n return undefined\n }\n\n try {\n const host = requestContext.headers?.['host'] || requestContext.headers?.['x-forwarded-host']\n if (!host || typeof host !== 'string') {\n return undefined\n }\n\n const protocol = requestContext.headers?.['x-forwarded-proto'] || 'http'\n const baseUrl = `${protocol}://${host}`\n\n // Use requestContext.url if available (e.g., '/mcp'), otherwise default to '/mcp'\n const path = requestContext.url || '/mcp'\n return `${baseUrl}${path}`\n } catch {\n return undefined\n }\n }\n\n /**\n * Authenticate an MCP request\n */\n async authenticate(\n extra: any,\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n options: { planId?: string } = {},\n agentId: string,\n serverName: string,\n name: string,\n kind: 'tool' | 'resource' | 'prompt',\n argsOrVars: any,\n ): Promise<AuthResult> {\n const authHeader = this.extractAuthHeaderFromContext(extra)\n if (!authHeader) {\n throw createRpcError(ERROR_CODES.PaymentRequired, 'Authorization required', {\n reason: 'missing',\n })\n }\n\n const accessToken = stripBearer(authHeader)\n const logicalUrl = buildLogicalUrl({ kind, serverName, name, argsOrVars })\n\n // Validate access with Nevermined - try logical URL first\n try {\n const decodedAccessToken = decodeAccessToken(accessToken)\n if (!decodedAccessToken) {\n throw new Error('Invalid access token')\n }\n\n const planId = decodedAccessToken.accepted?.planId\n\n // Extract subscriberAddress from payload.authorization.from per x402 spec\n const subscriberAddress = decodedAccessToken.payload?.authorization?.from\n\n if (!planId || !subscriberAddress) {\n throw new Error(\n 'Cannot determine plan_id or subscriber_address from token (expected payload.authorization.from)',\n )\n }\n\n const paymentRequired: X402PaymentRequired = buildPaymentRequired(planId, {\n endpoint: logicalUrl,\n agentId,\n httpVerb: 'POST',\n })\n\n const result = await this.payments.facilitator.verifyPermissions({\n paymentRequired,\n x402AccessToken: accessToken,\n maxAmount: 1n,\n })\n\n if (!result.isValid) {\n throw new Error('Permission verification failed')\n }\n\n return {\n token: accessToken,\n agentId,\n logicalUrl,\n planId,\n subscriberAddress,\n agentRequest: result.agentRequest,\n }\n } catch (e) {\n // If logical URL validation fails, try with HTTP endpoint\n const httpUrl = this.buildHttpUrlFromContext()\n if (httpUrl) {\n try {\n const decodedAccessToken = decodeAccessToken(accessToken)\n if (!decodedAccessToken) {\n throw new Error('Invalid access token')\n }\n // Extract planId from accepted.planId per x402 spec\n let planId = decodedAccessToken.accepted?.planId\n // Extract subscriberAddress from payload.authorization.from per x402 spec\n const subscriberAddress = decodedAccessToken.payload?.authorization?.from\n\n // If planId is not in the token, try to get it from the agent's plans\n if (!planId) {\n try {\n const agentPlans = await this.payments.agents.getAgentPlans(agentId)\n if (agentPlans && Array.isArray(agentPlans.plans) && agentPlans.plans.length > 0) {\n planId = agentPlans.plans[0].planId || agentPlans.plans[0].id\n }\n } catch (planError) {\n // Ignore errors fetching plans\n }\n }\n\n if (!planId || !subscriberAddress) {\n throw new Error(\n 'Cannot determine plan_id or subscriber_address from token (expected accepted.planId and payload.authorization.from)',\n )\n }\n\n const paymentRequired: X402PaymentRequired = buildPaymentRequired(planId, {\n endpoint: httpUrl,\n agentId,\n httpVerb: 'POST',\n })\n\n const result = await this.payments.facilitator.verifyPermissions({\n paymentRequired,\n x402AccessToken: accessToken,\n maxAmount: 1n,\n })\n\n if (!result.isValid) {\n throw new Error('Permission verification failed')\n }\n\n return {\n token: accessToken,\n agentId,\n logicalUrl,\n planId,\n subscriberAddress,\n agentRequest: result.agentRequest,\n }\n } catch (httpError) {\n void httpError\n }\n }\n\n // Enrich denial with suggested plans (best-effort)\n let plansMsg = ''\n try {\n const plans = await this.payments.agents.getAgentPlans(agentId)\n if (plans && Array.isArray(plans.plans) && plans.plans.length > 0) {\n const top = plans.plans.slice(0, 3)\n const summary = top\n .map((p: any) => `${p.planId || p.id || 'plan'}${p.name ? ` (${p.name})` : ''}`)\n .join(', ')\n plansMsg = summary ? ` Available plans: ${summary}...` : ''\n }\n } catch {\n // Ignore errors fetching plans - best effort only\n }\n\n throw createRpcError(ERROR_CODES.PaymentRequired, `Payment required.${plansMsg}`, {\n reason: 'invalid',\n })\n }\n }\n\n /**\n * Authenticate generic MCP meta operations (e.g., initialize, tools/list, resources/list, prompts/list).\n * Returns an AuthResult compatible with paywall flows (without redeem step).\n */\n async authenticateMeta(\n extra: any,\n options: { planId?: string } = {},\n agentId: string,\n serverName: string,\n method: string,\n ): Promise<AuthResult> {\n const authHeader = this.extractAuthHeaderFromContext(extra)\n if (!authHeader) {\n throw createRpcError(ERROR_CODES.PaymentRequired, 'Authorization required', {\n reason: 'missing',\n })\n }\n const accessToken = stripBearer(authHeader)\n const logicalUrl = buildLogicalMetaUrl(serverName, method)\n\n try {\n const decodedAccessToken = decodeAccessToken(accessToken)\n if (!decodedAccessToken) {\n throw new Error('Invalid access token')\n }\n const planId = options.planId\n // Extract subscriberAddress from payload.authorization.from per x402 spec\n const subscriberAddress = decodedAccessToken.payload?.authorization?.from\n if (!planId || !subscriberAddress) {\n throw new Error(\n 'Cannot determine plan_id or subscriber_address from token (expected payload.authorization.from)',\n )\n }\n\n const paymentRequired: X402PaymentRequired = buildPaymentRequired(planId, {\n endpoint: logicalUrl,\n agentId,\n httpVerb: 'POST',\n })\n\n const result = await this.payments.facilitator.verifyPermissions({\n paymentRequired,\n x402AccessToken: accessToken,\n maxAmount: 1n,\n })\n if (!result.isValid) {\n throw new Error('Permission verification failed')\n }\n return {\n token: accessToken,\n agentId,\n logicalUrl,\n planId,\n subscriberAddress,\n agentRequest: result.agentRequest,\n }\n } catch (e) {\n const httpUrl = this.buildHttpUrlFromContext()\n if (httpUrl) {\n try {\n const decodedAccessToken = decodeAccessToken(accessToken)\n if (!decodedAccessToken) {\n throw new Error('Invalid access token')\n }\n // Extract planId from accepted.planId per x402 spec\n const planId = decodedAccessToken.accepted?.planId\n // Extract subscriberAddress from payload.authorization.from per x402 spec\n const subscriberAddress = decodedAccessToken.payload?.authorization?.from\n if (!planId || !subscriberAddress) {\n throw new Error(\n 'Cannot determine plan_id or subscriber_address from token (expected accepted.planId and payload.authorization.from)',\n )\n }\n\n const paymentRequired: X402PaymentRequired = buildPaymentRequired(planId, {\n endpoint: httpUrl,\n agentId,\n httpVerb: 'POST',\n })\n\n const result = await this.payments.facilitator.verifyPermissions({\n paymentRequired,\n x402AccessToken: accessToken,\n maxAmount: 1n,\n })\n if (!result.isValid) {\n throw new Error('Permission verification failed')\n }\n return {\n token: accessToken,\n agentId,\n logicalUrl: httpUrl,\n planId,\n subscriberAddress,\n agentRequest: result.agentRequest,\n }\n } catch (httpError) {\n void httpError\n }\n }\n\n let plansMsg = ''\n try {\n const plans = await this.payments.agents.getAgentPlans(agentId)\n if (plans && Array.isArray(plans.plans) && plans.plans.length > 0) {\n const top = plans.plans.slice(0, 3)\n const summary = top\n .map((p: any) => `${p.planId || p.id || 'plan'}${p.name ? ` (${p.name})` : ''}`)\n .join(', ')\n plansMsg = summary ? ` Available plans: ${summary}...` : ''\n }\n } catch (_err) {\n void _err\n }\n\n throw createRpcError(ERROR_CODES.PaymentRequired, `Payment required.${plansMsg}`, {\n reason: 'invalid',\n })\n }\n }\n}\n"]}
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../src/mcp/core/auth.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Authentication handler for MCP paywall using X402 tokens\n */\nimport type { Payments } from '../../payments.js'\nimport { decodeAccessToken } from '../../utils.js'\nimport { getCurrentRequestContext } from '../http/mcp-handler.js'\nimport { AuthResult } from '../types/paywall.types.js'\nimport { ERROR_CODES, createRpcError } from '../utils/errors.js'\nimport { Address } from '../../common/types.js'\nimport { buildLogicalMetaUrl, buildLogicalUrl } from '../utils/logical-url.js'\nimport { extractAuthHeader, stripBearer } from '../utils/request.js'\nimport { buildPaymentRequired, type X402PaymentRequired } from '../../x402/facilitator-api.js'\n\ninterface VerifyContext {\n accessToken: string\n logicalUrl: string\n httpUrl: string | undefined\n maxAmount: bigint\n agentId: string\n planIdOverride?: string\n}\n\n/**\n * Handles authentication and authorization for MCP requests\n */\nexport class PaywallAuthenticator {\n constructor(private payments: Payments) {}\n\n /**\n * Extract authorization header from extra context or AsyncLocalStorage.\n * Tries SDK's extra context first, then falls back to HTTP request context.\n *\n * @param extra - MCP extra context from SDK\n * @returns Authorization header value or undefined\n */\n private extractAuthHeaderFromContext(extra: any): string | undefined {\n // Try to extract auth header from SDK's extra context first\n let authHeader = extractAuthHeader(extra)\n\n if (!authHeader) {\n const requestContext = getCurrentRequestContext()\n if (requestContext?.headers) {\n // Build an extra-like object for extractAuthHeader\n authHeader = extractAuthHeader({ requestInfo: { headers: requestContext.headers } })\n }\n }\n\n return authHeader\n }\n\n /**\n * Build HTTP endpoint URL from request context.\n *\n * @returns HTTP endpoint URL or undefined if context is not available\n */\n private buildHttpUrlFromContext(): string | undefined {\n const requestContext = getCurrentRequestContext()\n if (!requestContext) {\n return undefined\n }\n\n try {\n const host = requestContext.headers?.['host'] || requestContext.headers?.['x-forwarded-host']\n if (!host || typeof host !== 'string') {\n return undefined\n }\n\n const protocol = requestContext.headers?.['x-forwarded-proto'] || 'http'\n const baseUrl = `${protocol}://${host}`\n\n // Use requestContext.url if available (e.g., '/mcp'), otherwise default to '/mcp'\n const path = requestContext.url || '/mcp'\n return `${baseUrl}${path}`\n } catch {\n return undefined\n }\n }\n\n /**\n * Core verification logic shared by authenticate and authenticateMeta.\n * Tries logical URL first, falls back to HTTP URL if available.\n */\n private async verifyWithFallback(ctx: VerifyContext): Promise<AuthResult> {\n const { accessToken, logicalUrl, httpUrl, maxAmount, agentId, planIdOverride } = ctx\n\n // Try logical URL first\n try {\n const result = await this.verifyWithEndpoint(\n accessToken,\n logicalUrl,\n agentId,\n maxAmount,\n planIdOverride,\n )\n return {\n token: accessToken,\n agentId,\n logicalUrl,\n httpUrl,\n planId: result.planId,\n subscriberAddress: result.subscriberAddress,\n agentRequest: result.agentRequest,\n }\n } catch {\n // If logical URL fails and we have an HTTP URL, try that\n }\n\n if (httpUrl) {\n try {\n const result = await this.verifyWithEndpoint(\n accessToken,\n httpUrl,\n agentId,\n maxAmount,\n planIdOverride,\n )\n return {\n token: accessToken,\n agentId,\n logicalUrl,\n httpUrl,\n planId: result.planId,\n subscriberAddress: result.subscriberAddress,\n agentRequest: result.agentRequest,\n }\n } catch {\n // HTTP fallback also failed\n }\n }\n\n // Both attempts failed — enrich denial with suggested plans (best-effort)\n let plansMsg = ''\n try {\n const plans = await this.payments.agents.getAgentPlans(agentId)\n if (plans && Array.isArray(plans.plans) && plans.plans.length > 0) {\n const top = plans.plans.slice(0, 3)\n const summary = top\n .map((p: any) => `${p.planId || p.id || 'plan'}${p.name ? ` (${p.name})` : ''}`)\n .join(', ')\n plansMsg = summary ? ` Available plans: ${summary}...` : ''\n }\n } catch {\n // Ignore errors fetching plans - best effort only\n }\n\n throw createRpcError(ERROR_CODES.PaymentRequired, `Payment required.${plansMsg}`, {\n reason: 'invalid',\n })\n }\n\n /**\n * Verify permissions against a single endpoint URL.\n * Resolves planId from the token or from the agent's plans as fallback.\n */\n private async verifyWithEndpoint(\n accessToken: string,\n endpoint: string,\n agentId: string,\n maxAmount: bigint,\n planIdOverride?: string,\n ): Promise<{ planId: string; subscriberAddress: Address; agentRequest?: any }> {\n const decodedAccessToken = decodeAccessToken(accessToken)\n if (!decodedAccessToken) {\n throw new Error('Invalid access token')\n }\n\n let planId = planIdOverride ?? decodedAccessToken.accepted?.planId\n const subscriberAddress = decodedAccessToken.payload?.authorization?.from\n\n // If planId is not available, try to get it from the agent's plans\n if (!planId) {\n try {\n const agentPlans = await this.payments.agents.getAgentPlans(agentId)\n if (agentPlans && Array.isArray(agentPlans.plans) && agentPlans.plans.length > 0) {\n planId = agentPlans.plans[0].planId || agentPlans.plans[0].id\n }\n } catch {\n // Ignore errors fetching plans\n }\n }\n\n if (!planId || !subscriberAddress) {\n throw new Error(\n 'Cannot determine plan_id or subscriber_address from token (expected accepted.planId and payload.authorization.from)',\n )\n }\n\n const paymentRequired: X402PaymentRequired = buildPaymentRequired(planId, {\n endpoint,\n agentId,\n httpVerb: 'POST',\n })\n\n const result = await this.payments.facilitator.verifyPermissions({\n paymentRequired,\n x402AccessToken: accessToken,\n maxAmount,\n })\n\n if (!result.isValid) {\n throw new Error('Permission verification failed')\n }\n\n return { planId, subscriberAddress, agentRequest: result.agentRequest }\n }\n\n /**\n * Authenticate an MCP request\n */\n async authenticate(\n extra: any,\n options: { planId?: string; maxAmount?: bigint } = {},\n agentId: string,\n serverName: string,\n name: string,\n kind: 'tool' | 'resource' | 'prompt',\n argsOrVars: any,\n ): Promise<AuthResult> {\n const authHeader = this.extractAuthHeaderFromContext(extra)\n if (!authHeader) {\n throw createRpcError(ERROR_CODES.PaymentRequired, 'Authorization required', {\n reason: 'missing',\n })\n }\n\n return this.verifyWithFallback({\n accessToken: stripBearer(authHeader),\n logicalUrl: buildLogicalUrl({ kind, serverName, name, argsOrVars }),\n httpUrl: this.buildHttpUrlFromContext(),\n maxAmount: options.maxAmount ?? 1n,\n agentId,\n planIdOverride: options.planId,\n })\n }\n\n /**\n * Authenticate generic MCP meta operations (e.g., initialize, tools/list, resources/list, prompts/list).\n * Returns an AuthResult compatible with paywall flows (without redeem step).\n */\n async authenticateMeta(\n extra: any,\n options: { planId?: string; maxAmount?: bigint } = {},\n agentId: string,\n serverName: string,\n method: string,\n ): Promise<AuthResult> {\n const authHeader = this.extractAuthHeaderFromContext(extra)\n if (!authHeader) {\n throw createRpcError(ERROR_CODES.PaymentRequired, 'Authorization required', {\n reason: 'missing',\n })\n }\n\n return this.verifyWithFallback({\n accessToken: stripBearer(authHeader),\n logicalUrl: buildLogicalMetaUrl(serverName, method),\n httpUrl: this.buildHttpUrlFromContext(),\n maxAmount: options.maxAmount ?? 1n,\n agentId,\n planIdOverride: options.planId,\n })\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"paywall.d.ts","sourceRoot":"","sources":["../../../src/mcp/core/paywall.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAMjD,OAAO,EACL,SAAS,EAET,aAAa,EACb,eAAe,EACf,WAAW,EACZ,MAAM,2BAA2B,CAAA;AAElC,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAA;AAChD,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAA;AAE7D;;GAEG;AACH,qBAAa,gBAAgB;IAQzB,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,aAAa;IACrB,OAAO,CAAC,cAAc;IARxB,OAAO,CAAC,MAAM,CAGb;gBAGS,QAAQ,EAAE,QAAQ,EAClB,aAAa,EAAE,oBAAoB,EACnC,cAAc,EAAE,sBAAsB;IAGhD;;OAEG;IACH,SAAS,CAAC,OAAO,EAAE,SAAS,GAAG,IAAI;IAOnC;;OAEG;IAEH,OAAO,CAAC,KAAK,GAAG,GAAG,EACjB,OAAO,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,EACzD,OAAO,EAAE,WAAW,GAAG,aAAa,GACnC,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC;IAC7C,OAAO,CACL,OAAO,EAAE,CACP,GAAG,EAAE,GAAG,EACR,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,EAC5C,KAAK,CAAC,EAAE,GAAG,KACR,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,EACvB,OAAO,EAAE,eAAe,GACvB,CAAC,GAAG,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC;IAKxF;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAiH5B;;OAEG;YACW,aAAa;CAqC5B"}
1
+ {"version":3,"file":"paywall.d.ts","sourceRoot":"","sources":["../../../src/mcp/core/paywall.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAMjD,OAAO,EACL,SAAS,EAET,aAAa,EACb,eAAe,EACf,WAAW,EACZ,MAAM,2BAA2B,CAAA;AAElC,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAA;AAChD,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAA;AAE7D;;GAEG;AACH,qBAAa,gBAAgB;IAQzB,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,aAAa;IACrB,OAAO,CAAC,cAAc;IARxB,OAAO,CAAC,MAAM,CAGb;gBAGS,QAAQ,EAAE,QAAQ,EAClB,aAAa,EAAE,oBAAoB,EACnC,cAAc,EAAE,sBAAsB;IAGhD;;OAEG;IACH,SAAS,CAAC,OAAO,EAAE,SAAS,GAAG,IAAI;IAOnC;;OAEG;IAEH,OAAO,CAAC,KAAK,GAAG,GAAG,EACjB,OAAO,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,EACzD,OAAO,EAAE,WAAW,GAAG,aAAa,GACnC,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC;IAC7C,OAAO,CACL,OAAO,EAAE,CACP,GAAG,EAAE,GAAG,EACR,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,EAC5C,KAAK,CAAC,EAAE,GAAG,KACR,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,EACvB,OAAO,EAAE,eAAe,GACvB,CAAC,GAAG,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC;IAKxF;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAiH5B;;OAEG;YACW,aAAa;CA8D5B"}
@@ -42,7 +42,7 @@ export class PaywallDecorator {
42
42
  const extra = isResource ? allArgs[2] : allArgs[1];
43
43
  const argsOrVars = isResource ? allArgs[1] : allArgs[0];
44
44
  // 1. Authenticate request
45
- const authResult = await this.authenticator.authenticate(extra, { planId: options?.planId }, this.config.agentId, this.config.serverName, name, kind, argsOrVars);
45
+ const authResult = await this.authenticator.authenticate(extra, { planId: options?.planId, maxAmount: options?.maxAmount }, this.config.agentId, this.config.serverName, name, kind, argsOrVars);
46
46
  // 2. Pre-calculate credits if they are fixed (not a function)
47
47
  // This allows handlers to access credits during execution
48
48
  const creditsOption = options?.credits;
@@ -71,31 +71,29 @@ export class PaywallDecorator {
71
71
  // 6. If the result is an AsyncIterable (stream), redeem on completion
72
72
  if (isAsyncIterable(result)) {
73
73
  const onFinally = async () => {
74
- return await this.redeemCredits(effectivePlanId, authResult.token, authResult.subscriberAddress, credits, options, authResult.agentId, authResult.logicalUrl, 'POST');
74
+ return await this.redeemCredits(effectivePlanId, authResult.token, authResult.subscriberAddress, credits, options, authResult.agentId, authResult.logicalUrl, authResult.httpUrl, 'POST');
75
75
  };
76
76
  return wrapAsyncIterable(result, onFinally, effectivePlanId, authResult.subscriberAddress, credits);
77
77
  }
78
78
  // 7. Non-streaming: redeem immediately
79
- const creditsResult = await this.redeemCredits(effectivePlanId, authResult.token, authResult.subscriberAddress, credits, options, authResult.agentId, authResult.logicalUrl, 'POST');
80
- if (creditsResult.success) {
81
- result._meta = {
82
- ...result._meta,
83
- // Only include txHash if it has a value
84
- ...(creditsResult.transaction && { txHash: creditsResult.transaction }),
85
- creditsRedeemed: creditsResult.creditsRedeemed ?? credits.toString(),
86
- remainingBalance: creditsResult.remainingBalance,
87
- planId: authResult.planId,
88
- subscriberAddress: authResult.subscriberAddress,
89
- success: true,
90
- };
91
- }
79
+ const creditsResult = await this.redeemCredits(effectivePlanId, authResult.token, authResult.subscriberAddress, credits, options, authResult.agentId, authResult.logicalUrl, 'POST', authResult.httpUrl);
80
+ result._meta = {
81
+ ...result._meta,
82
+ ...(creditsResult.transaction && { txHash: creditsResult.transaction }),
83
+ creditsRedeemed: creditsResult.success ? (creditsResult.creditsRedeemed ?? credits.toString()) : '0',
84
+ remainingBalance: creditsResult.remainingBalance,
85
+ planId: authResult.planId,
86
+ subscriberAddress: authResult.subscriberAddress,
87
+ success: creditsResult.success,
88
+ ...(creditsResult.errorReason && { errorReason: creditsResult.errorReason }),
89
+ };
92
90
  return result;
93
91
  };
94
92
  }
95
93
  /**
96
94
  * Redeem credits after successful request
97
95
  */
98
- async redeemCredits(planId, token, subscriberAddress, credits, options, agentId, endpoint, httpVerb) {
96
+ async redeemCredits(planId, token, subscriberAddress, credits, options, agentId, endpoint, fallbackEndpoint, httpVerb) {
99
97
  let ret = {
100
98
  success: true,
101
99
  transaction: '',
@@ -115,11 +113,34 @@ export class PaywallDecorator {
115
113
  });
116
114
  }
117
115
  }
118
- catch (e) {
116
+ catch (primaryError) {
117
+ // If logical URL fails and we have an HTTP URL fallback, retry with it
118
+ let lastError = primaryError;
119
+ if (fallbackEndpoint) {
120
+ try {
121
+ const paymentRequired = buildPaymentRequired(planId, {
122
+ endpoint: fallbackEndpoint,
123
+ agentId,
124
+ httpVerb: httpVerb,
125
+ });
126
+ ret = await this.payments.facilitator.settlePermissions({
127
+ paymentRequired,
128
+ x402AccessToken: token,
129
+ maxAmount: credits,
130
+ });
131
+ return ret;
132
+ }
133
+ catch (fallbackError) {
134
+ // Fallback also failed, use fallback error as the reported error
135
+ lastError = fallbackError;
136
+ }
137
+ }
138
+ ret.success = false;
139
+ ret.errorReason = lastError instanceof Error ? lastError.message : String(lastError);
119
140
  if (options.onRedeemError === 'propagate') {
120
- throw createRpcError(ERROR_CODES.Misconfiguration, 'Failed to redeem credits');
141
+ throw createRpcError(ERROR_CODES.Misconfiguration, `Failed to redeem credits: ${ret.errorReason}`);
121
142
  }
122
- // Default: ignore redemption errors
143
+ // Default: attach error to result but don't throw
123
144
  }
124
145
  return ret;
125
146
  }
@@ -149,11 +170,12 @@ function wrapAsyncIterable(iterable, onFinally, planId, subscriberAddress, credi
149
170
  _meta: {
150
171
  // Only include txHash if it has a value
151
172
  ...(creditsResult?.transaction && { txHash: creditsResult.transaction }),
152
- creditsRedeemed: creditsResult?.creditsRedeemed ?? credits.toString(),
173
+ creditsRedeemed: creditsResult?.success ? (creditsResult.creditsRedeemed ?? credits.toString()) : '0',
153
174
  remainingBalance: creditsResult?.remainingBalance,
154
175
  planId: planId,
155
176
  subscriberAddress: subscriberAddress,
156
177
  success: creditsResult?.success || false,
178
+ ...(creditsResult?.errorReason && { errorReason: creditsResult.errorReason }),
157
179
  },
158
180
  };
159
181
  yield metadataChunk;
@@ -1 +1 @@
1
- {"version":3,"file":"paywall.js","sourceRoot":"","sources":["../../../src/mcp/core/paywall.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,oBAAoB,GAGrB,MAAM,+BAA+B,CAAA;AAQtC,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAIhE;;GAEG;AACH,MAAM,OAAO,gBAAgB;IAO3B,YACU,QAAkB,EAClB,aAAmC,EACnC,cAAsC;QAFtC,aAAQ,GAAR,QAAQ,CAAU;QAClB,kBAAa,GAAb,aAAa,CAAsB;QACnC,mBAAc,GAAd,cAAc,CAAwB;QAThD,iEAAiE;QACzD,WAAM,GAA4C;YACxD,OAAO,EAAE,EAAE;YACX,UAAU,EAAE,YAAY;SACzB,CAAA;IAME,CAAC;IAEJ;;OAEG;IACH,SAAS,CAAC,OAAkB;QAC1B,IAAI,CAAC,MAAM,GAAG;YACZ,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO;YAC/C,UAAU,EAAE,OAAO,CAAC,UAAU,IAAI,IAAI,CAAC,MAAM,CAAC,UAAU;SACzD,CAAA;IACH,CAAC;IAkBD,OAAO,CAAC,OAAY,EAAE,OAAuB;QAC3C,OAAO,IAAI,CAAC,oBAAoB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;IACpD,CAAC;IAED;;OAEG;IACK,oBAAoB,CAC1B,OAAyD,EACzD,OAAuB;QAEvB,OAAO,KAAK,EAAE,GAAG,OAAc,EAAgB,EAAE;YAC/C,yBAAyB;YACzB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACzB,MAAM,cAAc,CAClB,WAAW,CAAC,gBAAgB,EAC5B,0CAA0C,CAC3C,CAAA;YACH,CAAC;YAED,MAAM,IAAI,GAAG,OAAO,EAAE,IAAI,IAAI,MAAM,CAAA;YACpC,MAAM,IAAI,GAAG,OAAO,EAAE,IAAI,IAAI,SAAS,CAAA;YAEvC,qDAAqD;YACrD,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,YAAY,GAAG,CAAA;YACnE,MAAM,KAAK,GAAG,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;YAClD,MAAM,UAAU,GAAG,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;YAEvD,0BAA0B;YAC1B,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,YAAY,CACtD,KAAK,EACL,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAC3B,IAAI,CAAC,MAAM,CAAC,OAAO,EACnB,IAAI,CAAC,MAAM,CAAC,UAAU,EACtB,IAAI,EACJ,IAAI,EACJ,UAAU,CACX,CAAA;YAED,8DAA8D;YAC9D,0DAA0D;YAC1D,MAAM,aAAa,GAAG,OAAO,EAAE,OAAO,CAAA;YACtC,MAAM,cAAc,GAAG,OAAO,aAAa,KAAK,QAAQ,IAAI,aAAa,KAAK,SAAS,CAAA;YACvF,MAAM,oBAAoB,GAAG,cAAc;gBACzC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,aAAa,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,CAAC;gBAC1E,CAAC,CAAC,SAAS,CAAA;YAEb,4EAA4E;YAC5E,MAAM,eAAe,GAAG,OAAO,EAAE,MAAM,IAAI,UAAU,CAAC,MAAM,CAAA;YAE5D,sFAAsF;YACtF,MAAM,cAAc,GAAG;gBACrB,UAAU;gBACV,OAAO,EAAE,oBAAoB;gBAC7B,MAAM,EAAE,UAAU,CAAC,MAAM;gBACzB,iBAAiB,EAAE,UAAU,CAAC,iBAAiB;gBAC/C,YAAY,EAAE,UAAU,CAAC,YAAY;aACtC,CAAA;YAED,2CAA2C;YAC3C,MAAM,MAAM,GAAG,MAAO,OAAe,CAAC,GAAG,OAAO,EAAE,cAAc,CAAC,CAAA;YAEjE,6EAA6E;YAC7E,MAAM,OAAO,GAAG,cAAc;gBAC5B,CAAC,CAAC,CAAC,oBAAoB,IAAI,EAAE,CAAC;gBAC9B,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,aAAa,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,CAAC,CAAA;YAE9E,6CAA6C;YAC7C,cAAc,CAAC,OAAO,GAAG,OAAO,CAAA;YAEhC,sEAAsE;YACtE,IAAI,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC5B,MAAM,SAAS,GAAG,KAAK,IAAI,EAAE;oBAC3B,OAAO,MAAM,IAAI,CAAC,aAAa,CAC7B,eAAe,EACf,UAAU,CAAC,KAAK,EAChB,UAAU,CAAC,iBAAiB,EAC5B,OAAO,EACP,OAAO,EACP,UAAU,CAAC,OAAO,EAClB,UAAU,CAAC,UAAU,EACrB,MAAM,CACP,CAAA;gBACH,CAAC,CAAA;gBACD,OAAO,iBAAiB,CACtB,MAAM,EACN,SAAS,EACT,eAAe,EACf,UAAU,CAAC,iBAAiB,EAC5B,OAAO,CACR,CAAA;YACH,CAAC;YAED,uCAAuC;YACvC,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,aAAa,CAC5C,eAAe,EACf,UAAU,CAAC,KAAK,EAChB,UAAU,CAAC,iBAAiB,EAC5B,OAAO,EACP,OAAO,EACP,UAAU,CAAC,OAAO,EAClB,UAAU,CAAC,UAAU,EACrB,MAAM,CACP,CAAA;YACD,IAAI,aAAa,CAAC,OAAO,EAAE,CAAC;gBAC1B,MAAM,CAAC,KAAK,GAAG;oBACb,GAAG,MAAM,CAAC,KAAK;oBACf,wCAAwC;oBACxC,GAAG,CAAC,aAAa,CAAC,WAAW,IAAI,EAAE,MAAM,EAAE,aAAa,CAAC,WAAW,EAAE,CAAC;oBACvE,eAAe,EAAE,aAAa,CAAC,eAAe,IAAI,OAAO,CAAC,QAAQ,EAAE;oBACpE,gBAAgB,EAAE,aAAa,CAAC,gBAAgB;oBAChD,MAAM,EAAE,UAAU,CAAC,MAAM;oBACzB,iBAAiB,EAAE,UAAU,CAAC,iBAAiB;oBAC/C,OAAO,EAAE,IAAI;iBACd,CAAA;YACH,CAAC;YACD,OAAO,MAAM,CAAA;QACf,CAAC,CAAA;IACH,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,aAAa,CACzB,MAAc,EACd,KAAa,EACb,iBAA0B,EAC1B,OAAe,EACf,OAAuB,EACvB,OAAgB,EAChB,QAAiB,EACjB,QAAiB;QAEjB,IAAI,GAAG,GAA4B;YACjC,OAAO,EAAE,IAAI;YACb,WAAW,EAAE,EAAE;YACf,OAAO,EAAE,EAAE;SACZ,CAAA;QACD,IAAI,CAAC;YACH,IAAI,OAAO,IAAI,OAAO,GAAG,EAAE,IAAI,iBAAiB,IAAI,MAAM,EAAE,CAAC;gBAC3D,MAAM,eAAe,GAAwB,oBAAoB,CAAC,MAAM,EAAE;oBACxE,QAAQ,EAAE,QAAQ,IAAI,EAAE;oBACxB,OAAO;oBACP,QAAQ,EAAE,QAAQ;iBACnB,CAAC,CAAA;gBAEF,GAAG,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,iBAAiB,CAAC;oBACtD,eAAe;oBACf,eAAe,EAAE,KAAK;oBACtB,SAAS,EAAE,OAAO;iBACnB,CAAC,CAAA;YACJ,CAAC;QACH,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,OAAO,CAAC,aAAa,KAAK,WAAW,EAAE,CAAC;gBAC1C,MAAM,cAAc,CAAC,WAAW,CAAC,gBAAgB,EAAE,0BAA0B,CAAC,CAAA;YAChF,CAAC;YACD,oCAAoC;QACtC,CAAC;QACD,OAAO,GAAG,CAAA;IACZ,CAAC;CACF;AAED;;GAEG;AACH,SAAS,eAAe,CAAc,KAAU;IAC9C,OAAO,KAAK,IAAI,IAAI,IAAI,OAAO,KAAK,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,UAAU,CAAA;AAC3E,CAAC;AAED;;GAEG;AACH,SAAS,iBAAiB,CACxB,QAA0B,EAC1B,SAA6B,EAC7B,MAAc,EACd,iBAA0B,EAC1B,OAAe;IAEf,KAAK,SAAS,CAAC,CAAC,SAAS;QACvB,IAAI,aAAa,GAAQ,IAAI,CAAA;QAC7B,IAAI,CAAC;YACH,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;gBACnC,MAAM,KAAU,CAAA;YAClB,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,aAAa,GAAG,MAAM,SAAS,EAAE,CAAA;QACnC,CAAC;QAED,4DAA4D;QAC5D,MAAM,aAAa,GAAG;YACpB,KAAK,EAAE;gBACL,wCAAwC;gBACxC,GAAG,CAAC,aAAa,EAAE,WAAW,IAAI,EAAE,MAAM,EAAE,aAAa,CAAC,WAAW,EAAE,CAAC;gBACxE,eAAe,EAAE,aAAa,EAAE,eAAe,IAAI,OAAO,CAAC,QAAQ,EAAE;gBACrE,gBAAgB,EAAE,aAAa,EAAE,gBAAgB;gBACjD,MAAM,EAAE,MAAM;gBACd,iBAAiB,EAAE,iBAAiB;gBACpC,OAAO,EAAE,aAAa,EAAE,OAAO,IAAI,KAAK;aACzC;SACF,CAAA;QACD,MAAM,aAAkB,CAAA;IAC1B,CAAC;IACD,OAAO,SAAS,EAAE,CAAA;AACpB,CAAC","sourcesContent":["/**\n * Main paywall decorator for MCP handlers (tools, resources, prompts)\n */\nimport { Address } from '../../common/types.js'\nimport type { Payments } from '../../payments.js'\nimport {\n buildPaymentRequired,\n type SettlePermissionsResult,\n type X402PaymentRequired,\n} from '../../x402/facilitator-api.js'\nimport {\n McpConfig,\n PaywallOptions,\n PromptOptions,\n ResourceOptions,\n ToolOptions,\n} from '../types/paywall.types.js'\nimport { ERROR_CODES, createRpcError } from '../utils/errors.js'\nimport { PaywallAuthenticator } from './auth.js'\nimport { CreditsContextProvider } from './credits-context.js'\n\n/**\n * Main class for creating paywall-protected MCP handlers\n */\nexport class PaywallDecorator {\n // Internal config ensures serverName is always a concrete string\n private config: { agentId: string; serverName: string } = {\n agentId: '',\n serverName: 'mcp-server',\n }\n\n constructor(\n private payments: Payments,\n private authenticator: PaywallAuthenticator,\n private creditsContext: CreditsContextProvider,\n ) {}\n\n /**\n * Configure the paywall with agent and server information\n */\n configure(options: McpConfig): void {\n this.config = {\n agentId: options.agentId || this.config.agentId,\n serverName: options.serverName ?? this.config.serverName,\n }\n }\n\n /**\n * Create a paywall-protected handler (uncurried version only)\n */\n // Overloads per kind for stronger typing\n protect<TArgs = any>(\n handler: (args: TArgs, extra?: any) => Promise<any> | any,\n options: ToolOptions | PromptOptions,\n ): (args: TArgs, extra?: any) => Promise<any>\n protect(\n handler: (\n uri: URL,\n variables: Record<string, string | string[]>,\n extra?: any,\n ) => Promise<any> | any,\n options: ResourceOptions,\n ): (uri: URL, variables: Record<string, string | string[]>, extra?: any) => Promise<any>\n protect(handler: any, options: PaywallOptions): any {\n return this.createWrappedHandler(handler, options)\n }\n\n /**\n * Internal method to create the wrapped handler\n */\n private createWrappedHandler<TArgs = any>(\n handler: (args: TArgs, extra?: any) => Promise<any> | any,\n options: PaywallOptions,\n ): (...allArgs: any[]) => Promise<any> {\n return async (...allArgs: any[]): Promise<any> => {\n // Validate configuration\n if (!this.config.agentId) {\n throw createRpcError(\n ERROR_CODES.Misconfiguration,\n 'Server misconfiguration: missing agentId',\n )\n }\n\n const kind = options?.kind ?? 'tool'\n const name = options?.name ?? 'unnamed'\n\n // Detect resource signature: (url, variables, extra)\n const isResource = allArgs.length >= 2 && allArgs[0] instanceof URL\n const extra = isResource ? allArgs[2] : allArgs[1]\n const argsOrVars = isResource ? allArgs[1] : allArgs[0]\n\n // 1. Authenticate request\n const authResult = await this.authenticator.authenticate(\n extra,\n { planId: options?.planId },\n this.config.agentId,\n this.config.serverName,\n name,\n kind,\n argsOrVars,\n )\n\n // 2. Pre-calculate credits if they are fixed (not a function)\n // This allows handlers to access credits during execution\n const creditsOption = options?.credits\n const isFixedCredits = typeof creditsOption === 'bigint' || creditsOption === undefined\n const preCalculatedCredits = isFixedCredits\n ? this.creditsContext.resolve(creditsOption, argsOrVars, null, authResult)\n : undefined\n\n // Determine effective planId: explicit option overrides token-derived value\n const effectivePlanId = options?.planId ?? authResult.planId\n\n // 3. Build PaywallContext for handler (with extra wrapper for backward compatibility)\n const paywallContext = {\n authResult,\n credits: preCalculatedCredits,\n planId: authResult.planId,\n subscriberAddress: authResult.subscriberAddress,\n agentRequest: authResult.agentRequest,\n }\n\n // 4. Execute original handler with context\n const result = await (handler as any)(...allArgs, paywallContext)\n\n // 5. Resolve final credits to burn (may be different if credits are dynamic)\n const credits = isFixedCredits\n ? (preCalculatedCredits ?? 1n)\n : this.creditsContext.resolve(creditsOption, argsOrVars, result, authResult)\n\n // Update context with final resolved credits\n paywallContext.credits = credits\n\n // 6. If the result is an AsyncIterable (stream), redeem on completion\n if (isAsyncIterable(result)) {\n const onFinally = async () => {\n return await this.redeemCredits(\n effectivePlanId,\n authResult.token,\n authResult.subscriberAddress,\n credits,\n options,\n authResult.agentId,\n authResult.logicalUrl,\n 'POST',\n )\n }\n return wrapAsyncIterable(\n result,\n onFinally,\n effectivePlanId,\n authResult.subscriberAddress,\n credits,\n )\n }\n\n // 7. Non-streaming: redeem immediately\n const creditsResult = await this.redeemCredits(\n effectivePlanId,\n authResult.token,\n authResult.subscriberAddress,\n credits,\n options,\n authResult.agentId,\n authResult.logicalUrl,\n 'POST',\n )\n if (creditsResult.success) {\n result._meta = {\n ...result._meta,\n // Only include txHash if it has a value\n ...(creditsResult.transaction && { txHash: creditsResult.transaction }),\n creditsRedeemed: creditsResult.creditsRedeemed ?? credits.toString(),\n remainingBalance: creditsResult.remainingBalance,\n planId: authResult.planId,\n subscriberAddress: authResult.subscriberAddress,\n success: true,\n }\n }\n return result\n }\n }\n\n /**\n * Redeem credits after successful request\n */\n private async redeemCredits(\n planId: string,\n token: string,\n subscriberAddress: Address,\n credits: bigint,\n options: PaywallOptions,\n agentId?: string,\n endpoint?: string,\n httpVerb?: string,\n ): Promise<SettlePermissionsResult> {\n let ret: SettlePermissionsResult = {\n success: true,\n transaction: '',\n network: '',\n }\n try {\n if (credits && credits > 0n && subscriberAddress && planId) {\n const paymentRequired: X402PaymentRequired = buildPaymentRequired(planId, {\n endpoint: endpoint || '',\n agentId,\n httpVerb: httpVerb,\n })\n\n ret = await this.payments.facilitator.settlePermissions({\n paymentRequired,\n x402AccessToken: token,\n maxAmount: credits,\n })\n }\n } catch (e) {\n if (options.onRedeemError === 'propagate') {\n throw createRpcError(ERROR_CODES.Misconfiguration, 'Failed to redeem credits')\n }\n // Default: ignore redemption errors\n }\n return ret\n }\n}\n\n/**\n * Type guard to detect AsyncIterable values.\n */\nfunction isAsyncIterable<T = unknown>(value: any): value is AsyncIterable<T> {\n return value != null && typeof value[Symbol.asyncIterator] === 'function'\n}\n\n/**\n * Wrap an AsyncIterable with metadata injection at the end of the stream\n */\nfunction wrapAsyncIterable<T>(\n iterable: AsyncIterable<T>,\n onFinally: () => Promise<any>,\n planId: string,\n subscriberAddress: Address,\n credits: bigint,\n) {\n async function* generator() {\n let creditsResult: any = null\n try {\n for await (const chunk of iterable) {\n yield chunk as T\n }\n } finally {\n creditsResult = await onFinally()\n }\n\n // Yield a _meta chunk at the end with the redemption result\n const metadataChunk = {\n _meta: {\n // Only include txHash if it has a value\n ...(creditsResult?.transaction && { txHash: creditsResult.transaction }),\n creditsRedeemed: creditsResult?.creditsRedeemed ?? credits.toString(),\n remainingBalance: creditsResult?.remainingBalance,\n planId: planId,\n subscriberAddress: subscriberAddress,\n success: creditsResult?.success || false,\n },\n }\n yield metadataChunk as T\n }\n return generator()\n}\n"]}
1
+ {"version":3,"file":"paywall.js","sourceRoot":"","sources":["../../../src/mcp/core/paywall.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Main paywall decorator for MCP handlers (tools, resources, prompts)\n */\nimport { Address } from '../../common/types.js'\nimport type { Payments } from '../../payments.js'\nimport {\n buildPaymentRequired,\n type SettlePermissionsResult,\n type X402PaymentRequired,\n} from '../../x402/facilitator-api.js'\nimport {\n McpConfig,\n PaywallOptions,\n PromptOptions,\n ResourceOptions,\n ToolOptions,\n} from '../types/paywall.types.js'\nimport { ERROR_CODES, createRpcError } from '../utils/errors.js'\nimport { PaywallAuthenticator } from './auth.js'\nimport { CreditsContextProvider } from './credits-context.js'\n\n/**\n * Main class for creating paywall-protected MCP handlers\n */\nexport class PaywallDecorator {\n // Internal config ensures serverName is always a concrete string\n private config: { agentId: string; serverName: string } = {\n agentId: '',\n serverName: 'mcp-server',\n }\n\n constructor(\n private payments: Payments,\n private authenticator: PaywallAuthenticator,\n private creditsContext: CreditsContextProvider,\n ) {}\n\n /**\n * Configure the paywall with agent and server information\n */\n configure(options: McpConfig): void {\n this.config = {\n agentId: options.agentId || this.config.agentId,\n serverName: options.serverName ?? this.config.serverName,\n }\n }\n\n /**\n * Create a paywall-protected handler (uncurried version only)\n */\n // Overloads per kind for stronger typing\n protect<TArgs = any>(\n handler: (args: TArgs, extra?: any) => Promise<any> | any,\n options: ToolOptions | PromptOptions,\n ): (args: TArgs, extra?: any) => Promise<any>\n protect(\n handler: (\n uri: URL,\n variables: Record<string, string | string[]>,\n extra?: any,\n ) => Promise<any> | any,\n options: ResourceOptions,\n ): (uri: URL, variables: Record<string, string | string[]>, extra?: any) => Promise<any>\n protect(handler: any, options: PaywallOptions): any {\n return this.createWrappedHandler(handler, options)\n }\n\n /**\n * Internal method to create the wrapped handler\n */\n private createWrappedHandler<TArgs = any>(\n handler: (args: TArgs, extra?: any) => Promise<any> | any,\n options: PaywallOptions,\n ): (...allArgs: any[]) => Promise<any> {\n return async (...allArgs: any[]): Promise<any> => {\n // Validate configuration\n if (!this.config.agentId) {\n throw createRpcError(\n ERROR_CODES.Misconfiguration,\n 'Server misconfiguration: missing agentId',\n )\n }\n\n const kind = options?.kind ?? 'tool'\n const name = options?.name ?? 'unnamed'\n\n // Detect resource signature: (url, variables, extra)\n const isResource = allArgs.length >= 2 && allArgs[0] instanceof URL\n const extra = isResource ? allArgs[2] : allArgs[1]\n const argsOrVars = isResource ? allArgs[1] : allArgs[0]\n\n // 1. Authenticate request\n const authResult = await this.authenticator.authenticate(\n extra,\n { planId: options?.planId, maxAmount: options?.maxAmount },\n this.config.agentId,\n this.config.serverName,\n name,\n kind,\n argsOrVars,\n )\n\n // 2. Pre-calculate credits if they are fixed (not a function)\n // This allows handlers to access credits during execution\n const creditsOption = options?.credits\n const isFixedCredits = typeof creditsOption === 'bigint' || creditsOption === undefined\n const preCalculatedCredits = isFixedCredits\n ? this.creditsContext.resolve(creditsOption, argsOrVars, null, authResult)\n : undefined\n\n // Determine effective planId: explicit option overrides token-derived value\n const effectivePlanId = options?.planId ?? authResult.planId\n\n // 3. Build PaywallContext for handler (with extra wrapper for backward compatibility)\n const paywallContext = {\n authResult,\n credits: preCalculatedCredits,\n planId: authResult.planId,\n subscriberAddress: authResult.subscriberAddress,\n agentRequest: authResult.agentRequest,\n }\n\n // 4. Execute original handler with context\n const result = await (handler as any)(...allArgs, paywallContext)\n\n // 5. Resolve final credits to burn (may be different if credits are dynamic)\n const credits = isFixedCredits\n ? (preCalculatedCredits ?? 1n)\n : this.creditsContext.resolve(creditsOption, argsOrVars, result, authResult)\n\n // Update context with final resolved credits\n paywallContext.credits = credits\n\n // 6. If the result is an AsyncIterable (stream), redeem on completion\n if (isAsyncIterable(result)) {\n const onFinally = async () => {\n return await this.redeemCredits(\n effectivePlanId,\n authResult.token,\n authResult.subscriberAddress,\n credits,\n options,\n authResult.agentId,\n authResult.logicalUrl,\n authResult.httpUrl,\n 'POST',\n )\n }\n return wrapAsyncIterable(\n result,\n onFinally,\n effectivePlanId,\n authResult.subscriberAddress,\n credits,\n )\n }\n\n // 7. Non-streaming: redeem immediately\n const creditsResult = await this.redeemCredits(\n effectivePlanId,\n authResult.token,\n authResult.subscriberAddress,\n credits,\n options,\n authResult.agentId,\n authResult.logicalUrl,\n 'POST',\n authResult.httpUrl,\n )\n result._meta = {\n ...result._meta,\n ...(creditsResult.transaction && { txHash: creditsResult.transaction }),\n creditsRedeemed: creditsResult.success ? (creditsResult.creditsRedeemed ?? credits.toString()) : '0',\n remainingBalance: creditsResult.remainingBalance,\n planId: authResult.planId,\n subscriberAddress: authResult.subscriberAddress,\n success: creditsResult.success,\n ...(creditsResult.errorReason && { errorReason: creditsResult.errorReason }),\n }\n return result\n }\n }\n\n /**\n * Redeem credits after successful request\n */\n private async redeemCredits(\n planId: string,\n token: string,\n subscriberAddress: Address,\n credits: bigint,\n options: PaywallOptions,\n agentId?: string,\n endpoint?: string,\n fallbackEndpoint?: string,\n httpVerb?: string,\n ): Promise<SettlePermissionsResult> {\n let ret: SettlePermissionsResult = {\n success: true,\n transaction: '',\n network: '',\n }\n try {\n if (credits && credits > 0n && subscriberAddress && planId) {\n const paymentRequired: X402PaymentRequired = buildPaymentRequired(planId, {\n endpoint: endpoint || '',\n agentId,\n httpVerb: httpVerb,\n })\n\n ret = await this.payments.facilitator.settlePermissions({\n paymentRequired,\n x402AccessToken: token,\n maxAmount: credits,\n })\n }\n } catch (primaryError) {\n // If logical URL fails and we have an HTTP URL fallback, retry with it\n let lastError: unknown = primaryError\n if (fallbackEndpoint) {\n try {\n const paymentRequired: X402PaymentRequired = buildPaymentRequired(planId, {\n endpoint: fallbackEndpoint,\n agentId,\n httpVerb: httpVerb,\n })\n\n ret = await this.payments.facilitator.settlePermissions({\n paymentRequired,\n x402AccessToken: token,\n maxAmount: credits,\n })\n return ret\n } catch (fallbackError) {\n // Fallback also failed, use fallback error as the reported error\n lastError = fallbackError\n }\n }\n\n ret.success = false\n ret.errorReason = lastError instanceof Error ? lastError.message : String(lastError)\n if (options.onRedeemError === 'propagate') {\n throw createRpcError(ERROR_CODES.Misconfiguration, `Failed to redeem credits: ${ret.errorReason}`)\n }\n // Default: attach error to result but don't throw\n }\n return ret\n }\n}\n\n/**\n * Type guard to detect AsyncIterable values.\n */\nfunction isAsyncIterable<T = unknown>(value: any): value is AsyncIterable<T> {\n return value != null && typeof value[Symbol.asyncIterator] === 'function'\n}\n\n/**\n * Wrap an AsyncIterable with metadata injection at the end of the stream\n */\nfunction wrapAsyncIterable<T>(\n iterable: AsyncIterable<T>,\n onFinally: () => Promise<any>,\n planId: string,\n subscriberAddress: Address,\n credits: bigint,\n) {\n async function* generator() {\n let creditsResult: any = null\n try {\n for await (const chunk of iterable) {\n yield chunk as T\n }\n } finally {\n creditsResult = await onFinally()\n }\n\n // Yield a _meta chunk at the end with the redemption result\n const metadataChunk = {\n _meta: {\n // Only include txHash if it has a value\n ...(creditsResult?.transaction && { txHash: creditsResult.transaction }),\n creditsRedeemed: creditsResult?.success ? (creditsResult.creditsRedeemed ?? credits.toString()) : '0',\n remainingBalance: creditsResult?.remainingBalance,\n planId: planId,\n subscriberAddress: subscriberAddress,\n success: creditsResult?.success || false,\n ...(creditsResult?.errorReason && { errorReason: creditsResult.errorReason }),\n },\n }\n yield metadataChunk as T\n }\n return generator()\n}\n"]}
@@ -39,6 +39,11 @@ export interface BasePaywallOptions {
39
39
  * If omitted, the plan is inferred from the X402 access token.
40
40
  */
41
41
  planId?: string;
42
+ /**
43
+ * Maximum amount of credits to verify during authentication.
44
+ * Defaults to 1n if not specified.
45
+ */
46
+ maxAmount?: bigint;
42
47
  }
43
48
  export interface ToolOptions extends BasePaywallOptions {
44
49
  kind: 'tool';
@@ -60,6 +65,7 @@ export interface AuthResult {
60
65
  token: string;
61
66
  agentId: string;
62
67
  logicalUrl: string;
68
+ httpUrl?: string;
63
69
  planId: string;
64
70
  subscriberAddress: Address;
65
71
  agentRequest?: StartAgentRequest;
@@ -1 +1 @@
1
- {"version":3,"file":"paywall.types.d.ts","sourceRoot":"","sources":["../../../src/mcp/types/paywall.types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AAEvE;;GAEG;AACH;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,OAAO,CAAA;IACb,MAAM,EAAE,GAAG,CAAA;IACX,OAAO,EAAE;QACP,UAAU,EAAE,MAAM,CAAA;QAClB,UAAU,EAAE,MAAM,CAAA;QAClB,QAAQ,EAAE,MAAM,CAAA;KACjB,CAAA;CACF;AAED;;GAEG;AACH;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,cAAc,KAAK,MAAM,CAAC,CAAA;AAEtE;;GAEG;AACH;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,aAAa,CAAA;IACvB,aAAa,CAAC,EAAE,QAAQ,GAAG,WAAW,CAAA;IACtC;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,WAAY,SAAQ,kBAAkB;IACrD,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,eAAgB,SAAQ,kBAAkB;IACzD,IAAI,EAAE,UAAU,CAAA;CACjB;AAED,MAAM,WAAW,aAAc,SAAQ,kBAAkB;IACvD,IAAI,EAAE,QAAQ,CAAA;CACf;AAED,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,eAAe,GAAG,aAAa,CAAA;AAE1E;;GAEG;AAGH;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,MAAM,CAAA;IACd,iBAAiB,EAAE,OAAO,CAAA;IAC1B,YAAY,CAAC,EAAE,iBAAiB,CAAA;CACjC;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,UAAU,CAAA;IACtB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,iBAAiB,EAAE,OAAO,CAAA;IAC1B,YAAY,CAAC,EAAE,iBAAiB,CAAA;CACjC;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB"}
1
+ {"version":3,"file":"paywall.types.d.ts","sourceRoot":"","sources":["../../../src/mcp/types/paywall.types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AAEvE;;GAEG;AACH;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,OAAO,CAAA;IACb,MAAM,EAAE,GAAG,CAAA;IACX,OAAO,EAAE;QACP,UAAU,EAAE,MAAM,CAAA;QAClB,UAAU,EAAE,MAAM,CAAA;QAClB,QAAQ,EAAE,MAAM,CAAA;KACjB,CAAA;CACF;AAED;;GAEG;AACH;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,cAAc,KAAK,MAAM,CAAC,CAAA;AAEtE;;GAEG;AACH;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,aAAa,CAAA;IACvB,aAAa,CAAC,EAAE,QAAQ,GAAG,WAAW,CAAA;IACtC;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,WAAY,SAAQ,kBAAkB;IACrD,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,eAAgB,SAAQ,kBAAkB;IACzD,IAAI,EAAE,UAAU,CAAA;CACjB;AAED,MAAM,WAAW,aAAc,SAAQ,kBAAkB;IACvD,IAAI,EAAE,QAAQ,CAAA;CACf;AAED,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,eAAe,GAAG,aAAa,CAAA;AAE1E;;GAEG;AAGH;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,iBAAiB,EAAE,OAAO,CAAA;IAC1B,YAAY,CAAC,EAAE,iBAAiB,CAAA;CACjC;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,UAAU,CAAA;IACtB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,iBAAiB,EAAE,OAAO,CAAA;IAC1B,YAAY,CAAC,EAAE,iBAAiB,CAAA;CACjC;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB"}
@@ -1 +1 @@
1
- {"version":3,"file":"paywall.types.js","sourceRoot":"","sources":["../../../src/mcp/types/paywall.types.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Type definitions for MCP paywall functionality\n */\nimport type { Address, StartAgentRequest } from '../../common/types.js'\n\n/**\n * Context provided to dynamic credits functions\n */\n/**\n * Context provided to dynamic credits functions.\n */\nexport interface CreditsContext {\n args: unknown\n result: any\n request: {\n authHeader: string\n logicalUrl: string\n toolName: string\n }\n}\n\n/**\n * Credits calculation option - can be fixed amount or dynamic function\n */\n/**\n * Credits option: fixed bigint or a function receiving {@link CreditsContext}.\n */\nexport type CreditsOption = bigint | ((ctx: CreditsContext) => bigint)\n\n/**\n * Configuration options for paywall protection\n */\n/**\n * Unified paywall options for tools, resources and prompts.\n */\nexport interface BasePaywallOptions {\n name: string\n credits?: CreditsOption\n onRedeemError?: 'ignore' | 'propagate'\n /**\n * Optional override for the Nevermined plan to charge against.\n * If omitted, the plan is inferred from the X402 access token.\n */\n planId?: string\n}\n\nexport interface ToolOptions extends BasePaywallOptions {\n kind: 'tool'\n}\n\nexport interface ResourceOptions extends BasePaywallOptions {\n kind: 'resource'\n}\n\nexport interface PromptOptions extends BasePaywallOptions {\n kind: 'prompt'\n}\n\nexport type PaywallOptions = ToolOptions | ResourceOptions | PromptOptions\n\n/**\n * Options for decorating tools with paywall protection\n */\n// decorate* helpers removed\n\n/**\n * Authentication result from paywall validation\n */\nexport interface AuthResult {\n token: string\n agentId: string\n logicalUrl: string\n planId: string\n subscriberAddress: Address\n agentRequest?: StartAgentRequest\n}\n\n/**\n * Context provided to paywall-protected handlers\n */\nexport interface PaywallContext {\n authResult: AuthResult\n credits?: bigint\n planId: string\n subscriberAddress: Address\n agentRequest?: StartAgentRequest\n}\n\n/**\n * MCP integration configuration\n */\nexport interface McpConfig {\n agentId: string\n serverName?: string\n}\n"]}
1
+ {"version":3,"file":"paywall.types.js","sourceRoot":"","sources":["../../../src/mcp/types/paywall.types.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Type definitions for MCP paywall functionality\n */\nimport type { Address, StartAgentRequest } from '../../common/types.js'\n\n/**\n * Context provided to dynamic credits functions\n */\n/**\n * Context provided to dynamic credits functions.\n */\nexport interface CreditsContext {\n args: unknown\n result: any\n request: {\n authHeader: string\n logicalUrl: string\n toolName: string\n }\n}\n\n/**\n * Credits calculation option - can be fixed amount or dynamic function\n */\n/**\n * Credits option: fixed bigint or a function receiving {@link CreditsContext}.\n */\nexport type CreditsOption = bigint | ((ctx: CreditsContext) => bigint)\n\n/**\n * Configuration options for paywall protection\n */\n/**\n * Unified paywall options for tools, resources and prompts.\n */\nexport interface BasePaywallOptions {\n name: string\n credits?: CreditsOption\n onRedeemError?: 'ignore' | 'propagate'\n /**\n * Optional override for the Nevermined plan to charge against.\n * If omitted, the plan is inferred from the X402 access token.\n */\n planId?: string\n /**\n * Maximum amount of credits to verify during authentication.\n * Defaults to 1n if not specified.\n */\n maxAmount?: bigint\n}\n\nexport interface ToolOptions extends BasePaywallOptions {\n kind: 'tool'\n}\n\nexport interface ResourceOptions extends BasePaywallOptions {\n kind: 'resource'\n}\n\nexport interface PromptOptions extends BasePaywallOptions {\n kind: 'prompt'\n}\n\nexport type PaywallOptions = ToolOptions | ResourceOptions | PromptOptions\n\n/**\n * Options for decorating tools with paywall protection\n */\n// decorate* helpers removed\n\n/**\n * Authentication result from paywall validation\n */\nexport interface AuthResult {\n token: string\n agentId: string\n logicalUrl: string\n httpUrl?: string\n planId: string\n subscriberAddress: Address\n agentRequest?: StartAgentRequest\n}\n\n/**\n * Context provided to paywall-protected handlers\n */\nexport interface PaywallContext {\n authResult: AuthResult\n credits?: bigint\n planId: string\n subscriberAddress: Address\n agentRequest?: StartAgentRequest\n}\n\n/**\n * MCP integration configuration\n */\nexport interface McpConfig {\n agentId: string\n serverName?: string\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nevermined-io/payments",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "Typescript SDK to interact with the Nevermined Payments Protocol",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",