Skip to main content
Three parties can pay you for an MCP App, and which one you pick decides the rest of the integration:
  • a person, paying on a checkout page you already run
  • the agent itself, paying for a tool call out of a wallet its user funded
  • an advertiser, paying for a labeled slot in your tool results
The first is where almost every app shipping today sits. The second is newer and still experimental. They are not exclusive: an app can hand off to checkout and carry sponsored slots on the tools it gives away.

Hand Off to Your Own Checkout

The simplest option, and what the ecom template ships: send the shopper to a page you already run. The navigation has to go through the host with useOpenExternal.
views/carousel/detail/index.tsx
List your checkout origin in redirectDomains so the host skips its safe-link confirmation on the way out.
server.ts
What you give up is the return leg. The host opens the page outside the iframe and nothing comes back, so the view keeps rendering the cart and the model still believes the order is pending. This is usually fine for catalogs focused on sending qualified traffic to a storefront. If you want to update the cart after redirect, one strategy is to have the view poll the cart status until it’s updated elsewhere.

Charge the Agent

An agent can also pay for a tool call itself, with no person present, under the Machine Payments Protocol. An unpaid call comes back as a payment challenge, the agent fetches a credential from its user’s wallet, and the retry returns the real result with a receipt attached.
Charge from mcpMiddleware rather than inside the tool handler. The challenge has to reach the client as a JSON-RPC error, carrying the amount and the payment method the agent needs to pay: an exception thrown inside a handler is flattened into an isError tool result instead, which leaves the agent with a message and nothing to pay against.
server.ts
The middleware runs before the handler, so an unpaid call never reaches your code. Pricing lives in one table keyed by tool name, and a tool absent from it stays free. MPP_SECRET_KEY signs the challenges, so it has to be at least 32 bytes (openssl rand -base64 32), and setting realm avoids a fallback warning at boot.
This is experimental on every side. It needs Shared Payment Token access and a Stripe profile, the protocol is young, and no chat host pays for a tool call on a user’s behalf today. Agent harnesses with their own wallet, such as a coding agent, are where it currently applies. We have verified the challenge leg against a live Skybridge server; paying one and redeeming the receipt is untested.
mppx needs the v1 @modelcontextprotocol/sdk to build the payment error and declares it as an optional peer, so install it yourself alongside Skybridge’s v2 packages. Stripe documents a different shape for the same protocol, where a tool returns a payment link and a separate HTTP endpoint handles the exchange. That endpoint also serves human browsers, so it covers both audiences with one URL, at the cost of a custom route Alpic Cloud does not serve.

Carry Sponsored Slots

Instead of charging anyone for your app, you can let an advertiser pay for a slot in it. Lulu is an ad network for MCP servers and agent tools: it sells the demand, matches an advertiser to each tool call, and pays publishers 70% of the conversions it bills, with a $100 payout minimum. Its SDK attaches one labeled sponsored field to your tool results, through a Skybridge adapter built on mcpMiddleware.
server.ts
Credentials come from LULU_ADS_PUBLISHER_ID and LULU_ADS_API_KEY. Every tool registered on the server then carries a field like this one:
Two properties matter more than the integration. The field is data rather than an instruction, so the model decides on its own whether to surface it and nothing tells it to. And it fails open within 800ms, so a slow or dead ad backend leaves your tool result untouched instead of breaking the call. The payload lands on _meta["ads.getlulu.dev/sponsored"] and never on structuredContent. Protocol middleware sees the result but not the tool’s outputSchema, and an undeclared field in structuredContent would fail validation. Charging some users and showing ads to the rest needs the client directly, because the adapter above takes no per-request decision. Build a LuluAds yourself, and let the enabled flag read whatever your auth already knows about the caller.
server.ts
Reach for this instead of withLuluAdsSkybridge, not alongside it: the adapter already decorates every tool, so a tool doing its own slot would get two. Lulu also ships result widgets, four templates that render your own tool output with the sponsored strip built in, for hosts that support rendered widgets.

Go Further

Register Tools

Define what humans and agents can do

Configure CSP

Let your views reach the domains they need

Build an Ecommerce App

Scaffold a catalog and carousel UI