Merge remote-tracking branch 'origin/v2/main' into 1579-wave-1

# Conflicts:
#	clients/cli/package-lock.json
#	clients/cli/package.json
#	clients/web/src/App.tsx
#	clients/web/src/lib/environmentFactory.test.ts
#	clients/web/src/lib/environmentFactory.ts
#	clients/web/src/lib/remoteOAuthStorage.test.ts
#	clients/web/src/lib/remoteOAuthStorage.ts
#	clients/web/src/test/core/auth/connection-state.test.ts
#	clients/web/src/test/core/auth/providers.test.ts
#	clients/web/src/test/core/auth/storage-browser.test.ts
#	clients/web/src/test/core/auth/storage-remote.test.ts
#	clients/web/src/test/core/mcp/oauthManager.test.ts
#	clients/web/src/test/integration/auth/node/storage.test.ts
#	clients/web/src/test/integration/storage/adapters.test.ts
#	clients/web/src/utils/clearServerOAuthState.test.ts
#	clients/web/src/utils/clearServerOAuthState.ts
#	core/auth/connection-state.ts
#	core/auth/oauth-storage.ts
#	core/auth/providers.ts
#	core/auth/storage.ts
#	core/auth/store.ts
#	core/storage/adapters/remote-storage.ts
This commit is contained in:
cliffhall 2026-07-09 09:41:08 -04:00
commit 32a3610737

View file

@ -86,10 +86,7 @@ import { useServers } from "@inspector/core/react/useServers.js";
import { useSettingsDraft } from "@inspector/core/react/useSettingsDraft.js";
import { useClientSettingsDraft } from "@inspector/core/react/useClientSettingsDraft.js";
import { useEmaIdpLoginState } from "@inspector/core/react/useEmaIdpLoginState.js";
import {
getRemoteOAuthStorage,
getWebOAuthBaseUrl,
} from "./lib/remoteOAuthStorage";
import { getWebRemoteOAuthStorage } from "./lib/remoteOAuthStorage";
import { useManagedTools } from "@inspector/core/react/useManagedTools.js";
import { useManagedPrompts } from "@inspector/core/react/useManagedPrompts.js";
import { useManagedResources } from "@inspector/core/react/useManagedResources.js";
@ -1431,6 +1428,13 @@ function App() {
[messages],
);
// Shared OAuth runtime store (oauth.json via /api/storage/oauth). Memoized so
// connect, EMA IdP session, and per-server clear share one in-memory view.
const webOAuthStorage = useMemo(
() => getWebRemoteOAuthStorage(getAuthToken()),
[],
);
// Backend-backed session storage used to carry the fetch (Network) log
// across the OAuth full-page redirect. The auth handshake's first half —
// protected-resource + auth-server discovery and Dynamic Client
@ -2101,10 +2105,10 @@ function App() {
// `onToggleConnection` unloaded the previous one), so all React state is
// reset and we recover the initiating server from sessionStorage. We wait for
// `servers` to hydrate before acting; the ref guard keeps the exchange to a
// single run. The persisted PKCE verifier + DCR client info live in the
// backend-backed `RemoteOAuthStorage` (`~/.mcp-inspector/storage/oauth.json`)
// and survive the redirect, so `completeOAuthFlow` exchanges the code without
// needing the original in-memory state machine.
// single run. The persisted PKCE verifier + DCR client info live in shared
// `RemoteOAuthStorage` (`oauth.json`) and survive the redirect, so
// `completeOAuthFlow` exchanges the code without needing the original
// in-memory state machine.
useEffect(() => {
if (typeof window === "undefined") return;
if (window.location.pathname !== OAUTH_CALLBACK_PATH) return;
@ -2212,6 +2216,15 @@ function App() {
}
void (async () => {
try {
await webOAuthStorage.load();
} catch (err) {
connectStartRef.current = undefined;
queueMicrotask(() => {
showReAuthBanner(server.id, err instanceof Error ? err : String(err));
});
return;
}
const client = setupClientForServer(server, sessionId);
setActiveServerId(server.id);
try {
@ -2267,7 +2280,7 @@ function App() {
});
}
})();
}, [servers, setupClientForServer, showReAuthBanner]);
}, [servers, setupClientForServer, showReAuthBanner, webOAuthStorage]);
const onToggleConnection = useCallback(
async (id: string) => {
@ -3268,13 +3281,9 @@ function App() {
const clientSettingsModalValue = clientSettingsDraft ?? EMPTY_CLIENT_SETTINGS;
const emaOAuthStorage = useMemo(
() => getRemoteOAuthStorage(getWebOAuthBaseUrl(), getAuthToken()),
[],
);
const { loginState: emaIdpLoginState, logout: logoutEmaIdp } =
useEmaIdpLoginState(
emaOAuthStorage,
webOAuthStorage,
clientSettingsModalValue.emaEnabled
? clientSettingsModalValue.issuer
: undefined,
@ -3298,14 +3307,11 @@ function App() {
const clearServerOAuthAndDisconnect = useCallback(
async (server: { id: string; name: string; config: MCPServerConfig }) => {
const isActive = server.id === activeServerId;
const cleared = clearServerOAuthState({
const cleared = await clearServerOAuthState({
config: server.config,
inspectorClient: isActive ? inspectorClient : null,
isActiveConnection: isActive,
oauthStorage: getRemoteOAuthStorage(
getWebOAuthBaseUrl(),
getAuthToken(),
),
oauthStorage: webOAuthStorage,
});
if (!cleared) return;
@ -3331,6 +3337,7 @@ function App() {
[
activeServerId,
inspectorClient,
webOAuthStorage,
finalizeExplicitDisconnect,
clearOAuthResumeOnExplicitDisconnect,
],