Update MCP Routing
Endpoint
PUT /apiops/projects/{projectName}/apiProxies/{apiProxyName}/mcp-routing/
This endpoint returns HTTP 400 for API proxies whose type is not MCP — mcpRouting can only be set on MCP proxies. To create a new MCP proxy in one call, use Create MCP API Proxy — this endpoint only updates the routing config of a proxy that already exists.
Authentication
Requires a Personal API Access Token.
Permission: API_MANAGEMENT × MANAGE, or AI_DEVELOPMENT × MANAGE. Both are accepted on AI, MCP and A2A proxies.
Header
Authorization: Bearer YOUR_TOKEN
Request
Headers
| Header | Value | Required |
|---|---|---|
| Authorization | Bearer {token} | Yes |
| Content-Type | application/json | Yes |
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| projectName | string | Yes | Project name |
| apiProxyName | string | Yes | MCP API Proxy name |
Request Body
The body is the full McpRouting object — it replaces the proxy's current MCP routing configuration.
The routing has no call quota. A callQuota field sent by an older client is ignored and is not stored. Set call limits in a Limit Plan (per identity) or with the Quota or Throttling policy (per proxy).
This endpoint replaces the routing as a whole. A field you do not send is dropped, and it returns to its default. If you apply an old body (such as the examples below) to a proxy that uses the Standard MCP OAuth profile, authProfile, serviceClientName, the downstreamIdentity of each tool, riskPolicy, riskPolicyExemptions and toolRiskOverrides are lost. Read the current configuration first, change the fields you need, and send the whole object back.
Full JSON Body Example (TOOL_EXPOSE)
{
"routingMode": "TOOL_EXPOSE",
"protocolVersion": "2025-06-18",
"authProfile": "STANDARD_MCP_OAUTH",
"serviceClientName": "petstore-service-client",
"serverJsonOverride": "{\"name\":\"com.example/petstore-mcp\",\"description\":\"Petstore MCP server\",\"version\":\"1.0.0\",\"remotes\":[{\"type\":\"streamable-http\",\"url\":\"${GATEWAY_URL}/petstore-mcp/\"}]}",
"exposedTools": [
{
"proxyId": "665f1c2e8b3a4d0012ab34cd",
"toolName": "order_lookup",
"toolDescription": "Looks up an order by id",
"apiMethodHttpMethod": "GET",
"apiMethodPath": "/orders/{id}",
"requiredScopes": ["orders.read"],
"downstreamIdentity": "SERVICE",
"riskTier": "LOW",
"argumentConstraints": []
}
],
"riskPolicy": { "low": "ALLOW", "medium": "REQUIRE_APPROVAL", "high": "DENY" },
"riskPolicyExemptions": [],
"toolRiskOverrides": [],
"identityAcl": [],
"argumentConstraints": [],
"tokenForwardSettings": null
}
Full JSON Body Example (PASSTHROUGH, inline backend server)
{
"routingMode": "PASSTHROUGH",
"backendServers": [
{
"name": "internal-mcp-server",
"endpoint": "https://mcp.example.com/mcp",
"transport": "HTTP_SSE",
"authScheme": "BEARER",
"bearerToken": "raw-token-in-transit-only",
"driftPolicy": "WARN_ONLY"
}
],
"allowedToolNames": ["search", "fetch_document"],
"tokenForwardSettings": {
"enabled": true,
"source": "CLIENT_AUTHORIZATION_HEADER",
"audienceAllowList": ["https://mcp.example.com"],
"stripGatewayCredential": true
},
"forwardedHeaders": [
{
"name": "app-identifier",
"required": true,
"errorMessage": "{\"jsonrpc\":\"2.0\",\"id\":{{jsonRpcId}},\"error\":{\"code\":-32600,\"message\":\"Send the {{headerName}} header\"}}"
},
{ "name": "x-tenant-id", "required": false, "errorMessage": null }
],
"toolOutputFilter": null,
"identityAcl": []
}
Field Reference
| Field | Type | Description |
|---|---|---|
| routingMode | string | TOOL_EXPOSE (expose selected Apinizer proxies as MCP tools) or PASSTHROUGH (proxy one or more external backend MCP servers) |
| authProfile | string | Client authentication profile: LEGACY (default; also the value when the field is not sent) or STANDARD_MCP_OAUTH. See Standard MCP Client Profile |
| serviceClientName | string | Name of the existing API client that the proxy acts as in the Standard profile. Required there when the proxy exposes any tool. It is a reference only |
| protocolVersion | string | MCP protocol version: 2024-11-05, 2025-03-26, 2025-06-18, 2025-11-25 or 2026-07-28 (default). A blank value uses 2026-07-28. See Protocol Versions |
| serverJsonOverride | string | Optional. A hand-written replacement for the generated .well-known/mcp/server.json discovery document — see Discovery Document. Empty/omitted (the default) keeps the auto-generated document. The value is a JSON document encoded as a string; it must be valid JSON, but its schema completeness is not checked here — a document missing name/description/version/remotes saves successfully and only surfaces as a warning on the MCP Gateway console. ${VAR} placeholders inside the string are resolved to environment variables when the document is served, so the same document can be promoted across environments while advertising each one's own host. Once set, the document is served exactly as written — unauthenticated — at {relativePath}/.well-known/mcp/server.json; never put a credential in it |
| exposedTools | array | TOOL_EXPOSE only. Tool definitions — proxyId (target ApiProxy id), toolName, toolDescription, optional overrideSchema, apiMethodHttpMethod/apiMethodPath (pin to a specific method), requiredScopes, downstreamIdentity (SERVICE | DELEGATED; required on the Standard profile, ignored on Legacy), per-tool argumentConstraints, optional riskTier (LOW | MEDIUM | HIGH; empty = the HTTP method default, read only while riskPolicy is set). toolName is normalized before it is published in tools/list (lowercased, any character other than a letter or digit becomes _, repeated underscores collapsed, truncated to 64 chars; colliding names get a _2/_3 suffix). A blank toolName falls back to the normalized proxyId. The published catalog changes only after the proxy is deployed again |
| backendServers | array | PASSTHROUGH only. The only way to configure a backend target — there is no reference-based alternative. Inline MCP server definitions — up to 10. Each entry is a full connection object: name, endpoint, transport (HTTP_SSE), authScheme (BEARER | OAUTH2_CLIENT_CREDENTIALS | OAUTH2_TOKEN_EXCHANGE | MTLS | API_KEY | BASIC | NONE) plus the matching credential fields (bearerToken; credentialRef/tokenEndpoint/clientId/clientSecret/scope for the two OAuth2 schemes; tokenExchangeAudience (optional) for OAUTH2_TOKEN_EXCHANGE; apiKeyHeaderName and apiKey for API_KEY; basicUsername and basicPassword for BASIC; mTLSSettings), trustToolAnnotations (default false; true = the tool annotations of this server lower the risk tier), relayElicitation (default false; true = relay elicitation requests of this server to a legacy client), driftPolicy (WARN_ONLY | FAIL_CLOSED), allowPrivateNetworkEgress. Server-managed fields (id, discoveredTools, toolsParsedAt, healthStatus, lastHealthCheckAt, pinnedCatalogHash, driftDetected) are always read from the server's own persisted state — a client-supplied value for any of them is ignored, never trusted. Multiple entries aggregate behind one endpoint (see Multi-Target Aggregation). PASSTHROUGH mode requires at least one entry |
| riskPolicy | object | Tool risk policy: an action for each tier — low, medium, high — each one ALLOW | REQUIRE_APPROVAL | DENY. An unset tier means ALLOW. null (default) = the policy is off and every call is allowed. See Tool Risk Policy and Approval |
| riskPolicyExemptions | array | Identity rules (same shape as identityAcl entries). For a matching caller, REQUIRE_APPROVAL becomes ALLOW. DENY does not change |
| toolRiskOverrides | array | PASSTHROUGH. Risk tier of a tool by the tool name as tools/list shows it. Each entry: toolName, riskTier (LOW | MEDIUM | HIGH). A tool name can appear once |
| allowedToolNames | array | PASSTHROUGH tool filter — only listed backend tools are exposed |
| toolOutputFilter | object | PASSTHROUGH tool output filtering rules |
| identityAcl | array | Per-principal allow rules (principalType: CREDENTIAL | ROLE | ORGANIZATION). Enforced on the PASSTHROUGH dispatch path |
| argumentConstraints | array | Tool argument constraint rules. In PASSTHROUGH mode each entry names a toolName (as tools/list shows it, case-sensitive) and its rules; in TOOL_EXPOSE mode the rules sit on each exposed tool. A rule has argumentPath (property names separated by dots; no array index or wildcard), ruleType (REGEX | MAX_LENGTH | ENUM | REQUIRED) and value. The gateway skips a saved rule that is not valid |
| tokenForwardSettings | object | PASSTHROUGH. Caller-token forwarding; null = off. enabled (boolean), source (CLIENT_AUTHORIZATION_HEADER), audienceAllowList (optional JWT aud allow-list, ${VAR} supported; empty = any Bearer token), stripGatewayCredential (default true). When enabled, the caller's Bearer token is sent to the backend on every hop — initialize, tools/list and tools/call. When no token can be forwarded, the server row's own credential is used |
| forwardedHeaders | array | PASSTHROUGH. Caller request headers relayed to the backend MCP server on every hop (at most 20). Each entry: name (string, case-insensitive, unique), required (boolean, default false), errorMessage (string, optional). A header the caller does not send is skipped, unless required is true. Independent of tokenForwardSettings. Authorization, Proxy-Authorization, Host, Connection, Keep-Alive, Proxy-Connection, Transfer-Encoding, TE, Trailer, Upgrade, Content-Length, Content-Type, Content-Encoding, Accept, Accept-Encoding, Mcp-Session-Id and MCP-Protocol-Version are rejected with HTTP 400 (error.mcpRouting.forwardedHeaderNotAllowed). More than 20 entries return error.mcpRouting.tooManyForwardedHeaders. A duplicate name returns error.mcpRouting.duplicateForwardedHeader. A non-blank errorMessage that is not valid JSON (placeholders filled with sample values) returns error.mcpRouting.forwardedHeaderErrorMessageInvalidJson |
Behavior
- The configuration is validated (
McpA2aRoutingValidator) before save; invalid mode/field combinations return HTTP 400. - Required forwarded headers: in PASSTHROUGH mode, the gateway checks every JSON-RPC request for each
forwardedHeadersentry withrequired: true. A missing or blank header stops the request before the backend call. The gateway returns HTTP 400 withContent-Type: application/json. The body is the header'serrorMessage, or this default when it is blank:{"jsonrpc":"2.0","id":{{jsonRpcId}},"error":{"code":-32600,"message":"Missing required header: {{headerName}}"}}.{{headerName}}is replaced with the header name.{{jsonRpcId}}is replaced with the requestidas JSON (a number stays a number, a string is quoted, a missing id isnull). The traffic log records the request as blocked. - Tool risk policy:
riskPolicy,riskPolicyExemptions,toolRiskOverridesand theriskTierof an exposed tool are set with this endpoint. Create MCP API Proxy does not carry them. AtoolRiskOverridesentry with a blank or repeatedtoolName, or withoutriskTier, returns HTTP 400 (error.mcpRouting.toolRiskOverrideInvalid). Approving or rejecting a held call is done in the Manager, not through this API. - Updating an existing
backendServersentry: to update rather than replace a server, send back theidfrom a prior read (GET/getApiProxy) unchanged — a matchingidis treated as an update and its server-managed fields (discovered catalog, health, drift) are preserved. An entry whoseidis blank, or doesn't match anything currently persisted, is always treated as brand-new: a fresh id is minted server-side and any client-suppliedidis discarded. - Secret fields on
backendServersentries (bearerToken,clientSecret,apiKey,basicPassword) follow the platform's standard blank-preserves-existing convention: omit the field (or send it unset) to keep the currently stored secret, send a non-blank value to replace it. There is no separate boolean presence flag on this write path — unlike a read, which never returns the secret's plaintext. - Every environment the proxy is currently deployed to is marked redeploy required — redeploy to activate the new configuration.
Response
Success Response (200 OK)
{
"status": "SUCCESS",
"deploymentResult": {
"success": false,
"detailList": []
}
}
The call does not deploy the proxy. Redeploy it to activate the change.
Error Responses
| Status | Condition |
|---|---|
| 400 | Blank apiProxyName, empty body, proxy not found, proxy is not an MCP proxy, backendServers exceeds 10 entries, serverJsonOverride is not valid JSON, or another validation failure |
| 401 | Invalid or missing token |
| 500 | Unexpected server error |
Related Documentation
- Update AI Routing
- Create MCP API Proxy
- Update A2A Routing
- Discover MCP Tools for a Policy
- API Proxies
- MCP Gateway — concepts, inline server model, governance