Skip to content

Getting started

This page shows the smallest end-to-end setup:

  • a server that charges for a single tool
  • a client that can pay via Lightning BOLT11 over NWC

If you haven’t set up Nostr transports yet, start with the transport docs first.

Define what is paid using pricedCapabilities.

import type { PricedCapability } from '@contextvm/sdk/payments';
export const pricedCapabilities: PricedCapability[] = [
{
method: 'tools/call',
name: 'my-tool',
amount: 10,
currencyUnit: 'sats',
description: 'Example paid tool',
},
];

Create a processor, then wrap your server transport.

import {
LnBolt11NwcPaymentProcessor,
withServerPayments,
} from '@contextvm/sdk/payments';
import { NostrServerTransport } from '@contextvm/sdk/transport';
const baseTransport = new NostrServerTransport({
signer,
relayHandler,
});
const processor = new LnBolt11NwcPaymentProcessor({
nwcConnectionString: process.env.NWC_SERVER_CONNECTION!,
});
const paidTransport = withServerPayments(baseTransport, {
processors: [processor],
pricedCapabilities,
});

Server behavior for priced requests:

  1. emits notifications/payment_required (correlated to the request)
  2. waits for settlement verification
  3. emits notifications/payment_accepted
  4. forwards the request to the underlying MCP server

Create a handler and wrap your client transport.

import {
LnBolt11NwcPaymentHandler,
withClientPayments,
} from '@contextvm/sdk/payments';
import { NostrClientTransport } from '@contextvm/sdk/transport';
const baseTransport = new NostrClientTransport({
signer,
relayHandler,
serverPubkey,
});
const handler = new LnBolt11NwcPaymentHandler({
nwcConnectionString: process.env.NWC_CLIENT_CONNECTION!,
});
const paidTransport = withClientPayments(baseTransport, {
handlers: [handler],
});

Now priced calls will automatically pay when required.

Any request that matches pricedCapabilities will trigger the payment flow.

await client.callTool({
name: 'my-tool',
arguments: { example: true },
});