Get started
Seller quickstart
Sell an MCP tool on the hosted platform. Sign up, ask for verification, publish and serve with one call, make a test purchase, and see the sale land.
This walkthrough sells one MCP tool, Acme's invoice OCR, for $1.00 a call. For an HTTP endpoint, the steps are the same except step 3. See Sell an HTTP endpoint. Steps marked 👤 need a person.
Create a provider account 👤
At console.zanora.dev↗, choose Seller, enter your email, organisation and password, and click the emailed link. The console shows your provider API key once. Save it:
export ZANORA_API_KEY='zk.akey_….…'
curl -s https://api.zanora.dev/v1/whoami -H "x-api-key: $ZANORA_API_KEY" # → "kind":"provider","providerId":"prv_…"Ask to be verified
In the console, Seller → Publish shows a banner with a Request verification button. Or:
curl -s -X POST https://api.zanora.dev/v1/providers/prv_…/verification-request \
-H "x-api-key: $ZANORA_API_KEY" -H 'content-type: application/json' \
-d '{"note":"OCR API, live since 2024, ~40k calls/month","contact":"ops@acme.example"}'You can carry on while you wait. Your listing exists, but buyers won't find it until an operator approves you.
Publish and serve in one call
npm install @zanora/middleware-mcp @modelcontextprotocol/sdk zod expressimport express from "express";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { zanoraSeller } from "@zanora/middleware-mcp";
import { z } from "zod";
// Reads ZANORA_API_KEY (a provider key). The gateway defaults to https://api.zanora.dev.
const seller = zanoraSeller();
const paid = await seller.sell({
tool: "extract_invoice",
description: "Extract text and structured line items from scanned invoices",
category: "OCR",
price: "$1.00",
url: "https://acme.example/mcp", // where buyers reach this server; or set ZANORA_PUBLIC_URL
schema: {
type: "object",
properties: { imageUrl: { type: "string", description: "URL of the scanned invoice" } },
required: ["imageUrl"],
},
});
const app = express().use(express.json());
app.post("/mcp", async (req, res) => {
const server = new McpServer({ name: "acme-ocr", version: "1.0.0" });
server.tool("extract_invoice", "Extract text and line items from a scanned invoice. Costs $1.00.",
{ imageUrl: z.string() },
paid(async ({ imageUrl }) => {
const answer = await extractInvoice(String(imageUrl));
// structuredContent is what the receipt's hash covers, so it's what the buyer can prove they received.
return { content: [{ type: "text", text: JSON.stringify(answer) }], structuredContent: answer };
}),
);
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
res.on("close", () => void transport.close());
await server.connect(transport);
await transport.handleRequest(req, res, req.body);
});
app.listen(4025);
// Your OCR goes here.
async function extractInvoice(imageUrl: string) {
return { text: "INVOICE #42 — TOTAL $1,337.00", pages: 1, source: imageUrl };
}The first start publishes the capability. Later starts reconcile it: if you've changed the price or description in code, the listing is updated to match. A call without payment gets back a signed price quote, and your handler runs only once the call is paid for.
The URL must be public
Buyers' agents call https://acme.example/mcp themselves. While you develop, put the port behind a tunnel (for example ngrok http 4025) and publish that URL instead.
Make a test purchase
Once you're verified, buy from yourself with any buyer setup, for example the buyer quickstart:
Use zanora_discover to find invoice OCR tools, then extract the invoice at https://acme.example/invoice-42.png.Before verification, you can still check the handshake. A call without payment should come back as a PAYMENT_REQUIRED result carrying the challenge.
Watch the sale land
Seller → Sales lists every paid call with its receipt. Seller → Earnings shows your balance. At $1.00 the 5% fee is 5 cents, so each sale earns you "950000" (micro-dollars: $0.95). The fee is exact down to the micro-dollar, so a $0.001 call earns you "950". Or from the API:
curl -s "https://api.zanora.dev/v1/receipts?limit=10" -H "x-api-key: $ZANORA_API_KEY"Set a payout destination and cash out 👤
In Seller → Settings, save a USDC address on Base as your payout address, then request a payout in Seller → Earnings. See Earnings and payouts.