@@ -20,6 +20,9 @@ import {
2020} from "@modelcontextprotocol/sdk/shared/auth.js" ;
2121import { 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+
2326interface 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
3942class 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
4548const 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
6063const 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