Skip to content

Fix false positive 'Needs Login' for connected MCP servers#2931

Merged
omar-inkeep merged 2 commits intomainfrom
false-positive-needs-auth
Mar 31, 2026
Merged

Fix false positive 'Needs Login' for connected MCP servers#2931
omar-inkeep merged 2 commits intomainfrom
false-positive-needs-auth

Conversation

@omar-inkeep
Copy link
Copy Markdown
Contributor

Summary

  • Fixes intermittent false positive "Needs Login" status on MCP server detail pages when users have valid credentials (e.g., Linear returning transient 500 errors)
  • Introduces three-tier error classification: transient errors → unavailable, auth errors (401/403) → needs_auth, unknown errors with existing credential → unavailable (not needs_auth)
  • Expands isTimeoutOrConnectionError() to cover McpError(InternalError), StreamableHTTPError 5xx, ECONNREFUSED, ENOTFOUND, and other network codes

Test plan

  • 9 new unit tests covering every branch of the three-tier classification
  • All 1997 existing tests pass (132 test files)
  • Typecheck passes
  • Lint passes
  • Manual: temporarily inject McpError(500) in discoverToolsFromServer for a tool with a valid credential → confirm UI shows "Unavailable" instead of "Needs Login"

🤖 Generated with Claude Code

Transient server errors (e.g. Linear returning 500) were misclassified as
auth failures because detectAuthenticationRequired() only checked whether
the server supports OAuth, not whether a credential already existed.

Introduces three-tier error classification in dbResultToMcpTool:
1. Transient errors (timeout, 5xx, network) → 'unavailable'
2. Auth errors (401, 403, UnauthorizedError) → 'needs_auth'
3. Other errors + credential exists → 'unavailable' (not auth failure)
4. Other errors + no credential → probe OAuth metadata

Also expands isTimeoutOrConnectionError() to cover McpError InternalError,
StreamableHTTPError 5xx, ECONNREFUSED, ENOTFOUND, and other network codes.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Mar 31, 2026

🦋 Changeset detected

Latest commit: 8fe313d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 10 packages
Name Type
@inkeep/agents-core Patch
@inkeep/agents-api Patch
@inkeep/agents-manage-ui Patch
@inkeep/agents-cli Patch
@inkeep/agents-sdk Patch
@inkeep/agents-work-apps Patch
@inkeep/ai-sdk-provider Patch
@inkeep/create-agents Patch
@inkeep/agents-email Patch
@inkeep/agents-mcp Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel
Copy link
Copy Markdown

vercel Bot commented Mar 31, 2026

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

Project Deployment Actions Updated (UTC)
agents-api Ready Ready Preview, Comment Mar 31, 2026 8:15pm
agents-manage-ui Ready Ready Preview, Comment Mar 31, 2026 8:15pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
agents-docs Skipped Skipped Mar 31, 2026 8:15pm

Request Review

@pullfrog
Copy link
Copy Markdown
Contributor

pullfrog Bot commented Mar 31, 2026

TL;DR — Fixes false positive "Needs Login" status on MCP server detail pages by introducing a three-tier error classification. Transient server errors (5xx, timeouts, network failures) and unknown errors when a valid credential exists now resolve to unavailable instead of needs_auth, reserving the login prompt for genuine auth failures (401/403, UnauthorizedError).

Key changes

  • Add isAuthenticationError() classifier in tools.ts — Detects 401/403 StreamableHTTPError, UnauthorizedError, and auth-related message patterns to distinguish genuine credential failures from transient issues.
  • Expand isTimeoutOrConnectionError() to cover more transient failures — Now includes McpError(InternalError), StreamableHTTPError 5xx, fetch failed, ECONNREFUSED, ENOTFOUND, EHOSTUNREACH, ENETUNREACH, and EPIPE.
  • Add credential-aware fallback in dbResultToMcpTool catch block — Unknown errors with an existing credential are treated as unavailable (with a warning log) rather than triggering OAuth detection.
  • Add 9 unit tests for three-tier error classification — Covers every branch: transient errors, auth errors, credential-present fallback, and no-credential OAuth detection.

Summary | 3 files | 1 commit | base: mainfalse-positive-needs-auth


Three-tier error classification for MCP server status

Before: Any connection error during MCP tool discovery could trigger needs_auth status, even when the user had a valid credential and the server returned a transient 500 error.
After: Errors are classified into three tiers: (1) transient → unavailable, (2) auth (401/403/UnauthorizedError) → needs_auth, (3) unknown with existing credential → unavailable. Only genuine auth failures or OAuth detection without a credential can set needs_auth.

The new isAuthenticationError() function checks for UnauthorizedError from the MCP SDK, StreamableHTTPError with 401/403 status codes, and error messages containing auth-related keywords (unauthorized, forbidden, invalid_token, token expired, invalid_grant). The existing isTimeoutOrConnectionError() is expanded to recognize McpError(InternalError), StreamableHTTPError 5xx, fetch failed messages, and network error codes like ECONNREFUSED and ENOTFOUND.

The catch block in dbResultToMcpTool now evaluates errors in priority order: transient first, then explicit auth errors, then a credential-aware fallback that logs a warning and returns unavailable rather than falling through to OAuth detection.

When does OAuth detection still run?

OAuth detection via detectAuthenticationRequired() only runs when (a) the error is not transient, (b) the error is not an explicit auth error, and (c) there is no existing credential. This is the only remaining path to needs_auth for non-explicit-auth errors.

tools.ts · dbResultToMcpTool.test.ts

Pullfrog  | View workflow run | Triggered by Pullfrog | Using Claude Opus𝕏

Copy link
Copy Markdown
Contributor

@pullfrog pullfrog Bot left a comment

Choose a reason for hiding this comment

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

Solid fix for a real UX problem. The three-tier classification logic is well-structured and the test coverage is thorough. One correctness concern about ErrorCode.InternalError being overly broad, and a minor observation about the string-matching heuristic in isAuthenticationError.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using Claude Opus𝕏

return (
error.code === ErrorCode.RequestTimeout ||
error.code === ErrorCode.ConnectionClosed ||
error.code === ErrorCode.InternalError
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.

ErrorCode.InternalError (-32603) is the standard JSON-RPC "Internal error" code. MCP servers can return this for any server-side bug — not just transient/connectivity issues. Classifying it unconditionally as a timeout/connection error means a server that returns -32603 for a persistent misconfiguration will be shown as "Unavailable" forever instead of "Unhealthy".

This is the right pragmatic tradeoff for the false-positive fix (better to show "Unavailable" than "Needs Login"), but it's worth noting that the function name isTimeoutOrConnectionError is now a bit misleading — it's really isTransientOrServerError. Consider renaming or adding a brief inline note about why InternalError is included.

}

if (error instanceof StreamableHTTPError) {
return error.code !== undefined && error.code >= 500;
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.

Nit: StreamableHTTPError.code is typed number | undefined. The guard error.code !== undefined && error.code >= 500 is correct. However, the SDK also uses code -1 for unexpected content types — that would not match >= 500, which is the right behavior. Just calling this out as already-correct.

Comment on lines 111 to +116
if (
cause?.code === 'ETIMEDOUT' ||
cause?.code === 'ECONNABORTED' ||
cause?.code === 'ECONNRESET'
message.includes('unauthorized') ||
message.includes('forbidden') ||
message.includes('invalid_token') ||
message.includes('token expired') ||
message.includes('invalid_grant')
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.

The string-matching heuristic in isAuthenticationError is inherently fragile — any Error whose message happens to contain "unauthorized" or "forbidden" (e.g., "Forbidden by CORS policy", "unauthorized field access in GraphQL schema") would be misclassified as an auth error. This isn't new to this PR (the previous code had similar heuristics), but worth noting that the structured checks (instanceof UnauthorizedError, StreamableHTTPError status codes) should catch the vast majority of real auth errors. The string matching is a fallback for non-MCP-SDK errors — consider whether it's actually needed now that the structured checks exist.

});

it('should set status to needs_auth when no credential and OAuth is detected', async () => {
mockMcpConnect.mockRejectedValue(new Error('Connection refused'));
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.

This test injects new Error('Connection refused') which does NOT match isTimeoutOrConnectionError (the string check looks for 'timed out', 'timeout', 'fetch failed', and the cause code check is not triggered since there's no .cause). So it falls through to the else branch and hits detectAuthenticationRequired. The test name says "no credential and OAuth is detected" which is correct, but the error message 'Connection refused' is slightly misleading — it might suggest this would be caught as a transient error. Consider using a more neutral error like new Error('unknown issue') to make it clearer this exercises the fallthrough path.

Copy link
Copy Markdown
Contributor

@claude claude Bot left a comment

Choose a reason for hiding this comment

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

PR Review Summary

(0) Total Issues | Risk: Low

This is a well-designed fix that correctly addresses the false positive "Needs Login" problem. The three-tier error classification logic is sound:

  1. Transient errors (timeouts, 5xx, network codes) → unavailable
  2. Auth errors (401/403, UnauthorizedError) → needs_auth
  3. Unknown errors with existing credentialunavailable (core fix)

The implementation is clean, the error handling is explicit with appropriate logging, and the 9 new unit tests provide good coverage of the classification branches.

💭 Consider (4) 💭

💭 1) tools.ts:78-88 Consolidate transient network error codes

Issue: The inline transientCodes array duplicates most of RETRYABLE_NODE_ERROR_CODES from retry/retryable-errors.ts (7 of 8 codes overlap).

Why: Two parallel definitions of transient network codes can drift independently, making maintenance harder.

Fix: Consider importing RETRYABLE_NODE_ERROR_CODES and extending it with the additional codes (ECONNABORTED, ENOTFOUND) needed for MCP-specific detection.

Refs: retryable-errors.ts:31-39

💭 2) dbResultToMcpTool.test.ts Test remaining McpError codes

Issue: isTimeoutOrConnectionError handles RequestTimeout, ConnectionClosed, and InternalError, but only InternalError is tested.

Why: The other codes would catch regressions in timeout/connection handling.

Fix: Optionally add tests for McpError(ErrorCode.RequestTimeout) and McpError(ErrorCode.ConnectionClosed).

Refs: tools.ts:54-60

💭 3) dbResultToMcpTool.test.ts Test message-based auth error detection

Issue: isAuthenticationError checks for 'invalid_token', 'token expired', 'invalid_grant' in error messages, but these patterns aren't tested.

Why: OAuth providers may return these patterns when tokens expire; testing ensures correct needs_auth classification.

Fix: Consider adding a test like mockMcpConnect.mockRejectedValue(new Error('OAuth error: invalid_grant')).

Refs: tools.ts:109-119

💭 4) dbResultToMcpTool.test.ts Additional network error code coverage

Issue: Only ECONNREFUSED is tested among 8 transient codes.

Why: Adding ENOTFOUND (DNS failure) would increase confidence in the cause.code branch, though the logic is identical for all codes.

Fix: Low priority — optionally add one more network code test.

Refs: tools.ts:78-90


✅ APPROVE

Summary: This is a clean, well-tested fix. The three-tier classification correctly prevents false "Needs Login" prompts when users have valid credentials but the MCP server returns transient errors. The suggestions above are minor enhancements for test coverage and code consistency — none are blocking. 🎉

Reviewers (6)
Reviewer Returned Main Findings Consider While You're Here Inline Comments Pending Recs Discarded
pr-review-tests 3 0 3 0 0 0 0
pr-review-consistency 1 0 1 0 0 0 0
pr-review-standards 0 0 0 0 0 0 0
pr-review-errors 0 0 0 0 0 0 0
pr-review-types 0 0 0 0 0 0 0
pr-review-product 0 0 0 0 0 0 0
Total 4 0 4 0 0 0 0

@github-actions github-actions Bot deleted a comment from claude Bot Mar 31, 2026
@itoqa
Copy link
Copy Markdown

itoqa Bot commented Mar 31, 2026

Ito Test Report ✅

10 test cases ran. 10 passed.

The unified run passed all 10 of 10 test cases with zero failures, confirming expected MCP detail/list behavior, create flow behavior, security handling, and permission boundaries. Most importantly, transient credentialed failures consistently rendered as Unavailable (not Needs Login), explicit or missing-auth states correctly showed Needs Login with Connect (including single OAuth start on rapid clicks), loading states converged to Unavailable without regression, user-scoped custom server creation preserved stable Needs Login on first load/reload, XSS and unsafe URL inputs were safely blocked or rendered inert, and the view-only authorization concern was validated as a permission-simulation artifact rather than a confirmed product defect.

✅ Passed (10)
Category Summary Screenshot
Adversarial Injected script-like payloads in tool name/lastError rendered inert with no script execution side effects. ADV-1
Adversarial Invalid javascript:, data:, and malformed URL inputs were rejected and did not submit create requests. ADV-2
Adversarial Authorization-boundary finding was resolved as a permission-simulation artifact; code-level guards indicate expected server-side protection. ADV-3
Edge Rapid repeated Connect clicks triggered only one OAuth start. EDGE-1
Edge Credentialed unavailable project tool still exposes disconnect/delete action. EDGE-4
Logic Composio tool without connectedAccountId correctly shows Needs Login with Connect CTA. LOGIC-1
Happy-path Credentialed transient-failure detail view showed Unavailable (not Needs Login), with no Connect CTA. ROUTE-1
Happy-path Project- and user-scoped auth failures showed Needs Login with Connect CTA, and OAuth start was initiated on click. ROUTE-2
Happy-path Deterministic re-validation showed list/detail convergence from loading to Unavailable with no Needs Login regression. ROUTE-3
Happy-path User-scoped custom MCP server creation redirected to detail and showed stable Needs Login + Connect behavior on first load and reload. ROUTE-4

Commit: bf358c2

View Full Run


Tell us how we did: Give Ito Feedback

Tools with fake/unreachable URLs now correctly classify as 'unavailable'
(transient) instead of 'unhealthy' due to expanded transient error detection.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Copy link
Copy Markdown
Contributor

@claude claude Bot left a comment

Choose a reason for hiding this comment

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

PR Review Summary

(0) Total Issues | Risk: Low

Delta Review

This is a re-review scoped to changes since the last automated review.

Commits since last review: 1

  • 8fe313d96 — Update integration tests: unreachable MCP servers are now 'unavailable'

Changes reviewed:

  • agents-api/src/__tests__/manage/routes/crud/tools.test.ts (+4/-4)

The delta updates integration test assertions from unhealthyunavailable across 4 test cases:

  1. Filter tools by status query parameter
  2. Get tool by ID
  3. Create new tool

This is a straightforward test alignment with the core fix behavior — transient/unreachable MCP servers now correctly return unavailable status instead of unhealthy, preventing false "Needs Login" prompts.

No new findings — the test changes accurately reflect the intended behavior change.


✅ APPROVE

Summary: Delta LGTM. The integration test updates correctly align with the three-tier error classification implemented in the core fix. Combined with the prior review's approval, this PR is ready to merge. 🚀

Reviewers (0)
Reviewer Returned Main Findings Consider While You're Here Inline Comments Pending Recs Discarded
Total 0 0 0 0 0 0 0

Note: Delta was trivial (test expectation updates only) — no sub-reviewers dispatched.

@github-actions github-actions Bot deleted a comment from claude Bot Mar 31, 2026
@omar-inkeep omar-inkeep added this pull request to the merge queue Mar 31, 2026
Merged via the queue into main with commit 68a55f5 Mar 31, 2026
26 checks passed
@omar-inkeep omar-inkeep deleted the false-positive-needs-auth branch March 31, 2026 21:00
@inkeep
Copy link
Copy Markdown
Contributor

inkeep Bot commented Mar 31, 2026

No documentation changes needed for this PR. The fix improves the internal accuracy of status classification (transient errors → unavailable vs auth errors → needs_auth), but the user-facing status values documented in the MCP Servers guide remain the same.

@itoqa
Copy link
Copy Markdown

itoqa Bot commented Mar 31, 2026

Ito Test Report ✅

13 test cases ran. 2 additional findings, 11 passed.

Overall, 11 of 13 test cases passed, confirming that MCP status and auth-state handling is largely correct across list/details, navigation refresh loops, mobile viewport, deep-link stale-state scenarios, and API boundary checks (including query tampering rejection and unauthorized tenant/project access protection), with transient failures on already-credentialed tools consistently shown as Unavailable and true auth failures (401/403 or OAuth-required without credentials) shown as Needs Login with expected Connect behavior. The two confirmed defects were a high-severity race where rapid repeated Connect clicks can start duplicate OAuth launches before the disabled/loading state applies, and a medium-severity Composio error-handling issue where auth-check service failures can be misclassified as Needs Login instead of Unavailable, leading to unnecessary reconnect prompts.

✅ Passed (11)
Category Summary Screenshot
Adversarial Valid enum filtering returned constrained results, while tampered status values were rejected with 400 validation responses. ADV-1
Adversarial Unauthorized tenant/project probes were rejected and did not expose tool status or credential metadata. ADV-2
Adversarial Two-tab deep-link stale-state checks converged to Unavailable on details/list with project connection preserved and no false login prompt. ADV-4
Edge Across repeated details/back/forward/refresh loops, the transient credentialed MCP server consistently resolved to Unavailable and did not settle on Needs Login. EDGE-1
Edge At iPhone 13 viewport, the list and details views preserved Unavailable status and key connection/error sections without losing controls; no unexpected Connect prompt appeared. EDGE-2
Logic 401/403 fixtures were classified as Needs Login on list and details, with reconnect/connect affordance visible. LOGIC-1
Logic No-credential OAuth-required fixture stayed in Needs Login with a single Connect initiation request and no duplicate launch. LOGIC-2
Logic No-credential unknown non-auth failure remained unhealthy on details/list, with no Connect CTA and no auth wording in Last Error. LOGIC-3
Happy-path List and both details views showed Unavailable with credential context; no reconnect prompts appeared. ROUTE-1
Happy-path Project details kept Project Connection visible while status remained Unavailable and Connect stayed hidden. ROUTE-2
Happy-path User details showed Unavailable with Your Connection card and no Connect/re-auth prompt. ROUTE-3
ℹ️ Additional Findings (2)

These findings are unrelated to the current changes but were observed during testing.

Category Summary Screenshot
Edge ⚠️ Rapid Connect clicks can trigger duplicate OAuth initiation before the disabled/loading state takes effect. EDGE-3
Edge 🟠 Composio auth-check service failures can be classified as Needs Login instead of degrading to Unavailable. EDGE-4
⚠️ Rapid click stress on Connect action does not create duplicate auth launches
  • What failed: Multiple auth launches can start from rapid clicks before the UI disabled state applies; expected behavior is single-flight OAuth initiation.
  • Impact: Users can trigger duplicate OAuth starts from one interaction burst, causing redundant auth requests and inconsistent connect UX. This can create repeated external auth side effects for the same credential action.
  • Steps to reproduce:
    1. Open an MCP server details page for a tool that shows Needs Login and has a visible Connect button.
    2. Rapidly click Connect multiple times within about one second.
    3. Observe that multiple auth initiation attempts can occur before the UI settles into a disabled/loading state.
  • Stub / mock context: No stubs, mocks, or bypasses were applied for this test in the recorded run.
  • Code analysis: I inspected the OAuth click path in the manage UI hook and the details-page button wiring. The handler sets isConnecting but has no in-flight early-return guard, so repeated clicks can invoke concurrent login flows before React state-disabling fully applies.
  • Why this is likely a bug: The production click handler lacks a reentrancy guard, so rapid repeated invocations are possible before the disabled prop update is committed.

Relevant code:

agents-manage-ui/src/hooks/use-oauth-login.ts (lines 292-356)

const handleOAuthLogin = useCallback(
  async ({
    toolId,
    mcpServerUrl,
    toolName,
    thirdPartyConnectAccountUrl,
    credentialScope,
  }: OAuthLoginParams): Promise<void> => {
    setIsConnecting(true);
    try {
      if (mcpServerUrl.includes('composio.dev')) {
        // ...
      }
      // ...
    } finally {
      setIsConnecting(false);
    }
  },
);

agents-manage-ui/src/components/mcp-servers/view-mcp-server-details-project-scope.tsx (lines 206-221)

<Button
  size="sm"
  className="mt-3 w-fit"
  disabled={isConnecting}
  onClick={() => {
    handleOAuthLogin({
      toolId: tool.id,
      mcpServerUrl: tool.config.mcp.server.url,
      toolName: tool.name,
      credentialScope: 'project',
    });
  }}
>
🟠 Composio auth-check error path surfaces Unavailable without forcing login
  • What failed: Service-side auth-check failures can be treated as unauthenticated (Needs Login) instead of transient service degradation (Unavailable).
  • Impact: Users can be incorrectly prompted to reconnect credentials when the third-party auth-check service is the failing component. This drives unnecessary re-auth attempts and obscures true outage conditions.
  • Steps to reproduce:
    1. Use a Composio-linked MCP tool that already has a connected account.
    2. Force the third-party auth-check path to fail while loading tool status.
    3. Open details/list and verify the UI can show Needs Login instead of Unavailable for that service-failure case.
  • Stub / mock context: A temporary Composio auth-check fixture patch was applied to simulate third-party auth-check failure behavior while keeping the tool configured with a connected account.
  • Code analysis: I inspected Composio auth-check handling and status mapping. The caller in tools.ts correctly maps authResult.error to unavailable, but fetchComposioConnectedAccounts swallows API failures and returns null, which upstream is interpreted as authenticated: false (without error), causing an incorrect needs_auth classification.
  • Why this is likely a bug: The code path conflates auth-check transport failures with true unauthenticated state, producing a wrong user-facing status for third-party outage/error conditions.

Relevant code:

packages/agents-core/src/utils/third-party-mcp-servers/composio-client.ts (lines 193-212)

async function fetchComposioConnectedAccounts(
  derivedUserId: string
): Promise<Awaited<ReturnType<Composio['connectedAccounts']['list']>> | null> {
  // ...
  try {
    const connectedAccounts = await composioInstance.connectedAccounts.list({
      userIds: [derivedUserId],
      statuses: ['ACTIVE', 'INITIATED', 'EXPIRED'],
    });
    return connectedAccounts;
  } catch (error) {
    logger.error({ error }, 'Error fetching Composio connected accounts');
    return null;
  }
}

packages/agents-core/src/utils/third-party-mcp-servers/composio-client.ts (lines 253-268)

try {
  const [composioMcpServer, connectedAccounts] = await Promise.all([
    composioInstance.mcp.get(serverId),
    fetchComposioConnectedAccounts(composioUserId),
  ]);

  if (!firstAuthConfigId) {
    return { authenticated: false };
  }

  if (!connectedAccounts) {
    return { authenticated: false };
  }

packages/agents-core/src/data-access/manage/tools.ts (lines 504-510)

if (!authResult.authenticated && !authResult.error) {
  status = 'needs_auth';
  lastErrorComputed = 'Third-party authentication required. Try authenticating again.';
} else if (authResult.error) {
  status = 'unavailable';
  lastErrorComputed =
    'Could not verify third-party authentication status. The service may be temporarily unavailable.';
}

Commit: 8fe313d

View Full Run


Tell us how we did: Give Ito Feedback

Gaurav-Narayan-Varma pushed a commit that referenced this pull request Apr 1, 2026
* Fix false positive 'Needs Login' for MCP servers with valid credentials

Transient server errors (e.g. Linear returning 500) were misclassified as
auth failures because detectAuthenticationRequired() only checked whether
the server supports OAuth, not whether a credential already existed.

Introduces three-tier error classification in dbResultToMcpTool:
1. Transient errors (timeout, 5xx, network) → 'unavailable'
2. Auth errors (401, 403, UnauthorizedError) → 'needs_auth'
3. Other errors + credential exists → 'unavailable' (not auth failure)
4. Other errors + no credential → probe OAuth metadata

Also expands isTimeoutOrConnectionError() to cover McpError InternalError,
StreamableHTTPError 5xx, ECONNREFUSED, ENOTFOUND, and other network codes.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* Update integration tests: unreachable MCP servers are now 'unavailable'

Tools with fake/unreachable URLs now correctly classify as 'unavailable'
(transient) instead of 'unhealthy' due to expanded transient error detection.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
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