# One catalog, five machine faces: MCP, A2A, ActivityPub, llms.txt and x402 discovery

## One catalog, five machine faces: MCP, A2A, ActivityPub, llms.txt and x402 discovery

Agents discover paid APIs through whatever door their framework opens: an MCP server, an A2A agent card, an ActivityPub actor, an `llms.txt` file, or the x402 `/.well-known/x402` document. The Red Cup seller at market.561.group keeps one authored catalog of 130 offers and compiles every one of those doors from it, so no door can quote a different price, route or input schema than another. This article walks through each face with the live URLs and the one command that produces them all.

### Why not write each discovery document by hand?

Every agent-discovery format invites a hand-maintained list. MCP wants tool descriptions, A2A wants skills, `llms.txt` wants links, OpenAPI wants paths, a directory wants resources. A seller with 130 offers and five formats has 650 chances for the price in one place to disagree with the price in another, and an agent that sees two prices will believe the cheaper one and be refused by the 402. The remedy is the oldest one in software: one source, many projections.

In the market repository, the only author-editable offer list is `data/catalog.json`. A script, `scripts/compile-catalog.mjs`, derives the compiled declarations, the content identities and the ActivityPub outbox from it. The repository's own documentation is blunt about it: the compiled files are "generated outputs derived by `scripts/compile-catalog.mjs`; neither is a separate author-editable offer catalog", and a hand-edit to a generated file is forbidden by the operating procedure. Offers that are proposed but not yet qualified stay `listed:false` with `saleStatus:"held"` so that no projection can leak a held route into live charging.

### What are the faces, and where do I find each one?

| Face | Live URL | What an agent gets |
| --- | --- | --- |
| x402 discovery | [`/.well-known/x402`](https://market.561.group/.well-known/x402) | `x402Version: 2`, 130 `resources` with URL, method, price, asset, network, input and output schemas |
| Catalog | [`/v1/catalog`](https://market.561.group/v1/catalog) | The richest record: schemas, examples, pricing tariff, content-addressed `sku`, `cell` and `law` |
| OpenAPI | [`/openapi.json`](https://market.561.group/openapi.json) | OpenAPI 3.1.1 derived from the same catalog used for execution |
| A2A agent card | [`/.well-known/agent-card.json`](https://market.561.group/.well-known/agent-card.json) | Protocol 0.3.0 card with 130 skills, one per offer |
| MCP | `POST https://market.561.group/mcp` | Three read-only tools: `list_apis`, `search_apis`, `get_api` |
| ActivityPub | [`/actors/market`](https://market.561.group/actors/market) | A `Service` actor whose outbox holds 130 public `Offer` activities |
| llms.txt | [`/llms.txt`](https://market.561.group/llms.txt) | One line per offer: link, method, price, description |

The first five of those are the "faces" in the title; the catalog and OpenAPI are the structured source projections they share.

#### The x402 discovery document

`/.well-known/x402` is a JSON document of resource descriptions. Each entry has the shape a crawler or a directory needs to list it without calling the endpoint:

```json
{ "url": "https://market.561.group/v1/data/json-pointer", "method": "POST",
  "name": "RFC 6901 JSON Pointer", "price": "$0.0012",
  "pricing": {"mode": "canonical-json-byte-bands", "...": "..."},
  "asset": "0x8335…2913", "network": "eip155:8453",
  "inputSchema": {"...": "..."}, "outputSchema": {"...": "..."} }
```

(Shape from the live document; fields abbreviated. The `asset` is the USDC contract on Base, shortened here.) The advertised price is the starting price; for byte-priced offers see [Per-byte pricing](https://engineering.561.group/x402-per-byte-pricing/). It is a preview. The seller says so everywhere: "the current 402 challenge is authoritative".

#### The MCP server

The MCP face is stateless and read-only. It answers JSON-RPC at `/mcp`, supports protocol versions `2026-07-28` and `2025-06-18`, requires no session or handshake from modern clients, and exposes three tools annotated `readOnlyHint: true`. It performs no payment, no proxying and has no side effects; its source says so in the header comment. You can list the tools with the legacy version header:

```bash
curl -s -X POST https://market.561.group/mcp \
  -H 'content-type: application/json' \
  -H 'accept: application/json, text/event-stream' \
  -H 'MCP-Protocol-Version: 2025-06-18' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
```

and search the catalog by capability:

```bash
curl -s -X POST https://market.561.group/mcp \
  -H 'content-type: application/json' -H 'accept: application/json, text/event-stream' \
  -H 'MCP-Protocol-Version: 2025-06-18' \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"search_apis","arguments":{"query":"belnap","limit":2}}}'
```

On 2026-10-22 that search returned one structured result: the `FOUR Evidence Name Conversion` offer with its SKU, route `/v1/four/reading-convert`, preview price `$0.001` and the producing package export. The server does not execute offers. It finds them; the agent then makes an ordinary [x402 call](https://engineering.561.group/how-an-x402-payment-works/). Requests are capped at 32 KiB and results at 40 per page. A modern-protocol request with mismatched headers is refused with `HeaderMismatch`, which we saw when we sent the 2026 version without its companion metadata headers: the server validates rather than guesses.

#### The A2A agent card

The agent card at `/.well-known/agent-card.json` declares `protocolVersion` 0.3.0, `preferredTransport`, no streaming, and 130 `skills`. Each skill has an `id` (for example `publications/the-x402-economy`), a name, a description, tags and example requests. It is a directory entry for agents that discover peers by card. It is not a task endpoint; paying and calling still go through the routes.

#### The ActivityPub outbox

Following [ActivityPub](https://engineering.561.group/activitypub-publication-provider/) convention, the market has a `Service` actor at `https://market.561.group/actors/market`, WebFinger at `/.well-known/webfinger?resource=acct:market@market.561.group`, and an outbox. The outbox is an `OrderedCollection` with `totalItems: 130` and `first`/`last` links to 13 `OrderedCollectionPage`s. Pages are bounded by item count (12) and by bytes (192 KiB), so a fediverse peer never has to fetch a multi-megabyte first page. Each item is an `Offer` activity addressed to Public whose object is a document of media type `application/sen+cbor`, named by an RFC 6920 content address:

```json
{ "type": "Offer",
  "actor": "https://market.561.group/actors/market",
  "to": "https://www.w3.org/ns/activitystreams#Public",
  "object": { "type": "Document", "mediaType": "application/sen+cbor",
              "name": "ni:///sha-256;2Aafngw-StdkoQAmUKatPhGcduQ-oIO3Dyt5csPRT8Q" },
  "attachment": { "type": "Link", "name": "A2A Agent Card",
                  "href": "https://market.561.group/.well-known/agent-card.json" } }
```

(The `object.url` field, a base64 data URI carrying the CBOR, is omitted.) The outbox builder refuses to start if an activity is non-public, from a foreign origin, duplicated, or not an `Offer`. See [content-addressed names](https://engineering.561.group/content-addressed-names-ni-rfc-6920/) for what the `ni:` name commits to.

#### llms.txt

`/llms.txt` lists every offer as a Markdown bullet with its link, `METHOD route`, price per call and description, under a one-sentence blockquote that tells a language model how to obtain a payment challenge. A model with browsing but no protocol support can read it, pick a route and follow the instructions to inspect the 402.

### How does the agreement get enforced?

Three mechanisms keep the faces equal:

1. **One compile.** `npm run compile` regenerates the compiled offers, the static Worker imports and the ActivityPub outbox together. A checker, `npm run check:catalog`, verifies that the source catalog and the compiled identities agree.
2. **A receiving audit.** `npm run receive` checks the public release against the admitted seller catalog: fresh `/v1/status` and `/v1/catalog`, the unpaid 402 challenge of each listed route, and the payee, network and asset, with an independently supplied expected payee ([Never trust the payee](https://engineering.561.group/verify-x402-payee-buyer-side/)).
3. **Content addresses.** Each catalog entry carries `sku`, `cell` and `law` as `ni:` names. The same SKU is returned by the MCP `search_apis` result, so a client can confirm that the MCP face, the catalog and the outbox describe the same offer without parsing prose.

The faces still can disagree on *availability* in time (a deploy may update the Worker before a cache expires), so the 402 challenge on the route itself is the final authority for price. That is why the previews all say "preview".

### What does this cost the seller to maintain?

Almost nothing per format, which is the point. The catalog schema is one JSON Schema (`schemas/offer.schema.json` in the engine). An offer must name the typed operation it sells by its `compute` binding (package, export, argument projection), and may name its input admission and its scientific evidence. A new offer is one catalog entry; the compile step projects it into every face. The engine's `declareCatalog` refuses a route or gateway/operation declared twice.

### What is not claimed?

Listing in a directory is not demand. The market's own procedure states that "a listing or HTTP 402 does not establish a customer purchase" and that a self-purchase "is not external demand". We report 130 offers and say nothing about buyers.

Start with [what a seller should do beyond the spec](https://engineering.561.group/x402-seller-design/) for the other policies, or follow the [Belnap-valued logic offers](https://engineering.561.group/belnap-four-valued-logic-api/) that the search example found.
