ZanoraBetaDocs

Get started

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.

1

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:

Terminal
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_…"
2

Ask to be verified

In the console, Seller → Publish shows a banner with a Request verification button. Or:

Terminal
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.

3

Publish and serve in one call

Terminal
npm install @zanora/middleware-mcp @modelcontextprotocol/sdk zod express
server.ts
import 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.

4

Make a test purchase

Once you're verified, buy from yourself with any buyer setup, for example the buyer quickstart:

Output
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.

5

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:

Terminal
curl -s "https://api.zanora.dev/v1/receipts?limit=10" -H "x-api-key: $ZANORA_API_KEY"
6

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.