MCP Gateway
Overview
MCP (Model Context Protocol) is the standard AI agents use to discover and call external tools. In Apinizer, MCP is a first-class API proxy type — you create an MCP Gateway the same way you'd create a REST, SOAP, or AI Gateway, and configure its behavior on a dedicated routing tab. An MCP Gateway works in one of two directions:
- Expose Tools — publishes your existing API proxy operations as MCP tools that any MCP-compatible client can discover and call
- Passthrough — forwards MCP calls through to an external MCP server, applying tool filtering and output masking along the way
The MCP Gateway menu item takes you to a card-based list of your MCP Gateways, the same layout used for AI Gateways — open a card to reach that proxy's routing, policies, and trace/traffic/analytics tabs.
Proxy Information Fields
Each MCP Gateway's own Overview tab shows the proxy information panel every proxy type has, plus three MCP-specific fields:
| Field | Description |
|---|---|
| Routing Mode | Shows Expose Tools or Passthrough; for Expose Tools it also shows how many tools are published and which source API proxy they come from. |
| MCP Protocol Version | The negotiated protocol version that applies on the next redeploy — see Protocol Versions. |
| Client Authentication | None or Required — shows Required automatically once any request policy is attached to the proxy. |
Coming from an older release? MCP used to be configured on a separate "Inbound Server" screen with its own authentication settings, reachable at a dedicated path. That model has been replaced entirely — see Migrating from the Previous Model. New to the MCP protocol itself? See AI Fundamentals for a primer.
Quick Start
Create a new MCP Gateway the same way you'd create any other API proxy: give it a name and a single client-facing path.
Open the proxy's MCP Routing tab. Select Configure on the Routing Mode panel, pick Expose Tools or Passthrough in the drawer, then select Save. The tab shows the sections for the saved mode.
For Expose Tools, select which of your API proxy operations should be published as tools. For Passthrough, add one or more MCP servers directly on the routing tab (endpoint, transport, and authentication) and, optionally, restrict which of their discovered tools are allowed through.
Attach any of Apinizer's regular authentication policies (API Key, JWT, OAuth2, ...) to the proxy — the same way you would on any other proxy type.
Deploy the proxy. Its client path becomes the single endpoint MCP clients connect to, for both discovery and tool calls.
Open the proxy's Overview tab and use the Client Endpoints section to copy the MCP endpoint plus a ready-to-run snippet for your client — see Client Endpoints (Overview).
The MCP Routing tab has no Save button at the bottom and no "unsaved changes" warning. Every change is written to the server the moment you make it.
- Every section that holds settings — Routing Mode, Tool Output Filter, and Discovery — server.json — is shown read-only, with a Configure button that opens a right-side drawer. Change the setting in the drawer, then select Save; Cancel discards the edit.
- Each drawer — Add / Edit Tool, Add / Edit Server, and Configure for Routing Mode, the Tool Output Filter and
server.json— has its own Save button. It writes the change and closes only after the server accepted it. A rejected change leaves the drawer open with your text intact. - The few controls still shown directly on the tab are saved as soon as they change: forwarding the client token, the allowed-tool-names list, the Generate automatically / Custom selection in Discovery — server.json, and deleting a row.
- Deleting an exposed tool or a backend server row asks for confirmation first. Select Delete to remove the row — the removal is saved immediately; Cancel leaves everything as it was.
- The tab's controls are disabled for the moment a save is in flight.
- If a save is rejected, the tab returns to the last saved state. What you see is always what the server has.
- A saved change still needs a deploy to reach the gateway.
Creating One from an Existing API Proxy
If you already run a REST or SOAP API Proxy, you don't have to enter its endpoints as tool rows one by one: the Convert to MCP Gateway action generates a ready MCP Gateway next to that proxy.
The action is available in two places:
- the row menu in the API Proxy list
- the toolbar on the proxy's detail screen
It appears only for REST and SOAP proxies; MCP, A2A, AI, gRPC and WebSocket proxies don't offer it. It also requires the AI Gateway license module — without it the generated proxy would be unreachable afterward, since MCP Gateways live under the AI Gateway menu group.
Despite the name, converting replaces nothing: the source proxy does not change — its type, routing, policies, credentials, access lists and deploy state all stay as they were, and it keeps serving traffic exactly as before. The generated MCP Gateway runs in Expose Tools mode and loops every tool call back to the source proxy, so all of that proxy's policies still apply to MCP calls.
Before anything is saved, the dialog lists the tools that will be created: tool name, method, endpoint
and where the input schema comes from. The name and relative-path fields are pre-filled with the
suggested values (<source name> MCP and <source relative path>-mcp); you can change both.
How the tools are generated:
- Only active endpoints become tools. A deactivated endpoint is skipped.
- The tool name is derived from the specification's
operationId, then its summary, then<METHOD>_<path>; it is then normalized to the MCP naming convention and made unique within the catalog (_2,_3… suffixes). - For a REST source no schema is stored — the gateway derives an operation-specific schema from the
source proxy's specification on every
tools/listcall. Those rows are marked Automatic in the preview. - For a SOAP source a schema is stored, carrying a single
bodystring argument, because automatic WSDL-to-MCP schema mapping is lossy. Those rows are marked From WSDL; you can refine the schema later on the MCP Routing tab. - If the source proxy has no active endpoint, the conversion is rejected.
- If the target name or relative path is already taken, the conversion is rejected — change it in the dialog and try again.
The generated MCP Gateway is created undeployed. Review the tool list, then deploy it to an environment the same way you would any other proxy.
To do the same from a CI/CD pipeline, use the Create MCP Gateway from an API Proxy endpoint.
Modes
Expose Tools
Publishes selected operations from your existing API proxies as MCP tools. Each exposed tool maps to one API proxy operation, can override the tool's auto-generated input schema, and can optionally require a specific scope — enforced only when the caller authenticated through OAuth2 and ignored for every other authentication type. A caller missing the required scope is denied that particular tool while the rest of the catalog behaves normally.
How a Tool Name Is Published
The Tool Name you type on a row is normalized to the MCP naming convention before it appears in tools/list: lowercased, every character other than a letter or digit replaced with an underscore, repeated underscores collapsed, and the result truncated to 64 characters. For example Check Availability becomes check_availability and hotelDetails becomes hoteldetails. If two rows normalize to the same value, the second gets a _2 suffix and the third _3, so no tool ever disappears from the catalog.
Leave the field blank and the name is derived from the source proxy's technical id — fill it in to get a readable one.
You edit the name in the drawer that opens from the right of the row. The drawer's Apply to List button writes the change to the table only — nothing reaches the server at that step. To persist it you must press the Save button at the bottom of the tab; while unsaved changes exist a warning sits at the top of the tab, and it clears only once the save succeeds. The order is: Apply to List → Save → deploy. Skip the middle step and the table shows the new name while the gateway keeps serving the old one.
The gateway always serves the last deployed configuration. Changing a tool name and saving does not by itself change the tools/list response; the new name reaches clients only after you redeploy the proxy to that environment. When saved-but-undeployed changes exist, a warning banner appears at the top of the MCP Routing tab, and the deploy button at the top of the page switches to Redeploy so both indicators agree.
Passthrough mode does not apply this normalization — there the names come from the backend MCP server and Apinizer relays them verbatim. So a tools/list response containing a name with uppercase letters (hotelDetails, say) did not come from your Expose Tools configuration: it came either from a Passthrough configuration or from a deployment that has not been refreshed yet.
Passthrough
Forwards MCP calls to one or more external MCP servers, defined directly on the proxy's MCP Routing tab — there's nothing to create or reference elsewhere first.
Once a server's endpoint is saved, a Discover / Reparse Tools action fetches its tool catalog and pins it as the approved baseline.
Fetch tools in the server drawer lists what the server offers right now, without saving the row — see Checking a Server Before You Save It.
Leave the allow-list empty to pass every discovered tool through, or select specific tools to restrict what's reachable through this proxy.
Each server is probed periodically and reports Healthy, Down, or Unknown status with its last response time.
Discovery and tool calls can't reach cloud-metadata endpoints, loopback, or link-local addresses — see Security and Governance.
Backend Failure Responses
When the MCP server fails during a Passthrough call, the gateway returns a JSON-RPC error with code -32603. The error.message field keeps the error message defined for this error type, ERR-262 (default: Tool execution failed.). The error.data field tells the client why the call failed:
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32603,
"message": "Tool execution failed.",
"data": { "reason": "BACKEND_AUTH_FAILED", "phase": "discovery", "httpStatus": 401 }
}
}
phaseisdiscoverywhen the gateway could not get the tool list from the server. It iscallwhen the tool call itself failed.httpStatusappears only when the server sent an HTTP error status.backendErrorCodeappears only when the server sent a JSON-RPC error. It holds the server's error code.
reason | Meaning | What to check |
|---|---|---|
CONNECTION_CONFIGURATION_ERROR | The connection definition blocks the call. | Egress protection (private address), mTLS key store, catalog drift block. |
BACKEND_UNREACHABLE | The worker cannot reach the server. | Server address, DNS, firewall. Do not use localhost — it points to the worker pod. |
BACKEND_TIMEOUT | The server did not answer in time. | Server load and network latency. |
BACKEND_TLS_ERROR | The TLS handshake failed. | Server certificate and trust store. |
BACKEND_AUTH_FAILED | The server or its OAuth2 token endpoint rejected the credential. | Authentication settings of the server row. If token passthrough is on, the client token. |
BACKEND_HTTP_ERROR | The server sent another HTTP error status. | httpStatus and the server logs. |
BACKEND_TOOL_ERROR | The server sent a JSON-RPC error. The tool itself failed. | backendErrorCode and the server logs. |
BACKEND_PROTOCOL_ERROR | The response is not a valid MCP response, or the protocol version does not match. | The MCP Protocol Version of the server row. Make sure the address is an MCP endpoint. |
BACKEND_CONNECTION_ERROR | Another transport error occurred. | Worker log. |
The response never contains the server address, the exception text, or the server's error body. The worker log keeps the full detail in a MCP PASSTHROUGH warning line.
Both modes' tool results pass through the same output filtering used elsewhere in the gateway: a deny-pattern match blocks the whole response (not just the matched portion), and PII masking covers the platform's built-in personal-data types.
MCP Servers Are Configured Inline
There's no standalone "MCP connection" screen to visit first — an MCP server is defined at the exact place it's used:
- On a Passthrough proxy's MCP Routing tab, where it can be one of several aggregated servers (see Multi-Target Aggregation).
- Inside the MCP Call (LLM) / MCP Call (Fixed) policy, as a single server a model calls as part of any AI Gateway's own tool-call loop — no MCP Gateway required for that path at all.
Each definition carries its own endpoint, transport, authentication, discovered-tool catalog, health status, and approved-catalog fingerprint — nothing is shared by reference between proxies or policies, so there's no separate object to look up, and no cross-proxy blast radius when one server's credentials change.
Checking a Server Before You Save It
The Add / Edit Server drawer has a Fetch tools button next to the save row. It asks the server for its tool list there and then, using exactly the address, credential and mTLS settings currently typed in the drawer, and lists what came back — tool name and description — with a count badge.
- Nothing is saved by this. In Passthrough mode the gateway resolves the backends' catalogs at request time anyway, so the list is a confidence check ("the address and the credential work, and this is what the server offers"), not a catalog the proxy then depends on.
- When you're editing a server that's already saved, you don't have to retype its token: leave the masked field untouched and Apinizer uses the stored credential for the check.
- The button stays disabled until an endpoint is entered. If the call fails, the drawer keeps your text and states the reason the server gave — an HTTP status (
MCP server returned HTTP 404), a refused connection, a blocked egress target, or a reply that is not an MCPtools/listresult. Fix the address, the credential or the network path and select it again. - The check runs from the Manager, not from a gateway worker, so the address has to be reachable from there. Loopback addresses (
127.0.0.1,localhost), link-local and cloud-metadata addresses are refused even with Allow private-network egress on; that switch only opens RFC1918 and in-cluster targets. An MCP server that is only reachable on your own machine cannot be checked this way. - A common mistake is entering the address of a non-MCP proxy (an AI Gateway proxy, for example). Such an address answers, but not with an MCP handshake, so the check fails with a protocol error rather than a connection error.
The Backend MCP Server List
The Backend MCP Server section lists every server on its own card: the name, the endpoint, and badges for the protocol version, the authentication scheme, private-network egress and the capability cache. Configure opens that server's drawer; the bin icon removes it.
Under each card sits that server's own tool panel. Select Fetch tools on the card and Apinizer asks that one server — with its saved address and credential, so there is no token to retype — what it offers right now, then lists every tool it answered with, name and description.
- The list is never saved. It is live discovery, exactly like the drawer's check: the gateway asks the backend again on every request, so what you see is the answer of the moment, not a catalog the proxy keeps.
- Every tool carries an allowed or blocked badge, read from the Allowed Tool Names list in the Client Access & Forwarding section above the server list. An empty allow-list means every discovered tool is allowed. A tool the allow-list keeps out stays visible and is badged blocked on purpose — otherwise you could not tell a tool missing from the server apart from a tool merely missing from your allow-list.
- A caller-specific identity rule (see Security and Governance) can still hide an allowed tool from an individual caller. The badge reports the proxy-wide decision, not a per-caller one.
- When the server can't be reached, the panel says so and prints the reason the server gave — an HTTP status, a refused connection, a blocked egress target — so you can correct the address or the credential without leaving the page.
Routing Mode Panel
The Routing Mode panel at the top of the MCP Routing tab is a read-only summary: the active mode's card is highlighted, the other is dimmed, and the advertised protocol version is shown next to the panel title.
Select Configure to change either of them in a right-side drawer — pick the mode, pick the protocol version, then select Save; Cancel discards the edit. The mode decides which sections the tab shows (Exposed Tools for Expose Tools, Backend MCP Server for Passthrough), and those sections move only once the save succeeds, so browsing the two descriptions in the drawer never rearranges the page underneath you. Each mode keeps its own settings when you switch, so you can move back and forth without losing what you already entered.
Picking Passthrough in the drawer also shows a Backend MCP Server list right below the two mode cards, because Passthrough is only a valid configuration once at least one backend server exists. Select Add Backend MCP Server to open the server drawer on top of the mode drawer and fill it in there; the server is saved as soon as you select Save in it, and you're returned to the mode drawer with your selection intact. Existing servers are listed with Edit and Delete next to each one. While the list is empty, the mode drawer's own Save stays disabled and a warning explains why — so you can no longer save Passthrough first and meet the "Passthrough needs at least one backend MCP server" refusal afterwards.
Tool Output Filter
The Tool Output Filter panel is read-only in the same way: a badge shows whether the filter is Active or Disabled, a second badge shows whether PII masking is on, and the deny patterns are listed below (a dash means there are none).
Select Configure to open the drawer, where you switch the filter on or off, switch Mask PII on or off, and add or remove deny patterns; Save writes all of it in one step and Cancel discards it. A deny-pattern match blocks the whole response, so a pattern that is too broad also blocks legitimate ones — prefer specific patterns.
Discovery Document
By default, an MCP Gateway's server.json document — described in Protocol Versions and in Client Endpoints below — is generated automatically from the proxy's own settings and its request-authentication policy chain, and stays in sync with them across saves and redeploys.
On the MCP Routing tab's Discovery — server.json panel, switch to Custom to take the document over completely:
- The panel shows the published document read-only. Select Configure to open it in a right-side drawer, edit it there, then select Save — Cancel discards the edit, and Save stays disabled while the text is not valid JSON.
- The drawer is seeded with the document the gateway generates today, so switching over doesn't mean starting from a blank file — you only edit what you actually want to change.
- The drawer's Save writes the document straight to the server and closes only once it was accepted — there is no second Save button to press afterwards. A rejected document leaves the drawer open with your text intact.
- The text must still be valid JSON to save; a syntax error is flagged inline and blocks saving. A document missing a required MCP Registry member (
name,description,version, orremotes) only produces a non-blocking warning — it's your document, and Apinizer won't reject it on your behalf. ${VARIABLE_NAME}environment variables inside the custom text are resolved when the document is served, so the same hand-written text can still carry a differentremotes[].urlper environment.- To give the document back to the gateway, switch the same select button to Generate automatically. That discards the custom text and saves the change immediately; the gateway resumes generating the document from the next deploy on. There is no separate "back to automatic" link.
Because server.json is served completely unauthenticated, whatever you type is exactly what an anonymous caller receives — keep it free of secrets, and keeping it accurate afterward is entirely your own responsibility.
Client Endpoints (Overview)
The Overview tab's Client Endpoints section is where you copy the exact addresses and ready-to-run requests a client needs, scoped to an environment you pick from a selector at the top of the section. Every credential shown here — in the address list, the samples, and the numbered steps below them — is derived from the proxy's own request-authentication policy chain, not a fixed template: the section shows exactly what a caller has to send for the policies you've actually attached, nothing more and nothing assumed.
- MCP Endpoint — the single
POSTJSON-RPC address every MCP client calls forinitialize,tools/list, andtools/call, shown with a copy button. - server.json — the unauthenticated
GETaddress described in Protocol Versions, shown with a copy button, an Open link that loads it directly in a new browser tab, and a badge showing whether the document is Generated or Custom (see Discovery Document). - Token Endpoint — shown only when the attached policy chain actually issues a token (JWT or OAuth2 managed by this gateway): the
POSTaddress to call first to obtain one, with its own copy button. - Legacy SSE Stream — shown only when the proxy's negotiated protocol version is
2024-11-05; theGETaddress for that transport's push channel, with its own copy button. - Ready-to-run snippets — a tabbed set covering
tools/list,tools/call(with a tool picker so you can generate a call for any exposed tool without typing its name or arguments by hand),initialize,server.json, and a Client config tab with drop-in.mcp.jsonand Claude Desktop configuration blocks. Each sample carries whichever credential the attached policy chain actually needs — a Basic policy produces-u "<username>:<password>", an API key policy sends the configured header or query parameter, and a JWT/OAuth2/OIDC policy prefixes the sample with the token request described below.
Below the addresses, whenever the proxy has at least one request-authentication policy attached, an Authentication panel spells out the exact numbered steps for that chain — several policies stacked together (for example mTLS plus Basic) each contribute their own step, because the caller has to satisfy all of them:
- Basic — send the consumer's username and password as HTTP Basic (
-u "<username>:<password>", or anAuthorization: Basic <base64(username:password)>header). - API key — send the key in the configured request header, or as a query-string parameter if that's how the policy is set up.
- JWT / OAuth2 issued by this gateway — first
POSTthe Token Endpoint above (grant_type=client_credentialsorgrant_type=password, depending on the policy), then call the MCP endpoint withAuthorization: Bearer <access_token>. - OIDC — obtain an access token from the external identity provider configured on the policy, then send it the same way as a bearer token; Claude.ai / Claude Desktop "Custom connectors" can drive this automatically when the provider supports OAuth 2.0 Dynamic Client Registration.
- mTLS — present the client certificate on the TLS handshake (
--cert client.crt --key client.key); there's no header to copy.
Three cases can't be fully expressed in a static client configuration file, and the panel warns instead of showing a sample that would not work:
- HTTP Digest needs a challenge/response round-trip that MCP clients such as Claude Desktop and Cursor can't perform — prefer Basic or a token policy for MCP.
- An API key sent as a query parameter is appended to the URL in curl samples, but
.mcp.json/ Claude Desktop configuration has no field for it. - mTLS can't be presented by
mcp-remoteor Claude Desktop — terminate mTLS on a local bridge/proxy, or pair it with a token policy.
If the attached policy has no client-side recipe at all (for example JOSE message-level validation, or SAML), the panel says so directly instead of guessing — check the Policies tab and use a policy an MCP client can actually satisfy.
For example, given an MCP Endpoint of https://api.apinizer.com/apigateway/aigw/petstore-mcp/ protected by an OAuth2 policy (client-credentials grant, managed by this gateway), its server.json is published at https://api.apinizer.com/apigateway/aigw/petstore-mcp/.well-known/mcp/server.json, following the MCP Registry's server.json schema:
{
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
"name": "com.apinizer/aigw-petstore-mcp",
"description": "Apinizer MCP Gateway: Petstore MCP",
"version": "1.0.0",
"remotes": [
{
"type": "streamable-http",
"url": "https://api.apinizer.com/apigateway/aigw/petstore-mcp/",
"headers": [
{
"name": "Authorization",
"description": "Bearer <access_token> obtained from https://api.apinizer.com/apigateway/auth/token",
"isRequired": true,
"isSecret": true
}
]
}
],
"_meta": {
"com.apinizer": {
"protocolVersion": "2025-06-18",
"authRequired": true,
"auth": [
{
"scheme": "bearer",
"tokenEndpoint": "https://api.apinizer.com/apigateway/auth/token",
"grantType": "client_credentials"
}
]
}
}
}
The remotes[] array is what makes the document actionable — it carries the transport, the URL, and the header(s) a caller must actually send, derived from every active policy in the request-authentication chain; the _meta["com.apinizer"] block underneath repeats the same recipe machine-readably as auth (one entry per policy, in chain order), next to the negotiated protocolVersion and a boolean authRequired flag. Nothing about the proxy's internal id, name, or routing mode is published to this unauthenticated endpoint.
The tools/list snippet looks like:
curl -X POST https://api.apinizer.com/apigateway/aigw/petstore-mcp/ \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
Below the snippets, a collapsible Exposed tools table lists every tool the proxy currently publishes — its name, description, source API proxy, and whether it has an input schema — for a quick check before you copy a snippet.
If the source API proxy behind an exposed tool isn't deployed to the environment you've selected, that tool's input schema shows empty and a tools/call against it returns Tool backend proxy unavailable. Deploy the source proxy to the selected environment to resolve it.
Outbound Tool Call
When an AI Gateway's model wants to call a tool (tool_use / function calling), the component that actually forwards that call to an external MCP server is the MCP Call (LLM) / MCP Call (Fixed) policy, attached to the proxy's tool-call step. As with Passthrough, the target server is defined inline, on the policy's own screen:
Attach the MCP Call (LLM) policy to an AI Gateway's tool-call step.
In the MCP Server (Inline) section, enter the endpoint, protocol version, authentication scheme (Bearer / OAuth2 Client Credentials / mTLS / None), and capability-cache settings. It carries a single endpoint — there's no failover/multi-server list here; to call more than one external server, add a separate policy instance for each.
The Discover Tools button connects to the server, confirms the connection works, and fetches its tool catalog — it runs before you save, so there's no separate "test connection" step.
Leave it empty to allow every discovered tool; if no tools have been discovered yet, the call is denied fail-closed. To restrict, pick from the Allowed Tools list.
This server definition is subject to the same SSRF protection as Passthrough mode (see Security and Governance). The policy screen's MCP Connection field is kept for backward compatibility: it's disabled once an inline server is defined, and only one of the two is ever used at runtime.
This policy can also be added and updated through the APIops REST API — see API Reference: Policies.
Protocol Versions
An MCP Gateway negotiates one of three protocol behaviors, set per proxy on the MCP Routing tab (Routing Mode panel → Configure → MCP Protocol Version → Save) and applied on the next redeploy:
| Version | Behavior |
|---|---|
2024-11-05 | Legacy HTTP+SSE transport — a GET to the proxy's SSE sub-path opens a bounded push channel. |
2025-06-18 | Streamable HTTP — the default for new proxies. Responses carry an MCP-Protocol-Version header confirming the negotiated version. |
2026-07-28 | Stateless mode — no initialize handshake is required before calling tools. |
If a connecting client requests a version Apinizer doesn't recognize during initialize, the proxy falls back to the nearest earlier version it does support and logs a warning, rather than rejecting the request outright.
The server.json document is published, unauthenticated, at the proxy's client path plus /.well-known/mcp/server.json, following the MCP Registry's server.json schema — required so a client can see not just that the gateway exists but where to connect and with which transport, before it has credentials to authenticate. By default it's generated from the proxy's own settings and its request-authentication policy chain; you can hand-write it entirely instead — see Discovery Document. It's a best-effort discovery aid: whenever the proxy has any request policy attached, each entry in the document's remotes[] array carries the header(s) that chain actually requires, and the same recipe is repeated machine-readably as _meta["com.apinizer"].auth, next to a boolean _meta["com.apinizer"].authRequired — but the actual scheme enforced is whatever policy chain you've configured (see Authentication). The Overview tab's Client Endpoints section surfaces this same address with a copy button and an Open link, and — for a 2024-11-05 proxy — the legacy SSE address as well, so you don't have to build either address by hand.
Deployment Information
An MCP Gateway's Deployment Information table (see Deployment and Specification Information) carries two MCP-specific columns in addition to the fields every proxy type shows:
| Column | Description |
|---|---|
| server.json | A link to the server.json document published for that environment. |
| Protocol | The MCP protocol version negotiated for that environment's deployment. |
Clicking Show in the Specs column opens a dialog with four read-only tabs for that environment — tools/list, initialize, server.json, and Client config — instead of an OpenAPI document. An MCP Gateway has no REST/SOAP contract to display, so the dialog shows the same catalog, handshake, and server.json document a connecting client would actually see, plus a ready-to-use client configuration block.
Multi-Target Aggregation
A Passthrough proxy isn't limited to a single backend: define up to 10 MCP servers on its MCP Routing tab, behind one client-facing endpoint. Their tool catalogs are merged into a single list; if two servers expose a tool with the same name, the collision is resolved deterministically by prefixing the tool name with its source server (serverName__toolName). If one server is slow or unreachable, the others still respond — a single failing target doesn't take down the whole catalog.
Security and Governance
Authentication
Authentication for an MCP Gateway is whatever policy chain you attach to it — API Key, JWT, OAuth2, Basic, and so on — the same policies every other proxy type uses. There's no separate per-server auth-mode selector anymore; the proxy's own policy chain runs before a request ever reaches MCP handling. The one deliberate exception is server.json itself, which stays reachable without authentication — it only advertises the requirement (through remotes[].headers and _meta["com.apinizer"].auth/authRequired), it never enforces it; enforcement is always the policy chain's job. The exact client-side recipe — which header, which token endpoint, which curl flag — is derived per policy type and spelled out step by step in the Client Endpoints section.
Identity-Based Access Control
Optionally, restrict tool visibility by the caller's credential, role, or organization — in Passthrough mode only; Expose Tools mode never evaluates this list, and instead relies solely on each tool's own required-scope check (see Expose Tools). Each rule allows or denies specific tool names for the principal it matches; once at least one rule is configured, a caller not covered by any rule sees nothing in the Passthrough tool list (default-deny) and can't call any tool either. This is set on the proxy's MCP routing configuration — today through the Management REST API rather than a Manager UI screen:
{
"identityAcl": [
{
"principalType": "ROLE",
"principalName": "partner-readonly",
"allow": ["search", "fetch"],
"deny": []
}
]
}
Argument Constraints
Attach validation rules to a tool's arguments — a required field, a maximum string length, an allowed value list, or a regular expression — matched with dot-notation against the argument object (for example user.email). A malformed rule is skipped rather than blocking all traffic through the tool. Regular-expression rules run under a hard ceiling (a 4096-character input cap and a 200ms evaluation deadline) so a crafted value can't turn a rule into a denial-of-service vector.
{
"argumentConstraints": [
{
"toolName": "search",
"rules": [
{ "argumentPath": "query", "ruleType": "MAX_LENGTH", "value": "500" },
{ "argumentPath": "region", "ruleType": "ENUM", "value": "eu,us,apac" }
]
}
]
}
Call Quotas
Cap how often a tool can be called per minute, hour, or day — scoped per tool, per calling credential, or both combined. A call that would exceed the quota is rejected before it dispatches; an unauthorized or argument-invalid call never consumes a unit.
{
"callQuota": { "scope": "PER_CREDENTIAL", "period": "HOUR", "limit": 1000 }
}
Catalog Drift Protection
Each inline MCP server remembers a fingerprint of its tool catalog captured at the moment it was last approved (an explicit Discover / Reparse). A periodic health check compares the live catalog against that fingerprint; when they diverge, the server either just logs and surfaces the drift (the default) or blocks every tool call through it until an operator re-approves the new catalog. This guards against a backend server silently changing what a tool does, or what it expects, underneath an already-approved integration.
Token Passthrough
By default, Apinizer never forwards a caller's own token to a backend MCP server. The gateway credential on the server row is used instead. In a zero-trust setup, the backend MCP server must authorize the real caller. For this, turn on caller forwarding in Passthrough mode.
Forward client token to backend server (MCP Routing tab → Backend MCP Server → Client Access & Forwarding → Configure):
- When on, the caller's
Authorization: Bearertoken goes to the backend MCP server on every call:initialize,tools/listandtools/call. The gateway credential of the server row is not sent. - Accepted token audiences is optional. When filled, only a JWT whose
audclaim matches an entry is forwarded. We recommend it: it makes sure a token issued for this MCP server reaches only this MCP server (RFC 8707 resource indicators). - When no token can be forwarded (the caller sent none, or the audience did not match), the gateway credential of the server row is used. The call is never sent without authentication.
Forwarded client headers (MCP Routing tab → Backend MCP Server → Client Access & Forwarding → Configure):
- The gateway copies the listed headers from the client request to the backend MCP server on every call, for example
app-identifierorx-tenant-id. Names are case-insensitive. - A header the client does not send is not forwarded. Header forwarding works with or without token forwarding.
- These headers cannot be forwarded:
Authorization,Proxy-Authorization,Host,Connection,Keep-Alive,Transfer-Encoding,TE,Trailer,Upgrade,Content-Length,Content-Type,Content-Encoding,Accept,Accept-Encoding,Mcp-Session-Id,MCP-Protocol-Version. Use token forwarding for the caller token. The list can hold at most 20 headers. - A header value with a line break, or longer than 8 KB, is not forwarded.
Required headers. Select Required on a header row to make the header mandatory:
- The gateway checks every JSON-RPC request in Passthrough mode:
initialize,tools/list,tools/calland other methods. The discovery document and the SSE endpoint are not checked. - When a required header is missing, the gateway does not call the backend MCP server. It returns HTTP 400 with
Content-Type: application/json. A blank header value counts as missing. When more than one required header is missing, the first one in the table is reported. - The traffic log shows the request as Blocked.
- The check works with or without token forwarding. A present header is still forwarded.
The Error message column shows Default or Custom. Select the edit icon to change the response body. The body must be valid JSON. Reset to default restores the default body:
{"jsonrpc":"2.0","id":{{jsonRpcId}},"error":{"code":-32600,"message":"Missing required header: {{headerName}}"}}
You can use two placeholders in the body:
{{headerName}}— the header name as configured.{{jsonRpcId}}— theidof the JSON-RPC request, written as JSON. A number stays a number, a string gets quotes, and a missingidbecomesnull. Do not put this placeholder in quotes.
With forwarding on, the tool list cache is kept per caller. One caller's tool list is never returned to another caller.
Fetch tools on the server row uses the gateway credential of the row. When client token forwarding is on and the row has no credential (Authentication Scheme = None), the row shows Client token required instead of fetching: the tool list appears when a client calls the gateway with its own token. Test such a server with a real client call.
Guardrail Policies
Five of Apinizer's Advanced Guardrails — Personal Data Masking, Prompt Protection, Data-Loss Protection (DLP), Off-Topic Protection, and Retry-Storm (Loop) Protection — can be attached to an MCP Gateway the same way as on any other proxy type. They run through the proxy's regular policy chain and scan the JSON-RPC tool-call arguments (params.arguments) on the request side and the tool result content on the response side. This is a separate mechanism from the built-in output filtering described above (deny-pattern matching and PII masking on tool results) — a guardrail policy is configured, scoped, and reported on independently. Two of these five — Prompt Protection and Off-Topic Protection — can additionally call out to an external LLM judge for a second opinion, the same way they do on an AI Gateway. The remaining AI policy types (Semantic Cache, Token Quotas, Prompt Decorator, Prompt Templates, RAG Injection, Context-Integrity Protection) depend on an actual LLM invocation and are rejected if you try to attach them to an MCP Gateway; see Advanced Guardrails: MCP and A2A Gateways.
Egress Protection
Outbound MCP calls — discovery, tool calls, and health probes — can never reach cloud-metadata endpoints, loopback, link-local, multicast, or carrier-grade-NAT addresses, regardless of configuration. A server can optionally be allowed to reach other private-network ranges (for example an in-cluster MCP server), but that opt-in never lifts the addresses above.
This same protection is shared with A2A Gateway outbound calls.
Environment Variables in Configuration
Most of the text you type into an MCP Gateway's configuration accepts an environment variable reference in ${VARIABLE_NAME} form, resolved per environment when the proxy runs. That lets one configuration move from test to production without editing it — a tool that is named differently in each environment, an allowed-audience value, or a per-environment scope name is written once as a variable.
Fields that accept a variable:
| Where | Fields |
|---|---|
| Outbound tool call | Tool name, arguments template |
| Access rules | Allowed tool list, identity rule credential/role/organization name, allow and deny lists |
| Argument constraints | Argument path and rule value |
| Exposed tools | Required scopes |
| Token passthrough | Accepted audience list |
The arguments template additionally accepts #{...} context variables, which are evaluated per request after the environment variables have been substituted.
Resolution is fail-closed. If a variable is not defined in the environment the proxy is running in, the text stays as written — and because no real tool, scope, or audience is literally named ${VARIABLE_NAME}, the call is denied rather than falling through to an unintended match. A caller cannot send the literal text ${VARIABLE_NAME} as a tool name to slip past an access rule either; such a request is rejected outright.
Tool detection patterns and output deny-patterns do not accept variables. Those are regular expressions, and Apinizer probes each one at save time for patterns that could stall the gateway. A pattern arriving from an environment variable would reach the engine unprobed, so the check that protects you would be the thing disabled. The same reasoning applies to a manually overridden tool input schema, which is validated when you save it.
Logging and Analytics
Every MCP call is recorded in your traffic logs with the request's correlation ID, so a single agent turn can be followed end to end; an inbound gateway request additionally records the tool name and JSON-RPC id. Full per-call detail for an outbound tool call — which server it resolved to, every tool invoked in a batch, and each call's duration and outcome — is captured in the request's trace execution detail rather than as flat log columns, so a multi-turn agentic exchange doesn't collapse into "whatever the last turn did." These calls also appear in trace groups alongside the rest of a request's chain, and roll up into your usage reports.
An MCP Gateway's own detail page carries this same trace/traffic/analytics view under its Trace, Traffic, and Analytics tabs — so you don't have to leave the proxy to inspect its recent calls or trends.
On the Traffic tab — and the general API Traffic list — selecting Detailed View for an MCP call opens the same right-side drawer used for AI Gateway calls, instead of a separate dialog. The drawer's last tab is named for whatever the call actually carries: when the record includes AI usage data, that tab appears as LLM Insights; otherwise it takes the name MCP Details (or A2A Details for A2A traffic) and shows the tool name, the JSON-RPC id, and the outbound tool names/call count recorded for that request. If a call carries neither AI usage data nor any of these audit fields — a discovery request, for example — this last tab doesn't appear at all. REST, SOAP, and every other proxy type keep opening their call detail in the original dialog.
An inbound MCP tools/call row now carries the address it was routed to in the Routing column — the source API Proxy path reached through the gateway in Expose Tools mode, or the MCP server host:port in Passthrough mode — tagged with a tool call badge in place of the generic direct badge. Every JSON-RPC refusal the gateway returns for a call (HTTP 200 with a JSON-RPC error body, as the MCP spec requires) is also classified for the traffic list: client/policy refusals — an unknown tool, an insufficient scope, a call quota, an argument-rule violation, or an output-filter block — are recorded as Blocked, while gateway/backend faults — an unavailable tool backend proxy, a failed tool execution, or a session limit — are recorded as Error, with the error type kept alongside. The row's Routing badge reads refused at gateway or gateway error to match. This means a refused call shows up under the traffic list's Blocked/Error filters and in analytics like any other refused request, even though the HTTP status code returned to the client stays 200. See Routing Status Badges for the full badge list.
The same rule now extends to every MCP call the gateway answers itself, not just tools/call: an initialize, tools/list, or ping handled in Expose Tools mode shows the proxy's own MCP endpoint URL in the Routing column instead of an empty address. In Passthrough mode, tools/list is a real call to the backend MCP server — live discovery, not a cached list — so its row shows the target MCP server's endpoint URL (with any query string and embedded credentials stripped before logging), the same way a Passthrough tools/call does.
Outbound Tool Names: an Audit Trail Independent of Trace
Which outbound tool(s) a request tried to reach is also recorded on the main traffic log row itself, regardless of whether trace is on or off — unlike the rich per-call detail above, this record never disappears just because trace is disabled (the default). Every outbound tool call dispatched during the request is tracked through two fields:
- A deduplicated name list — repeated tool names within the same request are kept once, in first-seen order, comma-separated; capped at 256 characters, and a name is never cut mid-string — a name that would no longer fit is dropped whole and the truncation is marked with
,…(never a silent cut). - A raw call counter — the total number of outbound tool calls dispatched during the request, duplicates included; this stays accurate even when the name list above had to be capped.
This audit trail also includes tool calls that a guard or a quota rejected — its purpose is to answer "which tool was this trying to reach," not just to record the calls that succeeded; a rejected call is at least as interesting from a security standpoint as a successful one.
Elasticsearch and MongoDB traffic-log connectors carry these fields automatically. If your installation uses the SQL/JDBC traffic-log connector, see 2026.09.0 — Log Table Update Scripts.
Migrating from the Previous Model
- The separate MCP "Inbound Server" screen is gone; MCP is now configured directly on an MCP Gateway's MCP Routing tab.
- Per-server authentication mode (None / API Key / OAuth2) no longer exists — authentication is whatever policy chain you attach to the proxy, the same as any other proxy type.
- The old dedicated inbound path is gone; clients must be repointed at the MCP Gateway's own client path.
- Existing inbound server definitions aren't carried forward automatically — recreate your exposed tool list as an MCP Gateway.
- Outbound MCP connections are no longer a standalone, shared resource. The connection screen is gone; an MCP server is now defined inline, directly on a Passthrough proxy's MCP Routing tab or inside the MCP Call (LLM) / MCP Call (Fixed) policy that uses it (see MCP Servers Are Configured Inline). This is auto-migrated on upgrade: any proxy or policy that referenced an outbound MCP connection under the old model gets that connection copied inline automatically — there's nothing to recreate. If the same connection was shared by more than one proxy or policy, each one ends up with its own independent copy (credential included) after the upgrade; keep that in mind when you next rotate the credential. Discovery, health probing, catalog drift protection, and token passthrough all still work the same way; they're just scoped to the server's own definition instead of a shared record.
Export/Import
Like any other proxy type, an MCP Gateway exports and imports through the Export/Import Wizard as an API Proxy. When it exposes another API Proxy's endpoints as tools (Expose Tools mode), that source proxy surfaces as its own dependency, labeled MCP Source API Proxies, and on import is either mapped to its counterpart in the target project or created from the copy carried in the package. See Export Import for the resolution order and the errors that can come back.
Next Steps
Agent-to-agent task delegation, the other side of agentic connectivity
See how the tool-call loop fits into request routing
Reuse a guardrail or masking policy across proxies and tools
Manage the credentials behind MCP Gateway authentication and inline MCP servers