Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
b444ce6
fix(next): preserve request body when reading through the adapter (#3…
viviviviviid Sep 15, 2026
bb05610
fix(next): preserve leading empty query parameter values (#3456)
sunruize93-cmyk Sep 15, 2026
ba7fc20
fix(hono): preserve repeated query parameter values (#3455)
sunruize93-cmyk Sep 15, 2026
cbc4593
fix(axios): preserve base paths and params in payment hook URLs (#3454)
sunruize93-cmyk Sep 15, 2026
909b4fa
feat(python): add extra.minDeposit hint for EVM batch-settlement (#3480)
PhilBot402 Sep 15, 2026
2a3ab7a
fix(python): size MCP tool-call timeouts from accept maxTimeoutSecond…
PhilBot402 Sep 15, 2026
f59930b
fix(mcp,go): size tool-call timeouts from accept maxTimeoutSeconds (#…
PhilBot402 Sep 15, 2026
812fbaf
update codeowners (#3434)
phdargen Sep 15, 2026
c10d3bb
feat(evm): cache positive asset-contract checks (#3363)
PhilBot402 Sep 15, 2026
fab6ea8
feat(python): cache positive EVM asset-contract checks (#3362)
PhilBot402 Sep 15, 2026
f4c3f61
feat(ts): add Casper TypeScript SDK (#2877)
davidatwhiletrue Sep 15, 2026
6b93027
Add Casper TypeScript SDK setup docs to exact scheme (#3484)
mintlify[bot] Sep 15, 2026
087872f
prepare casper release (#3485)
phdargen Sep 15, 2026
78ef8f0
fix(mcp): cap tool-call timeouts (default 10m) (#3481)
phdargen Sep 15, 2026
a7ea804
Document MCP client timeout cap option (#3486)
mintlify[bot] Sep 15, 2026
57b4ef4
fix(MCP,Go): client dispatch HandlePaymentResponse after paid tool ca…
phdargen Sep 15, 2026
978b3ce
Pull request for mintlify/docs-update-1789494158578 (#3489)
mintlify[bot] Sep 15, 2026
cd10916
chore: version python package  (#3488)
phdargen Sep 15, 2026
dcfc16a
chore(go): release (#3490)
phdargen Sep 15, 2026
9b37f37
chore: version typescript packages  (#3487)
phdargen Sep 15, 2026
cf07e96
feat(go): add extra.minDeposit hint for EVM batch-settlement (#3410)
PhilBot402 Sep 16, 2026
cb2917d
fix(go): update MCP hook test mock for PaymentPayloadContext
PhilBot402 Sep 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
169 changes: 102 additions & 67 deletions .github/CODEOWNERS

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions .github/workflows/publish_npm_scoped_x402_all.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ jobs:
publish_package "packages/mechanisms/avm"
publish_package "packages/mechanisms/aptos"
publish_package "packages/mechanisms/cardano"
publish_package "packages/mechanisms/casper"
publish_package "packages/mechanisms/stellar"
publish_package "packages/mechanisms/hedera"
publish_package "packages/mechanisms/keeta"
Expand Down
48 changes: 48 additions & 0 deletions .github/workflows/publish_npm_scoped_x402_casper.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: Publish @x402/casper package to NPM

on:
workflow_dispatch:

jobs:
publish-npm-x402-casper:
if: github.repository == 'x402-foundation/x402' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment: npm
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Setup pnpm
uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8
with:
version: 11.1.1

- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: "24"
registry-url: "https://registry.npmjs.org"

- name: Update npm for OIDC trusted publishing
run: npm install -g npm@11.14.1 --ignore-scripts

- name: Configure npm for trusted publishing
run: npm config delete always-auth 2>/dev/null || true

- name: Install and build
working-directory: ./typescript
run: |
pnpm install --frozen-lockfile --ignore-scripts
pnpm -r --filter=@x402/core --filter=@x402/casper run build

- name: Publish @x402/casper package
working-directory: ./typescript/packages/mechanisms/casper
run: |
PACKAGE_NAME=$(node -p "require('./package.json').name")
PACKAGE_VERSION=$(node -p "require('./package.json').version")

echo "Package: $PACKAGE_NAME@$PACKAGE_VERSION"

echo "Publishing to NPM (main branch)"
pnpm publish --provenance --access public
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ app.use(
```shell
# All available reference sdks
npm install @x402/core \
@x402/evm @x402/svm @x402/avm @x402/aptos @x402/stellar @x402/tvm @x402/hedera @x402/keeta \
@x402/evm @x402/svm @x402/avm @x402/aptos @x402/casper @x402/stellar @x402/tvm @x402/hedera @x402/keeta \
@x402/axios @x402/fastify @x402/fetch @x402/express @x402/hono @x402/next @x402/paywall @x402/extensions @x402/mcp
```

Expand Down
144 changes: 104 additions & 40 deletions docs/advanced-concepts/lifecycle-hooks.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -467,28 +467,58 @@ Register hooks on the MCP client for payment lifecycle events specific to tool c
* **onBeforePayment** — Runs after payment approval but before the payment payload is created.
* **onAfterPayment** — Runs after the payment payload is submitted and the tool result is received.

```typescript
import { x402MCPClient } from "@x402/mcp";

const client = new x402MCPClient(mcpClient, paymentClient);

client
.onPaymentRequired(async ({ toolName, paymentRequired }) => {
if (blocklist.has(toolName)) {
return { abort: true };
}
// Return undefined to proceed with normal payment flow
})
.onBeforePayment(async ({ toolName, paymentRequired }) => {
console.log(`Creating payment for tool: ${toolName}`);
})
.onAfterPayment(async ({ toolName, paymentPayload, result, settleResponse }) => {
await auditLog.record({
tool: toolName,
transaction: settleResponse?.transaction,
});
});
```
<Tabs>
<Tab title="TypeScript">
```typescript
import { x402MCPClient } from "@x402/mcp";

const client = new x402MCPClient(mcpClient, paymentClient);

client
.onPaymentRequired(async ({ toolName, paymentRequired }) => {
if (blocklist.has(toolName)) {
return { abort: true };
}
// Return undefined to proceed with normal payment flow
})
.onBeforePayment(async ({ toolName, paymentRequired }) => {
console.log(`Creating payment for tool: ${toolName}`);
})
.onAfterPayment(async ({ toolName, paymentPayload, result, settleResponse }) => {
await auditLog.record({
tool: toolName,
transaction: settleResponse?.transaction,
});
});
```
</Tab>
<Tab title="Go">
```go
import x402mcp "github.com/x402-foundation/x402/go/v2/mcp"

client := x402mcp.NewX402MCPClient(session, paymentClient, x402mcp.Options{})

client.
OnPaymentRequired(func(ctx x402mcp.PaymentRequiredContext) (*x402mcp.PaymentRequiredHookResult, error) {
if blocklist[ctx.ToolName] {
return &x402mcp.PaymentRequiredHookResult{Abort: true}, nil
}
// Return nil to proceed with normal payment flow
return nil, nil
}).
OnBeforePayment(func(ctx x402mcp.PaymentRequiredContext) error {
log.Printf("Creating payment for tool: %s", ctx.ToolName)
return nil
}).
OnAfterPayment(func(ctx x402mcp.AfterPaymentContext) error {
if ctx.SettleResponse != nil {
return auditLog.Record(ctx.ToolName, ctx.SettleResponse.Transaction)
}
return nil
})
```
</Tab>
</Tabs>

### x402MCPServer payment wrapper (MCP server)

Expand All @@ -498,26 +528,60 @@ Register hooks in the `PaymentWrapperConfig.hooks` object when creating a paymen
* **onAfterExecution** — Runs after the tool handler returns, before settlement.
* **onAfterSettlement** — Runs after successful payment settlement.

```typescript
import { createPaymentWrapper } from "@x402/mcp";
<Tabs>
<Tab title="TypeScript">
```typescript
import { createPaymentWrapper } from "@x402/mcp";

const paid = createPaymentWrapper(resourceServer, {
accepts,
hooks: {
onBeforeExecution: async ({ toolName, paymentPayload }) => {
if (await isRateLimited(paymentPayload.payer)) {
return false; // Abort execution
}
},
onAfterExecution: async ({ toolName, result }) => {
console.log(`Tool ${toolName} executed successfully`);
},
onAfterSettlement: async ({ settlement }) => {
await sendReceipt({ transaction: settlement.transaction });
},
},
});
```
</Tab>
<Tab title="Go">
```go
import x402mcp "github.com/x402-foundation/x402/go/v2/mcp"

const paid = createPaymentWrapper(resourceServer, {
accepts,
hooks: {
onBeforeExecution: async ({ toolName, paymentPayload }) => {
if (await isRateLimited(paymentPayload.payer)) {
return false; // Abort execution
beforeExec := x402mcp.BeforeExecutionHook(func(ctx x402mcp.ServerHookContext) (bool, error) {
if isRateLimited(ctx.PaymentPayload.Payer) {
return false, nil // Abort execution
}
},
onAfterExecution: async ({ toolName, result }) => {
console.log(`Tool ${toolName} executed successfully`);
},
onAfterSettlement: async ({ settlement }) => {
await sendReceipt({ transaction: settlement.transaction });
},
},
});
```
return true, nil
})

afterExec := x402mcp.AfterExecutionHook(func(ctx x402mcp.AfterExecutionContext) error {
log.Printf("Tool %s executed successfully", ctx.ToolName)
return nil
})

afterSettle := x402mcp.AfterSettlementHook(func(ctx x402mcp.SettlementContext) error {
return sendReceipt(ctx.Settlement.Transaction)
})

wrapper := x402mcp.NewPaymentWrapper(resourceServer, x402mcp.PaymentWrapperConfig{
Accepts: accepts,
Hooks: &x402mcp.PaymentWrapperHooks{
OnBeforeExecution: &beforeExec,
OnAfterExecution: &afterExec,
OnAfterSettlement: &afterSettle,
},
})
```
</Tab>
</Tabs>

## Hook Chaining

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,55 @@ const client = createx402MCPClient({
});
```

#### Capping tool-call timeouts

By default, the x402 MCP client caps derived tool-call timeouts at **10 minutes** (600 seconds). The cap applies to both the initial probe call and the paid retry. You can lower or raise this limit using `maxRequestTimeoutSeconds` (TypeScript/Python) or `MaxRequestTimeout` (Go):

<Tabs>
<Tab title="TypeScript">
```typescript
import { createx402MCPClient } from "@x402/mcp";

const client = createx402MCPClient({
name: "my-agent",
version: "1.0.0",
schemes: [{ network: "eip155:84532", client: new ExactEvmScheme(account) }],
// Cap tool-call timeouts at 2 minutes (default: 600s)
maxRequestTimeoutSeconds: 120,
});
```

Per-call overrides are also supported via `callTool`'s `options.timeout` (milliseconds), which takes precedence over the cap.
</Tab>
<Tab title="Python">
```python
from x402.mcp import wrap_mcp_client_with_payment

x402_mcp = wrap_mcp_client_with_payment(
mcp_client,
payment_client,
max_request_timeout_seconds=120, # default: 600
)
```

Pass `read_timeout_seconds` to individual `call_tool` calls to override the cap for a single request.
</Tab>
<Tab title="Go">
```go
import (
"time"
x402mcp "github.com/x402-foundation/x402/go/v2/mcp"
)

client := x402mcp.NewX402MCPClient(session, paymentClient, x402mcp.Options{
MaxRequestTimeout: 2 * time.Minute, // default: 10 minutes
})
```
</Tab>
</Tabs>

The timeout is derived from the payment requirement's `maxTimeoutSeconds` field, capped by `maxRequestTimeoutSeconds`. If the accept's `maxTimeoutSeconds` exceeds the cap, the cap wins.

#### Using the onPaymentRequested hook

For per-call logic (e.g. checking tool name or prompting the user), use `onPaymentRequested`:
Expand Down
80 changes: 79 additions & 1 deletion docs/schemes/exact.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,83 @@ The `authorization` flow remains the default. Clients prefer `authorization` whe
</Tab>
</Tabs>

### Casper Setup

The Casper implementation uses CEP-3009 (`transfer_with_authorization`) — Casper's adaptation of EIP-3009 for CEP-18 tokens. The client signs an off-chain EIP-712 authorization; the facilitator relays it on-chain and pays the gas.

Install the Casper package:

```bash
npm install @x402/casper
```

**Server**

```typescript
import { x402ResourceServer } from "@x402/core/server";
import { ExactCasperScheme } from "@x402/casper/exact/server";

const resourceServer = new x402ResourceServer(facilitatorClient)
.register("casper:casper-test", new ExactCasperScheme());

// In your route config — price must be specified as an explicit asset amount:
{
"GET /data": {
accepts: [
{
scheme: "exact",
price: {
amount: "1500000000", // atomic units
asset: "0cb6f94834c60510d532b0ae077b18b4100874a4c867396d61c2b13c790ead52", // contract_package_hash
extra: { name: "csprUSD", version: "1" },
},
network: "casper:casper-test",
payTo: "007a9f9948cb7b258d18f3c5e85780372971b5b40096e724c9e596c284a01445fa",
},
],
description: "Data endpoint",
mimeType: "application/json",
},
}
```

**Client**

```typescript
import { createClientCasperSigner } from "@x402/casper";
import { ExactCasperScheme } from "@x402/casper/exact/client";
import { x402Client } from "@x402/core/client";

// Default algorithm is ED25519; pass 2 for secp256k1
const casperSigner = await createClientCasperSigner(process.env.CASPER_PRIVATE_KEY!);

const client = new x402Client()
.register("casper:*", new ExactCasperScheme(casperSigner));
```

**Facilitator**

```typescript
import { createFacilitatorCasperSigner } from "@x402/casper";
import { ExactCasperScheme } from "@x402/casper/exact/facilitator";
import { x402Facilitator } from "@x402/core/facilitator";

const casperSigner = await createFacilitatorCasperSigner(
process.env.CASPER_PRIVATE_KEY!,
1, // 1 = ED25519, 2 = secp256k1
{
rpcUrlConfig: { "casper:casper-test": process.env.CASPER_RPC_URL! },
// Optional: enable speculative execution for preflight validation
// speculativeRpcUrlConfig: { "casper:casper-test": process.env.CASPER_SPECULATIVE_RPC_URL! },
},
);

const facilitator = new x402Facilitator()
.register("casper:casper-test", new ExactCasperScheme(casperSigner));
```

The `asset` field is the 32-byte hex `contract_package_hash` of the CEP-18 token. The `extra.name` and `extra.version` fields are required — they are used to construct the CEP-3009 EIP-712 domain separator.

### XRPL Setup

The XRPL implementation uses payer-signed `Payment` transactions. The payer pays the XRPL transaction fee; facilitator-sponsored fees are not supported.
Expand Down Expand Up @@ -467,7 +544,7 @@ For testnet funds, get test ADA from the [Cardano testnets faucet](https://docs.

### Network Implementations

The `exact` scheme has network specifications for EVM, SVM, AVM, Stellar, Aptos, Hedera, TON, Cardano, Keeta, Sui, Concordium, NEAR, and XRPL.
The `exact` scheme has network specifications for EVM, SVM, AVM, Stellar, Aptos, Casper, Hedera, TON, Cardano, Keeta, Sui, Concordium, NEAR, and XRPL.

### SVM Smart Wallet Support

Expand Down Expand Up @@ -581,6 +658,7 @@ Permit2 may require a one-time approval. The gas sponsoring extensions can let t
* [`exact` Keeta spec](https://github.com/x402-foundation/x402/blob/main/specs/schemes/exact/scheme_exact_keeta.md)
* [`exact` Concordium spec](https://github.com/x402-foundation/x402/blob/main/specs/schemes/exact/scheme_exact_concordium.md)
* [`exact` Cardano spec](https://github.com/x402-foundation/x402/blob/main/specs/schemes/exact/scheme_exact_cardano.md)
* [`exact` Casper spec](https://github.com/x402-foundation/x402/blob/main/specs/schemes/exact/scheme_exact_casper.md)
* [`exact` Sui spec](https://github.com/x402-foundation/x402/blob/main/specs/schemes/exact/scheme_exact_sui.md)
* [`exact` NEAR spec](https://github.com/x402-foundation/x402/blob/main/specs/schemes/exact/scheme_exact_near.md)
* [`exact` XRPL spec](https://github.com/x402-foundation/x402/blob/main/specs/schemes/exact/scheme_exact_xrpl.md)
Expand Down
Loading
Loading