Skip to content

Commit 19f01e1

Browse files
committed
wip clean up some
1 parent be22205 commit 19f01e1

File tree

5 files changed

+115
-83
lines changed

5 files changed

+115
-83
lines changed

client/src/App.tsx

Lines changed: 29 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,12 @@ import {
3636
FolderTree,
3737
Hammer,
3838
Hash,
39-
Key,
4039
MessageSquare,
4140
} from "lucide-react";
4241

4342
import { z } from "zod";
4443
import "./App.css";
45-
import AuthDebugger from "./components/AuthDebugger";
44+
const AuthDebugger = React.lazy(() => import("./components/AuthDebugger"));
4645
import ConsoleTab from "./components/ConsoleTab";
4746
import HistoryAndNotifications from "./components/History";
4847
import PingTab from "./components/PingTab";
@@ -139,7 +138,7 @@ const App = () => {
139138
}
140139
>
141140
>([]);
142-
const [showAuthDebugger, setShowAuthDebugger] = useState(false);
141+
const [isAuthDebuggerVisible, setIsAuthDebuggerVisible] = useState(false);
143142
const nextRequestId = useRef(0);
144143
const rootsRef = useRef<Root[]>([]);
145144

@@ -244,10 +243,7 @@ const App = () => {
244243

245244
// Auto-connect to previously saved serverURL after OAuth callback
246245
const onOAuthDebugConnect = useCallback(() => {
247-
// setSseUrl(serverUrl);
248-
// setTransportType("sse");
249-
// void connectMcpServer();
250-
setShowAuthDebugger(true);
246+
setIsAuthDebuggerVisible(true);
251247
}, []);
252248

253249
useEffect(() => {
@@ -483,26 +479,35 @@ const App = () => {
483479
setStdErrNotifications([]);
484480
};
485481

486-
if (window.location.pathname === "/oauth/callback") {
487-
const OAuthCallback = React.lazy(
488-
() => import("./components/OAuthCallback"),
489-
);
482+
// Helper component for rendering the AuthDebugger
483+
const AuthDebuggerWrapper = () => (
484+
<Suspense fallback={<div>Loading...</div>}>
485+
<AuthDebugger
486+
sseUrl={sseUrl}
487+
onBack={() => setIsAuthDebuggerVisible(false)}
488+
/>
489+
</Suspense>
490+
);
491+
492+
// Helper function to render OAuth callback components
493+
const renderOAuthCallback = (path: string, onConnect: (serverUrl: string) => void) => {
494+
const Component = path === "/oauth/callback"
495+
? React.lazy(() => import("./components/OAuthCallback"))
496+
: React.lazy(() => import("./components/OAuthDebugCallback"));
497+
490498
return (
491499
<Suspense fallback={<div>Loading...</div>}>
492-
<OAuthCallback onConnect={onOAuthConnect} />
500+
<Component onConnect={onConnect} />
493501
</Suspense>
494502
);
503+
};
504+
505+
if (window.location.pathname === "/oauth/callback") {
506+
return renderOAuthCallback(window.location.pathname, onOAuthConnect);
495507
}
496508

497509
if (window.location.pathname === "/oauth/callback/debug") {
498-
const OAuthCallback = React.lazy(
499-
() => import("./components/OAuthDebugCallback"),
500-
);
501-
return (
502-
<Suspense fallback={<div>Loading...</div>}>
503-
<OAuthCallback onConnect={onOAuthDebugConnect} />
504-
</Suspense>
505-
);
510+
return renderOAuthCallback(window.location.pathname, onOAuthDebugConnect);
506511
}
507512

508513
return (
@@ -592,10 +597,6 @@ const App = () => {
592597
<FolderTree className="w-4 h-4 mr-2" />
593598
Roots
594599
</TabsTrigger>
595-
<TabsTrigger value="auth">
596-
<Key className="w-4 h-4 mr-2" />
597-
Auth
598-
</TabsTrigger>
599600
</TabsList>
600601

601602
<div className="w-full">
@@ -727,19 +728,13 @@ const App = () => {
727728
setRoots={setRoots}
728729
onRootsChange={handleRootsChange}
729730
/>
730-
<AuthDebugger
731-
sseUrl={sseUrl}
732-
onBack={() => setShowAuthDebugger(false)}
733-
/>
731+
<AuthDebuggerWrapper />
734732
</>
735733
)}
736734
</div>
737735
</Tabs>
738-
) : showAuthDebugger ? (
739-
<AuthDebugger
740-
sseUrl={sseUrl}
741-
onBack={() => setShowAuthDebugger(false)}
742-
/>
736+
) : isAuthDebuggerVisible ? (
737+
<AuthDebuggerWrapper />
743738
) : (
744739
<div className="flex flex-col items-center justify-center h-full gap-4">
745740
<p className="text-lg text-gray-500">
@@ -752,7 +747,7 @@ const App = () => {
752747
<Button
753748
variant="outline"
754749
size="sm"
755-
onClick={() => setShowAuthDebugger(true)}
750+
onClick={() => setIsAuthDebuggerVisible(true)}
756751
>
757752
Open Auth Settings
758753
</Button>

client/src/components/AuthDebugger.tsx

Lines changed: 64 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ import {
2020
} from "@modelcontextprotocol/sdk/shared/auth.js";
2121
import { CheckCircle2, Circle, ExternalLink } from "lucide-react";
2222

23+
// Type for the toast function from the useToast hook
24+
type ToastFunction = ReturnType<typeof useToast>["toast"];
25+
2326
interface AuthDebuggerProps {
2427
sseUrl: string;
2528
onBack: () => void;
@@ -37,14 +40,14 @@ type OAuthStep =
3740

3841
// Enhanced version of the OAuth client provider specifically for debug flows
3942
class DebugInspectorOAuthClientProvider extends InspectorOAuthClientProvider {
40-
get redirectUrl() {
41-
return window.location.origin + "/oauth/callback/debug";
43+
get redirectUrl(): string {
44+
return `${window.location.origin}/oauth/callback/debug`;
4245
}
4346
}
4447

4548
const validateOAuthMetadata = (
4649
metadata: OAuthMetadata | null,
47-
toast: (arg0: object) => void,
50+
toast: ToastFunction,
4851
): OAuthMetadata => {
4952
if (!metadata) {
5053
toast({
@@ -59,7 +62,7 @@ const validateOAuthMetadata = (
5962

6063
const validateClientInformation = async (
6164
provider: DebugInspectorOAuthClientProvider,
62-
toast: (arg0: object) => void,
65+
toast: ToastFunction,
6366
): Promise<OAuthClientInformation> => {
6467
const clientInformation = await provider.clientInformation();
6568

@@ -115,25 +118,29 @@ const AuthDebugger = ({ sseUrl, onBack }: AuthDebuggerProps) => {
115118
loadOAuthTokens();
116119
}, [sseUrl]); // Check for debug callback code
117120

121+
// Check for debug callback code and load client info
118122
useEffect(() => {
119-
const loadSessionInfo = async () => {
120-
const debugCode = sessionStorage.getItem(SESSION_KEYS.DEBUG_CODE);
121-
if (debugCode && sseUrl) {
122-
// We've returned from a debug OAuth callback with a code
123-
setAuthorizationCode(debugCode);
124-
setOAuthFlowVisible(true);
125-
126-
// Set the OAuth flow step to token request
127-
setOAuthStep("token_request");
128-
const provider = new DebugInspectorOAuthClientProvider(sseUrl);
129-
setOAuthClientInfo((await provider.clientInformation()) || null);
130-
131-
// Now that we've processed it, clear the debug code
132-
sessionStorage.removeItem(SESSION_KEYS.DEBUG_CODE);
133-
}
134-
};
123+
const debugCode = sessionStorage.getItem(SESSION_KEYS.DEBUG_CODE);
124+
if (debugCode && sseUrl) {
125+
// We've returned from a debug OAuth callback with a code
126+
setAuthorizationCode(debugCode);
127+
setOAuthFlowVisible(true);
128+
setOAuthStep("token_request");
129+
130+
// Load client info asynchronously
131+
const provider = new DebugInspectorOAuthClientProvider(sseUrl);
132+
provider
133+
.clientInformation()
134+
.then((info) => {
135+
setOAuthClientInfo(info || null);
136+
})
137+
.catch((error) => {
138+
console.error("Failed to load client information:", error);
139+
});
135140

136-
loadSessionInfo();
141+
// Now that we've processed it, clear the debug code
142+
sessionStorage.removeItem(SESSION_KEYS.DEBUG_CODE);
143+
}
137144
}, [sseUrl]);
138145

139146
const startOAuthFlow = () => {
@@ -177,39 +184,56 @@ const AuthDebugger = ({ sseUrl, onBack }: AuthDebuggerProps) => {
177184

178185
setOAuthStep("client_registration");
179186

187+
const clientMetadata = provider.clientMetadata;
188+
// Add all supported scopes to client registration.
189+
// This is the maximal set of scopes the client can request, the
190+
// scope of the actual token is specified below
191+
if (metadata.scopes_supported) {
192+
// TODO: add this to schema
193+
clientMetadata["scope"] = metadata.scopes_supported.join(" ");
194+
}
195+
180196
const fullInformation = await registerClient(sseUrl, {
181197
metadata,
182-
clientMetadata: provider.clientMetadata,
198+
clientMetadata,
183199
});
184200

185201
provider.saveClientInformation(fullInformation);
202+
// save it here to be more convenient for display
203+
setOAuthClientInfo(fullInformation);
186204
} else if (oauthStep === "client_registration") {
187205
const metadata = validateOAuthMetadata(oauthMetadata, toast);
188206
const clientInformation = await validateClientInformation(
189207
provider,
190208
toast,
191209
);
192210
setOAuthStep("authorization_redirect");
193-
// This custom implementation captures the OAuth flow step by step
194-
// First, get or register the client
195211
try {
196212
const { authorizationUrl, codeVerifier } = await startAuthorization(
197213
sseUrl,
198214
{
199215
metadata,
200216
clientInformation,
201217
redirectUrl: provider.redirectUrl,
218+
// TODO: fix this once SDK PR is merged
219+
// scope: metadata.scopes_supported,
202220
},
203221
);
204222

205223
provider.saveCodeVerifier(codeVerifier);
206-
// Save this so the debug callback knows what to do
207-
// await sessionStorage.setItem(SESSION_KEYS.SERVER_URL, sseUrl);
208-
setAuthorizationUrl(authorizationUrl.toString());
209-
// await provider.redirectToAuthorization(authorizationUrl);
210-
setOAuthStep("authorization_code");
211224

212-
// await auth(serverAuthProvider, { serverUrl: sseUrl });
225+
// TODO: remove this once scope is valid parameter above
226+
// Modify the authorization URL to include all supported scopes
227+
if (metadata.scopes_supported) {
228+
//Add all supported scopes to the authorization URL
229+
const url = new URL(authorizationUrl.toString());
230+
url.searchParams.set("scope", metadata.scopes_supported.join(" "));
231+
setAuthorizationUrl(url.toString());
232+
} else {
233+
setAuthorizationUrl(authorizationUrl.toString());
234+
}
235+
236+
setOAuthStep("authorization_code");
213237
} catch (error) {
214238
console.error("OAuth flow step error:", error);
215239
toast({
@@ -237,7 +261,6 @@ const AuthDebugger = ({ sseUrl, onBack }: AuthDebuggerProps) => {
237261
toast,
238262
);
239263

240-
// const clientInformation = await provider.clientInformation();
241264
const tokens = await exchangeAuthorization(sseUrl, {
242265
metadata,
243266
clientInformation,
@@ -308,6 +331,7 @@ const AuthDebugger = ({ sseUrl, onBack }: AuthDebuggerProps) => {
308331
setOAuthStep("not_started");
309332
setOAuthFlowVisible(false);
310333
setLatestError(null);
334+
setOAuthClientInfo(null);
311335
setAuthorizationCode("");
312336
toast({
313337
title: "Success",
@@ -329,7 +353,8 @@ const AuthDebugger = ({ sseUrl, onBack }: AuthDebuggerProps) => {
329353
metadata: oauthMetadata && (
330354
<details className="text-xs mt-2">
331355
<summary className="cursor-pointer text-muted-foreground font-medium">
332-
Retrieved OAuth Metadata
356+
Retrieved OAuth Metadata from {sseUrl}
357+
/.well-known/oauth-authorization-server
333358
</summary>
334359
<pre className="mt-2 p-2 bg-muted rounded-md overflow-auto max-h-[300px]">
335360
{JSON.stringify(oauthMetadata, null, 2)}
@@ -364,6 +389,8 @@ const AuthDebugger = ({ sseUrl, onBack }: AuthDebuggerProps) => {
364389
target="_blank"
365390
rel="noopener noreferrer"
366391
className="flex items-center text-blue-500 hover:text-blue-700"
392+
aria-label="Open authorization URL in new tab"
393+
title="Open authorization URL"
367394
>
368395
<ExternalLink className="h-4 w-4" />
369396
</a>
@@ -428,7 +455,11 @@ const AuthDebugger = ({ sseUrl, onBack }: AuthDebuggerProps) => {
428455
<summary className="cursor-pointer text-muted-foreground font-medium">
429456
Access Tokens
430457
</summary>
431-
<p>Try listTools to use these credentials</p>
458+
<p className="mt-1 text-sm">
459+
Authentication successful! You can now use the authenticated
460+
connection. These tokens will be used automatically for server
461+
requests.
462+
</p>
432463
<pre className="mt-2 p-2 bg-muted rounded-md overflow-auto max-h-[300px]">
433464
{JSON.stringify(oauthTokens, null, 2)}
434465
</pre>

0 commit comments

Comments
 (0)