@@ -17,6 +17,9 @@ import {
1717 Tool ,
1818 LoggingLevel ,
1919} 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" ;
2023import React , {
2124 Suspense ,
2225 useCallback ,
@@ -28,18 +31,21 @@ import { useConnection } from "./lib/hooks/useConnection";
2831import { useDraggablePane } from "./lib/hooks/useDraggablePane" ;
2932import { StdErrNotification } from "./lib/notificationTypes" ;
3033
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" ;
3236import {
3337 Bell ,
3438 Files ,
3539 FolderTree ,
3640 Hammer ,
3741 Hash ,
42+ Key ,
3843 MessageSquare ,
3944} from "lucide-react" ;
4045
4146import { z } from "zod" ;
4247import "./App.css" ;
48+ import AuthDebugger from "./components/AuthDebugger" ;
4349import ConsoleTab from "./components/ConsoleTab" ;
4450import HistoryAndNotifications from "./components/History" ;
4551import PingTab from "./components/PingTab" ;
@@ -111,6 +117,27 @@ const App = () => {
111117 }
112118 >
113119 > ( [ ] ) ;
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+ } ;
114141 const nextRequestId = useRef ( 0 ) ;
115142 const rootsRef = useRef < Root [ ] > ( [ ] ) ;
116143
@@ -208,11 +235,64 @@ const App = () => {
208235 ( serverUrl : string ) => {
209236 setSseUrl ( serverUrl ) ;
210237 setTransportType ( "sse" ) ;
238+ setIsAuthDebuggerVisible ( false ) ;
211239 void connectMcpServer ( ) ;
212240 } ,
213241 [ connectMcpServer ] ,
214242 ) ;
215243
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+
216296 useEffect ( ( ) => {
217297 fetch ( `${ getMCPProxyAddress ( config ) } /config` )
218298 . then ( ( response ) => response . json ( ) )
@@ -446,6 +526,19 @@ const App = () => {
446526 setStdErrNotifications ( [ ] ) ;
447527 } ;
448528
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
449542 if ( window . location . pathname === "/oauth/callback" ) {
450543 const OAuthCallback = React . lazy (
451544 ( ) => import ( "./components/OAuthCallback" ) ,
@@ -457,6 +550,17 @@ const App = () => {
457550 ) ;
458551 }
459552
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+
460564 return (
461565 < div className = "flex h-screen bg-background" >
462566 < Sidebar
@@ -544,6 +648,10 @@ const App = () => {
544648 < FolderTree className = "w-4 h-4 mr-2" />
545649 Roots
546650 </ TabsTrigger >
651+ < TabsTrigger value = "auth" >
652+ < Key className = "w-4 h-4 mr-2" />
653+ Auth
654+ </ TabsTrigger >
547655 </ TabsList >
548656
549657 < div className = "w-full" >
@@ -689,15 +797,36 @@ const App = () => {
689797 setRoots = { setRoots }
690798 onRootsChange = { handleRootsChange }
691799 />
800+ < AuthDebuggerWrapper />
692801 </ >
693802 ) }
694803 </ div >
695804 </ 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 >
696813 ) : (
697- < div className = "flex items-center justify-center h-full" >
814+ < div className = "flex flex-col items-center justify-center h-full gap-4 " >
698815 < p className = "text-lg text-gray-500" >
699816 Connect to an MCP server to start inspecting
700817 </ 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 >
701830 </ div >
702831 ) }
703832 </ div >
0 commit comments