An MCP client loads every tool definition from every server it connects to, into every turn. Ten servers with twenty tools each is a few hundred tool definitions in the context window before the conversation starts.
The gateway inverts that. You register your remote MCP endpoints on mcpi.app; your client connects to one server — this one — and sees three tools:
find_tools(request, k?)— describe what you want in words. A ranking model scores every tool in your catalog and returns the few that fit, with their input schemas.call_tool(name, arguments)— run one of them. The upstream's result comes back verbatim.call_tool_approval(name, arguments)— the same call, for the tools you set to need approval and the ones held because they changed. See Tool permissions.
Set it up
- Add your endpoints. Dashboard → Gateway → Add endpoint: a name, a
URL, and how it authenticates. Each endpoint must be a public
https://URL. Saving tests the endpoint straight away; if it cannot be reached the endpoint is still saved, with the error on its row. - Point a client at the gateway. It is this site's own MCP server, so any client that can already reach it works. With Claude Code:
claude mcp add --transport http mcpi https://mcpi.app/mcpOr, for a client configured with JSON:
{"mcpServers":{"mcpi":{"url":"https://mcpi.app/mcp","headers":{"Authorization":"Bearer oat_…"}}}}Authenticate with an API key from Settings (Authorization: Bearer oat_…
or X-API-Key), or let the client run OAuth against this site.
3. Check each endpoint's status line. Under its URL, every endpoint shows
what the last connect found — 23 tools · contract unchanged, or the
breaking / compatible / cosmetic counts when its contract moved — and
Never connected until one succeeds. A failed connect shows its error and
how long it has been failing. Test dials the endpoint again and refreshes
the line.
Authenticating to an upstream
Three cases, and an endpoint uses exactly one of them:
- No auth — for an open server.
- Bearer token or Custom header — paste a token (sent as
Authorization: Bearer …), or a header name (X-API-Key, …) and its value. The value is encrypted with AES-256-GCM before it is stored, never returned to the browser, and never written to a log. - OAuth — for the servers that want a real sign-in. See below.
An endpoint that already carries a pasted header refuses OAuth sign-in; remove the header first.
Signing in with OAuth
When an endpoint answers 401, its row shows needs sign-in. Select
Sign in and the browser goes to the upstream's own authorization page. After
you approve, it returns to the Gateway page and the row shows signed in.
What happens behind that button:
- Discovery starts from the
WWW-Authenticatechallenge the upstream sent, which names its protected-resource metadata (RFC 9728). A server that publishes no metadata is refused rather than probed at guessed paths. - The gateway registers itself as an OAuth client (RFC 7591), or uses its
client metadata document at
/gateway/oauth/client.jsonwhen the server supports that instead. If you supplied credentials of your own (below), those are used and nothing is registered. - Authorization uses PKCE
S256; a server that does not advertise S256 is refused. The issuer that answers is checked against the one discovery found. - The callback lands on
/gateway/oauth/callback—/callbackalso works, for servers that only accept short redirect paths — and exchanges the code.
The sign-in in progress is bound to your account and to the browser that started it, is good for ten minutes, and can be used once. A callback that arrives in a different browser, twice, or late does nothing.
What is stored
Encrypted with AES-256-GCM under the server's ENCRYPTION_KEY: the access
token, the refresh token, and the registered client's id and secret. None of it
is ever sent to the browser or written to a log — the endpoint list only reports
that you are signed in, and which issuer granted it.
Tokens refresh on their own. If the upstream rejects a token that has not expired yet — a revoked grant looks exactly like this — the gateway refreshes once and retries; a second rejection flips the endpoint back to needs sign-in.
Bring your own OAuth client
Some servers do not offer dynamic registration; they hand you a client id (and
sometimes a secret) from a console. Pick OAuth on the add form, fill them in, and
sign-in presents them instead of registering anything. Register
https://mcpi.app/gateway/oauth/callback as the redirect URI — /callback is
accepted too.
The secret is encrypted like every other credential here and never comes back to
the browser; the endpoint list only reports that a client id is set. How the
secret is presented at the token endpoint follows what the server advertises in
token_endpoint_auth_methods_supported: HTTP Basic (client_secret_basic) by
default, client_secret_post when the server advertises post and not basic.
Signing out
Sign out deletes the stored grant and keeps the endpoint, so you can sign in
again later. The delete happens first and always. If the server published a
revocation_endpoint, the gateway then tells it the grant is finished
(RFC 7009) — your refresh token if there is one, otherwise the access token.
That call is a courtesy: a server that is down, slow, or refuses cannot stop you
signing out here, and the failure is only logged.
Tool permissions
Behind the gateway your client sees call_tool, not the upstream tools, so its
own per-tool permission switches collapse into one. The gateway gives the
granularity back. On an endpoint's row, Tool permissions lists the tools its
last connect found, grouped by the upstream's read-only hint, each with one of:
- Always allow — runs through
call_tool. The default. - Needs approval —
find_toolsreturns it with"call_with": "call_tool_approval", the tool that runs it; plaincall_toolrefuses it and names the same tool. In your client, leavecall_toolallowed and setcall_tool_approvalto ask: every tool you marked then prompts, and nothing else does. - Blocked — removed from the catalog.
find_toolsnever returns it and neither proxy tool will call it.
The prompt itself is your client's. The gateway decides which of the two proxy
tools a call has to go through; a client that auto-approves
call_tool_approval will run those tools unprompted. Blocking does not depend
on the client at all.
A policy is stored per endpoint and tool name, so it survives a reconnect and a contract change. A tool the upstream adds later starts as Always allow — in the Default profile. For anything stricter, use a profile.
Changed tools are held
A server can rewrite a tool after you approved it: a new argument, a different description, a tool that was not there last week. Your client would call it on the strength of a definition nobody looked at — and a rewritten description is how a server slips instructions to a model.
So when a connect finds that a tool's definition moved, or that a tool was added, the gateway puts it on hold in every profile until you accept the change:
- A changed tool needs approval —
call_toolrefuses it and says why — andfind_toolskeeps describing it as you last reviewed it. The new description and schema reach no client until you have read them. If the change was breaking, a call written against the reviewed schema can fail upstream; accepting the change is what fixes that. - A new tool is hidden:
find_toolsdoes not return it and neither proxy tool calls it.
A hold only tightens: a blocked tool stays blocked.
The endpoint's status line shows how many tools are held; select the badge to open its Tool permissions, where each held tool shows what moved since you last reviewed it — the field, its breaking / compatible / cosmetic classification, and the text before and after — with Accept beside it, and Accept all at the top. A tool is held on any change, cosmetic ones included: a reworded description breaks no caller, and is exactly the change worth reading.
The first connect is the baseline, so adding an endpoint holds nothing. A tool that changes twice before you look is still compared against what you last accepted, and one that changes back is released on its own. Accept applies to what is on your screen: if the endpoint changed again after the page loaded, the accept is refused and the list reloads.
An endpoint that holds changed tools is only served once its tools have been checked. If the server cannot list its whole contract — a broken prompts or resources list, say — its tools are checked on their own against the last recorded contract. If there is no earlier contract to check against, or the check cannot be recorded, the endpoint is skipped with the error on its row rather than served unchecked.
An endpoint is checked whenever a client connects through it, and on a schedule when nobody does: one that has gone six hours without a connect is dialled in the background, so a change is usually held before an agent reaches for the tool. Paused endpoints and ones waiting for a sign-in are not dialled. When a contract moves you get an email with the breaking / compatible / cosmetic counts and how many tools are held, linking back to the Gateway page.
Holding is on for every endpoint. For a server whose releases you trust — your own, say — switch Hold changed tools off in its Permissions panel, which also releases anything it holds and stops the emails for that endpoint.
Profiles
A profile is its own set of tool permissions over the same endpoints, so two clients can see different tools: an agent that only reads, a desktop client that may do everything.
Add one in the Profiles card and choose what it does with a tool you have not set:
- Blocks tools by default — an allowlist. The profile reaches only the tools you allow, and a tool an upstream adds later stays out.
- Allows tools by default — like the Default profile, with its own exceptions.
To set what a profile reaches, open an endpoint's Tool permissions and choose the profile at the top of the panel. The panel says which API keys use the profile you are editing, and the Profiles card lists the same for each.
A client does not choose its profile per request. It is fixed by the credential:
- API key — pick the profile when you create the key in Settings. The key list shows which profile each key belongs to.
- OAuth — the consent screen asks which profile the client gets when it connects.
Either way the binding lasts for the life of the credential: to move a client to another profile, create a new key or reconnect. A key with no profile uses Default, as every key did before profiles existed. A profile cannot be removed while an active key uses it — revoke the key first — so a restricted key never falls back to Default.
Contract changes come with the answer
Every connect snapshots each endpoint's contract and classifies what moved
against the last one — the same engine the inspector and the directory use.
When find_tools returns a tool from an endpoint whose contract changed, the
result carries a contract_changes note with the breaking / compatible /
cosmetic counts. Your client finds out that a tool's arguments changed before
it calls it, rather than from a failed call.
What it costs
| Free | With a subscription | |
|---|---|---|
find_tools |
100 per day | Unlimited |
call_tool |
500 per day, 60 per minute | 60 per minute |
| Endpoints | 20 | 20 |
find_tools is the call that runs the ranking model, so it is what the plans
price. call_tool is capped only to keep the gateway from being used as an
open proxy. A proxied call may run for 60 seconds and return up to 2 MiB.
Over any limit, the tool returns an error saying so — your client sees a
readable message, not a failure.
The Gateway page shows what routing has actually done. The headline is an
estimate of the context kept out of your client over the last 7 days — the
tool definitions in your catalog that find_tools did not return, summed
over every call. Below it: find_tools and call_tool counts for the last 7
days and all-time, your most-called tools with the endpoint that served them,
and the hit rate — the share of routed call_tools in the last 7 days that
went to a tool the preceding find_tools had returned.
Calls the permissions turned away are listed too: the tool, whether it was blocked, needed approval or was held, and which profile and API key asked. A refusal is otherwise invisible — the client just gets an error — so this is where you see what an agent tried to do.
Limits
- Endpoints must be public HTTPS. Private, loopback, link-local and cloud-metadata addresses are refused when you save an endpoint, and every connection resolves the name through a connector that refuses any answer outside public address space — so a name that changes where it points later does not get a second chance.
- Remote (Streamable HTTP) servers only. A local stdio server has no URL this
site can reach; run
mcpi serveon your own machine for those. - OAuth covers the authorization-code flow with dynamic registration, a client metadata document, or credentials you supply. Incremental scope upgrades are not implemented, and a grant is only revoked upstream if the server publishes a revocation endpoint.
- Two endpoints of yours may not share a name. When two endpoints expose a tool with the same name, the gateway prefixes both with the endpoint name.