Address @claude review: format core/, typed output-validation error, document progress heuristic
- Run Prettier on the 60 core/ files this PR touched — the deterministic import rewrite had collapsed multi-line imports and left throws unwrapped; core/ is in no format gate so it slipped past CI. Now Prettier-clean. - Default-path output-schema violation now throws ProtocolError(ProtocolErrorCode.InvalidParams, …) instead of a bare Error, matching the prior v1 contract (SDK threw a typed McpError with a code) so downstream `instanceof`/`.code` branching keeps working. - Document the "assumes serial construction" invariant on pollTaskToolCall's progress-subscription revival (the `_progressHandlers` new-key heuristic). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
19a6df7ad3
commit
f9f79fff54
13 changed files with 296 additions and 95 deletions
|
|
@ -1,4 +1,7 @@
|
|||
import { discoverAuthorizationServerMetadata, discoverOAuthProtectedResourceMetadata } from "@modelcontextprotocol/client";
|
||||
import {
|
||||
discoverAuthorizationServerMetadata,
|
||||
discoverOAuthProtectedResourceMetadata,
|
||||
} from "@modelcontextprotocol/client";
|
||||
import type { OAuthClientInformation } from "@modelcontextprotocol/client";
|
||||
import { getAuthorizationServerUrl } from "./discovery.js";
|
||||
import type { BaseOAuthClientProvider } from "./providers.js";
|
||||
|
|
|
|||
|
|
@ -1,10 +1,18 @@
|
|||
import type { OAuthClientInformation, OAuthTokens } from "@modelcontextprotocol/client";
|
||||
import type {
|
||||
OAuthClientInformation,
|
||||
OAuthTokens,
|
||||
} from "@modelcontextprotocol/client";
|
||||
import type { EnterpriseManagedAuthIdpConfig } from "../client/types.js";
|
||||
import { getEmaIdpLoginState, normalizeIdpIssuer } from "./ema/idpSession.js";
|
||||
import { idpOAuthStorageKey } from "./ema/storage.js";
|
||||
import { isJwtExpired } from "./ema/jwt.js";
|
||||
import type { OAuthStorage } from "./storage.js";
|
||||
import type { AuthProtocol, OAuthClientRegistrationKind, OAuthConnectionState, OAuthFlowState } from "./types.js";
|
||||
import type {
|
||||
AuthProtocol,
|
||||
OAuthClientRegistrationKind,
|
||||
OAuthConnectionState,
|
||||
OAuthFlowState,
|
||||
} from "./types.js";
|
||||
import { authProtocolFromEnterpriseManaged } from "./types.js";
|
||||
|
||||
export interface BuildOAuthConnectionStateParams {
|
||||
|
|
@ -17,9 +25,7 @@ export interface BuildOAuthConnectionStateParams {
|
|||
}
|
||||
|
||||
/** True when persisted tokens include a non-expired access token (JWT exp when parseable). */
|
||||
export function isAccessTokenUsable(
|
||||
tokens: OAuthTokens | undefined,
|
||||
): boolean {
|
||||
export function isAccessTokenUsable(tokens: OAuthTokens | undefined): boolean {
|
||||
if (!tokens?.access_token) return false;
|
||||
return !isJwtExpired(tokens.access_token);
|
||||
}
|
||||
|
|
@ -27,10 +33,9 @@ export function isAccessTokenUsable(
|
|||
function resolveClient(
|
||||
preregistered: OAuthClientInformation | undefined,
|
||||
dynamic: OAuthClientInformation | undefined,
|
||||
dynamicRegistrationKind: Extract<
|
||||
OAuthClientRegistrationKind,
|
||||
"dcr" | "cimd"
|
||||
> | undefined,
|
||||
dynamicRegistrationKind:
|
||||
| Extract<OAuthClientRegistrationKind, "dcr" | "cimd">
|
||||
| undefined,
|
||||
): OAuthConnectionState["client"] | undefined {
|
||||
if (preregistered?.client_id) {
|
||||
return {
|
||||
|
|
@ -81,7 +86,7 @@ export async function buildOAuthConnectionState(
|
|||
? flowTokens
|
||||
: storedTokens && isAccessTokenUsable(storedTokens)
|
||||
? storedTokens
|
||||
: flowTokens ?? storedTokens;
|
||||
: (flowTokens ?? storedTokens);
|
||||
|
||||
const preregistered = await storage.getClientInformation(serverUrl, true);
|
||||
const dynamic = await storage.getClientInformation(serverUrl);
|
||||
|
|
@ -120,7 +125,9 @@ export async function buildOAuthConnectionState(
|
|||
const idp = params.enterpriseManagedAuth.idp;
|
||||
const issuer = normalizeIdpIssuer(idp.issuer);
|
||||
const idpSession = await getEmaIdpLoginState(storage, issuer);
|
||||
const idpMetadata = await storage.getServerMetadata(idpOAuthStorageKey(issuer));
|
||||
const idpMetadata = await storage.getServerMetadata(
|
||||
idpOAuthStorageKey(issuer),
|
||||
);
|
||||
state.ema = {
|
||||
idpIssuer: issuer,
|
||||
idpClientId: idp.clientId,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,12 @@
|
|||
import { discoverAuthorizationServerMetadata, exchangeAuthorization, startAuthorization } from "@modelcontextprotocol/client";
|
||||
import type { OAuthClientInformation, OAuthMetadata } from "@modelcontextprotocol/client";
|
||||
import {
|
||||
discoverAuthorizationServerMetadata,
|
||||
exchangeAuthorization,
|
||||
startAuthorization,
|
||||
} from "@modelcontextprotocol/client";
|
||||
import type {
|
||||
OAuthClientInformation,
|
||||
OAuthMetadata,
|
||||
} from "@modelcontextprotocol/client";
|
||||
import { OAuthMetadataSchema } from "@modelcontextprotocol/core";
|
||||
import type { OAuthStorage } from "../storage.js";
|
||||
import type { EnterpriseManagedAuthIdpConfig } from "../../client/types.js";
|
||||
|
|
@ -44,7 +51,9 @@ export async function discoverIdpMetadata(
|
|||
fetchFn,
|
||||
});
|
||||
if (!metadata) {
|
||||
throw new Error(`Failed to discover OIDC metadata for IdP issuer ${issuer}`);
|
||||
throw new Error(
|
||||
`Failed to discover OIDC metadata for IdP issuer ${issuer}`,
|
||||
);
|
||||
}
|
||||
return OAuthMetadataSchema.parse(metadata);
|
||||
}
|
||||
|
|
@ -56,7 +65,11 @@ export async function startIdpOidcAuthorization(params: {
|
|||
fetchFn?: typeof fetch;
|
||||
}): Promise<{ authorizationUrl: URL }> {
|
||||
const issuer = normalizeIdpIssuer(params.idp.issuer);
|
||||
const metadata = await resolveIdpMetadata(issuer, params.storage, params.fetchFn);
|
||||
const metadata = await resolveIdpMetadata(
|
||||
issuer,
|
||||
params.storage,
|
||||
params.fetchFn,
|
||||
);
|
||||
const clientInformation = idpClientInformation(params.idp);
|
||||
const storageKey = idpOAuthStorageKey(issuer);
|
||||
const state = generateOAuthState();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
import { discoverAuthorizationServerMetadata, discoverOAuthProtectedResourceMetadata, selectResourceURL } from "@modelcontextprotocol/client";
|
||||
import {
|
||||
discoverAuthorizationServerMetadata,
|
||||
discoverOAuthProtectedResourceMetadata,
|
||||
selectResourceURL,
|
||||
} from "@modelcontextprotocol/client";
|
||||
import type { OAuthClientProvider } from "@modelcontextprotocol/client";
|
||||
import type { OAuthProtectedResourceMetadata } from "@modelcontextprotocol/client";
|
||||
import { getAuthorizationServerUrl } from "../discovery.js";
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
import { selectClientAuthMethod } from "@modelcontextprotocol/client";
|
||||
import type { OAuthClientInformation, OAuthMetadata } from "@modelcontextprotocol/client";
|
||||
import type {
|
||||
OAuthClientInformation,
|
||||
OAuthMetadata,
|
||||
} from "@modelcontextprotocol/client";
|
||||
|
||||
export async function parseOAuthTokenErrorResponse(
|
||||
response: Response,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
import type { OAuthClientProvider } from "@modelcontextprotocol/client";
|
||||
import type { OAuthClientInformationMixed, OAuthClientMetadata, OAuthTokens } from "@modelcontextprotocol/client";
|
||||
import type {
|
||||
OAuthClientInformationMixed,
|
||||
OAuthClientMetadata,
|
||||
OAuthTokens,
|
||||
} from "@modelcontextprotocol/client";
|
||||
import type { BaseOAuthClientProvider } from "../providers.js";
|
||||
import {
|
||||
refreshEmaResourceTokens,
|
||||
|
|
@ -8,10 +12,7 @@ import {
|
|||
} from "./emaFlow.js";
|
||||
import { isJwtExpired } from "./jwt.js";
|
||||
|
||||
function isAccessTokenUsable(
|
||||
tokens: OAuthTokens,
|
||||
skewMs = 60_000,
|
||||
): boolean {
|
||||
function isAccessTokenUsable(tokens: OAuthTokens, skewMs = 60_000): boolean {
|
||||
if (!tokens.access_token) return false;
|
||||
return !isJwtExpired(tokens.access_token, skewMs);
|
||||
}
|
||||
|
|
@ -28,10 +29,7 @@ export class EmaTransportOAuthProvider implements OAuthClientProvider {
|
|||
private readonly inner: BaseOAuthClientProvider;
|
||||
private readonly emaConfig: EmaFlowConfig;
|
||||
|
||||
constructor(
|
||||
inner: BaseOAuthClientProvider,
|
||||
emaConfig: EmaFlowConfig,
|
||||
) {
|
||||
constructor(inner: BaseOAuthClientProvider, emaConfig: EmaFlowConfig) {
|
||||
this.inner = inner;
|
||||
this.emaConfig = emaConfig;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
import { discoverAuthorizationServerMetadata } from "@modelcontextprotocol/client";
|
||||
import type { OAuthClientInformation, OAuthTokens } from "@modelcontextprotocol/client";
|
||||
import type {
|
||||
OAuthClientInformation,
|
||||
OAuthTokens,
|
||||
} from "@modelcontextprotocol/client";
|
||||
import { OAuthTokensSchema } from "@modelcontextprotocol/core";
|
||||
import type { EnterpriseManagedAuthIdpConfig } from "../../client/types.js";
|
||||
import {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,14 @@
|
|||
* (the same path v2 `auth()` takes when refresh cannot widen scope).
|
||||
*/
|
||||
|
||||
import { auth as sdkAuth, discoverOAuthServerInfo, isHttpsUrl, registerClient, selectResourceURL, startAuthorization } from "@modelcontextprotocol/client";
|
||||
import {
|
||||
auth as sdkAuth,
|
||||
discoverOAuthServerInfo,
|
||||
isHttpsUrl,
|
||||
registerClient,
|
||||
selectResourceURL,
|
||||
startAuthorization,
|
||||
} from "@modelcontextprotocol/client";
|
||||
import type { OAuthClientProvider } from "@modelcontextprotocol/client";
|
||||
import type { FetchLike } from "@modelcontextprotocol/client";
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,5 +1,12 @@
|
|||
import type { OAuthClientInformation, OAuthTokens, OAuthMetadata } from "@modelcontextprotocol/client";
|
||||
import { OAuthClientInformationSchema, OAuthTokensSchema } from "@modelcontextprotocol/core";
|
||||
import type {
|
||||
OAuthClientInformation,
|
||||
OAuthTokens,
|
||||
OAuthMetadata,
|
||||
} from "@modelcontextprotocol/client";
|
||||
import {
|
||||
OAuthClientInformationSchema,
|
||||
OAuthTokensSchema,
|
||||
} from "@modelcontextprotocol/core";
|
||||
import type { OAuthStorage } from "./storage.js";
|
||||
import { type OAuthMemoryStore, type ServerOAuthState } from "./store.js";
|
||||
import type { OAuthPersistBackend } from "./oauth-persist.js";
|
||||
|
|
@ -173,7 +180,9 @@ export class OAuthStorageBase implements OAuthStorage {
|
|||
|
||||
async clearCodeVerifier(serverUrl: string): Promise<void> {
|
||||
await this.ensureLoaded();
|
||||
this.memory.getState().setServerState(serverUrl, { codeVerifier: undefined });
|
||||
this.memory
|
||||
.getState()
|
||||
.setServerState(serverUrl, { codeVerifier: undefined });
|
||||
await this.persist();
|
||||
}
|
||||
|
||||
|
|
@ -206,7 +215,9 @@ export class OAuthStorageBase implements OAuthStorage {
|
|||
metadata: OAuthMetadata,
|
||||
): Promise<void> {
|
||||
await this.ensureLoaded();
|
||||
this.memory.getState().setServerState(serverUrl, { serverMetadata: metadata });
|
||||
this.memory
|
||||
.getState()
|
||||
.setServerState(serverUrl, { serverMetadata: metadata });
|
||||
await this.persist();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,12 @@ import type {
|
|||
OAuthClientProvider,
|
||||
OAuthClientInformationContext,
|
||||
} from "@modelcontextprotocol/client";
|
||||
import type { OAuthClientInformation, OAuthClientMetadata, OAuthTokens, OAuthMetadata } from "@modelcontextprotocol/client";
|
||||
import type {
|
||||
OAuthClientInformation,
|
||||
OAuthClientMetadata,
|
||||
OAuthTokens,
|
||||
OAuthMetadata,
|
||||
} from "@modelcontextprotocol/client";
|
||||
import type { OAuthStorage, SaveClientInformationOptions } from "./storage.js";
|
||||
import { generateOAuthState } from "./utils.js";
|
||||
|
||||
|
|
@ -210,9 +215,13 @@ export class BaseOAuthClientProvider implements OAuthClientProvider {
|
|||
options && "registrationKind" in options
|
||||
? options.registrationKind
|
||||
: "dcr";
|
||||
await this.storage.saveClientInformation(this.serverUrl, clientInformation, {
|
||||
registrationKind,
|
||||
});
|
||||
await this.storage.saveClientInformation(
|
||||
this.serverUrl,
|
||||
clientInformation,
|
||||
{
|
||||
registrationKind,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
async saveScope(scope: string | undefined): Promise<void> {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
import type { OAuthClientInformation, OAuthTokens, OAuthMetadata } from "@modelcontextprotocol/client";
|
||||
import type {
|
||||
OAuthClientInformation,
|
||||
OAuthTokens,
|
||||
OAuthMetadata,
|
||||
} from "@modelcontextprotocol/client";
|
||||
import type { OAuthClientRegistrationKind } from "./types.js";
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -3,7 +3,11 @@
|
|||
* Persisted via `OAuthStorageBase` + `OAuthPersistBackend` backends.
|
||||
*/
|
||||
|
||||
import type { OAuthClientInformation, OAuthTokens, OAuthMetadata } from "@modelcontextprotocol/client";
|
||||
import type {
|
||||
OAuthClientInformation,
|
||||
OAuthTokens,
|
||||
OAuthMetadata,
|
||||
} from "@modelcontextprotocol/client";
|
||||
import type {
|
||||
IdpSessionState,
|
||||
OAuthClientRegistrationKind,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,10 @@
|
|||
import type { OAuthMetadata, OAuthClientInformation, OAuthClientInformationFull, OAuthTokens, OAuthProtectedResourceMetadata } from "@modelcontextprotocol/client";
|
||||
import type {
|
||||
OAuthMetadata,
|
||||
OAuthClientInformation,
|
||||
OAuthClientInformationFull,
|
||||
OAuthTokens,
|
||||
OAuthProtectedResourceMetadata,
|
||||
} from "@modelcontextprotocol/client";
|
||||
|
||||
// OAuth flow steps
|
||||
export type OAuthStep =
|
||||
|
|
|
|||
Loading…
Reference in a new issue