Marketplace
Find, install, buy and publish agents, skills, personas, prompts and stack templates; ratings and the review lifecycle.
The marketplace is the part of the hosted platform 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) |
| Agent | A multi-step worker the router can hand a whole task to. | Open Capability Manifest (spec) |
| 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
- Open Marketplace in the top navigation. Filter by kind, tag, price or search; sort by popular, top rated, newest or name.
- 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.
- 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.
- 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
osr stack apply registry://support-desk@1.0.0 # build and summarise (hosted marketplace)
osr stack apply registry://support-desk --route "Refund for order 42?"
osr stack apply registry://support-desk --out stack.yaml # flatten & keep
osr stack apply registry://support-desk --registry https://<platform> # a self-hosted marketplaceregistry:// references resolve against the marketplace the CLI is configured for (OSR_MARKETPLACE_URL,
by default the hosted platform); --registry points one command at another deployment.
Or reference it from your own stack and let imports do the work:
osr: "1"
kind: stack
name: my-desk
imports:
- registry://support-desk@1.0.0
targets:
- id: llm-onprem
kind: llmosr 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
On the hosted platform nothing more is needed. Installing an agent, skill, persona, tool, subagent or
command puts it into your workspace's routing pool: from the next request on, POST /api/v1/route with
plan: true (and the OpenAI-compatible endpoints with plan composition) picks the installed skill or persona
for the matching slot when it fits the request better than the catalogue's, and an installed primary competes
for the answer itself. GET /api/v1/targets lists your installs with source: marketplace; plan_mode
(sequential / parallel) decides whether several installed skills run one after another or at the same time
(platform guide). Other workspaces never see your installs. Try it in the
playground: install a skill, ask a matching question with Build a plan on, and the plan's prepare stage
names it. An install is seen by every API replica on the very next request - no waiting for a cache.
Skills, personas and prompts you never installed can help too. Every plan also offers the router the
published skills, personas and prompts whose listing matches the request - the same vocabulary the router
extracts (its domains and salient words) is searched in the listing's name, summary, tags and domains, and
only a listing that works in one of the request's domains qualifies - so the marketplace shapes an answer
without anyone browsing it first. A prompt joins as an instructions-only skill when it takes its input through
at most one {{variable}} (the placeholder stands for the user's message); one with several variables is a
template for a person to fill. The router's own strategies and quality floor still decide whether such a
companion is used; a discovered one never answers on its own, and the decision names it under marketplace
with installed: false. What may be discovered is a matter of trust: by default only listings an operator has
marked verified (the deployment's own seeded skills, personas and prompts are), because a discovered
skill's instructions reach the prompt of a workspace that did not choose it. A workspace widens or narrows that
with the marketplace_discovery policy key - verified (default), all (every published listing) or off
(installs only) - and the operator sets the deployment default with OSR_PLATFORM_MARKETPLACE_DISCOVERY.
Agents and tools are never discovered: they execute (a call to the publisher's endpoint with the request in
it), so they join the pool through an install only.
Installed agents and tools do not only get picked, they run: when a listing's manifest declares a public
https endpoint with protocol a2a (an agent card), http (a JSON agent or workflow endpoint) or mcp (a
Streamable-HTTP MCP server), the platform binds it - an executed request that lands on the listing calls the
agent, or lists the MCP server's tools once and runs the one the request names (context.tool), the only one
there is, or the closest match, with context.tool_arguments as its arguments. GET /api/v1/targets shows
executable: true for those; a listing whose endpoint the platform may not call (a private address, a
protocol that needs credentials such as openai or queue) stays routable but not executable and carries
the reason in metadata.endpoint_unbound, which the dashboard's Installed tab shows as a badge. Skills and
personas need no endpoint: their instructions run through the model that answers. An installed model card
(an llm listing) needs your own key: map it to one of your providers on the Providers page - it is offered
there next to the catalogue with the listing's upstream model id prefilled - and it runs on your account like
any other target; /v1/models lists it and a chat may name it (model: <slug>).
In your own code an OCM manifest becomes a target with one call:
from opensmartroute import Router
from opensmartroute.ocm import bind_endpoint, target_from_capability
manifest = {...} # GET /api/v1/registry/<slug>/manifest
router = Router([bind_endpoint(target_from_capability(manifest))]) # http / a2a / mcp endpoints get a handlerPrompts are text: read manifest["prompt"] and fill in manifest["variables"].
The package has a client for the whole loop - search, load, keep in sync - so a router follows the marketplace (which itself refreshes from its public sources every day) without manual downloads:
from opensmartroute import Router, TargetRegistry
from opensmartroute.adapters import MarketplaceClient
mk = MarketplaceClient() # https://api.opensmartroute.ai
skills = mk.targets("sql reporting", kind="skill", limit=5) # RouteTargets from the listings' manifests
agents = mk.targets(kind="agent", tag="a2a", limit=10) # A2A agent cards, endpoints attached
router = Router(TargetRegistry([*my_models, *skills, *agents]))
report = mk.sync(router.registry, kind="skill", tag="skill-md", limit=50) # run daily: adds new, updates changed
print(report.added, report.updated)MarketplaceClient.search returns Listing rows (slug, kind, version, installs, rating, source);
target(slug) loads one manifest; sync upserts by id and replaces a target when its version, text,
instructions or endpoints changed upstream. Only routable kinds (agent, skill, persona, tool, llm) become
targets; templates are stacks - import them with registry://slug@version.
sync is safe to run forever: the SyncReport lists added, updated, skipped (no loadable manifest),
removed, kept and seen. A followed listing the marketplace answers 404 / 410 for is removed from the
registry; a synced target you edited locally (the sync stamps a fingerprint in metadata["marketplace"]) is
kept as it is until you pass force=True; prune=True also removes the targets this marketplace delivered
earlier that no longer match the search (OSR_MARKETPLACE_PRUNE=1 does the same for the server-side
MarketplaceSync below). The same from the CLI:
osr marketplace search "sql reporting" --kind skill # list matching listings
osr marketplace pull acme-skills-sql-reporting --out skills.json # manifests as an OCM catalogue file
osr marketplace sync --kind skill --tag skill-md --limit 50 --targets-file marketplace.json # daily cron job
osr --targets marketplace.json route "Write the monthly revenue report" --plansync --targets-file creates or updates a catalogue file every routing command loads with --targets,
so a scheduled osr marketplace sync keeps a self-hosted router on the marketplace's daily state. The file
keeps each manifest's endpoints, and load_targets binds them again on the next start (http, a2a, mcp),
so an agent or MCP tool the router picks from that catalogue runs - the process restart loses nothing.
Following the marketplace from osr serve
A running server can follow the marketplace by itself - no cron, no restart: MarketplaceSync upserts the
followed listings into the served catalogue every OSR_MARKETPLACE_REFRESH_S seconds, so a skill published
(or re-versioned) upstream is routable on the next tick.
osr -t targets.yaml serve --marketplace-refresh 86400 # once a day
OSR_MARKETPLACE_REFRESH_S=86400 OSR_MARKETPLACE_KINDS=skill,tool osr -t targets.yaml serve
OSR_MARKETPLACE_QUERY="sql reporting" OSR_MARKETPLACE_SLUGS=acme-skills-sql-reporting ...OSR_MARKETPLACE_URL, OSR_MARKETPLACE_KINDS (default skill,tool), OSR_MARKETPLACE_QUERY,
OSR_MARKETPLACE_TAG, OSR_MARKETPLACE_LIMIT (per kind) and OSR_MARKETPLACE_SLUGS choose what is
followed (MarketplaceSettings). GET /stats reports the sync (marketplace: last run, added / updated,
last error) and POST /marketplace/sync runs one now (a write path; read-only tokens are refused). In
Python, MarketplaceSync(router.registry).start() does the same in any process.
From the IDE (MCP)
The local MCP server (osr -t targets.yaml mcp) carries marketplace_search and marketplace_get backed by
the marketplace in OSR_MARKETPLACE_URL (--marketplace-url to point elsewhere, --no-marketplace to leave
them out). marketplace_get with install: true also loads the listing into the running router's catalogue,
so an assistant can find a skill and route to it in the same conversation. To try a listing without installing
it, route takes marketplace: [slug, ...]: those listings join the candidates for that one request (and fill
the plan's slots) and the result names them under marketplace_candidates. When something in the catalogue can
run - a model behind OSR_LLM_BASE_URL, an agent or MCP server with a bound endpoint - the local server also
offers ask, which routes and runs in one call.
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}/reviewsGET /api/v1/registry filters with q (free text, 80 characters), kind, tag (one exact tag, 64
characters), price=free|paid, featured=true|false and orders with sort=popular|newest|name|rating;
q and tag are matched literally. Pages are limit (1-100) and offset (at most 10 000); the whole
catalogue is paged through GET /api/v1/registry/sitemap instead. Each page carries the kind counts and
top tags of the catalogue, refreshed every few minutes.
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.
Leaderboard
/rankings/marketplace ranks the published listings per kind - most installed, best rated or newest -
from GET /api/v1/rankings/listings?kind=<kind>&sort=installs|rating|newest. Best rated is a
Bayesian average: the mean rating shrunk towards 3.5 by the weight of five ratings, so a listing with
one five-star review sits below one with forty four-star reviews until more ratings arrive.
GET /api/v1/rankings/listings/kinds gives the published listings, installs and reviews per kind.
Publishers appear by display name only.
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 (paid plans, Pro and above). 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.mdfront matter complete (name,description);osr skills validatechecks 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 agentic ecosystem - Agent Skills, agent instructions, prompt files, slash commands and workflows, chat modes, act-as roles and Fabric patterns, custom agents and subagents, A2A agent cards, stack files, MCP servers, the reference LLM catalogue and routing stacks generated from it - harvested and refreshed every day by the platform itself, so every marketplace kind fills from public sources:
| Source | What is imported | Listing kind |
|---|---|---|
| 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 |
GitHub code search (github-agents) | every public AGENTS.md file - the cross-tool agent-instruction standard (needs a GitHub token; optional) | skill |
GitHub code search (github-instructions, github-cursor-rules) | .github/instructions/*.instructions.md (Copilot custom instructions, applyTo kept as metadata.applies_to) and .cursor/rules/*.mdc (Cursor project rules, globs) (needs a GitHub token; optional) | skill |
GitHub code search (github-prompts) | .github/prompts/*.prompt.md reusable prompt files: the frontmatter's description, mode, model and tools, the body as the prompt with ${input:name} / ${selection} turned into {{name}} placeholders declared as variables (needs a GitHub token; optional) | prompt |
GitHub code search (github-claude-commands) | .claude/commands/**/*.md Claude Code slash commands: description, argument-hint, allowed-tools, model; $ARGUMENTS becomes {{arguments}} and $1 ... $9 become {{arg1}} ... (needs a GitHub token; optional) | prompt |
GitHub code search (github-cursor-commands, github-windsurf-workflows, github-codex-prompts) | .cursor/commands/*.md (Cursor custom commands), .windsurf/workflows/*.md (Windsurf workflows) and .codex/prompts/*.md (Codex custom prompts), same conversion; slugs end in -cursor-command, -workflow, -codex-prompt (needs a GitHub token; optional) | prompt |
GitHub code search (github-chatmodes) | .github/chatmodes/*.chatmode.md - a voice, rules and tool set layered on a model (needs a GitHub token; optional) | persona |
Awesome ChatGPT Prompts (awesome-prompts) | every "Act as ..." role of the CC0 prompts.csv (the prompt is the persona's instructions, the first sentence its description); slugs persona-<role> | persona |
Fabric (fabric-patterns) | every pattern's system.md (identity and purpose, steps, output format; MIT) from one repository tarball; slugs fabric-<pattern> | persona |
GitHub code search (github-custom-agents) | .github/agents/*.agent.md custom agent definitions: instructions, tools, model, handoffs (needs a GitHub token; optional) | agent |
GitHub code search (github-claude-agents) | .claude/agents/*.md Claude Code subagents: name, description, comma-separated tools, model; slugs owner-repo-<name>-subagent (needs a GitHub token; optional) | agent |
GitHub code search (github-a2a) | public A2A agent cards (.well-known/agent.json): name, skills, capabilities and the a2a endpoint the router can call (needs a GitHub token; optional) | agent |
GitHub code search (github-stacks) | OpenSmartRoute stack files anywhere on GitHub (osr: "1", kind: stack, at least one target or import; Docker / Pulumi stacks and invalid documents are dropped by the stack validator); the manifest is the stack, so registry://<slug>@<version> imports it; slugs stack-owner-repo-<file> (needs a GitHub token; optional) | template |
Generated from the model catalogue (catalogue-stacks) | ready-made routing stacks the platform derives from the reference LLM catalogue: one tiers stack per vendor with at least three priced models (cheapest / middle / strongest by intelligence index, complexity rules, cost-aware objective) and themed stacks - free models, reasoning, vision, open weights (up to four models each). The targets are the catalogue's llm capabilities with their OpenRouter endpoint, so a stack installs with no mapping; regenerated as the line-up and prices change; slugs stack-<vendor> / stack-free-models ... | template |
| MCP registry | the latest active version of every server, with its remote endpoints and packages | tool |
Reference model catalogue (models) | every hosted LLM of the platform's model catalogue (the OpenRouter listing snapshot, <data dir>/models.json when the rankings loop has refreshed it): vendor, prices per million tokens (cost.usd_per_1k_tokens is the input/output average), context window, modalities, tool calling, open weights, the OpenAI-compatible endpoint https://openrouter.ai/api/v1 and a quality prior from the Artificial Analysis intelligence index when published | llm |
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.
The harvest runs inside the platform on its own schedule (OSR_PLATFORM_MARKETPLACE_REFRESH_S; operators can
run it now from the console): it only fetches what the catalogue does not have yet, re-checks stale rows, and
publishes each source's new listings as soon as that source finishes. Slugs are owner-repo-skill
(mcp-<server name> for tools,
model-<vendor>-<name> for models, owner-repo-<file>-<convention> such as acme-app-deploy-prompt for
prompt / chat-mode / agent / instruction files, so two conventions sharing a file name stay distinct),
truncated with a short hash when longer than 63 characters.
Imported text is screened before it is stored: control characters are dropped and credential-shaped
strings an author left in a file (AWS access keys, GitHub / OpenAI / Slack / Stripe / Google API keys,
platform keys, JWTs, PRIVATE KEY blocks) are replaced with [REDACTED <kind>], including documentation
placeholders - the original stays one click away upstream. Repositories that publish attack corpora
(poisoned, malicious or prompt-injection skill datasets) are never mirrored: the harvester skips them, the
cache replay drops rows cached earlier, and the admin import endpoint rejects them
(source: repository publishes attack corpora and is not mirrored).
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.
OSR_PLATFORM_MARKETPLACE_REFRESH_S is the interval (default 86400 = daily; 0 = only from the
console) and the process that owns the marketplace runs every source in
OSR_PLATFORM_MARKETPLACE_REFRESH_SOURCES (default: every source -
skills.sh,mcp-registry,models,catalogue-stacks,awesome-prompts,fabric-patterns,github,github-agents,github-instructions,github-cursor-rules,github-prompts,github-chatmodes,github-custom-agents,github-claude-agents,github-claude-commands,github-cursor-commands,github-windsurf-workflows,github-codex-prompts,github-a2a,github-stacks;
OSR_PLATFORM_MARKETPLACE_REFRESH_LIMIT new rows per source per run, default 2000) into
<data dir>/harvest/, publishes the listings the catalogue lacks in batches of 100, and re-fetches the
oldest cached rows older than OSR_PLATFORM_MARKETPLACE_STALE_DAYS (default 7; at most
OSR_PLATFORM_MARKETPLACE_STALE_LIMIT = 500 per source per run) so listings whose upstream file changed are
republished with the new text, version and endpoints. After harvesting, each cache file is compacted to the
newest line per key (re-fetched rows are appended, so the files would otherwise grow without bound), and the
publish step reads bodies back from the cache one batch at a time - a catalogue of a hundred thousand rows
costs the process tens of megabytes, not the size of the caches. Every run publishes what the caches already
hold before it harvests and again after each source, so a run interrupted by a deploy or an eviction loses
nothing: the next run puts out
the rows it had fetched and carries on. The first run after a (re)start waits five minutes, or until the
interval has passed since the last run finished; a run that started and never finished is resumed after five
minutes, then three times longer for every further interruption in a row, capped at one hour (a crash loop
backs off to hourly attempts; a deploy costs five minutes, and a service deployed several times a day still
completes its harvest). When the marketplace runs on several replicas, one of them takes a Redis lease
(osr:marketplace:leader) per run and the others skip it, so the shared cache files are written by one process
at a time. The lease lives ten minutes and the leader renews it while it works and releases it when done, so a
leader killed mid-run (a deploy, an eviction) frees the slot within ten minutes; a replica that found the lease
taken records the skip (refresh.last_skipped in the status) and looks again after fifteen minutes instead of
waiting a whole interval. The console's Refresh now bypasses the lease. Sources that need GitHub code search are skipped -
and reported as such - until OSR_PLATFORM_GITHUB_TOKEN is set (on Azure: the OSR_PLATFORM_GITHUB_TOKEN
deployment input becomes the github-token secret of every API app; a token with the public_repo scope is
enough). GET /api/v1/admin/registry/import
reports the schedule, the sources, the last run (new, updated and invalid rows per source) 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
@versionin production stacks.
Related
- Platform guide - authentication, routing, plans, organizations and the dashboard.
- REST API reference - the Marketplace group lists every endpoint.
- User guide - stack files,
osr stackand the Open Capability Manifest.
Something wrong or out of date on this page? Tell us - the page is named for you, so a one-line note is enough.
TypeScript SDK
Routing, feedback, estimates, streaming chat and every workspace call from Node, Deno, Bun, edge runtimes and browsers.
Cost estimates and the MCP server
Quote a request before sending it, recommended models per use case, and the MCP server that puts the router in VS Code, Cursor, Claude and any agent.