# CatalogHub MCP developer guide

Connect an MCP client to `https://cataloghub.hopshq.com/api/mcp`. Use OAuth to sign in as a CatalogHub user. The public interactive reference is at https://cataloghub.hopshq.com/developers#mcp-overview.

## Connect

Configure a remote Streamable HTTP server with the endpoint above. The unauthenticated endpoint returns HTTP 401 and a `WWW-Authenticate` header pointing to its protected-resource metadata. The client discovers `/.well-known/oauth-protected-resource/api/mcp` and `/.well-known/oauth-authorization-server`, registers its callback at `/api/oauth/register`, and opens `/oauth/authorize` with an authorization-code request and PKCE S256 challenge. After the user signs in and approves, exchange the code at `/api/oauth/token` with the original verifier and exact callback URI.

Supplier tools require OAuth; supplier REST keys cannot authenticate MCP. Personal `chub_` tokens remain supported for the existing feedback, support and Academy tools. Supplier machine integrations use scoped `chsup_` REST keys.

Callbacks must be HTTPS, loopback HTTP or registered native app schemes. PKCE is S256 only. Access tokens expire in 1 hour; refresh tokens expire in 30 days and rotate on every refresh. Reusing an old refresh token revokes the connection. Disconnect through CatalogHub's Connect an AI agent panel or `/api/oauth/revoke`. Keep tokens in the MCP client's secret store; never put them in prompts or logs.

## Consent and permissions

| Scope | Permission |
|---|---|
| `feedback` | Read/send the consenting user's own feedback; discover their accessible accounts |
| `supplier:catalog:read` | Read a supplier's full/private/public catalogue and reference prices |
| `supplier:relationships:read` | Read that supplier's trade relationships and buyer offers/accepted prices |
| `supplier:catalog:write` | Create, update and archive supplier products |
| `supplier:catalog:publish` | Propose selected products at buyer-specific prices for buyer review |
| `feedback:admin` | Support inbox, only with current support permission |
| `academy:edit` | Academy editing, only with current Academy permission |

Read, write and buyer-proposal choices are visible separately on consent. Writes and proposals default unchecked. Supplier tools require current direct supplier membership, verified account email, a verified supplier and enabled Supplier API access. Writes additionally require manager/admin. Resellers and support impersonation do not grant supplier tool authority. The selected `supplier_id` must be an account the consenting user currently belongs to; scopes never grant access to another supplier.

Every call checks live permissions. Changing membership, verification or API enablement immediately blocks supplier operations. Disconnecting invalidates the connection. Existing grants gain no new scopes automatically; reconnect and consent to supplier permissions.

## Tools

Start with `cataloghub_list_my_accounts`, then pass an explicit `supplier_id`. Call `tools/list` to discover only the tools your token scopes permit. Download exact argument schemas at https://cataloghub.hopshq.com/developer/mcp-tools.json.

| Tool | Arguments beyond `supplier_id` |
|---|---|
| `cataloghub_list_supplier_catalog` | `limit`, `offset`, `visibility` (`all` or `public`) |
| `cataloghub_upsert_supplier_products` | `products`: complete REST product objects, maximum 500 |
| `cataloghub_list_supplier_trade_relationships` | `limit`, `offset` |
| `cataloghub_get_supplier_trade_catalog` | `relationship_id`, `limit`, `offset` |
| `cataloghub_publish_supplier_products` | `relationship_id`, `items` (`productId`, `price`), optional `notes`, required `idempotency_key` |

Products use supplier-scoped trimmed case-insensitive SKUs. Upserts are atomic complete replacements, preserve UUIDs for existing SKUs, and leave omitted SKUs unchanged. Prices are nonnegative JSON numbers with at most 2 decimals and a maximum 99,999,999.99. All prices are net per ordering unit. Public visibility is optional; anonymous discovery never includes prices.

Publication requires a confirmed restaurant-scoped relationship in `approved`, `terms_set`, `catalogue_enabled` or `ordering_enabled`, trust `supplier_confirmed` or `admin_confirmed`, and one active supplier assignment. Snapshotting creates the normal buyer review. Approval is staged until the buyer completes the review. Publishing to Hops is a separate buyer action through the normal integration workflow. MCP does not approve reviews or send accepted products to Hops.

## Example

POST `/api/mcp` with `Content-Type: application/json` and `Authorization: Bearer <OAuth access token>`:

```json
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "cataloghub_publish_supplier_products",
    "arguments": {
      "supplier_id": "<supplier UUID from account discovery>",
      "relationship_id": "<confirmed relationship UUID>",
      "items": [{ "productId": "<active supplier product UUID>", "price": 4.25 }],
      "idempotency_key": "proposal-00000001"
    }
  }
}
```

Use the same idempotency key for exact retries, including concurrent retries. A changed payload with that key conflicts. The key accepts 8–120 letters, numbers, dots, underscores, colons or hyphens. Success appears inside JSON-RPC `result.structuredContent` and a JSON text content block. Publication returns the immutable initial `pending_review` status; read trade-catalogue products for the current review state and accepted prices.

## Pagination, errors and protocol

Lists default to `limit=100`, accept 1–100, and use `offset`. Relationship catalogue results have separate `offersTotal` and `acceptedTotal`; both arrays use the same offset, so continue until both totals are exhausted. Company relationships are readable but cannot receive restaurant proposals.

Tool failures return `result.isError=true` with a stable `code`, localised `messageKey` and safe error details. Treat HTTP 200 as transport success only and check `isError`. Authentication failures return HTTP 401; rate limiting returns 429 with `Retry-After`. Origin checks prevent an unrelated browser site from driving MCP. Supplier tool arguments are limited to 2 MiB. The shared MCP transport allows 120 requests/minute per client IP and at most 20 messages per batch. GET returns 405 because this stateless server provides no server-initiated stream.

Initialize with an MCP protocol version (supported: 2025-11-25, 2025-06-18, 2025-03-26, 2024-11-05), then send `notifications/initialized`. Supported methods include `initialize`, `ping`, `tools/list` and `tools/call`. No session header is required. Tool annotations describe read-only/idempotent behavior; they never substitute for OAuth scopes or live permissions.

Supplier OAuth consent grants only the supplier scopes in the original request. Selecting the read choice does not add relationship access when only catalogue access was requested. Product edits and buyer proposals must also be requested explicitly. Exact publication retries do not send another review notification.

## Supplier allergen evidence

The supplier surface includes three OAuth tools using existing supplier scopes:

| Tool | Scope | Required arguments |
| --- | --- | --- |
| `cataloghub_list_supplier_allergen_evidence` | `supplier:catalog:read` | `supplier_id`, `product_id` (optional `limit`, `offset`) |
| `cataloghub_submit_supplier_allergen_evidence` | `supplier:catalog:write` | `supplier_id`, `product_id`, `evidence`, `idempotency_key` |
| `cataloghub_publish_supplier_allergen_evidence` | `supplier:catalog:publish` | `supplier_id`, `relationship_id`, `items`, `idempotency_key` |

Read the exact evidence and publication schemas in `/developer/mcp-tools.json`. Evidence requires an explicit supplier attestation, source reference/description, exact statements using the fourteen canonical codes, and `partial` or `complete` coverage. Ask the user to attest the source and statements before submitting; OAuth consent alone is not allergen sign-off. Contains and declared absence answer Contains; May contain is independent. Partial sources never imply missing allergens are absent.

Sharing creates buyer holding evidence against an exact accepted catalogue product. Buyers review the source and match, confirm statements and close their declaration in CatalogHub. Separate human Hops mapping/preview approval remains required. These tools cannot sign buyer declarations, write Hops allergens or overwrite a buyer’s accepted evidence. Supplier identity verification is distinct from supplier attestation and buyer declaration. Evidence revisions are private and immutable; later revisions need new buyer review. Public guide: https://cataloghub.hopshq.com/developers#rest-allergens.

### Customer catalogues and reviewed imports

OAuth exposes six additional tools with the same user-bound supplier checks:

- `cataloghub_get_customer_catalogue`, `cataloghub_preview_customer_catalogue`: `supplier:relationships:read`.
- `cataloghub_save_customer_catalogue`, `cataloghub_preview_supplier_import`, `cataloghub_commit_supplier_import`: `supplier:catalog:write`.
- `cataloghub_publish_customer_catalogue`: `supplier:catalog:publish`.

Each requires `supplier_id`. Customer operations also require `relationship_id`. Save takes `catalogue: { revision, name, productIds, discountPercent, discountedProductIds }`. Revision 0 creates; subsequent edits use the last returned revision. Null `discountedProductIds` discounts all shared products; an array discounts only that subset. Other items retain standard RRP (`basePrice`), excluding tax per ordering unit. No quantity tiers. Preview returns `previewHash` and calculated half-up prices. Publish takes that exact `previewHash`, optional `notes` and `idempotency_key`; changed products/tiers require a fresh review. Exact retries return the same buyer review. Never publish without the user's intent to propose those commercial terms.

Import preview accepts editable JSON `products` and returns corrected candidate products, row issues and `baseline`, without saving. After supplier review, commit accepts `products`, `baseline` and `idempotency_key`. Invalid batches fail atomically; intervening catalogue changes reject the stale baseline. Omitted fields preserve existing metadata/visibility; new products start private. Binary CSV/Excel parsing is available through REST `POST /api/supplier/v1/imports/file-preview` and the portal. Neither import nor saved pricing automatically changes accepted buyer products, declares allergens or writes to Hops.

See `/developers#rest-customer-catalogues`, `/developers#rest-import-review` and the downloadable OpenAPI/MCP JSON contracts for copyable examples and schemas.
