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 for $0.10 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":"Market data 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: "quote",
description: "Latest price for a stock ticker",
category: "finance",
price: "$0.10",
url: "https://acme.example/mcp", // where buyers reach this server; or set ZANORA_PUBLIC_URL
});
const app = express().use(express.json());
app.post("/mcp", async (req, res) => {
const server = new McpServer({ name: "acme-quotes", version: "1.0.0" });
server.tool("quote", "Latest price for a ticker. Costs $0.10.", { ticker: z.string() },
paid(async ({ ticker }) => {
const answer = { ticker: String(ticker), price: await lookup(String(ticker)) };
// 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);
async function lookup(ticker: string) { return 187.42; }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 "stock price" tools, then buy a quote for AAPL.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. The 3% fee rounds down to the cent, so at $0.10 it rounds to zero and you keep all "10". At $1.00 you would keep "97". 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 → Earnings, save a bank account id (ext_…) or a USDC address, then request a payout. See Earnings and payouts.
Runnable versions
The platform repository has complete starters: starters/c-seller-mcp (this page) and starters/b-seller-rest (HTTP), each with a --request-verification flag that does step 2.