# Capabilities and discovery

> What a capability is, the two protocols and two kinds of address, how discovery ranks results, and why an unverified seller doesn't appear in it.

A **capability** is one thing a buyer can pay for, called once per payment. It's either an HTTP endpoint or a single tool on an MCP server.

## The record

```json
{
  "capability": {
    "id": "cap_d2383f37…",
    "providerId": "prv_3652ec9e…",
    "name": "Invoice OCR",
    "description": "Extract text and structured line items from scanned invoices",
    "category": "OCR",
    "protocol": "mcp",
    "toolName": "extract_invoice",
    "priceMinor": "100",
    "currency": "USD",
    "tags": ["invoices"],
    "schema": { "type": "object", "properties": { "imageUrl": { "type": "string" } }, "required": ["imageUrl"] },
    "latencyP50Ms": 900,
    "status": "active"
  },
  "version": {
    "id": "ver_…",
    "version": "1.0.0",
    "endpoint": "https://demo-seller.zanora.dev/mcp",
    "address": { "kind": "url", "url": "https://demo-seller.zanora.dev/mcp" },
    "pricingModel": "per_call",
    "hash": "…"
  }
}
```

| Field | Meaning |
|---|---|
| `protocol` | `"rest"` (an HTTP endpoint) or `"mcp"` (a tool on an MCP server) |
| `toolName` | **required for `mcp`, and not allowed for `rest`.** One MCP server hosts many tools, so the URL alone doesn't say what was bought |
| `priceMinor` | the price per call, as a string of cents |
| `schema` | the request body's JSON Schema, when the seller published one. Read it before paying, so you don't pay for a call that fails with a `400` |
| `latencyP50Ms` | the seller's advertised median response time, used in ranking |
| `version.address` | where to reach it: a URL to call, or a package to run (see below). `version.endpoint` is the same thing as a display string |
| `version.hash` | covers the fields that define what's being sold. Changing the schema publishes a new version |
| `status` | `active`, `draft` or `deprecated`. Only `active` capabilities appear in discovery, but deprecated ids still resolve |

## Two kinds of address

| `address.kind` | Buyer does | For |
|---|---|---|
| `url` | calls the URL (HTTP, or MCP over Streamable HTTP) | any hosted endpoint or MCP server |
| `package` | **runs** a pinned npm package locally over stdio | an MCP server whose seller doesn't host it |

A package address pins an exact version, such as `@acme/ocr-mcp@1.4.2`, because the buyer runs that code. Discovery marks these results `runsLocally: true`. Buyers don't run them unless they have explicitly allowed that package. See [Running seller packages](https://docs.zanora.dev/buyers/local-packages.md).

> **Tip — Resolve the address; don't read `endpoint`:**
>
> `version.endpoint` is a display string. In code, call `resolveCapabilityAddress(version)` from `@zanora/core`. It returns the URL or the package, and your code handles each correctly.

## Discovery

```bash
curl -s -X POST https://api.zanora.dev/v1/discovery/search -H "x-api-key: $KEY" \
  -H 'content-type: application/json' \
  -d '{"query":"extract text from invoices","maxPriceMinor":"200","protocol":"mcp","limit":5}'
```

Every filter is optional: `query`, `category`, `protocol`, `maxPriceMinor`, `maxLatencyMs`, `limit`. Each result has the capability, its version, its provider (`id`, `name`, `verified`, `reputation`) and a `score`.

Results are **ranked** on:

- **Relevance**: how closely the name, description and tags match your query.
- **Price**: cheaper ranks higher, within your filter.
- **Latency**: the seller's advertised median response time.
- **Reputation**: ratings from buyers who hold a receipt for what they rated.
- **Verification**: verified sellers rank higher.

### The verification gate

> **Warning — Unverified sellers aren't in discovery:**
>
> A seller can publish as soon as they sign up, but their capabilities stay **out of discovery** until a Zanora operator verifies them. If a seller publishes correctly and searches return `{"results":[]}`, this is the usual reason. Sellers can ask for verification from the console or the API. See [Getting verified](https://docs.zanora.dev/sellers/verification.md).

## Changing a listing

Sellers can edit a live capability's price, wording, tags, schema and advertised latency without changing its id. They **can't** change its address, protocol or tool name, because a buyer's saved capability id has to keep pointing at the same thing. Moving a capability means deprecating it and publishing a new one.

Changing a price is safe even while buyers are mid-flow. Every call pays the price in the challenge Zanora signs at call time, never a price the buyer saw earlier.
