Documentation

Docs

A CLI, a gateway, two catalogues and a publisher API, written down in the order people meet them. If you have never installed a server, start with the four commands below. If you already know what you are after, filter.

54 entries across 9 groups, plus 14 commands. Each one opens here — nothing links to an article that does not exist.

From nothing to a working install

Four commands. The first is the only one you have to run; the other three are the ones people wish they had run first.

Pick your client and the commands rewrite themselves
01

Install a server

Prints the capabilities you are granting, opens one browser tab if the server authenticates, writes a single config entry.

npx mcprush@latest add key-value-mcp
02

Cap what it can spend

A wall, not a report: the gateway refuses the call that would cross it and warns you at the alert threshold first.

npx mcprush@latest budget --max '$900/mo' --alert 80%
03

Give the model instructions

A server gives your agent hands; a skill tells it what to do with them. This one: automate ActiveCampaign tasks via Rube MCP (Composio).

npx mcprush@latest skill add composiohq/activecampaign-automation-via-rube-mcp
04

Or take a whole desk at once

A stack is the 6 servers and 6 skills somebody already picked for one job, with the reasoning attached.

npx mcprush@latest stack add pr-desk
Written for Claude Code — the commands run as they are printed.
A server, a skill, a stack What the three things are, and how each one fails
First principles

A server, a skill, a stack

Three things are sold here, and they fail in three different ways
An MCP server

Reach. It runs somewhere — the publisher’s own infrastructure behind our gateway, or your own machine — holds the credentials for whatever it talks to, and exposes a handful of tools the model can call. Installing one is a decision about capability and money: a call can cost you, and it can change something outside the conversation.

An agent skill

Judgement. A folder of markdown that loads into the context window when the work matches its triggers, tells the model how to do one particular job well, and holds no credentials of its own. It cannot reach anything a server has not already given the model. What it changes is how the model decides.

A stack

A decision somebody already made. Servers and skills chosen for one job — ship the feature, run the incident — each carrying a line on why it and not the obvious alternative, and a monthly total for the whole desk. Install it whole, or take the two parts you were missing.

Getting started

From nothing to a server your agent can call, and what an account does or does not buy you.

5

add resolves the name against the catalogue, records the install against your account and writes one entry into your client's MCP config — the gateway address, and your key in an Authorization header. It opens no browser and asks nothing. What it prints when it is done is the file it wrote and the server's tool surface: how many of its tools read, and how many can write.

Restart the client and the tools appear in the model's tool list. remove takes an install back out of the client and off the account, and the gateway refuses calls to it from that moment; the key itself stays valid until you revoke it in your dashboard.

npx mcprush@latest add key-value-mcp

login opens nothing. It prints the one address where a key is minted — mcprush.com/dashboard#access, where the key is shown once — and then waits for you to paste it in at the Key: prompt. What you paste is checked against the account before it is stored, so a typo is caught while you are still looking at the terminal rather than on your first install.

Two ways to skip the prompt, and a pipeline needs one of them: mcprush login <key> passes the key as an argument, and MCPRUSH_KEY in the environment is read directly, with nothing written to disk. With no terminal to ask on, login refuses rather than hanging.

The key is written to ~/.mcprush/config.json, readable only by you, and revoked from Access in your dashboard. Revoking kills the key rather than one machine — everything holding it stops at once, which is the reason to mint one per environment.

There is no anonymous install. Every command that touches your account carries the key, add on a free, open-source listing included; without one the tool stops with No key held yet and writes nothing. What the account buys is that the install belongs to you rather than to a folder: a licence you bought resolves, a subscription is charged to the account rather than to a machine, and the account's spend cap applies. A paid listing still will not install from a terminal — add stops with the price, the reason and a link to the checkout rather than half-writing a config entry.

npx mcprush@latest login

Claude Code is the default, so add with no flag writes there. Every other editor takes --client: claude-desktop, cursor, vscode, zed, windsurf. VS Code also has its own one-liner if you would rather not run Node.

The Agent SDK and plain HTTP have no config file to write. They hold a key and talk to the gateway directly, which is why their snippets look nothing like the CLI's.

npx mcprush@latest add key-value-mcp --client cursor

A remote server is a URL: add writes one entry pointing at the gateway, and your client opens a Streamable HTTP connection to it the first time the model calls one of its tools. A local server is not installed from here at all — add refuses it and prints its page, because a server that runs on your own machine is set up by its own instructions rather than by a config entry from us. Either way nothing of ours is left running: no daemon, no service.

Your key goes into that entry. It is written as Authorization: Bearer <your key> beside the gateway address, in the client's own file — ~/.claude.json, ~/.cursor/mcp.json and the rest — which is why that file is written readable only by you and the previous version is kept beside it as .bak. Nothing is held on your behalf as a handle: revocation works because the gateway checks the key itself on every call.

VS Code is the exception, because its config lives in the repository rather than in your home folder. There the header is written as ${input:mcprush-key} with a matching inputs entry, so the value is typed once into the editor's secret store and the file that gets committed carries no key.

The catalogue, prices, context costs, scan results and every listing page are open to anyone. Installing is where the line is: mcprush add carries a key on every call it makes, so even a free, open-source server is installed against an account. What the account adds beyond that is saved lists, release alerts, the spend cap, one invoice — and the entitlement that makes a paid install resolve at all.

Installing servers

What happens in order — in the terminal or in the client's own settings — and what you agree to when it finishes.

8

It resolves the name — the bare id, or the <publisher>/<address> form printed on the listing page — and stops there if what it found is not something it can install: a skill is a folder of text rather than a route, so skill add writes it instead; a local server has no endpoint to forward to; a paid one needs a card; a listing with no verified endpoint has nothing behind it to route to. Every refusal names the reason and the page to open.

Otherwise it records the install against your account and writes one entry into the client's config: the gateway address and the Authorization header. There is no approval prompt and no OAuth round trip — it asks nothing and waits for nothing. What it prints afterwards is the file it wrote and the server's tool surface, how many of its tools read and how many can write, and then that you should restart the client to pick the entry up.

Nothing from the listing is executed during an install. A remote server never runs on your machine at all; a local one is started later by your client, not by the CLI.

Claude Desktop — Settings → Connectors → Add custom connector, paste the server's gateway URL, and approve the tools when Claude asks. That approval screen is the same scope list the CLI prints, and it is the last step: there is nothing to install on disk for a remote server.

Cursor — Settings → MCP → Add new global MCP server, and paste the JSON entry. It writes the same ~/.cursor/mcp.json the CLI writes, so the two routes are interchangeable and you can switch between them.

VS Code — Command Palette → MCP: Add Server → HTTP, or the code --add-mcp one-liner. Either lands in the workspace rather than in your user profile, so committing it gives everyone on the repo the same servers.

Every listing page carries the exact entry for each client, and so does the receipt after a purchase — pick the client there and the instruction changes with it.

{ "mcpServers": { "key-value-mcp": { "type": "http", "url": "https://mcprush.com/gw/key-value-mcp/mcp", "headers": { "Authorization": "Bearer <your key>" } } } }

An install points at the publisher's endpoint through the gateway, so a new release reaches you the moment the publisher ships it. There is no pinning to a version number from the terminal: --version is accepted for the sake of commands copied off older pages and then ignored out loud, and the install carries the release the publisher is serving.

What does not change silently is the tool surface. A release that adds a tool, widens a scope or rewrites a description goes back through review before it reaches anybody (see the rubric below), and the version history on the listing shows what changed and when. Because tool descriptions ship with the release, that history is the text the model was reading, not only the code behind it.

If a release breaks something for you, the answer today is the publisher's own channel and, if it is a policy matter, the report button on the listing. Rolling an install back to a chosen version is not something this marketplace does yet.

npx mcprush@latest add key-value-mcp

A remote server runs on the publisher’s own infrastructure, and every call reaches it through our gateway — which is what makes an entitlement check, a plan allowance and a rate limit possible at all. A local server runs on your machine with your file system and your network, and nothing about it passes through us, so it can only be free or a one-time licence: a subscription needs something somebody can switch off when it is cancelled, and a local server offers nothing to switch.

The catalogue filter “No local install needed” is the quick way to see only the first kind.

Each listing declares its tools and what each one touches. The install prints that list; the listing's security tab shows the same thing plus the egress allowlist, which is the complete set of hosts the listing may reach. Anything else is refused and written to your audit trail.

Treat the list as a budget rather than a promise: it bounds what a careless or compromised server can do, and it is re-checked on every release.

A list is a named set of servers you saved. add-list installs the free servers in it and names the rest with the reason — a paid listing needs a card, which a terminal is the wrong place for. Somebody joining runs one command instead of six.

Sharing a list makes it readable by your team. Removing a server from the list later does not uninstall it for anyone who already ran the command — the list is a plan, not a policy engine.

npx mcprush@latest add-list <your-list>

Every listing that runs behind our gateway has a Playground tab beside Tools, and the cabinet has the same panel under Playground for everything you already have. Pick a tool, edit the arguments — they arrive filled in with the publisher’s own example — and press Run.

It is a real call and it is treated as one: it needs an account, it goes through the same checks as a call from your own client, and the publisher sees it. What it will not do is write — the playground holds a read-only credential that lives for exactly one call, so a tool that writes or acts outside the conversation is refused here and has to be run from your own client.

A listing installed from its publisher’s own package, image or address has no playground: we are not in the path of those calls, and there is nothing here to send one to.

The free plan runs 50 playground calls a month; Pro has no limit. A call the gateway refuses — because the listing is not installed, or its endpoint is not verified — costs you nothing from that allowance.

A server that reads your accounting software, your database or your workspace cannot use the publisher’s key — it needs yours. Where a listing asks for one it declares what it needs, and the fields appear in your cabinet under In use → Variables, one set per install.

What you type is sealed before it is written down: encrypted with a key that lives on the server and never in the database, so a copy of the database on its own opens nothing. It is never read back to any page — the cabinet shows the last four characters so you can tell two credentials apart, and nothing else. You can replace it or clear it at any time; you cannot see it again.

It is decrypted for the length of one call and sent to the publisher’s endpoint in the header they declared, and nowhere else. It is not written to the call log, and it does not travel with anything you export.

A required field left empty is the usual reason a server answers with an authorisation error: the playground says so before you press Run, and the cabinet marks it.

Agent skills

Instructions rather than tools: where they land, when they load, and what they cost.

5

skill add writes to .claude/skills/<name>/ in the current project. The bundle is text throughout: the instructions the model reads and the templates it fills in.

Nothing is executed at install time, and uninstalling is deleting the folder — no config entry, no key, no bill. Add --global to install it once for every project instead.

npx mcprush@latest skill add composiohq/activecampaign-automation-via-rube-mcp

Claude Desktop — Settings → Capabilities → Skills → Add skill, and point it at the folder. Downloading the bundle from a listing gives you exactly that folder, so the CLI is a convenience rather than a requirement.

Cursor, VS Code, Zed and Windsurf read skills out of the project they have open, so a skill added to a repository travels with it and everyone on that repo gets it. Nothing needs restarting either way — a skill is read when the work matches its trigger.

Because a skill is text and nothing else, adding one by hand is the same operation as adding one with a command: put the folder in the right place. That is also why uninstalling is deleting it.

A skill declares the phrasings and situations it is for. When one matches, the instructions enter the context window; on every other turn they are not there at all, which is why a skill you never invoke costs you nothing.

That is also the failure to look for: a skill that never seems to fire usually has triggers that do not match how you actually ask.

A free skill is not a cheap one. Every card carries its token weight, the catalogue filters on that number, and the tray totals a selection before you install any of it.

Assemble a desk out of four heavy skills and a good part of a small context window is spent before the model has read your question.

A skill cannot reach anything by itself. Where it needs to read a repository or query a database it uses a server you have already installed, and the listing names which one. Install the skill without that server and it degrades to advice.

Instruction-only skills have no scopes, no keys and no invoice. They change how the model decides, not what it can touch.

Stacks

A whole desk for one job: servers and skills that were chosen together, with the reasoning attached.

4

A stack answers a job — ship the feature, run the incident, close the books. Each member carries a line saying why it and not the obvious alternative, which is the part a list of links usually leaves out.

It is not a discount. You pay each publisher's normal price, and the stack page totals it per month before you install anything.

The command resolves the stack, prints the combined capability list and the monthly cost, then installs the parts. Anything already installed is skipped rather than duplicated.

The --client flag behaves exactly as it does for a single server.

npx mcprush@latest stack add pr-desk

Nothing binds the parts together after installation. Passing several names to add installs exactly those, which is what the stack page copies once you deselect a member.

Skills in the stack are added with skill add, since they land in a different place and cost context rather than calls.

npx mcprush@latest add <server> <another-server>

The stack page adds up the monthly plans and the one-off licences, spreading the one-offs so they sit in the same column as a subscription. What you see is what the checkout quotes.

Everything arrives on one mcprush invoice rather than one per publisher.

Billing and budget caps

What a call costs, what it can never cost you, and where the limit is actually enforced.

8

The order matters, because the first one that fails is the error you get and none of the later ones run. It is the same seven whatever you installed and whatever it costs.

1 · Is the key live? Revoked, expired or unknown → 401. This is the only check that runs before we know which listing you meant.

2 · Does this account hold the install? An install belongs to an account, not to a machine — a key from a machine you removed fails here even though the key itself is fine.

3 · Is the entitlement current? A subscription that ended, a licence for a version you are not on, a trial that expired → 402, with what would fix it. This is the check a local server cannot have, which is why a local server cannot carry a subscription.

4 · Is the version still served? A withdrawn release fails closed and names the version to move to; a deprecated listing keeps answering until its end date and then returns a permanent error rather than a silent one.

5 · Is there budget left? Two different ceilings, both hard: the plan’s monthly call allowance on the install, and the account cap in dollars across everything. Over either → 429 before the call is made, so nothing is billed for a call that never ran.

6 · Is the rate inside the limit? 3 rps on Free, 20 rps on Pro, per install. Over → 429 with a retry hint.

7 · Is the tool one the install approved? A release that adds a tool does not get to use it until the install accepts the new capability. Unapproved → 403, and it appears in Changes.

Only then does the call reach the publisher. What comes back is counted against your plan on the way out, and an answer of 500 or above from their side does not count — the counter records work that happened, not requests that were made. A 4xx is work that happened: it is their server answering, and it is billed.

A call is billable when it reached the publisher’s code and came back with an answer. That is the whole test, and it is the same test the publisher’s earnings are computed from — there is no version of this figure where you are charged for something they were not paid for.

Never billed: an answer of 500 or above from the publisher’s side, a timeout, a call the gateway refused for a cap, a rate limit, an expired entitlement or an unapproved tool, and a repeat served from cache inside fifteen minutes.

Billed: a call that answered, including one whose answer you did not like. A tool that returns "no rows found" did the work you asked for; a tool that returns a 502 did not.

A retry is a new call. If your client retries a failed call three times and the fourth answers, you are billed for one — the three that failed are not billable and never were.

It follows that a listing can be quietly useless without ever costing you anything, which is exactly why the dashboard puts failures above spend rather than below it: the number that tells you something is broken is not the invoice.

Six things are written down the moment you install and never change underneath you: the price and its model, the licence, the declared capabilities, the egress allowlist, the version track you chose and the refund window. Your dashboard shows all six on the install itself.

A publisher may change any of them on a new version. What that produces is a new snapshot offered to you in Changes, with the two versions side by side — accepting is how it takes effect, and until you accept, the gateway judges every call against the old one.

A price rise is the same mechanism plus a clock: existing installs keep their price to the end of the term they are in, and are emailed 30 days before the new one applies. A subscription bought at $19 stays $19 for that term whatever the listing prints today.

This is why the marketplace can let publishers price freely: nothing they do reaches an install that already exists, so there is no version of “they raised it overnight” that we have to promise not to allow.

Every price in the catalogue, every invoice and every payout is USD. There is no local pricing, no currency selector and no conversion on our side — your card issuer converts at its own rate if your account is in something else, and that conversion is between you and them.

MCPRUSH INC. is the merchant of record on every sale, so the name on your statement is ours rather than the publisher’s, and VAT, GST or sales tax is registered, collected and remitted by us on top of the listed price. The publisher is paid net of it and never handles it.

A cycle owing less than $1 is not charged; it carries into the next one. That is a rule about cost rather than generosity — collecting 40¢ costs more than 40¢.

A plan is one figure a month, so there is nothing to cap in dollars on the install itself: what runs out is the monthly call allowance the plan carries, and when it does the gateway refuses the rest with an error naming the plan. Nothing is queued and nothing is billed past the allowance — an allowance that ends is never an invoice that grew.

The dollar limit lives one level up, on the account: a single hard ceiling for the cycle across every server and every seat, with a warning threshold under it. It is there because subscriptions accumulate — the month nobody meant to buy is usually twelve installs nobody cancelled rather than one runaway loop.

Staying on a listing’s free tier is a plan choice rather than a cap. --plan free installs on the zero-priced tier, so the call past its allowance fails instead of quietly starting a subscription.

The gateway stops the call and answers with an error the model reads like any other. It does not silently keep spending and it does not hang, so a well-written agent reports the wall instead of retrying into it.

An anomaly alert fires when spend deviates from your own 14-day baseline, which catches the case where the cap was set high enough to hurt.

You are billed once per cycle for everything, with the per-server split shown in your dashboard. Publishers never see your card and you never chase an invoice from one of them. One charge is also why the 30¢ Stripe adds per charge lands once on your whole account rather than once per listing you run.

Subscriptions renew on your cycle date and are prorated to the day in both directions. A cycle that owes less than a dollar is not charged at all — it carries to the next invoice as its own line, because a charge under a dollar costs more to collect than it collects.

Free and open-source listings never charge. A subscription may price its first tier at 0, and that tier is the listing’s free tier: the allowance on it is real, and the call past it is refused rather than repriced — which is why the catalogue filter reads “has a free tier” rather than “is free”.

Crossing the line is never automatic. --plan free installs on the listing’s free tier, so the call past its allowance fails instead of costing anything; naming a paid plan is what starts a subscription.

Publishing

Listing a server or a skill, pricing it, shipping releases, and getting paid.

9

Publishing is a browser flow from end to end: the wizard in the studio reads your source, lists every tool it finds, asks for the scopes and the price, and submits the draft for review. Nothing about it has a terminal equivalent — npx mcprush@latest is the BUYER's tool, and its whole surface is install, remove, skills, stacks, lists and the spend ceiling.

The reason is the same one the review exists for: creating a listing means agreeing, as a person, to what a buyer will be asked to approve — the tool surface, the scopes, what each write tool writes. A pipeline can push code; it cannot make that statement on your behalf.

What a pipeline CAN have is a key to the publisher API, and that key only reads: your own listings, their releases, their metrics. It writes nothing, so a leaked pipeline token cannot publish, cannot cut a release and cannot move money. Mint it in the studio under API keys; it arrives as MCPRUSH_TOKEN and every request it makes appears in your own audit trail.

# the buyer's tool — the only CLI there is npx mcprush@latest login npx mcprush@latest add <server> # publishing lives here instead https://mcprush.com/studio

A public repository is read over HTTPS with no credential of yours involved. A private one cannot be, so it is a permission you grant rather than a field you fill in: install the mcprush GitHub App and choose the repositories it can see.

What it asks for: contents, read-only — the branch, the manifest and the files a release is cut from; and metadata, read-only — the default branch and the commit a version points at. No write scope, no issues, no actions, and no repository you did not pick.

Revoking it in GitHub stops the next read and nothing else. The listing stays published and every release already cut keeps working, because a release is signed and stored at the moment it is cut rather than re-read from your repository afterwards.

A first listing is read by a person. Four things send it back, and they are the same four every time: a tool description that does not match what the tool does; a scope wider than the tools need, with “network” and no allowlist the usual case; a listing that describes a different product from the manifest; and a write path the listing never mentions.

One of them is not appealable: a tool description that instructs the model to do something the tool does not do. That is the attack this marketplace exists to catch, and the answer is final. Everything else is a change and a resubmission, and the email says which line to change.

Later releases skip the human unless the scan finds something: a new write tool, a scope that grew, or a description that changed meaning. Those go back to a person automatically.

Vague scopes cost conversions: “network” with no allowlist reads as “anywhere”. Name the hosts and the write paths and the install screen becomes short enough to say yes to.

The part we enforce is the tool surface. A call naming a tool the listing does not publish is refused before it is forwarded, a tool recorded as writing is refused for a read-only key, and both refusals are recorded against the install and never billed. The hosts your server itself reaches are a declaration to the reader rather than something we can hold you to: we forward one call to your endpoint, and whatever it calls next happens on your side of the wire.

A subscription carries plans you write yourself: a name, a price a month, a monthly call allowance and a rate limit. Print an allowance you can actually serve — it is what the gateway enforces, and running out returns an error naming the plan rather than a larger invoice. A one-time licence suits a local server there is nothing recurring to charge for.

You can change model on a new major version. Existing installs stay on the terms they bought.

Tool descriptions are part of the attack surface, so a change to the text the model reads is reviewed like a change to the code. The diff is public on your listing, which is what lets a security team approve an upgrade in a minute rather than a week.

Breaking changes need a deprecation window, and the studio will not let you skip it quietly.

The 12% covers the gateway, OAuth, call accounting against the plan, fraud, chargebacks, tax handling and payouts. There is no fee on a free listing, because nothing is charged on one.

Payouts run every Friday. New publishers hold 10% for the first 60 days against refunds, and a refund returns the platform share too, so it costs you 88% of the sale rather than all of it.

Give the listing an HTTPS address that speaks MCP over JSON-RPC, and make it refuse any request that does not carry the token we send. That is the whole contract. We never receive your code, never run it and never bill you for compute.

Verification runs three tests, and a listing goes live only when all three pass: we can reach it, it accepts our token, and it refuses a request that carries none. The third is what makes the gateway the only way in — an endpoint that answers strangers is one anybody can call for free, and nobody would be billed for it.

The token is minted when you first press Verify and shown once, on that answer — so the first check is expected to fail, because the endpoint cannot know a value that did not exist a moment earlier. After that the studio prints its first twelve characters, which is enough to tell one from another and not enough to use. Minting a new one from the listing’s Connection row replaces it for the next call we forward, so mint one only when you can deploy it in the same minute.

Buyers are handed our address rather than yours. That is the arrangement: the key they hold is ours to revoke, the plan they are on is ours to enforce, and neither works if they can reach you directly.

The headers a forwarded call carries, what happens to your answer, the timeouts, the circuit and what each source is read for are written out under Connecting your server.

x-mcprush-token: mgw_… # every call we forward carries this POST https://mcprush.com/gw/<listing>/mcp # what a buyer calls POST https://your-server.example/mcp # what we call

A call reaches you only if the key is live, the account installed the listing, the install is not paused or cancelled, the account is inside its calls a second and its calls a month, the install is inside its own plan allowance, and the tool named is one your manifest publishes with a reach the buyer's key is allowed to use.

Everything else is refused here, before your server is touched. A refusal is recorded and is never billed — to the buyer or to you — and it is told apart from a failure of yours in both dashboards, because 'we never got the call' and 'we refused it' are different answers to why your traffic dropped.

An endpoint that fails three times in a row is left alone for a minute rather than hammered on behalf of every buyer. Nothing is charged while it is held, and the Connection row says it is holding and why.

What we forward is the JSON-RPC body the client sent, plus our token and the listing and install ids. Never who the buyer is: a marketplace that hands the customer list to every seller is a lead broker.

Connecting your server

What the gateway sends to your endpoint, and what each source needs before it can have one.

6

Every server listing here is one the gateway forwards to. Buyers are given our address — https://mcprush.com/gw/<listing>/mcp — and a key of ours, so the plan they are on is ours to enforce and the key they hold is ours to revoke. Neither works if they can reach your server directly.

Two things are asked of the endpoint: that it answers MCP over JSON-RPC at one HTTPS address, and that it refuses any request which does not carry the token we send. Verification tests both, and that we can reach it at all. A listing goes live only when all three pass.

The source a listing starts from decides what we could read for its page and nothing else. Four of the five — a Git repository, an npm package, a PyPI project, a container image — are ways of shipping code rather than running a server, so on those the endpoint is something you add: the package stays where it is, and one instance of it runs somewhere with a public HTTPS address. The fifth source is an address you already run, which is where the other four end up.

A server listing recorded as running on the buyer’s own machine is not forwarded to. The gateway answers such a call with runs on your own machine — there is nothing here to forward to, mcprush add refuses to write a config entry for it, and submitting a server listing of that kind is refused as well. So a package listed here is a package with one running instance behind it. The exception is a skill, which is not a server: it is instructions, it runs inside the buyer’s client, and it never passes through us.

Your upstream keys stay yours. Every credential the server needs lives on the instance you run; the only credential travelling between us is the token, and it travels one way.

The Authentication field on the listing is what buyers read about your server. It is not how we reach you — the gateway sends the token and asks your endpoint for nothing else, so an endpoint that demands OAuth from us cannot be verified.

The token does not exist until you press Verify for the first time, so that first press is expected to fail: the endpoint cannot know a value that was made a moment ago. Its answer carries the token once. Put it in, press again.

POST https://mcprush.com/gw/<listing>/mcp # what a buyer calls POST https://your-server.example/mcp # what we call, exactly as you write it

We send one POST to the address exactly as you wrote it, path and query included, with nothing appended: we do not add /mcp and we do not rewrite it. The body is the buyer’s JSON-RPC request, unchanged. If buyers should reach a different path from the one the listing was read at, put that path in the endpoint field and verify it.

The headers. A forwarded call carries host, user-agent, accept, content-type, content-length, x-mcprush-listing, x-mcprush-install, and the token — as x-mcprush-token, or as authorization: Bearer … if the listing is set to that scheme. That is the whole request. Nothing the buyer’s client sent as a header survives it.

x-mcprush-listing names the listing and x-mcprush-install is stable per installation, so your own logs can tell installs apart without learning who anybody is. Nothing identifies the buyer — not their account, not their key, not their address. The user agent reads mcprush-verify/1.0 (+https://mcprush.com) on live calls as well as on checks, so it is not something to filter on.

Two other kinds of request of ours reach the same address, and no buyer is behind either: the verification described below, which you start by pressing a button, and a sandbox call you make yourself from the studio. The sandbox one carries x-mcprush-sandbox: 1 and no install id, because there is no install.

What we read. A listing started from an address is read by calling that address — initialize, then tools/list — with no token on either, because at that moment there is no listing to hold one. An endpoint that already refuses untokened requests, which is exactly what is asked of it below, answers 401 or 403, and the read comes back as That endpoint asks for authentication before it will say what it does. Nothing is wrong: type the name, the description and the tools yourself. The read is written to your account’s audit log and has no bearing on verification.

The token. It is minted the first time you press Verify and comes back on that answer whether or not the check passed, which is the only time it is shown. After that the studio prints its first twelve characters, on the listing’s Connection row, which is also where a new one is minted. A new one replaces the old for the very next call we forward, so mint one only when you can put it into your server in the same minute: in the gap your endpoint refuses what we send, and a 4xx of yours is an answer — it reaches the buyer, and it is billed.

Both schemes are a string comparison, so pick the one your framework already reads. A check files the scheme it was taken under, and the gateway reads that column on every call, so a scheme is only ever proved by verifying under it.

Your answer. What reaches the buyer is your status, your body, a content type we pick by looking at that body, and one header of ours, x-mcprush-ms. Every header you set is dropped, so a session id in a header survives in neither direction: mcp-session-id is not carried up and not handed back. A Streamable HTTP transport that insists on a session cannot be proxied, and it will still pass verification, because verification only ever calls initialize, tools/list and ping. Run it stateless, and if a call has to carry something, put it in the body.

We read at most 256 KiB of your answer and stop there. The cut is not tidy: the chunk that crosses the line is dropped whole, so the buyer gets the body up to the chunk before it — nothing at all, if the first chunk was already over. The count is of characters of decoded text rather than bytes, so a reply full of non-ASCII gets a little more than the figure says. Your status travels with whatever is left, and your status is what decides whether the call is billed.

Time. 30 seconds per hop, and it measures silence rather than duration: the timer sits on the socket and every byte we receive resets it. So a response that keeps arriving is never ended by it — an endpoint holding an SSE connection open with keepalives is not timed out, it is read until it has sent 256 KiB and cut there. What the timeout catches is a connection that is accepted and then says nothing. Answer once, and end the response.

Up to 3 redirects are followed, each with its own 30 seconds, and every header including the token is carried to the new host — so only redirect where you would be willing to send the token. If a connection cannot be made at all, the next address the name resolves to is tried with the same body. Anything you answer, including an error, is taken as the answer and never sent again.

A body beginning with event: is passed on as text/event-stream, which is how a completed SSE reply reaches the buyer intact.

When it fails. The circuit. 3 failures in a row — unreachable, or an answer of 500 and above — hold calls for 60 seconds rather than piling them onto you. While it is held nothing is forwarded at all, so nothing your server does can shorten it; when the hold lapses the next answer below 500 clears it, and a 4xx counts as an answer for that purpose. Nothing is charged for a call that did not happen.

The tool list is a contract. The name in a tools/call is checked against the tools recorded on the listing before anything is forwarded: a name that is not among them is refused publishes no tool called …, and a tool recorded as writing is refused for a buyer’s read-only key. Neither refusal reaches you, so a tool you have shipped but not added to the listing looks, in your logs, like a call that was never made.

The address. https only, on the port in the URL. We resolve the name ourselves, connect to the address and carry the name in SNI and in host, so the certificate has to be valid for that hostname and a self-signed one is not something we can be told to accept. The name has to resolve publicly: private, link-local and carrier-NAT addresses are refused. Addresses are tried IPv4 first, and IPv4 is the one that has to answer — the machine these calls leave from has no working IPv6 route today, so a name publishing only AAAA records cannot be reached from here.

Verifying. The check sends 8 requests: initialize and tools/list carrying the token, one tools/list carrying none, then 5 pings whose median becomes the latency on the listing. Each request waits 5 seconds, and the check can be run 6 times a minute. The initialize declares protocol version 2025-06-18. Three things have to hold, and the first two are read from one answer: the initialize carrying our token comes back below 500, which is what counts as reaching you, and below 400, which is what counts as taking the token; then the untokened request is answered 401 or 403. The tool count is whatever your tools/list returned, and it gates nothing.

Reading a refusal. Three failures, three different repairs, and the sentence says which. When nothing answered at all the reason is given in its own words — Nothing is listening on … at that port, The certificate on … has expired, … does not resolve. We could not reach it. covers those and also an endpoint that answered 500 or worse to initialize, so read it as no usable answer rather than as DNS. It refused our own token. means the answer carrying it was 400 or above: usually the check reads a different header or a different value, sometimes the address is not the MCP endpoint at all. It answered a request that carried no token instead of refusing it. is the one that matters: until an untokened request is answered 401 or 403, anybody who learns the URL can call your server directly and nobody is billed for it.

Checking a new address and failing changes nothing about a listing that is already forwarding somewhere: the endpoint which passed stays in place and keeps taking calls. That is what makes the Connection row safe to try an address from.

There is no request signature, no timestamp and no nonce, so there is no replay protection on our side: a captured request replayed to your endpoint passes the token check. There is no client certificate and no sidecar either — the token is the whole of what we offer today.

POST /mcp HTTP/1.1 host: mcp.your-domain.dev user-agent: mcprush-verify/1.0 (+https://mcprush.com) accept: application/json, text/event-stream content-type: application/json content-length: 73 x-mcprush-listing: your-listing x-mcprush-install: ins_3f7c1ad9e42b6805cf x-mcprush-token: mgw_… {"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"search"}}

What we read. raw.githubusercontent.com, at HEAD of the default branch — or api.github.com through the GitHub App, if you marked the repository private. A path you typed into the manifest field is tried first, then server.json, .well-known/mcp/server.json, mcp.json, .mcp.json, server-card.json and package.json. The first one that parses is the one used, and if none does, the fields on the listing are yours to write. The read itself is written to your account’s audit log.

If that manifest declares a hosted remote over https, we call it — initialize and tools/list — and the tools on the listing are its own rather than the manifest’s.

What the source cannot say. A repository is source. Tools exist while a server is running, so unless the manifest named a running endpoint there is nothing to read a tool list from. No branch and no commit are honoured either: a ref in the address is ignored, and the read is always at HEAD.

What reaches the page. The name, the description, the tags and the tools, as far as the manifest carried them. All of it is a first draft: every field is yours to correct before the listing is submitted, and nothing is read from the repository again afterwards.

Where the endpoint comes from. The repository is where this listing’s words came from; it is not what buyers get. Run the server somewhere with a public HTTPS address and speak Streamable HTTP on it — we POST JSON-RPC to one URL, and a server that only speaks stdio needs an HTTP transport in front of it, which the official MCP SDKs ship in every language they cover. Run that transport statelessly: no header of ours or yours crosses the gateway, so a session id cannot be handed out and quoted back. Then refuse any request that does not carry our token. That last part is a string comparison, and it is the one thing we ask of your code.

The other way this could go, and why it is not offered. A server listing recorded as running on the buyer’s own machine is refused by the gateway — runs on your own machine — there is nothing here to forward tomcprush add will not write a config entry for one, and submitting one is refused. Every install command on a listing page is our address, so a repository listed here is a repository with one running instance behind it.

What the gateway then does to a call — the headers it carries, what happens to your answer, the timeouts and the circuit — is the same for every source and is written out in full under A server you already host.

if (req.headers["x-mcprush-token"] !== process.env.MCPRUSH_TOKEN) { return res.status(401).json({ jsonrpc: "2.0", id: req.body?.id ?? null, error: { code: -32001, message: "This endpoint answers mcprush only." } }); }

What we read. registry.npmjs.org/<package>/latest — the name, the description, the keywords, the version, and the mcpName if the record carries one. Then the repository that record names, read the way a repository is read: the same manifest candidates in the same order, and the call to a hosted remote if the manifest declares one. Two things do not carry over from the repository source — the manifest path field is not used on this chain, and the read is public, so a private repository behind an npm package cannot be followed. Only GitHub is followed at all; a repository anywhere else is not read, and the fields stay as npm had them. The read is written to your account’s audit log.

What the source cannot say. The tarball is never downloaded and nothing in it is executed, so the bin entry, the transport it starts with and its tools are all invisible from here. A package that talks over stdio cannot be reached by us at all: what we do is POST to one HTTPS URL on the public internet.

Environment variables found in a manifest are shown to you and nothing more. They are not saved with the listing and buyers never set them — on a listing we forward to, they belong on your own instance.

What reaches the page. The name, the description, the tags, the version and the tools, as far as npm and the repository behind it carried them. All of it is a first draft: every field is yours to correct before the listing is submitted.

Where the endpoint comes from. Keep publishing the package — it stays where it is and this listing keeps pointing at it. What changes is that buyers do not run it: they call our address, and we call yours. Put the MCP SDK’s Streamable HTTP transport in front of the same server on a public HTTPS address and run it stateless, since no header crosses the gateway in either direction; keep the stdio entry point exactly as it is for anyone installing from npm directly, and give that instance the env it needs. Then refuse any request that does not carry our token.

The other way this could go, and why it is not offered. A server listing recorded as running on the buyer’s own machine is refused by the gateway — runs on your own machine — there is nothing here to forward tomcprush add will not write a config entry for one, and submitting one is refused. So an npm package listed here is a package with one running instance behind it.

What the gateway then does to a call — the headers it carries, what happens to your answer, the timeouts and the circuit — is the same for every source and is written out in full under A server you already host.

if (req.headers["x-mcprush-token"] !== process.env.MCPRUSH_TOKEN) { return res.status(401).json({ jsonrpc: "2.0", id: req.body?.id ?? null, error: { code: -32001, message: "This endpoint answers mcprush only." } }); }

What we read. pypi.org/pypi/<project>/json — the name, the summary, the keywords and the version of the latest release. Then a repository, if the record names one: the first project link whose label mentions source, repo, github or code, and the home page if none of them do. It is read the way a repository is read, with the same manifest candidates and the same call to a hosted remote — but on this chain the manifest path field is not used, and the read is public, so a private repository behind a PyPI project cannot be followed. Only GitHub is followed; a project whose links point anywhere else is not read further. The read is written to your account’s audit log.

What the source cannot say. The wheel is never downloaded and nothing in it is imported, so the console script, the transport it starts with and its tools are all invisible from here. A project that talks over stdio cannot be reached by us at all: what we do is POST to one HTTPS URL on the public internet.

Environment variables found in a manifest are shown to you and nothing more. They are not saved with the listing and buyers never set them — on a listing we forward to, they belong on your own instance.

What reaches the page. The name, the summary, the tags, the version and the tools, as far as PyPI and the repository behind it carried them. All of it is a first draft: every field is yours to correct before the listing is submitted.

Where the endpoint comes from. Keep publishing the project — it stays where it is and this listing keeps pointing at it. The Python MCP SDK ships a Streamable HTTP app, and mounting it in front of the same server is a few lines; run it stateless, since no header crosses the gateway in either direction, and leave the console script exactly as it is for anyone installing from PyPI directly. Give that instance the env it needs, and refuse any request that does not carry our token.

The other way this could go, and why it is not offered. A server listing recorded as running on the buyer’s own machine is refused by the gateway — runs on your own machine — there is nothing here to forward tomcprush add will not write a config entry for one, and submitting one is refused. So a PyPI project listed here is a project with one running instance behind it.

What the gateway then does to a call — the headers it carries, what happens to your answer, the timeouts and the circuit — is the same for every source and is written out in full under A server you already host.

if request.headers.get("x-mcprush-token") != os.environ["MCPRUSH_TOKEN"]: return JSONResponse(status_code=401, content={"jsonrpc": "2.0", "id": None, "error": {"code": -32001, "message": "This endpoint answers mcprush only."}})

What we read. registry.modelcontextprotocol.io, searched for the last segment of the image name — the registry host and the namespace are not part of the search, and neither is the tag. A record counts as yours only if it declares this image among its packages; if one does, its server.json fills the fields on the listing. If that record declares a hosted remote over https, we call it — initialize and tools/list — and the tools are its own. The read is written to your account’s audit log.

What the source cannot say. The image is not pulled, the registry it lives in is not contacted, and no label or entrypoint is read: reading an image means running it, and this marketplace does not run publishers’ code. Pinning a tag changes nothing about what we read, for the same reason. An image that is not published to the MCP registry cannot be read at all — the fields on the listing are then yours to write, and the tools yours to enter.

What reaches the page. Whatever the MCP registry record carried, and nothing else. All of it is a first draft: every field is yours to correct before the listing is submitted.

Where the endpoint comes from. Run the container on your own host with the token in its environment — buyers never pull it, they call our address and we call yours. The process inside speaks plain HTTP on a port, so something in front of it has to hold a certificate for the exact hostname in the endpoint URL. Serve it stateless: no header crosses the gateway in either direction, so a session id handed out by the transport can never come back. There is no sidecar to put in front: we publish no image and no proxy of our own, and the token check belongs inside the process.

The other way this could go, and why it is not offered. A server listing recorded as running on the buyer’s own machine is refused by the gateway — runs on your own machine — there is nothing here to forward tomcprush add will not write a config entry for one, and submitting one is refused. So an image listed here is an image with one running instance behind it.

What the gateway then does to a call — the headers it carries, what happens to your answer, the timeouts and the circuit — is the same for every source and is written out in full under A server you already host.

docker run -d --restart=always \ -e MCPRUSH_TOKEN=$MCPRUSH_TOKEN \ -p 127.0.0.1:8080:8080 ghcr.io/org/image:tag # then your own TLS terminator in front of :8080, with a certificate for the # hostname in the endpoint URL. The process inside reads the x-mcprush-token # header and refuses anything without it.
The gateway API

Calling what you installed, with no client in the way.

4

Keys are minted in your dashboard, one per environment, and each carries its own scope. Revoking one kills every install that was using it, immediately. The cap is not one of a key's properties: there is a single spend ceiling for the whole account, which is why mcprush budget takes no listing name and no key name.

The key is what goes into a client config. mcprush add writes Authorization: Bearer <your key> straight into the file the client reads, and writes that file readable only by you. Nothing is kept on your behalf as a handle — the gateway checks the key itself on every call, which is what makes revoking one a single action. VS Code is the one exception, because its config lives in the repository: there the header is written as ${input:mcprush-key} and the value stays in the editor's secret store.

curl -sS https://mcprush.com/gw/key-value-mcp/mcp \ -H "Authorization: Bearer $MCPRUSH_KEY"

Any runtime that speaks MCP over Streamable HTTP can point straight at this URL. Metering, the egress allowlist and your budget cap are enforced at the gateway, so a custom agent gets the same guarantees as Claude Code.

Local listings have no endpoint. There is nothing to proxy, so they are installed rather than called — and for the same reason they are only ever free or a one-time licence.

Nothing is executed by the download: a bundle is markdown and reference files. Unpack it, read it, then point your own runtime at the folder.

The key only fetches a skill your account has installed.

Pin the version in production. A skill is a prompt, and a prompt changing under you changes behaviour without changing a line of your code.

curl -sS https://mcprush.com/api/skills/activecampaign-automation-via-rube-mcp/bundle.tar.gz \ -H "Authorization: Bearer $MCPRUSH_KEY" | tar -xz

The marketplace runs one MCP endpoint that is provably correct: it answers initialize, tools/list and one echo tool, and it refuses anything without a token. Point a draft listing at it and the whole path — verify, install, call, log — works before you have written a line of your own.

It is also what our own conformance check runs against, which is why it is the one endpoint on this marketplace whose behaviour is guaranteed.

curl -sS https://mcprush.com/gw/echo \ -H "x-mcprush-token: anything" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
Security and trust

What is checked, what a grade means, and what none of it can promise.

5

Publishing triggers a static scan of the source and the manifest, a capability-flow analysis of what each tool can reach, a secret scan, and a diff of every tool description against the previous release. Anything unresolved goes to a human before the listing can be marked verified.

Every release is re-scanned. A listing that was clean a year ago is not evidence about the version you are installing today.

The grade summarises unresolved findings and provenance — whether there is any sign this server is malicious or negligent. It is threat only. Blast radius, what the server could reach if the model driving it were manipulated, is measured on its own axis and never lowers the letter: a capable server is not a suspect one, so broad write access is subtracted as its own term in the client score rather than held against the grade.

Grades below A are not hidden from the catalogue. Sorting by trust ranks them, and the security tab prints the findings behind the letter.

A server can change what the model believes a tool does without changing a line of code. Every description change is diffed on release, labelled benign or breaking, and pushed to your changes feed.

This is the check that catches an instruction quietly added to a description — the class of problem a package signature cannot see, because the package is signed either way.

Listings declare which tools return attacker-controlled text, and well-behaved ones return it inside a delimited block tagged as untrusted with its origin attached. A server cannot chain an internal call off that content; your agent decides every step.

None of that makes the text safe. Treat any output that came from the open web as data, and keep write-capable tools behind a human step.

Your dashboard totals what every install can touch — write tools, network reach, credentials brokered — so “what could go wrong today” has an answer that fits on one screen.

Org policy can refuse a capability outright rather than relying on each engineer to notice it, and revoking a key removes every install that used it at once.

Reference

Every command this marketplace prints

The full reference is its own page — the same commands, grouped by what you are doing. What follows is the short version. Two dialects and no third: the marketplace CLI, which writes the config of whichever client you name, and your client's own command, which does the same job without Node. The right-hand column says where each line appears elsewhere on this site. The Agent SDK and plain HTTP are not a third dialect — they hold a key and call the gateway instead of writing a config file, and their snippets are in The gateway API.

CommandWhat it doesSeen on
npx mcprush@latest add <server> Installs a server into Claude Code's config, records it against your account and points it at the gateway. Restart the client to pick it up. Home · listing page · docs
npx mcprush@latest add <server> --client <client> The same, writing another client's config: claude, cursor, vscode, zed, windsurf. Claude Code is the default. Listing page
npx mcprush@latest add-list <list> Installs the free servers in one of your saved lists, and names the rest with the reason. Library
npx mcprush@latest stack add <stack> Installs the free members of a curated stack. A paid one is named rather than bought — a card is a browser flow. Home · stacks · stack page
npx mcprush@latest skill add <skill> Writes a skill you have taken into the client's skills folder — Claude Code reads .claude/skills/<skill>/ in the project, and --global puts it in your home directory instead. A skill has no endpoint: the files are the whole of it. Skill page · home · docs
npx mcprush@latest budget --max '$900/mo' --alert 80% The ceiling on what this account can spend in a month, and where it warns you. A raise applies to the next call and never backwards. Only here
npx mcprush@latest list What this account has installed, and which of them are paused. Only here
npx mcprush@latest remove <server> Takes it out of the client and off the account. A subscription is cancelled where the invoice is, not here. Only here
npx mcprush@latest skill remove <skill> Deletes that folder again. The account still holds the skill — taking it off the account is done in your dashboard. Only here
npx mcprush@latest login Holds a key from your dashboard. It is checked before it is stored, and kept in ~/.mcprush/config.json. Checkout, after a purchase
npx mcprush@latest whoami Which account the held key belongs to, what it may reach, and what it has called. Only here
claude mcp add --transport http <server> https://mcprush.com/gw/<server>/mcp \ --header "Authorization: Bearer $MCPRUSH_KEY" Claude Code's own command, for a machine without Node. The header is what the gateway checks: without it every call comes back 401. Listing
code --add-mcp '{"name":"<server>","type":"http","url":"https://mcprush.com/gw/<server>/mcp","headers":{"Authorization":"Bearer $MCPRUSH_KEY"}}' VS Code's own command. The address and the header are the same two things `mcprush add` writes for you; here you paste them yourself. Listing
curl -sS https://mcprush.com/api/skills/<skill>/bundle.tar.gz -H "Authorization: Bearer $MCPRUSH_KEY" | tar -xz The same folder without Node: your buyer key, the whole skill as a tar.gz. Nothing is executed — a skill is text. Skill page · docs

Dimmed parts are placeholders you fill in. Flags are the same across the CLI: --client chooses whose config is written, and Claude Code is the default when you leave it out.