Merge branch 'main' into feature/elicitations
This commit is contained in:
commit
eda6c05ba1
7 changed files with 237 additions and 67 deletions
|
|
@ -123,6 +123,14 @@ const App = () => {
|
||||||
return localStorage.getItem("lastHeaderName") || "";
|
return localStorage.getItem("lastHeaderName") || "";
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [oauthClientId, setOauthClientId] = useState<string>(() => {
|
||||||
|
return localStorage.getItem("lastOauthClientId") || "";
|
||||||
|
});
|
||||||
|
|
||||||
|
const [oauthScope, setOauthScope] = useState<string>(() => {
|
||||||
|
return localStorage.getItem("lastOauthScope") || "";
|
||||||
|
});
|
||||||
|
|
||||||
const [pendingSampleRequests, setPendingSampleRequests] = useState<
|
const [pendingSampleRequests, setPendingSampleRequests] = useState<
|
||||||
Array<
|
Array<
|
||||||
PendingRequest & {
|
PendingRequest & {
|
||||||
|
|
@ -210,6 +218,8 @@ const App = () => {
|
||||||
env,
|
env,
|
||||||
bearerToken,
|
bearerToken,
|
||||||
headerName,
|
headerName,
|
||||||
|
oauthClientId,
|
||||||
|
oauthScope,
|
||||||
config,
|
config,
|
||||||
onNotification: (notification) => {
|
onNotification: (notification) => {
|
||||||
setNotifications((prev) => [...prev, notification as ServerNotification]);
|
setNotifications((prev) => [...prev, notification as ServerNotification]);
|
||||||
|
|
@ -308,6 +318,14 @@ const App = () => {
|
||||||
localStorage.setItem("lastHeaderName", headerName);
|
localStorage.setItem("lastHeaderName", headerName);
|
||||||
}, [headerName]);
|
}, [headerName]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
localStorage.setItem("lastOauthClientId", oauthClientId);
|
||||||
|
}, [oauthClientId]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
localStorage.setItem("lastOauthScope", oauthScope);
|
||||||
|
}, [oauthScope]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
saveInspectorConfig(CONFIG_LOCAL_STORAGE_KEY, config);
|
saveInspectorConfig(CONFIG_LOCAL_STORAGE_KEY, config);
|
||||||
}, [config]);
|
}, [config]);
|
||||||
|
|
@ -782,6 +800,10 @@ const App = () => {
|
||||||
setBearerToken={setBearerToken}
|
setBearerToken={setBearerToken}
|
||||||
headerName={headerName}
|
headerName={headerName}
|
||||||
setHeaderName={setHeaderName}
|
setHeaderName={setHeaderName}
|
||||||
|
oauthClientId={oauthClientId}
|
||||||
|
setOauthClientId={setOauthClientId}
|
||||||
|
oauthScope={oauthScope}
|
||||||
|
setOauthScope={setOauthScope}
|
||||||
onConnect={connectMcpServer}
|
onConnect={connectMcpServer}
|
||||||
onDisconnect={disconnectMcpServer}
|
onDisconnect={disconnectMcpServer}
|
||||||
stdErrNotifications={stdErrNotifications}
|
stdErrNotifications={stdErrNotifications}
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,10 @@ interface SidebarProps {
|
||||||
setBearerToken: (token: string) => void;
|
setBearerToken: (token: string) => void;
|
||||||
headerName?: string;
|
headerName?: string;
|
||||||
setHeaderName?: (name: string) => void;
|
setHeaderName?: (name: string) => void;
|
||||||
|
oauthClientId: string;
|
||||||
|
setOauthClientId: (id: string) => void;
|
||||||
|
oauthScope: string;
|
||||||
|
setOauthScope: (scope: string) => void;
|
||||||
onConnect: () => void;
|
onConnect: () => void;
|
||||||
onDisconnect: () => void;
|
onDisconnect: () => void;
|
||||||
stdErrNotifications: StdErrNotification[];
|
stdErrNotifications: StdErrNotification[];
|
||||||
|
|
@ -83,6 +87,10 @@ const Sidebar = ({
|
||||||
setBearerToken,
|
setBearerToken,
|
||||||
headerName,
|
headerName,
|
||||||
setHeaderName,
|
setHeaderName,
|
||||||
|
oauthClientId,
|
||||||
|
setOauthClientId,
|
||||||
|
oauthScope,
|
||||||
|
setOauthScope,
|
||||||
onConnect,
|
onConnect,
|
||||||
onDisconnect,
|
onDisconnect,
|
||||||
stdErrNotifications,
|
stdErrNotifications,
|
||||||
|
|
@ -95,7 +103,7 @@ const Sidebar = ({
|
||||||
}: SidebarProps) => {
|
}: SidebarProps) => {
|
||||||
const [theme, setTheme] = useTheme();
|
const [theme, setTheme] = useTheme();
|
||||||
const [showEnvVars, setShowEnvVars] = useState(false);
|
const [showEnvVars, setShowEnvVars] = useState(false);
|
||||||
const [showBearerToken, setShowBearerToken] = useState(false);
|
const [showAuthConfig, setShowAuthConfig] = useState(false);
|
||||||
const [showConfig, setShowConfig] = useState(false);
|
const [showConfig, setShowConfig] = useState(false);
|
||||||
const [shownEnvVars, setShownEnvVars] = useState<Set<string>>(new Set());
|
const [shownEnvVars, setShownEnvVars] = useState<Set<string>>(new Set());
|
||||||
const [copiedServerEntry, setCopiedServerEntry] = useState(false);
|
const [copiedServerEntry, setCopiedServerEntry] = useState(false);
|
||||||
|
|
@ -308,51 +316,6 @@ const Sidebar = ({
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
onClick={() => setShowBearerToken(!showBearerToken)}
|
|
||||||
className="flex items-center w-full"
|
|
||||||
data-testid="auth-button"
|
|
||||||
aria-expanded={showBearerToken}
|
|
||||||
>
|
|
||||||
{showBearerToken ? (
|
|
||||||
<ChevronDown className="w-4 h-4 mr-2" />
|
|
||||||
) : (
|
|
||||||
<ChevronRight className="w-4 h-4 mr-2" />
|
|
||||||
)}
|
|
||||||
Authentication
|
|
||||||
</Button>
|
|
||||||
{showBearerToken && (
|
|
||||||
<div className="space-y-2">
|
|
||||||
<label className="text-sm font-medium">Header Name</label>
|
|
||||||
<Input
|
|
||||||
placeholder="Authorization"
|
|
||||||
onChange={(e) =>
|
|
||||||
setHeaderName && setHeaderName(e.target.value)
|
|
||||||
}
|
|
||||||
data-testid="header-input"
|
|
||||||
className="font-mono"
|
|
||||||
value={headerName}
|
|
||||||
/>
|
|
||||||
<label
|
|
||||||
className="text-sm font-medium"
|
|
||||||
htmlFor="bearer-token-input"
|
|
||||||
>
|
|
||||||
Bearer Token
|
|
||||||
</label>
|
|
||||||
<Input
|
|
||||||
id="bearer-token-input"
|
|
||||||
placeholder="Bearer Token"
|
|
||||||
value={bearerToken}
|
|
||||||
onChange={(e) => setBearerToken(e.target.value)}
|
|
||||||
data-testid="bearer-token-input"
|
|
||||||
className="font-mono"
|
|
||||||
type="password"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
@ -521,6 +484,94 @@ const Sidebar = ({
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => setShowAuthConfig(!showAuthConfig)}
|
||||||
|
className="flex items-center w-full"
|
||||||
|
data-testid="auth-button"
|
||||||
|
aria-expanded={showAuthConfig}
|
||||||
|
>
|
||||||
|
{showAuthConfig ? (
|
||||||
|
<ChevronDown className="w-4 h-4 mr-2" />
|
||||||
|
) : (
|
||||||
|
<ChevronRight className="w-4 h-4 mr-2" />
|
||||||
|
)}
|
||||||
|
Authentication
|
||||||
|
</Button>
|
||||||
|
{showAuthConfig && (
|
||||||
|
<>
|
||||||
|
{/* Bearer Token Section */}
|
||||||
|
<div className="space-y-2 p-3 rounded border">
|
||||||
|
<h4 className="text-sm font-semibold flex items-center">
|
||||||
|
API Token Authentication
|
||||||
|
</h4>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="text-sm font-medium">Header Name</label>
|
||||||
|
<Input
|
||||||
|
placeholder="Authorization"
|
||||||
|
onChange={(e) =>
|
||||||
|
setHeaderName && setHeaderName(e.target.value)
|
||||||
|
}
|
||||||
|
data-testid="header-input"
|
||||||
|
className="font-mono"
|
||||||
|
value={headerName}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
className="text-sm font-medium"
|
||||||
|
htmlFor="bearer-token-input"
|
||||||
|
>
|
||||||
|
Bearer Token
|
||||||
|
</label>
|
||||||
|
<Input
|
||||||
|
id="bearer-token-input"
|
||||||
|
placeholder="Bearer Token"
|
||||||
|
value={bearerToken}
|
||||||
|
onChange={(e) => setBearerToken(e.target.value)}
|
||||||
|
data-testid="bearer-token-input"
|
||||||
|
className="font-mono"
|
||||||
|
type="password"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{transportType !== "stdio" && (
|
||||||
|
// OAuth Configuration
|
||||||
|
<div className="space-y-2 p-3 rounded border">
|
||||||
|
<h4 className="text-sm font-semibold flex items-center">
|
||||||
|
OAuth 2.0 Flow
|
||||||
|
</h4>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="text-sm font-medium">Client ID</label>
|
||||||
|
<Input
|
||||||
|
placeholder="Client ID"
|
||||||
|
onChange={(e) => setOauthClientId(e.target.value)}
|
||||||
|
value={oauthClientId}
|
||||||
|
data-testid="oauth-client-id-input"
|
||||||
|
className="font-mono"
|
||||||
|
/>
|
||||||
|
<label className="text-sm font-medium">
|
||||||
|
Redirect URL
|
||||||
|
</label>
|
||||||
|
<Input
|
||||||
|
readOnly
|
||||||
|
placeholder="Redirect URL"
|
||||||
|
value={window.location.origin + "/oauth/callback"}
|
||||||
|
className="font-mono"
|
||||||
|
/>
|
||||||
|
<label className="text-sm font-medium">Scope</label>
|
||||||
|
<Input
|
||||||
|
placeholder="Scope (space-separated)"
|
||||||
|
onChange={(e) => setOauthScope(e.target.value)}
|
||||||
|
value={oauthScope}
|
||||||
|
data-testid="oauth-scope-input"
|
||||||
|
className="font-mono"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
{/* Configuration */}
|
{/* Configuration */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Button
|
<Button
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,10 @@ describe("Sidebar Environment Variables", () => {
|
||||||
setArgs: jest.fn(),
|
setArgs: jest.fn(),
|
||||||
sseUrl: "",
|
sseUrl: "",
|
||||||
setSseUrl: jest.fn(),
|
setSseUrl: jest.fn(),
|
||||||
|
oauthClientId: "",
|
||||||
|
setOauthClientId: jest.fn(),
|
||||||
|
oauthScope: "",
|
||||||
|
setOauthScope: jest.fn(),
|
||||||
env: {},
|
env: {},
|
||||||
setEnv: jest.fn(),
|
setEnv: jest.fn(),
|
||||||
bearerToken: "",
|
bearerToken: "",
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,64 @@ import {
|
||||||
} from "@modelcontextprotocol/sdk/shared/auth.js";
|
} from "@modelcontextprotocol/sdk/shared/auth.js";
|
||||||
import { SESSION_KEYS, getServerSpecificKey } from "./constants";
|
import { SESSION_KEYS, getServerSpecificKey } from "./constants";
|
||||||
|
|
||||||
|
export const getClientInformationFromSessionStorage = async ({
|
||||||
|
serverUrl,
|
||||||
|
isPreregistered,
|
||||||
|
}: {
|
||||||
|
serverUrl: string;
|
||||||
|
isPreregistered?: boolean;
|
||||||
|
}) => {
|
||||||
|
const key = getServerSpecificKey(
|
||||||
|
isPreregistered
|
||||||
|
? SESSION_KEYS.PREREGISTERED_CLIENT_INFORMATION
|
||||||
|
: SESSION_KEYS.CLIENT_INFORMATION,
|
||||||
|
serverUrl,
|
||||||
|
);
|
||||||
|
|
||||||
|
const value = sessionStorage.getItem(key);
|
||||||
|
if (!value) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await OAuthClientInformationSchema.parseAsync(JSON.parse(value));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const saveClientInformationToSessionStorage = ({
|
||||||
|
serverUrl,
|
||||||
|
clientInformation,
|
||||||
|
isPreregistered,
|
||||||
|
}: {
|
||||||
|
serverUrl: string;
|
||||||
|
clientInformation: OAuthClientInformation;
|
||||||
|
isPreregistered?: boolean;
|
||||||
|
}) => {
|
||||||
|
const key = getServerSpecificKey(
|
||||||
|
isPreregistered
|
||||||
|
? SESSION_KEYS.PREREGISTERED_CLIENT_INFORMATION
|
||||||
|
: SESSION_KEYS.CLIENT_INFORMATION,
|
||||||
|
serverUrl,
|
||||||
|
);
|
||||||
|
sessionStorage.setItem(key, JSON.stringify(clientInformation));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const clearClientInformationFromSessionStorage = ({
|
||||||
|
serverUrl,
|
||||||
|
isPreregistered,
|
||||||
|
}: {
|
||||||
|
serverUrl: string;
|
||||||
|
isPreregistered?: boolean;
|
||||||
|
}) => {
|
||||||
|
const key = getServerSpecificKey(
|
||||||
|
isPreregistered
|
||||||
|
? SESSION_KEYS.PREREGISTERED_CLIENT_INFORMATION
|
||||||
|
: SESSION_KEYS.CLIENT_INFORMATION,
|
||||||
|
serverUrl,
|
||||||
|
);
|
||||||
|
sessionStorage.removeItem(key);
|
||||||
|
};
|
||||||
|
|
||||||
export class InspectorOAuthClientProvider implements OAuthClientProvider {
|
export class InspectorOAuthClientProvider implements OAuthClientProvider {
|
||||||
constructor(public serverUrl: string) {
|
constructor(protected serverUrl: string) {
|
||||||
// Save the server URL to session storage
|
// Save the server URL to session storage
|
||||||
sessionStorage.setItem(SESSION_KEYS.SERVER_URL, serverUrl);
|
sessionStorage.setItem(SESSION_KEYS.SERVER_URL, serverUrl);
|
||||||
}
|
}
|
||||||
|
|
@ -31,24 +87,30 @@ export class InspectorOAuthClientProvider implements OAuthClientProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
async clientInformation() {
|
async clientInformation() {
|
||||||
const key = getServerSpecificKey(
|
// Try to get the preregistered client information from session storage first
|
||||||
SESSION_KEYS.CLIENT_INFORMATION,
|
const preregisteredClientInformation =
|
||||||
this.serverUrl,
|
await getClientInformationFromSessionStorage({
|
||||||
);
|
serverUrl: this.serverUrl,
|
||||||
const value = sessionStorage.getItem(key);
|
isPreregistered: true,
|
||||||
if (!value) {
|
});
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
return await OAuthClientInformationSchema.parseAsync(JSON.parse(value));
|
// If no preregistered client information is found, get the dynamically registered client information
|
||||||
|
return (
|
||||||
|
preregisteredClientInformation ??
|
||||||
|
(await getClientInformationFromSessionStorage({
|
||||||
|
serverUrl: this.serverUrl,
|
||||||
|
isPreregistered: false,
|
||||||
|
}))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
saveClientInformation(clientInformation: OAuthClientInformation) {
|
saveClientInformation(clientInformation: OAuthClientInformation) {
|
||||||
const key = getServerSpecificKey(
|
// Save the dynamically registered client information to session storage
|
||||||
SESSION_KEYS.CLIENT_INFORMATION,
|
saveClientInformationToSessionStorage({
|
||||||
this.serverUrl,
|
serverUrl: this.serverUrl,
|
||||||
);
|
clientInformation,
|
||||||
sessionStorage.setItem(key, JSON.stringify(clientInformation));
|
isPreregistered: false,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async tokens() {
|
async tokens() {
|
||||||
|
|
@ -92,9 +154,10 @@ export class InspectorOAuthClientProvider implements OAuthClientProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
clear() {
|
clear() {
|
||||||
sessionStorage.removeItem(
|
clearClientInformationFromSessionStorage({
|
||||||
getServerSpecificKey(SESSION_KEYS.CLIENT_INFORMATION, this.serverUrl),
|
serverUrl: this.serverUrl,
|
||||||
);
|
isPreregistered: false,
|
||||||
|
});
|
||||||
sessionStorage.removeItem(
|
sessionStorage.removeItem(
|
||||||
getServerSpecificKey(SESSION_KEYS.TOKENS, this.serverUrl),
|
getServerSpecificKey(SESSION_KEYS.TOKENS, this.serverUrl),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ export const SESSION_KEYS = {
|
||||||
SERVER_URL: "mcp_server_url",
|
SERVER_URL: "mcp_server_url",
|
||||||
TOKENS: "mcp_tokens",
|
TOKENS: "mcp_tokens",
|
||||||
CLIENT_INFORMATION: "mcp_client_information",
|
CLIENT_INFORMATION: "mcp_client_information",
|
||||||
|
PREREGISTERED_CLIENT_INFORMATION: "mcp_preregistered_client_information",
|
||||||
SERVER_METADATA: "mcp_server_metadata",
|
SERVER_METADATA: "mcp_server_metadata",
|
||||||
AUTH_DEBUGGER_STATE: "mcp_auth_debugger_state",
|
AUTH_DEBUGGER_STATE: "mcp_auth_debugger_state",
|
||||||
} as const;
|
} as const;
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,8 @@ jest.mock("../../auth", () => ({
|
||||||
InspectorOAuthClientProvider: jest.fn().mockImplementation(() => ({
|
InspectorOAuthClientProvider: jest.fn().mockImplementation(() => ({
|
||||||
tokens: jest.fn().mockResolvedValue({ access_token: "mock-token" }),
|
tokens: jest.fn().mockResolvedValue({ access_token: "mock-token" }),
|
||||||
})),
|
})),
|
||||||
|
clearClientInformationFromSessionStorage: jest.fn(),
|
||||||
|
saveClientInformationToSessionStorage: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe("useConnection", () => {
|
describe("useConnection", () => {
|
||||||
|
|
|
||||||
|
|
@ -31,13 +31,17 @@ import {
|
||||||
ElicitRequestSchema,
|
ElicitRequestSchema,
|
||||||
} from "@modelcontextprotocol/sdk/types.js";
|
} from "@modelcontextprotocol/sdk/types.js";
|
||||||
import { RequestOptions } from "@modelcontextprotocol/sdk/shared/protocol.js";
|
import { RequestOptions } from "@modelcontextprotocol/sdk/shared/protocol.js";
|
||||||
import { useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useToast } from "@/lib/hooks/useToast";
|
import { useToast } from "@/lib/hooks/useToast";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { ConnectionStatus } from "../constants";
|
import { ConnectionStatus } from "../constants";
|
||||||
import { Notification, StdErrNotificationSchema } from "../notificationTypes";
|
import { Notification, StdErrNotificationSchema } from "../notificationTypes";
|
||||||
import { auth } from "@modelcontextprotocol/sdk/client/auth.js";
|
import { auth } from "@modelcontextprotocol/sdk/client/auth.js";
|
||||||
import { InspectorOAuthClientProvider } from "../auth";
|
import {
|
||||||
|
clearClientInformationFromSessionStorage,
|
||||||
|
InspectorOAuthClientProvider,
|
||||||
|
saveClientInformationToSessionStorage,
|
||||||
|
} from "../auth";
|
||||||
import packageJson from "../../../package.json";
|
import packageJson from "../../../package.json";
|
||||||
import {
|
import {
|
||||||
getMCPProxyAddress,
|
getMCPProxyAddress,
|
||||||
|
|
@ -57,6 +61,8 @@ interface UseConnectionOptions {
|
||||||
env: Record<string, string>;
|
env: Record<string, string>;
|
||||||
bearerToken?: string;
|
bearerToken?: string;
|
||||||
headerName?: string;
|
headerName?: string;
|
||||||
|
oauthClientId?: string;
|
||||||
|
oauthScope?: string;
|
||||||
config: InspectorConfig;
|
config: InspectorConfig;
|
||||||
onNotification?: (notification: Notification) => void;
|
onNotification?: (notification: Notification) => void;
|
||||||
onStdErrNotification?: (notification: Notification) => void;
|
onStdErrNotification?: (notification: Notification) => void;
|
||||||
|
|
@ -76,6 +82,8 @@ export function useConnection({
|
||||||
env,
|
env,
|
||||||
bearerToken,
|
bearerToken,
|
||||||
headerName,
|
headerName,
|
||||||
|
oauthClientId,
|
||||||
|
oauthScope,
|
||||||
config,
|
config,
|
||||||
onNotification,
|
onNotification,
|
||||||
onStdErrNotification,
|
onStdErrNotification,
|
||||||
|
|
@ -97,6 +105,22 @@ export function useConnection({
|
||||||
>([]);
|
>([]);
|
||||||
const [completionsSupported, setCompletionsSupported] = useState(false);
|
const [completionsSupported, setCompletionsSupported] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!oauthClientId) {
|
||||||
|
clearClientInformationFromSessionStorage({
|
||||||
|
serverUrl: sseUrl,
|
||||||
|
isPreregistered: true,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
saveClientInformationToSessionStorage({
|
||||||
|
serverUrl: sseUrl,
|
||||||
|
clientInformation: { client_id: oauthClientId },
|
||||||
|
isPreregistered: true,
|
||||||
|
});
|
||||||
|
}, [oauthClientId, sseUrl]);
|
||||||
|
|
||||||
const pushHistory = (request: object, response?: object) => {
|
const pushHistory = (request: object, response?: object) => {
|
||||||
setRequestHistory((prev) => [
|
setRequestHistory((prev) => [
|
||||||
...prev,
|
...prev,
|
||||||
|
|
@ -283,7 +307,10 @@ export function useConnection({
|
||||||
if (is401Error(error)) {
|
if (is401Error(error)) {
|
||||||
const serverAuthProvider = new InspectorOAuthClientProvider(sseUrl);
|
const serverAuthProvider = new InspectorOAuthClientProvider(sseUrl);
|
||||||
|
|
||||||
const result = await auth(serverAuthProvider, { serverUrl: sseUrl });
|
const result = await auth(serverAuthProvider, {
|
||||||
|
serverUrl: sseUrl,
|
||||||
|
scope: oauthScope,
|
||||||
|
});
|
||||||
return result === "AUTHORIZED";
|
return result === "AUTHORIZED";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue