sandermuller / laravel-x402-mcp
Bridge between laravel/mcp and laravel-x402 — gate MCP tools behind x402 stablecoin payments.
Requires
- php: ^8.2
- illuminate/support: ^11.0|^12.0
- laravel/mcp: ^0.6 || ^0.7
- sandermuller/laravel-x402: ^0.1@dev
Requires (Dev)
- dg/bypass-finals: ^1.9
- driftingly/rector-laravel: ^2.3
- larastan/larastan: ^3.0
- laravel/pint: ^1.29
- mockery/mockery: ^1.6
- mrpunyapal/rector-pest: ^0.2
- nunomaduro/collision: ^8.0
- orchestra/testbench: ^9.0|^10.0
- pestphp/pest: ^3.0
- pestphp/pest-plugin-arch: ^3.0
- pestphp/pest-plugin-laravel: ^3.0
- phpstan/extension-installer: ^1.4
- phpstan/phpstan: ^2.0
- phpstan/phpstan-deprecation-rules: ^2.0
- phpstan/phpstan-phpunit: ^2.0
- phpstan/phpstan-strict-rules: ^2.0
- rector/rector: ^2.0
- rector/type-perfect: ^2.1
- sandermuller/package-boost: ^0.10
- spatie/ray: ^1.48
- spaze/phpstan-disallowed-calls: ^4.10
- symplify/phpstan-extensions: ^12.0
- tomasvotruba/cognitive-complexity: ^1.1
- tomasvotruba/type-coverage: ^2.1
This package is auto-updated.
Last update: 2026-05-07 15:47:55 UTC
README
Gate laravel/mcp tools behind x402 stablecoin payments. Conformant with the x402 v2 MCP transport spec (specs/transports-v2/mcp.md).
Status: scaffolding. Not yet usable.
Bridge between sandermuller/laravel-x402 and laravel/mcp (^0.6 || ^0.7). Annotate paid tools with the #[X402Price] attribute. Agents include the signed payment payload in params._meta["x402/payment"] (JSON-RPC level — not an HTTP header).
Install
composer require sandermuller/laravel-x402-mcp
Usage
1. Annotate a paid tool
use Laravel\Mcp\Request; use Laravel\Mcp\Response; use Laravel\Mcp\Server\Tool; use X402\Laravel\Mcp\Attributes\X402Price; #[X402Price(amount: '0.01', asset: 'USDC', network: 'base')] final class FetchPremiumData extends Tool { public function description(): string { return 'Premium dataset. Costs $0.01 USDC on Base.'; } public function handle(Request $request): Response { // Runs only after payment is settled. return Response::json(['data' => '...']); } }
2. Wire the gating method handler on your Server
use Laravel\Mcp\Server\Server; use X402\Laravel\Mcp\Server\Concerns\WithX402Payment; final class MyMcpServer extends Server { use WithX402Payment; protected array $tools = [ FetchPremiumData::class, ]; }
The WithX402Payment trait swaps tools/call for X402CallTool, which:
- Looks up the invoked tool, checks for
#[X402Price]. - If unpriced — passes through to the standard
CallTool. - If priced — reads
params._meta["x402/payment"]for the signed payload, verifies + settles via the configured facilitator, then runs the tool. - On success, injects
result._meta["x402/payment-response"]with the settlement receipt. - On any failure, returns a tool result with
isError: true+structuredContent: PaymentRequired+content[0].text(JSON-stringified).
Wire format
Per specs/transports-v2/mcp.md:
| Direction | Location | Shape |
|---|---|---|
| Client → Server (payment) | params._meta["x402/payment"] |
PaymentPayload v2 envelope |
| Server → Client (settled) | result._meta["x402/payment-response"] |
{success, transaction, network, payer} |
| Server → Client (required) | result.structuredContent + result.content[0].text + result.isError = true |
PaymentRequired |
The HTTP-level PAYMENT-SIGNATURE / PAYMENT-RESPONSE headers used by the x402 HTTP transport are NOT used in MCP — payment travels at the JSON-RPC layer, inside the request/response body.
Stdio transport
Stdio MCP servers can also receive _meta["x402/payment"] because _meta is a JSON-RPC field, not an HTTP envelope. Paid tools work on stdio as well as HTTP.
License
MIT.