<!-- OpenSmartRoute: Marketplace. Source https://github.com/isathish/OpenSmartRoute/blob/main/docs/MARKETPLACE.md; HTML https://opensmartroute.ai/docs/MARKETPLACE -->
# Marketplace

The marketplace is the part of the [hosted platform](https://opensmartroute.ai/docs/PLATFORM.md) where routing building blocks are
shared: **agents, skills, personas, prompts, tools, model profiles and stack templates**, with
versions, ratings and a short review lifecycle. Every listing is a plain YAML or JSON document that
the OpenSmartRoute SDK, CLI and hosted API understand, so what you install is what you run; there
is no marketplace-only format.

Browsing, installing, rating and publishing prompts and personas work from the website without
writing code. Templates, skills and agents come with copy-paste snippets for the CLI and the SDK.

## What is on it

| Kind | What it is | Manifest |
|---|---|---|
| **Template** (stack) | A complete routing setup - models, rules, weights - in one file. Apply it and you have a working router. | `kind: stack` document ([`opensmartroute.stack`](https://opensmartroute.ai/docs/REFERENCE.md#opensmartroutestack)) |
| **Agent** | A multi-step worker the router can hand a whole task to. | Open Capability Manifest ([spec](https://opensmartroute.ai/docs/ocm.md)) |
| **Skill** | An Agent-Skills package (`SKILL.md`) that is also a routable target. | OCM, generated from the `SKILL.md` |
| **Persona** | A system prompt with a voice, a domain and a complexity band. | OCM with `kind: persona` |
| **Tool** | A function or MCP tool with typed parameters. | OCM with `kind: tool` |
| **Model profile** (llm) | A model with cost, latency, context window and quality prior. | OCM with `kind: llm` |
| **Prompt** | A reusable prompt with `{{variables}}`. | `{"prompt": "...", "variables": [...]}` |

Every listing has a **slug** (its address, `/marketplace/<slug>`), a **version** (`1.2.3`), a
publisher, a licence, tags, a Markdown description, an install count and a star rating.

## Finding and installing

1. Open **Marketplace** in the top navigation. Filter by kind, tag, price or search; sort by
   popular, top rated, newest or name.
2. Open a listing. The **About** tab is the publisher's description; **Use it** shows ready-made
   snippets (CLI, Python, HTTP); **Manifest** is the raw document; **Reviews** lists ratings.
3. Click **Install** (free) or **Buy** (paid). You need an account - signing up is free. Free
   installs complete at once; paid ones go through the checkout and are recorded on your account
   when the payment completes.
4. Everything you installed is listed under **Dashboard -> Marketplace -> Installed by you**, together with
   the version you installed and whether a newer one exists.

Installing records the listing on your account and returns the manifest and snippets. Nothing is
executed on your behalf.

### Using an installed template

```bash
osr stack apply registry://support-desk@1.0.0 --registry https://<platform>   # build and summarise
osr stack apply registry://support-desk --registry https://<platform> --route "Refund for order 42?"
osr stack apply registry://support-desk --registry https://<platform> --out stack.yaml  # flatten & keep
```

Or reference it from your own stack and let imports do the work:

```yaml
osr: "1"
kind: stack
name: my-desk
imports:
  - registry://support-desk@1.0.0
targets:
  - id: llm-onprem
    kind: llm
```

`osr stack validate my-desk.yaml`, `osr stack plan my-desk.yaml --against deployed.yaml` and
`osr stack apply my-desk.yaml` are the Terraform-style verbs: check, diff, apply.

### Using an installed agent, skill, persona, tool or model

An OCM manifest becomes a target with one call:

```python
from opensmartroute import Router
from opensmartroute.ocm import target_from_capability

manifest = {...}  # GET /api/v1/registry/<slug>/manifest
router = Router([target_from_capability(manifest)])
```

Prompts are text: read `manifest["prompt"]` and fill in `manifest["variables"]`.

The HTTP endpoints are public for published listings:

```
GET /api/v1/registry?kind=template&q=support&sort=popular
GET /api/v1/registry/{slug}
GET /api/v1/registry/{slug}/manifest?version=1.0.0
GET /api/v1/registry/{slug}/reviews
```

## Ratings and reviews

Anyone with an account can rate a listing from 1 to 5 stars and leave a short review. One review per
account per listing (writing again replaces the previous one). Reviews from accounts that installed
the listing are marked as such. A listing shows its average rating, the number of ratings and a star
histogram.

## Publishing

**Marketplace -> Publish** walks through four steps: choose the kind, describe it, paste or write
the manifest, review and submit. Prompts and personas need no code at all; for templates the wizard
offers a starter you can edit. You can also post the same payload to `POST /api/v1/registry`.

### The lifecycle

```
draft  ->  review  ->  published  ->  archived
  ^          |
  +--- rejected
```

- **Draft** - only you can see it. Edit anything, including the manifest.
- **Submit** - the manifest is validated (OCM schema, stack schema or prompt shape). Free listings
  are published immediately; paid listings go to **review**. (Operators can require review for
  everything with `OSR_PLATFORM_REGISTRY_AUTO_PUBLISH=false`.)
- **Review** - a platform reviewer approves or rejects with a note. Rejected listings go back to
  you with the note; fix and submit again.
- **Published** - live. Name, summary, description, tags, price and licence stay editable. The
  manifest is frozen: ship a **new version** instead, with a changelog. Old versions remain
  downloadable (`?version=`).
- **Archived** - hidden from the marketplace; existing installs keep working. You can restore it as
  a draft.

Your listings, their status and any reviewer note are under **Dashboard -> Marketplace -> Published
by you**; each one has an edit page with the version form.

### Paid listings

Set a price in USD when publishing (Pro and Enterprise plans). Buyers pay through the platform's
checkout; the purchase is confirmed by the payment webhook and the listing is then recorded on their
account. Free plans can publish a limited number of free listings.

### Writing a good listing

- A one-line summary that says what it does and for whom.
- A description that covers what it needs (models, keys, data) and how to use it - Markdown, no
  raw HTML or images.
- Tags people would search for (`support`, `pii`, `sql`, `on-prem`).
- Test templates locally first: `osr stack validate stack.yaml`.
- Skills: keep `SKILL.md` front matter complete (`name`, `description`); `osr skills validate`
  checks it.

## Built-in content

Every platform ships seeded with the repository's own Agent-Skills packages, a starter template, the
`examples/stack.yaml` support-desk template, a persona and a couple of prompts, all published by
`OpenSmartRoute` and marked verified. Set `OSR_PLATFORM_REGISTRY_SEED=false` to run an empty
marketplace.

## Imported catalogue

The hosted marketplace also carries the public Agent Skills ecosystem and the official MCP server
registry, imported by `platform/api/scripts/harvest_skills.py` (module `osr_platform.harvest`):

| Source | What is imported | Listing kind |
|---|---|---|
| [skills.sh](https://skills.sh) | every `SKILL.md` in each repository on the leaderboard (about 2 400 repositories) | `skill` |
| GitHub code search | the long tail of `SKILL.md` files (needs a GitHub token; optional) | `skill` |
| [MCP registry](https://registry.modelcontextprotocol.io) | the latest active version of every server, with its remote endpoints and packages | `tool` |

Imported listings are published under the upstream owner's name, are not marked verified, and carry
`metadata.source` in their manifest (`provider`, `repository`, `path`, `ref`, `url`) so the listing page
links back to the original file; the readme starts with the upstream install command
(`npx skills add owner/repo --skill name`). Copyright and licence stay with the author (`license` is
taken from the frontmatter when present). Domains are inferred from the description with the router's
own lexicon; the `skill-md` / `mcp` tags and the source name are added to the author's tags.

```bash
# collect into a resumable JSONL cache (one file per source)
python -X utf8 platform/api/scripts/harvest_skills.py collect --work-dir .osr-harvest \
    --source skills.sh --source mcp-registry [--source github] [--limit N]

# publish into a running platform (batches of 200 through the admin import endpoint) ...
OSR_PLATFORM_ADMIN_TOKEN=... python -X utf8 platform/api/scripts/harvest_skills.py publish --work-dir .osr-harvest \
    --url https://api.example.com
# ... or straight into a local platform database
python -X utf8 platform/api/scripts/harvest_skills.py publish --work-dir .osr-harvest --data-dir .osr-platform
```

`collect` only fetches what the cache does not have yet, so re-running it picks up new skills;
`publish --refresh` overwrites listings that already exist (same version, manifest replaced in place),
without it existing slugs are skipped. Slugs are `owner-repo-skill` (or `mcp-<server name>`), truncated
with a short hash when longer than 63 characters.

For a platform whose SQLite file lives on a network share (the Azure Container Apps deployment mounts
Azure Files), publish tens of thousands of rows **offline**: build the database locally with
`publish --data-dir`, stop the API, replace `platform.sqlite3` on the share, start it again. Sustained
bulk writes through the HTTP endpoint over SMB have corrupted the listings table before; the endpoint is
meant for incremental updates of a few hundred rows.

The hosted marketplace stays current from inside the platform process - no CI job or cron is involved. Set
`OSR_PLATFORM_MARKETPLACE_REFRESH_S` (seconds between runs; `604800` = weekly, `0` = off, the Azure
deployment defaults to weekly) and the process that owns the marketplace harvests only rows that are new
upstream (`OSR_PLATFORM_MARKETPLACE_REFRESH_SOURCES`, default `skills.sh,mcp-registry`;
`OSR_PLATFORM_MARKETPLACE_REFRESH_LIMIT` new rows per source per run, default 2000) into
`<data dir>/harvest/` and publishes the ones the catalogue lacks in batches of 100. Set
`OSR_PLATFORM_GITHUB_TOKEN` so the GitHub API allows the repository listings. `GET /api/v1/admin/registry/import`
reports the last run and the next run time under `refresh`; `POST /api/v1/admin/registry/import/refresh` runs it now.
From a machine, `publish --url ... --only-new` does the same: it asks
`POST /api/v1/admin/registry/import/check {"slugs": [...]}` which slugs already exist and sends only the rest.

## For operators

| Setting | Default | Effect |
|---|---|---|
| `OSR_PLATFORM_REGISTRY_AUTO_PUBLISH` | `true` | Free listings go live on submit; `false` sends everything to review. |
| `OSR_PLATFORM_REGISTRY_SEED` | `true` | Seed built-in skills, templates, personas and prompts at startup. |

Moderation uses the admin token (`X-Admin-Token`):

```
GET  /api/v1/admin/registry?status=review
POST /api/v1/admin/registry/{slug}/approve      {"note": "..."}
POST /api/v1/admin/registry/{slug}/reject       {"note": "why"}
POST /api/v1/admin/registry/{slug}/flags        {"featured": true, "verified": true}
POST /api/v1/admin/registry/{slug}/unpublish    {"note": "..."}
GET  /api/v1/admin/registry/import              published listings per source
POST /api/v1/admin/registry/import              {"items": [...], "refresh": false}  (up to 500 harvested rows)
POST /api/v1/admin/registry/import/check        {"slugs": [...]} -> {"existing": [...]}  (up to 2000 slugs)
POST /api/v1/admin/registry/import/refresh      run the scheduled harvest now (202; 409 while running)
```

Paid purchases need billing to be configured (Stripe keys); without it `POST .../install` on a paid
listing answers `402`.

## Security notes

- Manifests are validated against the OCM / stack schemas and size-limited; descriptions are
  rendered without raw HTML, scripts or images and external links carry `rel="nofollow"`.
- Installing never runs anything: it returns a document. Review what a template or agent points at
  (endpoints, tools) before applying it, the same way you would review a Terraform module.
- Slugs are stable; a new version never changes the slug, so pin `@version` in production stacks.

## Related

* [Platform guide](https://opensmartroute.ai/docs/PLATFORM.md) - authentication, routing, plans, organizations and the dashboard.
* [REST API reference](https://github.com/isathish/OpenSmartRoute/blob/main/platform/api/openapi.json) - the *Marketplace* group lists every endpoint (rendered at `/docs/api/marketplace`).
* [User guide](https://opensmartroute.ai/docs/GUIDE.md) - stack files, `osr stack` and the Open Capability Manifest.
