Skip to main content
An agent connects to one URL and sees one catalogue of tools, prompts and resources. Behind it, the gateway fans each listing out to the MCP servers the application is allowed to use, merges the answers, and routes each call to the server that owns it. The agent never learns an upstream address and never holds an upstream credential. That is the whole design. Everything else on this plane is a consequence of putting the gateway, rather than the agent, in charge of three things: which servers are visible, whose account reaches each one, and what is recorded.

Which servers an agent sees

An application’s surface is exactly the MCP servers bound to it, narrowed by its tool restrictions. It is the same for every caller the application admits. To give two audiences different tools, make two applications — the surface is a property of the application, not of who is calling. People using the Employee portal are the exception: their surface is per person, made of what they installed, bounded by Access.

Tool names

Every tool is published as <server>_<tool>. <server> is a short name from the catalog code: the reverse-DNS label and a trailing /mcp are dropped, so com.notion/mcp becomes notion and com.google_cloud/developerknowledge becomes google_cloud_developerknowledge. Notion’s search is notion_search. A tool that already starts with that name and an underscore is not prefixed again, so devexpress_docs_search stays devexpress_docs_search. The prefix is added a second time only when leaving it off would publish two tools of that server under one name. A server configured by hand, with no catalog code, is named s plus eight hex characters. A single instance of a catalog server keeps the plain name. While several instances of the same server are installed — a second Snowflake schema, or a second Employee portal install of the same server — each instance that is served gains six hex characters, so query is snowflake_, those six characters, and _query. An instance that is turned off still counts, so the ones that remain keep that suffix. An instance whose access grant was revoked still counts the same way. When two servers would otherwise publish the same name, only those names gain a four-character hex suffix. Google’s drive_list and Google Drive’s list would both be google_drive_list, so each is published as google_drive_list_ plus four hex characters, and google_search stays google_search. A name longer than 64 bytes keeps the full server prefix, then equal shares of the start and end of the tool name — the odd byte goes to the end — with an eight-character hash of the full published name between them on single underscores. Cuts stay on rune boundaries, so a multibyte character is never split, and an underscore next to the hash is dropped, leaving a single underscore on each side that still has text. The cut can still stop mid-word: Endor Labs publishes query_docs_filesystem_endor_labs_documentation as endorlabs_docs_mcp_query_docs_filesy_dca24876_labs_documentation. A server name too long to leave any of the tool is replaced by an eight-character hash of that name. When a name both collides and is too long for the four-character suffix to fit on the end, that suffix — four hex characters, not eight — sits between the server prefix and the end of the tool name. Only that end is kept, not both ends, as in google_b2a9_ve_list_qqq..._act. Prompts are published as <server>_<prompt> and stay within 64 characters. A prompt that already starts with the server still receives the prefix. Call the name from the current list. tools/list publishes only the current names. For now, tools/call and prompts/get also accept a spelling from before this release when no current name matches and exactly one tool or prompt the application can use has it; a name the application’s toolkit denies is never accepted this way. This fallback will be removed, so update clients to the names in the current list. Any other tool name returns an unknown-tool error (Unknown tool: <name>, JSON-RPC -32602), not a permission denial; an unknown prompt name also returns -32602 only when the application has at least one MCP server. With no servers, prompts/get returns -32600. A tool the application is not allowed to use is refused as not permitted. An application with no MCP servers bound still connects: initialize succeeds, and tools/list returns only the gateway meta-tool trustgate_list_tools. tools/call with any other tool name returns an unknown-tool error (JSON-RPC -32602, Unknown tool). resources/read and prompts/get return -32600.

When an upstream is down

Fail mode on the application decides: open skips the unreachable server and serves the rest, naming the skipped server’s cause if nothing at all was reachable; closed fails the call. Open is the right default for an interactive agent; closed for anything where a partial tool list would be worse than no answer.

Whose account reaches upstream

This is the decision that shapes an MCP application, and it is set per server on the registry entry: An account per caller is how a ticket tracker or a repo host keeps its own permissions and its own audit trail: the gateway vaults one credential per person per server instance, refreshes it, and re-asks for consent only when a grant can no longer be refreshed. Two people installing the same server never share a credential. A call for someone who has not connected yet does not fail opaquely. It returns a consent required error carrying a connect link and a cause — never connected, token expired without refresh, refresh rejected, and so on — so the agent can show the link and the person signs in once. Machine callers authenticating with an API key carry no token of their own. On a shared-account server they use that account like everyone else. On a per-caller server they have to name the person the call is for — see acting for end users — because the key on its own has no account there; the call is refused with a message saying so rather than falling back to someone else’s. For modes that need a caller token they get an error naming the fix rather than a silent no-op.

The gateway’s own tools

Every MCP application with a server bound also lists a few tools the gateway implements itself, marked as plumbing so a model does not mistake them for capabilities:
  • trustgate_list_tools — a server-by-server inventory with each server’s state: ready, needs connect (with the connect tool to call), unavailable, or no tools after restriction.
  • trustgate_connect_<provider> — one per forwarded-auth server still waiting on an account; returns the connect link the agent should show.
  • trustgate_store_* — on the Employee portal endpoint only: search, install and uninstall servers, list the person’s models, ask for a provider’s models, and get a personal key. The inventory is there as well. The connect tools are not: on the portal, installing a server again returns its connect link.

Agents authenticate to the gateway

The MCP plane is an OAuth2 authorization server for the agents connecting to it, with dynamic client registration and PKCE. An agent registers, obtains a token, calls the endpoint, and completes a one-time connection when a downstream server needs per-user authorization. Machine callers use the application key instead. Connect an agent covers both paths — an agent with its own MCP client, and your own code through the TrustGate SDK.

Keeping catalogues fresh

The gateway pushes a tools changed notification when a stream opens and whenever the caller’s surface changes — a server bound or unbound, a restriction edited, an account connected or revoked, an Employee portal install or a grant. Clients that honour it re-list without reconnecting.

The pieces

Servers catalog

Who holds the credential, custom servers, and the full catalogue.

Employee portal

What people install for themselves, and what they see.

Acting for end users

An application whose users connect their own accounts through your product.

APIs as tools

Turn an OpenAPI document into tools without running a server.