Find the tool and its policy
Fetch /.well-known/mcp.json, /.well-known/x402.json, and /.well-known/agent-payment-policy.json.
Ontario returns HTTP 402 for paid REST endpoints and a standard PaymentRequired tool result for MCP, then accepts a signed retry.
Your client still owns the wallet decision. Run the free checks first, then let a payment-capable x402 client
sign locally only when the exact task, quote, network, asset, and budget match policy.
Fetch /.well-known/mcp.json, /.well-known/x402.json, and /.well-known/agent-payment-policy.json.
Run readiness and /api/agent/can-pay for the exact target, task, network, asset, and maximum amount.
Call without payment. Require parseable accepts, bounded amount, matching payTo, resource, and expiry.
A raw HTTP/MCP client cannot sign. Use an x402 client or MCP bridge with an explicit spend policy.
HTTP clients send PAYMENT-SIGNATURE. MCP clients send the signed object in params._meta["x402/payment"]. The older _x402_payment argument remains supported.
Store the bounded endpoint, quote, amount, report, network, and PAYMENT-RESPONSE evidence.
That is expected. MCP discovers and invokes tools, but a plain client does not have a wallet scheme registered.
Ontario returns the payment requirements instead of guessing or spending. A payment-capable bridge performs the
next step: parse the requirements, apply policy, sign locally, and retry. If the bridge is missing, the correct
result is a payment-required tool result, not an automatic payment. MCP returns HTTP 200 with isError: true and the actual PaymentRequired object; HTTP status alone is not purchase evidence.
The live tools/list response now includes the structured x402 contract as well as the human description:
price_usdc, asset, network_id=eip155:8453,
PAYMENT-REQUIRED, PAYMENT-SIGNATURE, and PAYMENT-RESPONSE.
Hosts can budget and choose a wallet bridge before invoking the tool.
The official x402 buyer flow follows the same handshake: the client receives 402 requirements, registers a payment scheme, signs locally, then retries the request. See the official MCP x402 guide and official buyer quickstart.
curl -sS -X POST https://ontarioprotocol.com/api/agent/can-pay \
-H 'Content-Type: application/json' \
-d '{
"endpoint": "https://ontarioprotocol.com/api/agent/pay",
"agent_policy": "strict",
"max_usdc": "0.002",
"verify_live": true
}'
Continue only when decision is allow. A free result is already enough when the task is
browsing, discovery, readiness, or sandbox rehearsal. Payment is for the incremental task-specific answer only.
onPaymentRequested.
The dependency-free hook runs at the exact point where @x402/mcp asks whether it may pay.
It rejects malformed x402 v2 metadata, non-Base networks, non-USDC assets, prices above your limit,
policy denials, timeouts, and unavailable verification. Tool arguments and URL query strings are never sent to Ontario.
import { createx402MCPClient } from "@x402/mcp";
import { createOntarioPaymentHook } from "./ontario-x402-payment-hook/index.js";
const client = createx402MCPClient({
name: "my-agent",
version: "1.0.0",
schemes,
autoPayment: true,
onPaymentRequested: createOntarioPaymentHook({
maxUsdc: "0.01",
onDecision: ({ allow, reason, endpoint, reportId }) =>
console.log({ allow, reason, endpoint, reportId }),
}),
});
The archive is deliberately marked private so it cannot be published to npm accidentally. Review the source,
keep wallet limits enabled, and return false whenever policy evidence is missing.
This example uses the official x402 client packages. It is intentionally blocked unless the operator explicitly
sets ALLOW_X402_PAYMENT=1. The key stays in the local process and is never sent to Ontario.
npm init -y
npm install @x402/fetch @x402/evm viem
ALLOW_X402_PAYMENT=1 \
EVM_PRIVATE_KEY='0xREPLACE_WITH_A_DEDICATED_LIMITED_WALLET_KEY' \
node --input-type=module <<'NODE'
import { x402Client, wrapFetchWithPayment, x402HTTPClient } from "@x402/fetch";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";
const base = "https://ontarioprotocol.com";
const endpoint = `${base}/api/agent/pay/v2`;
const body = {
query: "What evidence should an agent inspect before paying an x402 endpoint?",
endpoint: `${base}/api/x402/agent-trust-scan`,
max_usdc: "0.002",
agent_policy: "strict"
};
if (process.env.ALLOW_X402_PAYMENT !== "1") {
throw new Error("Refusing to sign: set ALLOW_X402_PAYMENT=1 after reviewing the quote.");
}
if (!process.env.EVM_PRIVATE_KEY || process.env.EVM_PRIVATE_KEY.includes("REPLACE_")) {
throw new Error("Set EVM_PRIVATE_KEY only in the local wallet runtime; never commit or send it.");
}
const signer = privateKeyToAccount(process.env.EVM_PRIVATE_KEY);
const client = new x402Client();
registerExactEvmScheme(client, { signer });
const fetchWithPayment = wrapFetchWithPayment(fetch, client);
const response = await fetchWithPayment(endpoint, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body)
});
const payload = await response.json();
const payment = new x402HTTPClient(client)
.getPaymentSettleResponse((name) => response.headers.get(name));
console.log(JSON.stringify({ http_status: response.status, payload, payment }, null, 2));
NODE
This is an integration example, not a request for Ontario's wallet credentials. Review the live 402, verify the amount and resource, and use a wallet with a strict limit. Ontario does not run this payment as part of the guide.
For provider publication, call ontario_validate-service free before considering ontario_list-service.
Probe the paid tool once without payment. Standard x402 MCP clients receive HTTP 200 with isError: true,
matching structuredContent and JSON text containing x402Version, resource, and accepts.
Only after local policy approves, retry with the signed object in params._meta["x402/payment"].
Settlement evidence is returned in result._meta["x402/payment-response"] and PAYMENT-RESPONSE.
See the current Coinbase MCP payment guide.
For older HTTP-wrapper integrations, send X-Ontario-X402-Transport: http to retain HTTP 402 signaling.
A retry can carry PAYMENT-SIGNATURE or the base64 _x402_payment argument. Do not send conflicting payloads.
No mode creates a wallet or authorizes spending.
# No-wallet probe: HTTP 200; inspect isError and structuredContent.accepts.
curl -i -X POST https://ontarioprotocol.com/mcp \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"ontario_agent-pay-query-v2","arguments":{"query":"Is this endpoint ready for the current task?","endpoint":"https://example.com/api/paid","agent_policy":"strict","max_usdc":"0.002"}}}'
# Legacy argument retry, only after can-pay and local policy allow.
POST https://ontarioprotocol.com/mcp
{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"ontario_agent-pay-query-v2","arguments":{"query":"Is this endpoint ready for the current task?","endpoint":"https://example.com/api/paid","agent_policy":"strict","max_usdc":"0.002","_x402_payment":"BASE64_PAYMENT_PAYLOAD"}}}
can-pay returns review or deny.payTo, resource, or amount does not match policy.accepts, expiry, or a usable resource.Ontario provides readiness and trust signals from observable endpoint behavior, manifest metadata, payment challenge behavior, report history, and public evidence. It does not guarantee endpoint safety, service quality, financial outcome, or future behavior. Agents should preserve their own policy decision and payment evidence.