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.
On the MCP server: wrap the payment tool
Section titled “On the MCP server: wrap the payment tool”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.
On the agent host: observe tool calls
Section titled “On the agent host: observe tool calls”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.
Check it works
Section titled “Check it works”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.