How the gateway works
Aggregation, per-user credentials and search-first discovery, and why each exists.
Five ideas cover everything Omniio does. If you read one page before using it, read this one.
One endpoint, many servers#
Your client connects to a single MCP endpoint:
https://mcp.omniio.devEveryone uses that same address. Identity comes from the OAuth flow your client runs against it, not from the URL, so there is no per-account secret to leak in a config file that gets committed.
Behind it, Omniio holds a connection to every server you have enabled and acts as an MCP client to each one. Your agent never talks to an upstream server directly and never holds an upstream credential.
Three tools instead of four hundred#
A client that connects to twelve MCP servers directly receives every tool definition from all twelve, in its context, on every turn. That is the problem the three meta-tools exist to solve.
- search_toolsquery, limit
- Ranked search across every tool on every server you have enabled. Returns one line per match: name, server, description. Nothing upstream is contacted.
- describe_toolname
- One tool's full definition — title, description and JSON input schema — so the agent can fill the arguments in correctly rather than guessing.
- run_toolname, arguments
- Calls the tool. This is the only one of the three that reaches an upstream server, and the only one a policy can hold or refuse.
The three tools has the exact schemas and the annotations each one carries.
Qualified names#
A tool is identified by its server and its own name, joined by a double underscore:
github__get_issueTwo servers can both publish search without ambiguity, and a qualified name is
stable enough to write a policy against. search_tools returns them in this
form, and run_tool expects them in this form.
The catalogue is cached#
Omniio reads each server's tools/list and caches the result for an hour, so
search_tools answers out of the database rather than fanning out to a dozen
upstream servers on every query. A refresh is triggered lazily: the first search
after the cache goes stale kicks one off.
Two consequences worth knowing:
- A server that publishes a new tool may take up to an hour to appear in search. Refreshing the library screen forces it sooner.
- The cache is keyed on the server, not on you. A tool definition is a property of the server, so one copy serves every account — which is why any part of a definition that names the connected account is stripped before it is stored. Data handling covers that rule.
What one call passes through#
run_tool arrives and, in this order:
- The token is checked. An expired or revoked token gets an OAuth challenge, not a tool result.
- The burst ceiling. Calls per minute, per account. A throttled call reaches no upstream server, so it is refused before anything is billed.
- The monthly quota. On a plan with a hard limit, an over-quota call is refused with the limit and its reset time named.
- Policy.
denyrefuses here, without the upstream server being contacted and without its credentials being decrypted.askopens an approval and holds the call.allowcontinues — unless the tool's definition has changed since you allowed it, in which case the allow rule is suspended and the call is treated asask. - The upstream call. Omniio decrypts your credential for that server, calls the tool, and waits.
- The result is scanned for content that reads as an instruction to your agent rather than as data. Findings are prepended as a warning and recorded on the audit row; the upstream's own reply is passed through in full.
- The audit row is written — arguments, result, duration, outcome — and the result goes back to your agent.
Everything is attributable#
Each client that authorizes gets its own OAuth registration, so every audit row names the client that made the call — not just the account. Revoking one client's access leaves every other client connected.


That is the model. Next: Client configuration to get a client talking to it, or Connecting servers for what sits behind it.