Skip to main content

Troubleshooting: MCP server registration rejected

Agents can call external tool servers you register yourself. Registration goes through POST /api/v1/agents/:agentId/mcp-servers, and the request is validated at write time. Three errors account for nearly every rejected registration: The first two are guard rejections on a URL field; the third is a uniqueness conflict on the server name. Work the checklist below.

The SSRF guard — every reject class

A registered server_url is a server-side fetch target: the agent runtime discovers tools and executes calls against it. An OAuth2 token_url is a fetch target too — the platform exchanges it for an access token. The write-time guard keeps an authenticated operator from steering either request into internal network addresses, and runs on both fields. A URL is rejected if any of the following holds:
  • Non-HTTPS schemehttp://, file:, ftp:, gopher:, or any scheme that is not https://.
  • Loopback or private address127.x, 10.x, 172.16–172.31, 192.168.x, or the CGN block 100.64.0.0/10.
  • Link-local or cloud-metadata host169.254.x, localhost, metadata.google.internal, and other blocked hostnames.
  • Internal-only hostname suffix — a .internal or .local suffix, or a trailing-dot FQDN.
  • DNS-rebind — the DNS record resolved at register time returns a private or internal IP. The check runs at registration, not deferred to call time.
The 422 response carries a failure_code naming which class tripped (protocol, blocked_host, internal_suffix, dns_invalid, or private_ip) alongside the user-safe message — the raw resolved address is deliberately left out of the response. Read error.details.failure_code to identify what your URL hit.

Decision checklist

  1. Read the failure class. Look at error.details.failure_code in the 422 response before changing anything — it names the reject class.
  2. Check the URL is public HTTPS end to end. Scheme https://, a publicly resolvable hostname, and a public A/AAAA record. Test resolution from outside your own network if the class is private_ip or dns_invalid.
  3. Repeat for the OAuth2 token_url when you supplied one — it is validated identically to server_url, and the error code switches to INVALID_MCP_OAUTH_TOKEN_URL when that is the field that failed.
  4. For a 409 name conflict, list the agent’s servers with GET /api/v1/agents/:agentId/mcp-servers and either rename the new server or update the existing registration (PATCH /api/v1/agents/:agentId/mcp-servers/:id). The conflict never overwrites the earlier registration.

What NOT to do

  • Do not point the URL at a redirect chain that resolves to a private address. The register-time DNS check sees the resolved record; a redirect to internal infrastructure is the exact traffic the guard blocks.
  • Do not retry the exact same payload. The guard is deterministic — the same URL fails the same class every time. Fix the URL first.
  • Do not delete and re-post to dodge a 409 unless you want the old registration gone. Use PATCH to update the existing server in place.

When to escalate

Escalate to support when a genuinely public https:// URL is rejected with private_ip or dns_invalid after you confirmed public resolution from outside your network. Include the failure_code, the request_id from the response meta, and the hostname (not the resolved address). Support can check whether the public DNS answer changed — a public DNS record that returns a private IP is a DNS configuration change on your side, not a guard defect.

See also