(
auth: McpAuthProvider["Service"],
request: Request,
outcome: Exclude<AuthOutcome, { readonly _tag: "Authenticated" }>,
)
| 110 | : jsonRpcResponse(404, -32001, message); |
| 111 | |
| 112 | const renderAuthError = ( |
| 113 | auth: McpAuthProvider["Service"], |
| 114 | request: Request, |
| 115 | outcome: Exclude<AuthOutcome, { readonly _tag: "Authenticated" }>, |
| 116 | ): Response => { |
| 117 | if (Predicate.isTagged(outcome, "Unauthorized")) { |
| 118 | return jsonRpcResponse( |
| 119 | 401, |
| 120 | -32001, |
| 121 | "Unauthorized", |
| 122 | outcome.challenge ?? `Bearer resource_metadata="${auth.resourceMetadataUrl(request)}"`, |
| 123 | ); |
| 124 | } |
| 125 | if (Predicate.isTagged(outcome, "Forbidden")) { |
| 126 | return jsonRpcResponse(403, outcome.code ?? -32001, outcome.message); |
| 127 | } |
| 128 | // Unavailable: a transient auth-infra failure (JWKS blip OR a WorkOS |
| 129 | // membership-lookup 429/5xx/timeout). Both are retryable, so advertise a |
| 130 | // Retry-After so the client (and any polite retry layer) backs off instead of |
| 131 | // hammering (same rendering as the shared envelope's Unavailable branch). |
| 132 | // Crucially, this path NEVER reaches the session-destroy branch below — a |
| 133 | // transient failure must not condemn a live session. |
| 134 | // |
| 135 | // Note this 503 shares JSON-RPC code -32001 with the terminated-session 404 |
| 136 | // ("Session timed out, please reconnect"); that is intentional — -32001 is |
| 137 | // the generic auth/session envelope code, and the HTTP STATUS is the |
| 138 | // discriminator clients act on: 503 = retry the SAME session id, 404 = the |
| 139 | // id is dead, reconnect. |
| 140 | return jsonRpcErrorBody(503, -32001, outcome.message, { |
| 141 | retryAfterSeconds: UNAVAILABLE_RETRY_AFTER_SECONDS, |
| 142 | }); |
| 143 | }; |
| 144 | |
| 145 | const authenticate = (request: Request, authProvider: Layer.Layer<McpAuthProvider>) => |
| 146 | Effect.gen(function* () { |
no test coverage detected