@@ -17,6 +17,9 @@ import {
17
17
Tool ,
18
18
LoggingLevel ,
19
19
} from "@modelcontextprotocol/sdk/types.js" ;
20
+ import { OAuthTokensSchema } from "@modelcontextprotocol/sdk/shared/auth.js" ;
21
+ import { SESSION_KEYS , getServerSpecificKey } from "./lib/constants" ;
22
+ import { AuthDebuggerState } from "./lib/auth-types" ;
20
23
import React , {
21
24
Suspense ,
22
25
useCallback ,
@@ -28,18 +31,21 @@ import { useConnection } from "./lib/hooks/useConnection";
28
31
import { useDraggablePane } from "./lib/hooks/useDraggablePane" ;
29
32
import { StdErrNotification } from "./lib/notificationTypes" ;
30
33
31
- import { Tabs , TabsList , TabsTrigger } from "@/components/ui/tabs" ;
34
+ import { Tabs , TabsContent , TabsList , TabsTrigger } from "@/components/ui/tabs" ;
35
+ import { Button } from "@/components/ui/button" ;
32
36
import {
33
37
Bell ,
34
38
Files ,
35
39
FolderTree ,
36
40
Hammer ,
37
41
Hash ,
42
+ Key ,
38
43
MessageSquare ,
39
44
} from "lucide-react" ;
40
45
41
46
import { z } from "zod" ;
42
47
import "./App.css" ;
48
+ import AuthDebugger from "./components/AuthDebugger" ;
43
49
import ConsoleTab from "./components/ConsoleTab" ;
44
50
import HistoryAndNotifications from "./components/History" ;
45
51
import PingTab from "./components/PingTab" ;
@@ -111,6 +117,27 @@ const App = () => {
111
117
}
112
118
>
113
119
> ( [ ] ) ;
120
+ const [ isAuthDebuggerVisible , setIsAuthDebuggerVisible ] = useState ( false ) ;
121
+
122
+ // Auth debugger state
123
+ const [ authState , setAuthState ] = useState < AuthDebuggerState > ( {
124
+ isInitiatingAuth : false ,
125
+ oauthTokens : null ,
126
+ loading : true ,
127
+ oauthStep : "metadata_discovery" ,
128
+ oauthMetadata : null ,
129
+ oauthClientInfo : null ,
130
+ authorizationUrl : null ,
131
+ authorizationCode : "" ,
132
+ latestError : null ,
133
+ statusMessage : null ,
134
+ validationError : null ,
135
+ } ) ;
136
+
137
+ // Helper function to update specific auth state properties
138
+ const updateAuthState = ( updates : Partial < AuthDebuggerState > ) => {
139
+ setAuthState ( ( prev ) => ( { ...prev , ...updates } ) ) ;
140
+ } ;
114
141
const nextRequestId = useRef ( 0 ) ;
115
142
const rootsRef = useRef < Root [ ] > ( [ ] ) ;
116
143
@@ -208,11 +235,64 @@ const App = () => {
208
235
( serverUrl : string ) => {
209
236
setSseUrl ( serverUrl ) ;
210
237
setTransportType ( "sse" ) ;
238
+ setIsAuthDebuggerVisible ( false ) ;
211
239
void connectMcpServer ( ) ;
212
240
} ,
213
241
[ connectMcpServer ] ,
214
242
) ;
215
243
244
+ // Update OAuth debug state during debug callback
245
+ const onOAuthDebugConnect = useCallback (
246
+ ( {
247
+ authorizationCode,
248
+ errorMsg,
249
+ } : {
250
+ authorizationCode ?: string ;
251
+ errorMsg ?: string ;
252
+ } ) => {
253
+ setIsAuthDebuggerVisible ( true ) ;
254
+ if ( authorizationCode ) {
255
+ updateAuthState ( {
256
+ authorizationCode,
257
+ oauthStep : "token_request" ,
258
+ } ) ;
259
+ }
260
+ if ( errorMsg ) {
261
+ updateAuthState ( {
262
+ latestError : new Error ( errorMsg ) ,
263
+ } ) ;
264
+ }
265
+ } ,
266
+ [ ] ,
267
+ ) ;
268
+
269
+ // Load OAuth tokens when sseUrl changes
270
+ useEffect ( ( ) => {
271
+ const loadOAuthTokens = async ( ) => {
272
+ try {
273
+ if ( sseUrl ) {
274
+ const key = getServerSpecificKey ( SESSION_KEYS . TOKENS , sseUrl ) ;
275
+ const tokens = sessionStorage . getItem ( key ) ;
276
+ if ( tokens ) {
277
+ const parsedTokens = await OAuthTokensSchema . parseAsync (
278
+ JSON . parse ( tokens ) ,
279
+ ) ;
280
+ updateAuthState ( {
281
+ oauthTokens : parsedTokens ,
282
+ oauthStep : "complete" ,
283
+ } ) ;
284
+ }
285
+ }
286
+ } catch ( error ) {
287
+ console . error ( "Error loading OAuth tokens:" , error ) ;
288
+ } finally {
289
+ updateAuthState ( { loading : false } ) ;
290
+ }
291
+ } ;
292
+
293
+ loadOAuthTokens ( ) ;
294
+ } , [ sseUrl ] ) ;
295
+
216
296
useEffect ( ( ) => {
217
297
fetch ( `${ getMCPProxyAddress ( config ) } /config` )
218
298
. then ( ( response ) => response . json ( ) )
@@ -446,6 +526,19 @@ const App = () => {
446
526
setStdErrNotifications ( [ ] ) ;
447
527
} ;
448
528
529
+ // Helper component for rendering the AuthDebugger
530
+ const AuthDebuggerWrapper = ( ) => (
531
+ < TabsContent value = "auth" >
532
+ < AuthDebugger
533
+ serverUrl = { sseUrl }
534
+ onBack = { ( ) => setIsAuthDebuggerVisible ( false ) }
535
+ authState = { authState }
536
+ updateAuthState = { updateAuthState }
537
+ />
538
+ </ TabsContent >
539
+ ) ;
540
+
541
+ // Helper function to render OAuth callback components
449
542
if ( window . location . pathname === "/oauth/callback" ) {
450
543
const OAuthCallback = React . lazy (
451
544
( ) => import ( "./components/OAuthCallback" ) ,
@@ -457,6 +550,17 @@ const App = () => {
457
550
) ;
458
551
}
459
552
553
+ if ( window . location . pathname === "/oauth/callback/debug" ) {
554
+ const OAuthDebugCallback = React . lazy (
555
+ ( ) => import ( "./components/OAuthDebugCallback" ) ,
556
+ ) ;
557
+ return (
558
+ < Suspense fallback = { < div > Loading...</ div > } >
559
+ < OAuthDebugCallback onConnect = { onOAuthDebugConnect } />
560
+ </ Suspense >
561
+ ) ;
562
+ }
563
+
460
564
return (
461
565
< div className = "flex h-screen bg-background" >
462
566
< Sidebar
@@ -544,6 +648,10 @@ const App = () => {
544
648
< FolderTree className = "w-4 h-4 mr-2" />
545
649
Roots
546
650
</ TabsTrigger >
651
+ < TabsTrigger value = "auth" >
652
+ < Key className = "w-4 h-4 mr-2" />
653
+ Auth
654
+ </ TabsTrigger >
547
655
</ TabsList >
548
656
549
657
< div className = "w-full" >
@@ -689,15 +797,36 @@ const App = () => {
689
797
setRoots = { setRoots }
690
798
onRootsChange = { handleRootsChange }
691
799
/>
800
+ < AuthDebuggerWrapper />
692
801
</ >
693
802
) }
694
803
</ div >
695
804
</ Tabs >
805
+ ) : isAuthDebuggerVisible ? (
806
+ < Tabs
807
+ defaultValue = { "auth" }
808
+ className = "w-full p-4"
809
+ onValueChange = { ( value ) => ( window . location . hash = value ) }
810
+ >
811
+ < AuthDebuggerWrapper />
812
+ </ Tabs >
696
813
) : (
697
- < div className = "flex items-center justify-center h-full" >
814
+ < div className = "flex flex-col items-center justify-center h-full gap-4 " >
698
815
< p className = "text-lg text-gray-500" >
699
816
Connect to an MCP server to start inspecting
700
817
</ p >
818
+ < div className = "flex items-center gap-2" >
819
+ < p className = "text-sm text-muted-foreground" >
820
+ Need to configure authentication?
821
+ </ p >
822
+ < Button
823
+ variant = "outline"
824
+ size = "sm"
825
+ onClick = { ( ) => setIsAuthDebuggerVisible ( true ) }
826
+ >
827
+ Open Auth Settings
828
+ </ Button >
829
+ </ div >
701
830
</ div >
702
831
) }
703
832
</ div >
0 commit comments