( transport: string, message: string, cause: unknown, )
| 430 | }); |
| 431 | |
| 432 | const connectionFailure = ( |
| 433 | transport: string, |
| 434 | message: string, |
| 435 | cause: unknown, |
| 436 | ): McpConnectionError | McpOAuthReauthorizationRequired => { |
| 437 | if (hasNestedOAuthReauthorization(cause)) { |
| 438 | return new McpOAuthReauthorizationRequired({ message: "MCP OAuth re-authorization required" }); |
| 439 | } |
| 440 | if (Predicate.isTagged(cause, "McpInsufficientScopeError")) { |
| 441 | // Surfaced as a connection error with the 403 status; the invoke/connect |
| 442 | // catch sites detect the tag and classify as oauth_scope_insufficient. |
| 443 | return new McpConnectionError({ |
| 444 | transport, |
| 445 | message: `${message} (HTTP 403: insufficient scope)`, |
| 446 | httpStatus: 403, |
| 447 | insufficientScope: true, |
| 448 | }); |
| 449 | } |
| 450 | const httpTransportError = nestedMcpHttpTransportError(cause); |
| 451 | if (Option.isSome(httpTransportError)) { |
| 452 | return new McpConnectionError({ |
| 453 | transport, |
| 454 | message: CONNECTION_FAILURE_MESSAGES[httpTransportError.value.failureKind], |
| 455 | failureKind: httpTransportError.value.failureKind, |
| 456 | }); |
| 457 | } |
| 458 | // Carry the handshake HTTP status structurally (and in the message for |
| 459 | // humans) so the liveness health check can classify a rejected credential |
| 460 | // as expired rather than a generic connection failure. |
| 461 | const status = connectionHttpStatusFromCause(cause); |
| 462 | const failureKind: McpConnectionFailureKind = |
| 463 | isStreamableHttpProtocolError(cause) || |
| 464 | (status !== undefined && PROTOCOL_HTTP_STATUSES.has(status)) |
| 465 | ? "protocol" |
| 466 | : status === undefined |
| 467 | ? "network" |
| 468 | : "http"; |
| 469 | return new McpConnectionError({ |
| 470 | transport, |
| 471 | message: status === undefined ? message : `${message} (HTTP ${status})`, |
| 472 | failureKind, |
| 473 | ...(status === undefined ? {} : { httpStatus: status }), |
| 474 | }); |
| 475 | }; |
| 476 | |
| 477 | const connectionAttemptSummary = (failure: McpConnectionError): string => { |
| 478 | if (failure.httpStatus !== undefined) return `HTTP ${failure.httpStatus}`; |
no test coverage detected