@@ -19,7 +19,8 @@ import {
1919} from "@modelcontextprotocol/sdk/types.js" ;
2020import { OAuthTokensSchema } from "@modelcontextprotocol/sdk/shared/auth.js" ;
2121import { SESSION_KEYS , getServerSpecificKey } from "./lib/constants" ;
22- import { AuthDebuggerState } from "./lib/auth-types" ;
22+ import { AuthDebuggerState , EMPTY_DEBUGGER_STATE } from "./lib/auth-types" ;
23+ import { OAuthStateMachine } from "./lib/oauth-state-machine" ;
2324import { cacheToolOutputSchemas } from "./utils/schemaUtils" ;
2425import React , {
2526 Suspense ,
@@ -29,7 +30,10 @@ import React, {
2930 useState ,
3031} from "react" ;
3132import { useConnection } from "./lib/hooks/useConnection" ;
32- import { useDraggablePane } from "./lib/hooks/useDraggablePane" ;
33+ import {
34+ useDraggablePane ,
35+ useDraggableSidebar ,
36+ } from "./lib/hooks/useDraggablePane" ;
3337import { StdErrNotification } from "./lib/notificationTypes" ;
3438
3539import { Tabs , TabsContent , TabsList , TabsTrigger } from "@/components/ui/tabs" ;
@@ -121,19 +125,8 @@ const App = () => {
121125 const [ isAuthDebuggerVisible , setIsAuthDebuggerVisible ] = useState ( false ) ;
122126
123127 // Auth debugger state
124- const [ authState , setAuthState ] = useState < AuthDebuggerState > ( {
125- isInitiatingAuth : false ,
126- oauthTokens : null ,
127- loading : true ,
128- oauthStep : "metadata_discovery" ,
129- oauthMetadata : null ,
130- oauthClientInfo : null ,
131- authorizationUrl : null ,
132- authorizationCode : "" ,
133- latestError : null ,
134- statusMessage : null ,
135- validationError : null ,
136- } ) ;
128+ const [ authState , setAuthState ] =
129+ useState < AuthDebuggerState > ( EMPTY_DEBUGGER_STATE ) ;
137130
138131 // Helper function to update specific auth state properties
139132 const updateAuthState = ( updates : Partial < AuthDebuggerState > ) => {
@@ -164,6 +157,11 @@ const App = () => {
164157 const progressTokenRef = useRef ( 0 ) ;
165158
166159 const { height : historyPaneHeight , handleDragStart } = useDraggablePane ( 300 ) ;
160+ const {
161+ width : sidebarWidth ,
162+ isDragging : isSidebarDragging ,
163+ handleDragStart : handleSidebarDragStart ,
164+ } = useDraggableSidebar ( 320 ) ;
167165
168166 const {
169167 connectionStatus,
@@ -243,27 +241,81 @@ const App = () => {
243241
244242 // Update OAuth debug state during debug callback
245243 const onOAuthDebugConnect = useCallback (
246- ( {
244+ async ( {
247245 authorizationCode,
248246 errorMsg,
247+ restoredState,
249248 } : {
250249 authorizationCode ?: string ;
251250 errorMsg ?: string ;
251+ restoredState ?: AuthDebuggerState ;
252252 } ) => {
253253 setIsAuthDebuggerVisible ( true ) ;
254- if ( authorizationCode ) {
254+
255+ if ( errorMsg ) {
255256 updateAuthState ( {
256- authorizationCode,
257- oauthStep : "token_request" ,
257+ latestError : new Error ( errorMsg ) ,
258258 } ) ;
259+ return ;
259260 }
260- if ( errorMsg ) {
261+
262+ if ( restoredState && authorizationCode ) {
263+ // Restore the previous auth state and continue the OAuth flow
264+ let currentState : AuthDebuggerState = {
265+ ...restoredState ,
266+ authorizationCode,
267+ oauthStep : "token_request" ,
268+ isInitiatingAuth : true ,
269+ statusMessage : null ,
270+ latestError : null ,
271+ } ;
272+
273+ try {
274+ // Create a new state machine instance to continue the flow
275+ const stateMachine = new OAuthStateMachine ( sseUrl , ( updates ) => {
276+ currentState = { ...currentState , ...updates } ;
277+ } ) ;
278+
279+ // Continue stepping through the OAuth flow from where we left off
280+ while (
281+ currentState . oauthStep !== "complete" &&
282+ currentState . oauthStep !== "authorization_code"
283+ ) {
284+ await stateMachine . executeStep ( currentState ) ;
285+ }
286+
287+ if ( currentState . oauthStep === "complete" ) {
288+ // After the flow completes or reaches a user-input step, update the app state
289+ updateAuthState ( {
290+ ...currentState ,
291+ statusMessage : {
292+ type : "success" ,
293+ message : "Authentication completed successfully" ,
294+ } ,
295+ isInitiatingAuth : false ,
296+ } ) ;
297+ }
298+ } catch ( error ) {
299+ console . error ( "OAuth continuation error:" , error ) ;
300+ updateAuthState ( {
301+ latestError :
302+ error instanceof Error ? error : new Error ( String ( error ) ) ,
303+ statusMessage : {
304+ type : "error" ,
305+ message : `Failed to complete OAuth flow: ${ error instanceof Error ? error . message : String ( error ) } ` ,
306+ } ,
307+ isInitiatingAuth : false ,
308+ } ) ;
309+ }
310+ } else if ( authorizationCode ) {
311+ // Fallback to the original behavior if no state was restored
261312 updateAuthState ( {
262- latestError : new Error ( errorMsg ) ,
313+ authorizationCode,
314+ oauthStep : "token_request" ,
263315 } ) ;
264316 }
265317 } ,
266- [ ] ,
318+ [ sseUrl ] ,
267319 ) ;
268320
269321 // Load OAuth tokens when sseUrl changes
@@ -285,8 +337,6 @@ const App = () => {
285337 }
286338 } catch ( error ) {
287339 console . error ( "Error loading OAuth tokens:" , error ) ;
288- } finally {
289- updateAuthState ( { loading : false } ) ;
290340 }
291341 } ;
292342
@@ -565,32 +615,58 @@ const App = () => {
565615
566616 return (
567617 < div className = "flex h-screen bg-background" >
568- < Sidebar
569- connectionStatus = { connectionStatus }
570- transportType = { transportType }
571- setTransportType = { setTransportType }
572- command = { command }
573- setCommand = { setCommand }
574- args = { args }
575- setArgs = { setArgs }
576- sseUrl = { sseUrl }
577- setSseUrl = { setSseUrl }
578- env = { env }
579- setEnv = { setEnv }
580- config = { config }
581- setConfig = { setConfig }
582- bearerToken = { bearerToken }
583- setBearerToken = { setBearerToken }
584- headerName = { headerName }
585- setHeaderName = { setHeaderName }
586- onConnect = { connectMcpServer }
587- onDisconnect = { disconnectMcpServer }
588- stdErrNotifications = { stdErrNotifications }
589- logLevel = { logLevel }
590- sendLogLevelRequest = { sendLogLevelRequest }
591- loggingSupported = { ! ! serverCapabilities ?. logging || false }
592- clearStdErrNotifications = { clearStdErrNotifications }
593- />
618+ < div
619+ style = { {
620+ width : sidebarWidth ,
621+ minWidth : 200 ,
622+ maxWidth : 600 ,
623+ transition : isSidebarDragging ? "none" : "width 0.15s" ,
624+ } }
625+ className = "bg-card border-r border-border flex flex-col h-full relative"
626+ >
627+ < Sidebar
628+ connectionStatus = { connectionStatus }
629+ transportType = { transportType }
630+ setTransportType = { setTransportType }
631+ command = { command }
632+ setCommand = { setCommand }
633+ args = { args }
634+ setArgs = { setArgs }
635+ sseUrl = { sseUrl }
636+ setSseUrl = { setSseUrl }
637+ env = { env }
638+ setEnv = { setEnv }
639+ config = { config }
640+ setConfig = { setConfig }
641+ bearerToken = { bearerToken }
642+ setBearerToken = { setBearerToken }
643+ headerName = { headerName }
644+ setHeaderName = { setHeaderName }
645+ onConnect = { connectMcpServer }
646+ onDisconnect = { disconnectMcpServer }
647+ stdErrNotifications = { stdErrNotifications }
648+ logLevel = { logLevel }
649+ sendLogLevelRequest = { sendLogLevelRequest }
650+ loggingSupported = { ! ! serverCapabilities ?. logging || false }
651+ clearStdErrNotifications = { clearStdErrNotifications }
652+ />
653+ { /* Drag handle for resizing sidebar */ }
654+ < div
655+ onMouseDown = { handleSidebarDragStart }
656+ style = { {
657+ cursor : "col-resize" ,
658+ position : "absolute" ,
659+ top : 0 ,
660+ right : 0 ,
661+ width : 6 ,
662+ height : "100%" ,
663+ zIndex : 10 ,
664+ background : isSidebarDragging ? "rgba(0,0,0,0.08)" : "transparent" ,
665+ } }
666+ aria-label = "Resize sidebar"
667+ data-testid = "sidebar-drag-handle"
668+ />
669+ </ div >
594670 < div className = "flex-1 flex flex-col overflow-hidden" >
595671 < div className = "flex-1 overflow-auto" >
596672 { mcpClient ? (
0 commit comments