fix(mcp): classify unresolvable and egress-refused server URLs in discovery

Merged
WA
6d ago
+50-6

Summary

  • An MCP server whose hostname no longer resolves (e.g. an expired tunnel URL) made /api/mcp/tools/discover answer 500 and log at error: McpDnsResolutionError from the SSRF pre-check fell through isMcpServerUnavailableError and categorizeError
  • isMcpServerUnavailableError now treats it as server-unavailable: discover answers 422 + warn, v2 GET /api/v2/mcp-servers/{id}/tools answers its documented 503
  • categorizeError maps it to 502 (Server hostname could not be resolved) and McpSsrfError / McpDomainNotAllowedError to 403, matching test-connection and server lifecycle; reaches discover refresh, servers/[id]/refresh, and MCP tool execution
  • Messages stay generic constants; no hostname or address reaches the caller. Failure status, negative cache, and the discovery retry predicate are unchanged (they never keyed on these helpers)

Type of Change

  • Bug fix

Testing

  • Regression cases in discover/route.test.ts (DNS → 422, SSRF and domain refusal → 403) and v2/mcp-servers/[mcpServerId]/tools/route.test.ts (DNS → 503); all four fail on the pre-fix code (500) and pass with the fix
  • Targeted vitest: app/api/mcp/tools, app/api/mcp/servers/[id]/refresh, app/api/v2/mcp-servers, lib/internal/mcp, lib/mcp/utils.test.ts
  • biome, check:api-validation:strict, check:comment-hygiene, check:test-patterns; full suite, type-check, and audits run in CI

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing (new tests pass the test-audit authoring gate)
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)