mppx 0.8.19 → 0.9.1
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/CHANGELOG.md +24 -0
- package/README.md +0 -6
- package/dist/Challenge.d.ts +7 -0
- package/dist/Challenge.js +32 -6
- package/dist/Constants.d.ts +1 -0
- package/dist/Constants.js +1 -0
- package/dist/Credential.d.ts +11 -5
- package/dist/Credential.js +8 -8
- package/dist/Errors.d.ts +10 -0
- package/dist/Errors.js +12 -0
- package/dist/Html.js +1 -1
- package/dist/Mcp.d.ts +9 -1
- package/dist/Mcp.js +18 -0
- package/dist/Method.d.ts +7 -0
- package/dist/cli/THIRD-PARTY-LICENSES.txt +1 -1
- package/dist/cli/cli.d.ts +2 -2
- package/dist/cli/cli.js +134 -134
- package/dist/cli/plugins/index.js +13 -13
- package/dist/cli/sessions/Manager.js +4 -6
- package/dist/cli/sessions/commands.d.ts +1 -1
- package/dist/cli/sessions/store.js +13 -19
- package/dist/cli/validate/index.d.ts +2 -2
- package/dist/client/Mppx.d.ts +39 -2
- package/dist/client/Mppx.js +102 -46
- package/dist/client/Transport.js +1 -1
- package/dist/client/internal/protocols/Mcp.js +2 -1
- package/dist/client/internal/protocols/Mpp.d.ts +1 -1
- package/dist/client/internal/protocols/Mpp.js +5 -3
- package/dist/client/internal/protocols/Protocol.d.ts +3 -1
- package/dist/client/internal/protocols/Shared.js +5 -1
- package/dist/integrations/x402/Evm.d.ts +29 -0
- package/dist/integrations/x402/Evm.js +150 -0
- package/dist/integrations/x402/Negotiator.d.ts +36 -0
- package/dist/integrations/x402/Negotiator.js +165 -0
- package/dist/mcp/client/McpClient.js +22 -10
- package/dist/mcp/internal/Credential.d.ts +9 -0
- package/dist/mcp/internal/Credential.js +16 -0
- package/dist/mcp/server/Transport.d.ts +2 -2
- package/dist/mcp/server/Transport.js +11 -6
- package/dist/middlewares/express.js +5 -12
- package/dist/middlewares/internal/express.d.ts +8 -0
- package/dist/middlewares/internal/express.js +24 -0
- package/dist/middlewares/internal/node.d.ts +10 -0
- package/dist/middlewares/internal/node.js +34 -0
- package/dist/proxy/Proxy.js +15 -9
- package/dist/server/Mppx.d.ts +8 -0
- package/dist/server/Mppx.js +92 -20
- package/dist/server/Transport.d.ts +8 -2
- package/dist/server/Transport.js +14 -18
- package/dist/server/internal/SecretKey.d.ts +3 -0
- package/dist/server/internal/SecretKey.js +9 -0
- package/dist/server/internal/html/serviceWorker.client.d.ts +1 -1
- package/dist/server/internal/html/serviceWorker.client.js +2 -2
- package/dist/server/internal/html/serviceWorker.gen.d.ts +1 -1
- package/dist/server/internal/html/serviceWorker.gen.js +1 -1
- package/dist/stripe/Methods.d.ts +6 -0
- package/dist/stripe/Methods.js +3 -1
- package/dist/stripe/client/Charge.d.ts +6 -0
- package/dist/stripe/client/Methods.d.ts +6 -0
- package/dist/stripe/internal/payment-intent.d.ts +7 -0
- package/dist/stripe/internal/payment-intent.js +6 -0
- package/dist/stripe/server/Charge.d.ts +6 -0
- package/dist/stripe/server/Charge.js +8 -3
- package/dist/stripe/server/Methods.js +62 -7
- package/dist/stripe/server/internal/html.gen.d.ts +1 -1
- package/dist/stripe/server/internal/html.gen.js +1 -1
- package/dist/tempo/Methods.d.ts +0 -10
- package/dist/tempo/Methods.js +3 -12
- package/dist/tempo/Tokens.d.ts +13 -0
- package/dist/tempo/Tokens.js +13 -0
- package/dist/tempo/client/Charge.d.ts +1 -4
- package/dist/tempo/client/Charge.js +20 -13
- package/dist/tempo/client/Methods.d.ts +0 -10
- package/dist/tempo/index.d.ts +1 -0
- package/dist/tempo/index.js +1 -0
- package/dist/tempo/internal/account.d.ts +15 -6
- package/dist/tempo/internal/account.js +17 -26
- package/dist/tempo/internal/defaults.d.ts +0 -11
- package/dist/tempo/internal/defaults.js +0 -11
- package/dist/tempo/internal/fee-payer.d.ts +22 -4
- package/dist/tempo/internal/fee-payer.js +12 -31
- package/dist/tempo/internal/fee-token.d.ts +2 -0
- package/dist/tempo/internal/fee-token.js +7 -0
- package/dist/tempo/internal/remote-fee-payer.d.ts +12 -0
- package/dist/tempo/internal/remote-fee-payer.js +17 -0
- package/dist/tempo/legacy/client/Session.d.ts +0 -7
- package/dist/tempo/server/Charge.d.ts +0 -3
- package/dist/tempo/server/Charge.js +26 -68
- package/dist/tempo/server/Methods.d.ts +4 -18
- package/dist/tempo/server/Methods.js +4 -8
- package/dist/tempo/server/internal/html.gen.d.ts +1 -1
- package/dist/tempo/server/internal/html.gen.js +1 -1
- package/dist/tempo/session/client/ChannelOps.d.ts +0 -14
- package/dist/tempo/session/client/ChannelOps.js +0 -22
- package/dist/tempo/session/client/ChannelStore.d.ts +5 -0
- package/dist/tempo/session/client/ChannelStore.js +2 -2
- package/dist/tempo/session/client/CredentialState.d.ts +2 -12
- package/dist/tempo/session/client/CredentialState.js +75 -73
- package/dist/tempo/session/client/Session.d.ts +2 -9
- package/dist/tempo/session/client/Session.js +71 -176
- package/dist/tempo/session/client/SessionManager.js +7 -27
- package/dist/tempo/session/precompile/Chain.d.ts +0 -4
- package/dist/tempo/session/precompile/Chain.js +1 -29
- package/dist/tempo/session/precompile/Protocol.d.ts +0 -13
- package/dist/tempo/session/precompile/Voucher.d.ts +1 -3
- package/dist/tempo/session/precompile/Voucher.js +12 -6
- package/dist/tempo/session/server/ChannelStore.d.ts +0 -6
- package/dist/tempo/session/server/ChannelStore.js +4 -11
- package/dist/tempo/session/server/CredentialVerification.d.ts +7 -9
- package/dist/tempo/session/server/CredentialVerification.js +118 -184
- package/dist/tempo/session/server/RequestState.d.ts +0 -17
- package/dist/tempo/session/server/RequestState.js +15 -59
- package/dist/tempo/session/server/Session.d.ts +0 -9
- package/dist/tempo/session/server/Session.js +13 -39
- package/dist/tempo/session/server/Settlement.d.ts +0 -7
- package/dist/tempo/session/server/Settlement.js +16 -50
- package/dist/viem/Client.d.ts +7 -2
- package/dist/viem/Client.js +17 -5
- package/dist/x402/Types.d.ts +2 -0
- package/dist/x402/Types.js +2 -0
- package/dist/x402/mcp.d.ts +20 -0
- package/dist/x402/mcp.js +154 -0
- package/dist/x402/middlewares/express.d.ts +23 -0
- package/dist/x402/middlewares/express.js +49 -0
- package/dist/x402/middlewares/hono.d.ts +13 -0
- package/dist/x402/middlewares/hono.js +36 -0
- package/dist/x402/middlewares/next.d.ts +17 -0
- package/dist/x402/middlewares/next.js +58 -0
- package/package.json +41 -1
- package/dist/tempo/internal/machine-token-charge.d.ts +0 -51
- package/dist/tempo/internal/machine-token-charge.js +0 -113
- package/dist/tempo/internal/machine-token-session.d.ts +0 -88
- package/dist/tempo/internal/machine-token-session.js +0 -173
- package/dist/tempo/internal/signature-envelope.d.ts +0 -14
- package/dist/tempo/internal/signature-envelope.js +0 -28
package/dist/x402/mcp.js
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { McpError } from '@modelcontextprotocol/sdk/types.js';
|
|
2
|
+
import { decodePaymentRequiredHeader } from '@x402/core/http';
|
|
3
|
+
import { createPaymentWrapper, MCP_PAYMENT_META_KEY, } from '@x402/mcp';
|
|
4
|
+
import * as Challenge from '../Challenge.js';
|
|
5
|
+
import * as Errors from '../Errors.js';
|
|
6
|
+
import * as Negotiator from '../integrations/x402/Negotiator.js';
|
|
7
|
+
import * as Mcp from '../Mcp.js';
|
|
8
|
+
import * as McpCredential from '../mcp/internal/Credential.js';
|
|
9
|
+
import * as Receipt from '../Receipt.js';
|
|
10
|
+
import { paymentRequiredHeader } from './Types.js';
|
|
11
|
+
/**
|
|
12
|
+
* Creates an MCP tool wrapper that accepts both MPP and x402 payment metadata.
|
|
13
|
+
*
|
|
14
|
+
* Existing x402 credentials and lifecycle hooks continue through `@x402/mcp`.
|
|
15
|
+
* MPP credentials reuse the same x402 requirements, verifier, and facilitator;
|
|
16
|
+
* x402-only hooks in `PaymentWrapperConfig.hooks` therefore run only on x402 calls.
|
|
17
|
+
*/
|
|
18
|
+
export function mpp(resourceServer, config) {
|
|
19
|
+
const toolName = parseToolName(config.resource.url);
|
|
20
|
+
const x402 = createPaymentWrapper(resourceServer, config);
|
|
21
|
+
const resourceUrl = config.resource.url;
|
|
22
|
+
const route = {
|
|
23
|
+
accepts: config.accepts.map(toPaymentOption),
|
|
24
|
+
...(config.extensions ? { extensions: config.extensions } : {}),
|
|
25
|
+
...(config.resource.description ? { description: config.resource.description } : {}),
|
|
26
|
+
...(config.resource.mimeType ? { mimeType: config.resource.mimeType } : {}),
|
|
27
|
+
resource: resourceUrl,
|
|
28
|
+
};
|
|
29
|
+
const negotiator = Negotiator.create({
|
|
30
|
+
routes: route,
|
|
31
|
+
server: resourceServer,
|
|
32
|
+
secretKey: config.secretKey,
|
|
33
|
+
...(config.realm ? { realm: config.realm } : {}),
|
|
34
|
+
});
|
|
35
|
+
return (handler) => {
|
|
36
|
+
const x402Handler = x402(handler);
|
|
37
|
+
return async (arguments_, rawExtra) => {
|
|
38
|
+
const extra = (rawExtra ?? {});
|
|
39
|
+
if (extra._meta?.[MCP_PAYMENT_META_KEY])
|
|
40
|
+
return x402Handler(arguments_, rawExtra);
|
|
41
|
+
const credential = McpCredential.parse(extra._meta?.[Mcp.credentialMetaKey]);
|
|
42
|
+
const headers = new Headers({
|
|
43
|
+
Accept: 'application/json',
|
|
44
|
+
'Content-Type': 'application/json',
|
|
45
|
+
});
|
|
46
|
+
if (credential)
|
|
47
|
+
headers.set('Authorization', credential.header);
|
|
48
|
+
const request = new Request('https://mppx.invalid/tool', {
|
|
49
|
+
body: JSON.stringify(arguments_),
|
|
50
|
+
headers,
|
|
51
|
+
method: 'POST',
|
|
52
|
+
});
|
|
53
|
+
const result = await negotiator.negotiate(request, Negotiator.createContext(request, arguments_));
|
|
54
|
+
if (result.status === 'unprotected')
|
|
55
|
+
return x402Handler(arguments_, rawExtra);
|
|
56
|
+
if (result.status === 'x402')
|
|
57
|
+
return x402Handler(arguments_, rawExtra);
|
|
58
|
+
if (result.status === 'handled') {
|
|
59
|
+
if (result.response.status === 402)
|
|
60
|
+
throw createCombinedChallenge(result.response, credential ? new Errors.VerificationFailedError() : undefined);
|
|
61
|
+
return attachReceipt(await responseToToolResult(result.response.clone()), result.response, credential);
|
|
62
|
+
}
|
|
63
|
+
const toolResult = await handler(arguments_, {
|
|
64
|
+
arguments: arguments_,
|
|
65
|
+
...(extra._meta ? { meta: extra._meta } : {}),
|
|
66
|
+
toolName,
|
|
67
|
+
});
|
|
68
|
+
if (!credential)
|
|
69
|
+
return toolResult;
|
|
70
|
+
const response = result.withReceipt(Response.json(toolResult));
|
|
71
|
+
return attachReceipt(toolResult, response, credential);
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function attachReceipt(result, response, credential) {
|
|
76
|
+
if (!credential || !response.headers.has('Payment-Receipt'))
|
|
77
|
+
return result;
|
|
78
|
+
const receipt = Receipt.fromResponse(response);
|
|
79
|
+
return {
|
|
80
|
+
...result,
|
|
81
|
+
_meta: {
|
|
82
|
+
...result._meta,
|
|
83
|
+
[Mcp.receiptMetaKey]: {
|
|
84
|
+
...receipt,
|
|
85
|
+
challengeId: credential.value.challenge.id,
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/** Converts an HTTP-side bridge response into a valid MCP tool result. */
|
|
91
|
+
async function responseToToolResult(response) {
|
|
92
|
+
const text = await response.text();
|
|
93
|
+
const value = parseResponseBody(text, response.headers.get('Content-Type'));
|
|
94
|
+
if (isWrappedToolResult(value))
|
|
95
|
+
return value;
|
|
96
|
+
const message = getErrorMessage(value) ?? (text || `HTTP ${response.status}`);
|
|
97
|
+
return {
|
|
98
|
+
content: [{ text: message, type: 'text' }],
|
|
99
|
+
...(response.status >= 400 ? { isError: true } : {}),
|
|
100
|
+
...(isRecord(value) && response.status < 400 ? { structuredContent: value } : {}),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function parseResponseBody(text, contentType) {
|
|
104
|
+
if (!text || !contentType?.includes('application/json'))
|
|
105
|
+
return undefined;
|
|
106
|
+
try {
|
|
107
|
+
return JSON.parse(text);
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function isWrappedToolResult(value) {
|
|
114
|
+
return isRecord(value) && Array.isArray(value.content);
|
|
115
|
+
}
|
|
116
|
+
function getErrorMessage(value) {
|
|
117
|
+
return isRecord(value) && typeof value.error === 'string' ? value.error : undefined;
|
|
118
|
+
}
|
|
119
|
+
function isRecord(value) {
|
|
120
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
121
|
+
}
|
|
122
|
+
function createCombinedChallenge(response, error) {
|
|
123
|
+
const challenges = Challenge.fromResponseList(response);
|
|
124
|
+
const encoded = response.headers.get(paymentRequiredHeader);
|
|
125
|
+
const x402 = encoded ? decodePaymentRequiredHeader(encoded) : undefined;
|
|
126
|
+
return new McpError(Mcp.errorCode(error), error?.title ?? 'Payment Required', {
|
|
127
|
+
challenges,
|
|
128
|
+
httpStatus: 402,
|
|
129
|
+
...(x402 ? { x402 } : {}),
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
function toPaymentOption(requirement) {
|
|
133
|
+
return {
|
|
134
|
+
network: requirement.network,
|
|
135
|
+
payTo: requirement.payTo,
|
|
136
|
+
price: {
|
|
137
|
+
amount: requirement.amount,
|
|
138
|
+
asset: requirement.asset,
|
|
139
|
+
...(requirement.extra ? { extra: requirement.extra } : {}),
|
|
140
|
+
},
|
|
141
|
+
scheme: requirement.scheme,
|
|
142
|
+
...(requirement.extra ? { extra: requirement.extra } : {}),
|
|
143
|
+
...(requirement.maxTimeoutSeconds ? { maxTimeoutSeconds: requirement.maxTimeoutSeconds } : {}),
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
function parseToolName(resource) {
|
|
147
|
+
if (!resource.startsWith('mcp://tool/'))
|
|
148
|
+
throw new Error('MPP x402 MCP resources must use mcp://tool/{toolName}.');
|
|
149
|
+
const toolName = resource.slice('mcp://tool/'.length);
|
|
150
|
+
if (!toolName || toolName.includes('/') || toolName.includes('?') || toolName.includes('#'))
|
|
151
|
+
throw new Error('MPP x402 MCP resources must use mcp://tool/{toolName}.');
|
|
152
|
+
return toolName;
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=mcp.js.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { RoutesConfig, x402ResourceServer } from '@x402/core/server';
|
|
2
|
+
import type { RequestHandler } from 'express';
|
|
3
|
+
import * as Negotiator from '../../integrations/x402/Negotiator.js';
|
|
4
|
+
/** Express-specific configuration for {@link mpp}. */
|
|
5
|
+
export type Config = Omit<Negotiator.Config, 'routes' | 'server'>;
|
|
6
|
+
/**
|
|
7
|
+
* Adds MPP negotiation to the official x402 Express middleware.
|
|
8
|
+
*
|
|
9
|
+
* x402 retains ownership of response buffering, handler cancellation, hooks, and
|
|
10
|
+
* post-handler settlement. This wrapper only handles MPP credentials and appends
|
|
11
|
+
* an MPP challenge to unpaid x402 responses.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* import { mpp } from 'mppx/x402/express'
|
|
16
|
+
*
|
|
17
|
+
* app.use(mpp(routes, resourceServer, {
|
|
18
|
+
* secretKey: process.env.MPP_SECRET_KEY!,
|
|
19
|
+
* }))
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export declare function mpp(routes: RoutesConfig, server: x402ResourceServer, config: Config): RequestHandler;
|
|
23
|
+
//# sourceMappingURL=express.d.ts.map
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { ExpressAdapter, paymentMiddlewareFromHTTPServer } from '@x402/express';
|
|
2
|
+
import * as Negotiator from '../../integrations/x402/Negotiator.js';
|
|
3
|
+
import * as ExpressAdapter_ from '../../middlewares/internal/express.js';
|
|
4
|
+
/**
|
|
5
|
+
* Adds MPP negotiation to the official x402 Express middleware.
|
|
6
|
+
*
|
|
7
|
+
* x402 retains ownership of response buffering, handler cancellation, hooks, and
|
|
8
|
+
* post-handler settlement. This wrapper only handles MPP credentials and appends
|
|
9
|
+
* an MPP challenge to unpaid x402 responses.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* import { mpp } from 'mppx/x402/express'
|
|
14
|
+
*
|
|
15
|
+
* app.use(mpp(routes, resourceServer, {
|
|
16
|
+
* secretKey: process.env.MPP_SECRET_KEY!,
|
|
17
|
+
* }))
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export function mpp(routes, server, config) {
|
|
21
|
+
const mpp = Negotiator.create({ ...config, routes, server });
|
|
22
|
+
const x402 = paymentMiddlewareFromHTTPServer(mpp.httpServer, config.paywallConfig, undefined, false);
|
|
23
|
+
return async (req, res, next) => {
|
|
24
|
+
const request = ExpressAdapter_.toRequest(req);
|
|
25
|
+
const paymentHeader = Negotiator.getX402Credential(request);
|
|
26
|
+
const context = {
|
|
27
|
+
adapter: new ExpressAdapter(req),
|
|
28
|
+
method: req.method,
|
|
29
|
+
path: req.path,
|
|
30
|
+
...(paymentHeader ? { paymentHeader } : {}),
|
|
31
|
+
};
|
|
32
|
+
try {
|
|
33
|
+
const result = await mpp.negotiate(request, context);
|
|
34
|
+
if (result.status === 'unprotected')
|
|
35
|
+
return next();
|
|
36
|
+
if (result.status === 'handled')
|
|
37
|
+
return ExpressAdapter_.sendResponse(res, result.response);
|
|
38
|
+
if (result.status === 'paid') {
|
|
39
|
+
ExpressAdapter_.copyHeaders(res, result.withReceipt(new Response()).headers);
|
|
40
|
+
return next();
|
|
41
|
+
}
|
|
42
|
+
return x402(req, res, next);
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
return next(error);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=express.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { RoutesConfig, x402ResourceServer } from '@x402/core/server';
|
|
2
|
+
import type { MiddlewareHandler } from 'hono';
|
|
3
|
+
import * as Negotiator from '../../integrations/x402/Negotiator.js';
|
|
4
|
+
/** Hono-specific configuration for {@link mpp}. */
|
|
5
|
+
export type Config = Omit<Negotiator.Config, 'routes' | 'server'>;
|
|
6
|
+
/**
|
|
7
|
+
* Adds MPP negotiation to the official x402 Hono middleware.
|
|
8
|
+
*
|
|
9
|
+
* The official adapter retains ownership of Hono response handling, cancellation,
|
|
10
|
+
* extensions, and post-handler x402 settlement.
|
|
11
|
+
*/
|
|
12
|
+
export declare function mpp(routes: RoutesConfig, server: x402ResourceServer, config: Config): MiddlewareHandler;
|
|
13
|
+
//# sourceMappingURL=hono.d.ts.map
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { HonoAdapter, paymentMiddlewareFromHTTPServer } from '@x402/hono';
|
|
2
|
+
import * as Negotiator from '../../integrations/x402/Negotiator.js';
|
|
3
|
+
/**
|
|
4
|
+
* Adds MPP negotiation to the official x402 Hono middleware.
|
|
5
|
+
*
|
|
6
|
+
* The official adapter retains ownership of Hono response handling, cancellation,
|
|
7
|
+
* extensions, and post-handler x402 settlement.
|
|
8
|
+
*/
|
|
9
|
+
export function mpp(routes, server, config) {
|
|
10
|
+
const mpp = Negotiator.create({ ...config, routes, server });
|
|
11
|
+
const x402 = paymentMiddlewareFromHTTPServer(mpp.httpServer, config.paywallConfig, undefined, false);
|
|
12
|
+
return async (context, next) => {
|
|
13
|
+
const request = context.req.raw;
|
|
14
|
+
const paymentHeader = Negotiator.getX402Credential(request);
|
|
15
|
+
const requestContext = {
|
|
16
|
+
adapter: new HonoAdapter(context),
|
|
17
|
+
method: request.method,
|
|
18
|
+
path: new URL(request.url).pathname,
|
|
19
|
+
...(paymentHeader ? { paymentHeader } : {}),
|
|
20
|
+
};
|
|
21
|
+
const result = await mpp.negotiate(request, requestContext);
|
|
22
|
+
if (result.status === 'unprotected')
|
|
23
|
+
return next();
|
|
24
|
+
if (result.status === 'handled') {
|
|
25
|
+
context.res = result.response;
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (result.status === 'paid') {
|
|
29
|
+
await next();
|
|
30
|
+
context.res = result.withReceipt(context.res);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
return x402(context, next);
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=hono.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { RouteConfig, RoutesConfig, x402ResourceServer } from '@x402/core/server';
|
|
2
|
+
import { type NextRequest } from 'next/server.js';
|
|
3
|
+
import * as Negotiator from '../../integrations/x402/Negotiator.js';
|
|
4
|
+
type RouteHandler<arguments_ extends unknown[] = []> = (request: NextRequest, ...arguments_: arguments_) => Promise<Response> | Response;
|
|
5
|
+
/** Next.js-specific configuration for MPP compatibility wrappers. */
|
|
6
|
+
export type Config = Omit<Negotiator.Config, 'routes' | 'server'>;
|
|
7
|
+
/**
|
|
8
|
+
* Adds MPP negotiation to the official x402 Next.js route wrapper.
|
|
9
|
+
*
|
|
10
|
+
* The official adapter retains ownership of route execution, cancellation,
|
|
11
|
+
* extensions, and post-handler x402 settlement.
|
|
12
|
+
*/
|
|
13
|
+
export declare function mpp<arguments_ extends unknown[]>(handler: RouteHandler<arguments_>, route: RouteConfig, server: x402ResourceServer, config: Config): RouteHandler<arguments_>;
|
|
14
|
+
/** Creates a dual-protocol Next.js proxy handler backed by the official x402 proxy. */
|
|
15
|
+
export declare function mppProxy(routes: RoutesConfig, server: x402ResourceServer, config: Config): RouteHandler;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=next.d.ts.map
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { NextAdapter, paymentProxyFromHTTPServer, withX402FromHTTPServer } from '@x402/next';
|
|
2
|
+
import { NextResponse } from 'next/server.js';
|
|
3
|
+
import * as Negotiator from '../../integrations/x402/Negotiator.js';
|
|
4
|
+
/**
|
|
5
|
+
* Adds MPP negotiation to the official x402 Next.js route wrapper.
|
|
6
|
+
*
|
|
7
|
+
* The official adapter retains ownership of route execution, cancellation,
|
|
8
|
+
* extensions, and post-handler x402 settlement.
|
|
9
|
+
*/
|
|
10
|
+
export function mpp(handler, route, server, config) {
|
|
11
|
+
return createHandler(handler, { '*': route }, server, config);
|
|
12
|
+
}
|
|
13
|
+
/** Creates a dual-protocol Next.js proxy handler backed by the official x402 proxy. */
|
|
14
|
+
export function mppProxy(routes, server, config) {
|
|
15
|
+
const mpp = Negotiator.create({ ...config, routes, server });
|
|
16
|
+
const x402 = paymentProxyFromHTTPServer(mpp.httpServer, config.paywallConfig, undefined, false);
|
|
17
|
+
return async (request) => {
|
|
18
|
+
const result = await mpp.negotiate(request, createContext(request));
|
|
19
|
+
if (result.status === 'unprotected')
|
|
20
|
+
return NextResponse.next();
|
|
21
|
+
if (result.status === 'handled')
|
|
22
|
+
return result.response;
|
|
23
|
+
if (result.status === 'paid')
|
|
24
|
+
return result.withReceipt(NextResponse.next());
|
|
25
|
+
return x402(request);
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function createHandler(handler, routes, server, config) {
|
|
29
|
+
const mpp = Negotiator.create({ ...config, routes, server });
|
|
30
|
+
const argumentsByRequest = new WeakMap();
|
|
31
|
+
const x402 = withX402FromHTTPServer(((request) => handler(request, ...argumentsByRequest.get(request))), mpp.httpServer, config.paywallConfig, undefined, false);
|
|
32
|
+
return async (request, ...arguments_) => {
|
|
33
|
+
const result = await mpp.negotiate(request, createContext(request));
|
|
34
|
+
if (result.status === 'unprotected')
|
|
35
|
+
return handler(request, ...arguments_);
|
|
36
|
+
if (result.status === 'handled')
|
|
37
|
+
return result.response;
|
|
38
|
+
if (result.status === 'paid')
|
|
39
|
+
return result.withReceipt(await handler(request, ...arguments_));
|
|
40
|
+
argumentsByRequest.set(request, arguments_);
|
|
41
|
+
try {
|
|
42
|
+
return await x402(request);
|
|
43
|
+
}
|
|
44
|
+
finally {
|
|
45
|
+
argumentsByRequest.delete(request);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function createContext(request) {
|
|
50
|
+
const paymentHeader = Negotiator.getX402Credential(request);
|
|
51
|
+
return {
|
|
52
|
+
adapter: new NextAdapter(request),
|
|
53
|
+
method: request.method,
|
|
54
|
+
path: request.nextUrl.pathname,
|
|
55
|
+
...(paymentHeader ? { paymentHeader } : {}),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=next.js.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mppx",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.9.1",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://github.com/wevm/mppx#readme",
|
|
@@ -103,6 +103,22 @@
|
|
|
103
103
|
"types": "./dist/x402/index.d.ts",
|
|
104
104
|
"default": "./dist/x402/index.js"
|
|
105
105
|
},
|
|
106
|
+
"./x402/express": {
|
|
107
|
+
"types": "./dist/x402/middlewares/express.d.ts",
|
|
108
|
+
"default": "./dist/x402/middlewares/express.js"
|
|
109
|
+
},
|
|
110
|
+
"./x402/hono": {
|
|
111
|
+
"types": "./dist/x402/middlewares/hono.d.ts",
|
|
112
|
+
"default": "./dist/x402/middlewares/hono.js"
|
|
113
|
+
},
|
|
114
|
+
"./x402/next": {
|
|
115
|
+
"types": "./dist/x402/middlewares/next.d.ts",
|
|
116
|
+
"default": "./dist/x402/middlewares/next.js"
|
|
117
|
+
},
|
|
118
|
+
"./x402/mcp": {
|
|
119
|
+
"types": "./dist/x402/mcp.d.ts",
|
|
120
|
+
"default": "./dist/x402/mcp.js"
|
|
121
|
+
},
|
|
106
122
|
"./tempo": {
|
|
107
123
|
"types": "./dist/tempo/index.d.ts",
|
|
108
124
|
"default": "./dist/tempo/index.js"
|
|
@@ -142,15 +158,36 @@
|
|
|
142
158
|
},
|
|
143
159
|
"peerDependencies": {
|
|
144
160
|
"@modelcontextprotocol/sdk": ">=1.25.0",
|
|
161
|
+
"@x402/core": ">=2.22.0",
|
|
162
|
+
"@x402/express": ">=2.22.0",
|
|
163
|
+
"@x402/hono": ">=2.22.0",
|
|
164
|
+
"@x402/mcp": ">=2.22.0",
|
|
165
|
+
"@x402/next": ">=2.22.0",
|
|
145
166
|
"elysia": ">=1",
|
|
146
167
|
"express": ">=5",
|
|
147
168
|
"hono": ">=4.12.25",
|
|
169
|
+
"next": ">=16.2.6",
|
|
148
170
|
"viem": ">=2.54.0"
|
|
149
171
|
},
|
|
150
172
|
"peerDependenciesMeta": {
|
|
151
173
|
"@modelcontextprotocol/sdk": {
|
|
152
174
|
"optional": true
|
|
153
175
|
},
|
|
176
|
+
"@x402/core": {
|
|
177
|
+
"optional": true
|
|
178
|
+
},
|
|
179
|
+
"@x402/express": {
|
|
180
|
+
"optional": true
|
|
181
|
+
},
|
|
182
|
+
"@x402/hono": {
|
|
183
|
+
"optional": true
|
|
184
|
+
},
|
|
185
|
+
"@x402/mcp": {
|
|
186
|
+
"optional": true
|
|
187
|
+
},
|
|
188
|
+
"@x402/next": {
|
|
189
|
+
"optional": true
|
|
190
|
+
},
|
|
154
191
|
"elysia": {
|
|
155
192
|
"optional": true
|
|
156
193
|
},
|
|
@@ -159,6 +196,9 @@
|
|
|
159
196
|
},
|
|
160
197
|
"hono": {
|
|
161
198
|
"optional": true
|
|
199
|
+
},
|
|
200
|
+
"next": {
|
|
201
|
+
"optional": true
|
|
162
202
|
}
|
|
163
203
|
},
|
|
164
204
|
"dependencies": {
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import type * as Hex from 'ox/Hex';
|
|
2
|
-
import { type Address, type Client } from 'viem';
|
|
3
|
-
type Call = {
|
|
4
|
-
data?: Hex.Hex | undefined;
|
|
5
|
-
to?: Address | undefined;
|
|
6
|
-
value?: bigint | undefined;
|
|
7
|
-
};
|
|
8
|
-
type RouteCall = {
|
|
9
|
-
data: Hex.Hex;
|
|
10
|
-
to: Address;
|
|
11
|
-
};
|
|
12
|
-
type Route = {
|
|
13
|
-
calls: readonly [RouteCall, RouteCall];
|
|
14
|
-
settlementSender: Address;
|
|
15
|
-
transfers: readonly [{
|
|
16
|
-
amount: bigint;
|
|
17
|
-
memo: Hex.Hex;
|
|
18
|
-
recipient: Address;
|
|
19
|
-
}];
|
|
20
|
-
};
|
|
21
|
-
type Transfer = {
|
|
22
|
-
amount: bigint | string;
|
|
23
|
-
memo?: string | undefined;
|
|
24
|
-
recipient: Address;
|
|
25
|
-
};
|
|
26
|
-
/** Returns whether a first-party machine-token charge route is deployed on a chain. */
|
|
27
|
-
export declare function isSupported(chainId: number | undefined): boolean;
|
|
28
|
-
/** Resolves the canonical first-party settlement route for a compatible charge. */
|
|
29
|
-
export declare function getRoute(parameters: {
|
|
30
|
-
chainId: number | undefined;
|
|
31
|
-
currency: Address;
|
|
32
|
-
transfers: readonly Transfer[];
|
|
33
|
-
}): Route | undefined;
|
|
34
|
-
/** Builds and simulates the first-party settlement route. */
|
|
35
|
-
export declare function findRoute(client: Client, parameters: {
|
|
36
|
-
account: Address;
|
|
37
|
-
chainId: number;
|
|
38
|
-
currency: Address;
|
|
39
|
-
transfers: readonly Transfer[];
|
|
40
|
-
}): Promise<Route | undefined>;
|
|
41
|
-
/** Matches an exact first-party settlement route. */
|
|
42
|
-
export declare function matchRoute(parameters: {
|
|
43
|
-
calls: readonly Call[];
|
|
44
|
-
chainId: number | undefined;
|
|
45
|
-
currency: Address;
|
|
46
|
-
transfers: readonly Transfer[];
|
|
47
|
-
}): Route | undefined;
|
|
48
|
-
/** Returns the trusted sender that settles the configured charge route on a chain. */
|
|
49
|
-
export declare function getSettlementSender(chainId: number | undefined): Address | undefined;
|
|
50
|
-
export {};
|
|
51
|
-
//# sourceMappingURL=machine-token-charge.d.ts.map
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import { decodeFunctionData, encodeFunctionData, isAddressEqual, } from 'viem';
|
|
2
|
-
import { call, readContract } from 'viem/actions';
|
|
3
|
-
import { Abis, Actions } from 'viem/tempo';
|
|
4
|
-
import * as defaults from './defaults.js';
|
|
5
|
-
const swapAbi = [
|
|
6
|
-
{
|
|
7
|
-
inputs: [
|
|
8
|
-
{ name: 'inputToken', type: 'address' },
|
|
9
|
-
{ name: 'amount', type: 'uint256' },
|
|
10
|
-
{ name: 'targetToken', type: 'address' },
|
|
11
|
-
{ name: 'recipient', type: 'address' },
|
|
12
|
-
{ name: 'memo', type: 'bytes32' },
|
|
13
|
-
],
|
|
14
|
-
name: 'swapTo',
|
|
15
|
-
outputs: [],
|
|
16
|
-
stateMutability: 'nonpayable',
|
|
17
|
-
type: 'function',
|
|
18
|
-
},
|
|
19
|
-
];
|
|
20
|
-
function getDeployment(chainId) {
|
|
21
|
-
if (chainId === undefined)
|
|
22
|
-
return undefined;
|
|
23
|
-
return defaults.machineToken[chainId];
|
|
24
|
-
}
|
|
25
|
-
/** Returns whether a first-party machine-token charge route is deployed on a chain. */
|
|
26
|
-
export function isSupported(chainId) {
|
|
27
|
-
return !!getDeployment(chainId);
|
|
28
|
-
}
|
|
29
|
-
/** Resolves the canonical first-party settlement route for a compatible charge. */
|
|
30
|
-
export function getRoute(parameters) {
|
|
31
|
-
const deployment = getDeployment(parameters.chainId);
|
|
32
|
-
const transfer = parameters.transfers.length === 1 ? parameters.transfers[0] : undefined;
|
|
33
|
-
if (!deployment || !transfer?.memo)
|
|
34
|
-
return undefined;
|
|
35
|
-
const amount = BigInt(transfer.amount);
|
|
36
|
-
const memo = transfer.memo;
|
|
37
|
-
return {
|
|
38
|
-
calls: [
|
|
39
|
-
{
|
|
40
|
-
data: encodeFunctionData({
|
|
41
|
-
abi: Abis.tip20,
|
|
42
|
-
functionName: 'approve',
|
|
43
|
-
args: [deployment.swap, amount],
|
|
44
|
-
}),
|
|
45
|
-
to: deployment.token,
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
data: encodeFunctionData({
|
|
49
|
-
abi: swapAbi,
|
|
50
|
-
functionName: 'swapTo',
|
|
51
|
-
args: [deployment.token, amount, parameters.currency, transfer.recipient, memo],
|
|
52
|
-
}),
|
|
53
|
-
to: deployment.swap,
|
|
54
|
-
},
|
|
55
|
-
],
|
|
56
|
-
settlementSender: deployment.swap,
|
|
57
|
-
transfers: [{ amount, memo, recipient: transfer.recipient }],
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
/** Builds and simulates the first-party settlement route. */
|
|
61
|
-
export async function findRoute(client, parameters) {
|
|
62
|
-
const route = getRoute(parameters);
|
|
63
|
-
if (!route)
|
|
64
|
-
return undefined;
|
|
65
|
-
try {
|
|
66
|
-
const balance = await readContract(client, Actions.token.getBalance.call(client, {
|
|
67
|
-
account: parameters.account,
|
|
68
|
-
token: route.calls[0].to,
|
|
69
|
-
}));
|
|
70
|
-
if (balance < route.transfers[0].amount)
|
|
71
|
-
return undefined;
|
|
72
|
-
await call(client, { account: parameters.account, calls: route.calls });
|
|
73
|
-
return route;
|
|
74
|
-
}
|
|
75
|
-
catch {
|
|
76
|
-
return undefined;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
/** Matches an exact first-party settlement route. */
|
|
80
|
-
export function matchRoute(parameters) {
|
|
81
|
-
const transfer = parameters.transfers.length === 1 ? parameters.transfers[0] : undefined;
|
|
82
|
-
const swap = parameters.calls[1];
|
|
83
|
-
if (!transfer || parameters.calls.length !== 2 || !swap?.data)
|
|
84
|
-
return undefined;
|
|
85
|
-
try {
|
|
86
|
-
const decoded = decodeFunctionData({ abi: swapAbi, data: swap.data });
|
|
87
|
-
const route = getRoute({
|
|
88
|
-
...parameters,
|
|
89
|
-
transfers: [{ ...transfer, memo: transfer.memo ?? decoded.args[4] }],
|
|
90
|
-
});
|
|
91
|
-
if (!route)
|
|
92
|
-
return undefined;
|
|
93
|
-
if (parameters.calls.some((call, index) => {
|
|
94
|
-
const expected = route.calls[index];
|
|
95
|
-
return (!call.data ||
|
|
96
|
-
!call.to ||
|
|
97
|
-
!expected ||
|
|
98
|
-
(call.value ?? 0n) !== 0n ||
|
|
99
|
-
!isAddressEqual(call.to, expected.to) ||
|
|
100
|
-
call.data.toLowerCase() !== expected.data.toLowerCase());
|
|
101
|
-
}))
|
|
102
|
-
return undefined;
|
|
103
|
-
return route;
|
|
104
|
-
}
|
|
105
|
-
catch {
|
|
106
|
-
return undefined;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
/** Returns the trusted sender that settles the configured charge route on a chain. */
|
|
110
|
-
export function getSettlementSender(chainId) {
|
|
111
|
-
return getDeployment(chainId)?.swap;
|
|
112
|
-
}
|
|
113
|
-
//# sourceMappingURL=machine-token-charge.js.map
|