Skip to content

fix(api): answer upstream MCP, OAuth, and id-conflict failures with 4xx; keep PII keep-alive past the load balancer - #9210

Merged
waleedlatif1 merged 2 commits into
stagingfrom
fix/upstream-failure-statuses
Oct 18, 2026
Merged

waleedlatif1 merged 2 commits into
stagingfrom
fix/upstream-failure-statuses

Conversation

@waleedlatif1

Copy link
Copy Markdown
Collaborator

Summary

  • MCP tool discovery: a registered server that is unreachable, times out, or is in its post-failure cooldown now answers 422 and logs at warn, instead of 502/503 + ERROR. It is the registered server's fault, not Sim's, and the request itself was valid. 422 is what the MCP route family already uses when an upstream can't complete the operation (the DCR-unsupported branch in oauth/start). The repo uses no 424 anywhere. 400 would blame the request. Other discovery failures keep categorizeError's status and log at error only for 5xx. categorizeError itself is unchanged, so tool execution (lib/internal/mcp/execute-tool.ts) and server refresh keep their mapping. The v2 tools endpoint has its own typed policy (documented 503) and is untouched. McpClient connect and ping failures log at warn.
  • MCP OAuth start: any OAuthError from the authorization server answers 422 with the same surfaced message. Previously it was 500 + ERROR, plus a second ERROR from timedStep. The refusal is caught inside the bounded step, like the redirect signal, so timedStep no longer logs it either. This deliberately includes ServerError: SDK 1.31.0 parseErrorResponse maps any error code it doesn't know (RFC 7591 invalid_redirect_uri among them) and any non-spec body to ServerError, so filtering on the subclass would miss the refusals we actually get. Non-OAuth failures (DB, decryption) stay 500 with the generic message.
  • POST /api/workflows: a 23505 on workflow_pkey (a caller-supplied id that already exists) is now a conflict → 409, not a 500. It also no longer triggers a deduplicate-name retry, and it logs the id at warn. The name-index handling is unchanged.
  • PII service: uvicorn's 5s keep-alive default closed idle connections that the load balancer (300s idle timeout) was still reusing. A request sent on one of those connections got an instant load-balancer 502. The entrypoint now sets --timeout-keep-alive 310 (overridable with PII_TIMEOUT_KEEP_ALIVE), so the load balancer is always the side that closes.
  • PII mask client: the mask-batch route rejects any NUL in a body, so one log string with a NUL scrubbed its whole chunk. The client now sends texts through sanitizeForJsonb (NUL and unpaired surrogates → U+FFFD). The substitution is length-preserving, and text without either character is unchanged.

Type of Change

  • Bug fix

Testing

  • app/api/mcp/tools/discover/route.test.ts (real handler): connection error → 422, cooldown → 422, a non-server error stays 500. Red on the old code (502/503).
  • app/api/mcp/oauth/start/route.test.ts (real handler, real SDK registerClient with an injected fetch): unknown RFC 7591 code, a typed OAuth code, and a non-spec body all → 422 with the server message. A non-OAuth failure stays 500. Red on the old code (500 ×3).
  • lib/guardrails/mask-client.test.ts: the client is driven through the real mask-batch route. A NUL string is masked instead of scrubbing its chunk, and NUL-free text reaches Presidio byte-identical. Red on the old code (2 strings scrubbed).
  • lib/workflows/orchestration/create-workflow-with-state.integration.ts (real Postgres): a taken id → conflict and the original row is untouched, the deduplicate path with a taken id → conflict, and the exact-name conflict is unchanged. Red on the old code (2 failures, internal).
  • Entrypoint arg list checked with a stub uvicorn (defaults and env overrides). --timeout-keep-alive default of 5 confirmed in uvicorn 0.49.0.
  • bun run lint, check:test-patterns, check:comment-hygiene. Existing MCP, workflow, and guardrails suites pass.

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)

…xx; keep PII keep-alive past the load balancer

- MCP tool discovery answers 422 (warn) when the registered server is unreachable or cooling down
- MCP OAuth start answers 422 (warn) with the server's message for any authorization-server refusal
- POST /api/workflows answers 409 when a caller-supplied id already exists
- PII service keeps idle connections open longer than the load balancer's idle timeout
- PII mask client replaces NUL with U+FFFD so the route does not reject the whole chunk
@vercel

vercel Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
docs Ready Ready Preview Oct 18, 2026 11:35pm UTC

Request Review

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 11 files

Reply with feedback, questions, or to request a fix.

Turn on auto-fix | Re-trigger cubic

Comment thread apps/sim/app/api/mcp/tools/discover/route.ts Outdated
Comment thread apps/sim/app/api/mcp/tools/discover/route.ts Outdated
@greptile-apps

greptile-apps Bot commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no actionable new issues were found.

Summary

The PR changes how MCP and workflow creation failures are reported, and improves PII request reliability.

  • Tool discovery returns 422 when a registered MCP server cannot answer.
  • OAuth start returns 422 when an authorization server refuses the flow.
  • Workflow creation reports an already-used ID as a conflict.
  • PII masking replaces invalid text characters before sending a batch.
  • MCP connection and ping failures log as warnings.
  • The PII service keeps idle connections open for 310 seconds by default.

No actionable new issues were found. No previous review threads were supplied. Tests were not run during this review.

Reviews (2) · Last reviewed commit: "fix(mcp): treat tools/list timeouts and ..." · Reviewed by Greptile

… server; fix test route contexts

- Share the v2 discovery classification (timeout, connection, protocol, HTTP error) as isMcpServerUnavailableError and use it in the internal discover route
- Restore the original POST refresh catch, which never sees per-server failures
- Pass a route context in the new and existing oauth start and mask client tests; drop the oauth start test-types baseline entry
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cubic-dev-ai review this PR

@cubic-dev-ai

cubic-dev-ai Bot commented Oct 18, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review this PR

@waleedlatif1 I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 14 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Turn on auto-fix | Re-trigger cubic

@waleedlatif1
waleedlatif1 merged commit da5d059 into staging Oct 18, 2026
53 checks passed
@waleedlatif1
waleedlatif1 deleted the fix/upstream-failure-statuses branch October 19, 2026 03:21

This branch was successfully deployed

1 active deployment
Preview — cf47f894 Deployed Oct 18, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant