Skip to content

MCP payment tools

Many agents pay by calling a tool, such as pay, purchase or x402_fetch, over the Model Context Protocol (MCP). Two small hooks record those payments. Both are optional, and neither changes the tool’s result.

If you run an MCP server that exposes a payment tool, wrap its handler. Each call then emits an event from the tool’s result:

import { HalvorClient } from '@halvorai/ingest';
import { wrapPaymentTool } from '@halvorai/ingest/mcp';
const halvor = new HalvorClient();
const payTool = wrapPaymentTool(
{ name: 'pay', handler: async (args) => runPayment(args) },
{
client: halvor,
rail: 'x402',
mapResult: (args, result) => {
if (result.isError) return undefined; // nothing was paid, so no event
const paid = JSON.parse(String(result.content?.[0]?.text ?? '{}'));
return {
type: 'payment.settled',
payment: {
payment_id: paid.id,
rail: 'x402',
amount: { value: String(paid.amount), unit: 'atomic', asset: paid.asset },
status: 'settled',
rail_ref: { network: paid.network, tx_hash: paid.txHash },
},
payee: { type: 'api_provider', name: String(args['payee_name']) },
};
},
},
);

mapResult decides what each result means. Return undefined to skip an outcome that was not a payment. The wrapper marks the event with the mcp adapter and a label naming the tool, returns the tool’s own result unchanged, and swallows any error in the mapping so a payment is never affected by it.

If you build the agent that calls the tools, observe the MCP client so every payment-looking call records its tool call id:

import { observeToolCalls } from '@halvorai/ingest/mcp';
const client = observeToolCalls(mcpClient, { match: /pay|purchase|checkout/i });
const result = await client.callTool({ name: 'pay', arguments: { payee_name: 'Example Market Data', amount: '10000' } });

Matching calls run inside an agent context that carries instruction.tool_call_id. Rail adapters used inside the call, such as the x402 wrapper, pick it up, so the payment links back to the exact tool call that caused it. Pass an arguments.tool_call_id to use your own id, and a different match to change which tools count.

Call the tool once. In Payments the event shows the agent, the payee, and the tool call id on its instruction.

HalvorAI records and monitors the payment. It does not approve or block the tool call.