chore: close quality-gate coverage gaps (#1689) (#1766)
Some checks failed
Run install, format, lint, build, and test on every push / build (push) Has been cancelled
Run install, format, lint, build, and test on every push / publish (push) Has been cancelled
Run install, format, lint, build, and test on every push / publish-github-container-registry (push) Has been cancelled
Some checks failed
Run install, format, lint, build, and test on every push / build (push) Has been cancelled
Run install, format, lint, build, and test on every push / publish (push) Has been cancelled
Run install, format, lint, build, and test on every push / publish-github-container-registry (push) Has been cancelled
Closes #1689. Typecheck cli/tui src, add a core/ format+lint gate, and gate core/json + core/client coverage. See PR #1766 for the full review history.
This commit is contained in:
parent
d76c4d5379
commit
373f346e5f
7 changed files with 2210 additions and 467 deletions
|
|
@ -65,11 +65,7 @@ export class AuthChallengeError extends Error {
|
|||
readonly authChallenge: AuthChallenge;
|
||||
readonly status: number;
|
||||
|
||||
constructor(
|
||||
authChallenge: AuthChallenge,
|
||||
status: number,
|
||||
message?: string,
|
||||
) {
|
||||
constructor(authChallenge: AuthChallenge, status: number, message?: string) {
|
||||
super(message ?? `Auth challenge: ${authChallenge.reason}`);
|
||||
this.name = "AuthChallengeError";
|
||||
this.authChallenge = authChallenge;
|
||||
|
|
@ -262,8 +258,7 @@ export function parseAuthChallengeFromError(
|
|||
}
|
||||
|
||||
const status =
|
||||
(err as { status?: number }).status ??
|
||||
(err as { code?: number }).code;
|
||||
(err as { status?: number }).status ?? (err as { code?: number }).code;
|
||||
if (status !== 401 && status !== 403) {
|
||||
return undefined;
|
||||
}
|
||||
|
|
@ -271,8 +266,9 @@ export function parseAuthChallengeFromError(
|
|||
const wwwAuthenticate =
|
||||
authChallenge?.raw?.wwwAuthenticate ??
|
||||
(err as { wwwAuthenticate?: string }).wwwAuthenticate ??
|
||||
(err as { headers?: { get?: (name: string) => string | null } }).headers
|
||||
?.get?.("WWW-Authenticate") ??
|
||||
(
|
||||
err as { headers?: { get?: (name: string) => string | null } }
|
||||
).headers?.get?.("WWW-Authenticate") ??
|
||||
undefined;
|
||||
|
||||
if (!wwwAuthenticate?.length) {
|
||||
|
|
@ -309,7 +305,8 @@ export function isAuthChallengeError(err: unknown): boolean {
|
|||
return false;
|
||||
}
|
||||
|
||||
const authChallenge = (err as { authChallenge?: AuthChallenge }).authChallenge;
|
||||
const authChallenge = (err as { authChallenge?: AuthChallenge })
|
||||
.authChallenge;
|
||||
if (authChallenge?.reason) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -323,8 +320,9 @@ export function isAuthChallengeError(err: unknown): boolean {
|
|||
const wwwAuthenticate =
|
||||
authChallenge?.raw?.wwwAuthenticate ??
|
||||
(err as { wwwAuthenticate?: string }).wwwAuthenticate ??
|
||||
(err as { headers?: { get?: (name: string) => string | null } }).headers
|
||||
?.get?.("WWW-Authenticate") ??
|
||||
(
|
||||
err as { headers?: { get?: (name: string) => string | null } }
|
||||
).headers?.get?.("WWW-Authenticate") ??
|
||||
undefined;
|
||||
|
||||
return wwwAuthenticate !== undefined && wwwAuthenticate.length > 0;
|
||||
|
|
|
|||
|
|
@ -22,7 +22,10 @@ export {
|
|||
normalizeIdpIssuer,
|
||||
type EmaIdpLoginState,
|
||||
} from "./idpSession.js";
|
||||
export { discoverEmaResourceContext, resolveEmaScopes } from "./resourceContext.js";
|
||||
export {
|
||||
discoverEmaResourceContext,
|
||||
resolveEmaScopes,
|
||||
} from "./resourceContext.js";
|
||||
export {
|
||||
EmaClientNotConfiguredError,
|
||||
emaClientNotConfiguredMessage,
|
||||
|
|
|
|||
|
|
@ -25,13 +25,14 @@ export type { BuildOAuthConnectionStateParams } from "./connection-state.js";
|
|||
export { ensureCimdClientRegistration } from "./cimd.js";
|
||||
|
||||
export { mcpAuth, type McpAuthOptions, type McpAuthResult } from "./mcpAuth.js";
|
||||
export {
|
||||
computeScopeUnion,
|
||||
isStrictScopeSuperset,
|
||||
} from "./scopes.js";
|
||||
export { computeScopeUnion, isStrictScopeSuperset } from "./scopes.js";
|
||||
|
||||
// Storage
|
||||
export type { OAuthStorage, IdpSessionState, SaveClientInformationOptions } from "./storage.js";
|
||||
export type {
|
||||
OAuthStorage,
|
||||
IdpSessionState,
|
||||
SaveClientInformationOptions,
|
||||
} from "./storage.js";
|
||||
export { getServerSpecificKey, OAUTH_STORAGE_KEYS } from "./storage.js";
|
||||
|
||||
// Providers
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
export {
|
||||
NodeOAuthStorage,
|
||||
clearAllOAuthClientState,
|
||||
} from "./storage-node.js";
|
||||
export { NodeOAuthStorage, clearAllOAuthClientState } from "./storage-node.js";
|
||||
export {
|
||||
createOAuthCallbackServer,
|
||||
OAuthCallbackServer,
|
||||
|
|
@ -21,9 +18,7 @@ export {
|
|||
parseRunnerOAuthCallbackUrl,
|
||||
} from "./runner-oauth-callback.js";
|
||||
export type { RunnerOAuthCallbackConfig } from "./runner-oauth-callback.js";
|
||||
export {
|
||||
runRunnerInteractiveOAuth,
|
||||
} from "./runner-interactive-oauth.js";
|
||||
export { runRunnerInteractiveOAuth } from "./runner-interactive-oauth.js";
|
||||
export type {
|
||||
RunRunnerInteractiveOAuthOptions,
|
||||
RunnerInteractiveOAuthClient,
|
||||
|
|
|
|||
|
|
@ -32,9 +32,7 @@ export function parseRunnerOAuthCallbackUrl(
|
|||
cliCallbackUrl?: string,
|
||||
): RunnerOAuthCallbackConfig {
|
||||
const raw =
|
||||
cliCallbackUrl?.trim() ||
|
||||
process.env.MCP_OAUTH_CALLBACK_URL?.trim() ||
|
||||
"";
|
||||
cliCallbackUrl?.trim() || process.env.MCP_OAUTH_CALLBACK_URL?.trim() || "";
|
||||
if (!raw) {
|
||||
return {
|
||||
hostname: RUNNER_OAUTH_CALLBACK_DEFAULT_HOSTNAME,
|
||||
|
|
|
|||
|
|
@ -13,10 +13,7 @@
|
|||
* browser side never imports this; it gets values rehydrated into the
|
||||
* `/api/servers` response by the Hono handler.
|
||||
*/
|
||||
import {
|
||||
AsyncEntry,
|
||||
findCredentialsAsync,
|
||||
} from "@napi-rs/keyring";
|
||||
import { AsyncEntry, findCredentialsAsync } from "@napi-rs/keyring";
|
||||
|
||||
const SERVICE_NAME = "mcp-inspector";
|
||||
|
||||
|
|
|
|||
|
|
@ -84,8 +84,7 @@ export function isStepUpConfirmation(
|
|||
options?: { enterpriseManaged?: boolean },
|
||||
): boolean {
|
||||
return (
|
||||
isStandardOAuthStepUp(challenge, options) ||
|
||||
isEmaStepUp(challenge, options)
|
||||
isStandardOAuthStepUp(challenge, options) || isEmaStepUp(challenge, options)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -124,9 +123,7 @@ export function stepUpFollowUpMessage(options?: {
|
|||
export function stepUpAuthorizeActionLabel(options?: {
|
||||
enterpriseManaged?: boolean;
|
||||
}): string {
|
||||
return options?.enterpriseManaged
|
||||
? "Authorize"
|
||||
: "Authorize (opens browser)";
|
||||
return options?.enterpriseManaged ? "Authorize" : "Authorize (opens browser)";
|
||||
}
|
||||
|
||||
export function stepUpModalTitle(options?: {
|
||||
|
|
@ -198,7 +195,9 @@ export function oauthPreRedirectToastCopy(
|
|||
const name = options.serverName;
|
||||
if (authKind === "step_up") {
|
||||
return {
|
||||
title: name ? `Step-up authorization for "${name}"` : "Step-up authorization",
|
||||
title: name
|
||||
? `Step-up authorization for "${name}"`
|
||||
: "Step-up authorization",
|
||||
message: "Redirecting to authorize additional permissions…",
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue