@@ -138,11 +138,9 @@ const App = () => {
138
138
> ( [ ] ) ;
139
139
const [ isAuthDebuggerVisible , setIsAuthDebuggerVisible ] = useState ( false ) ;
140
140
141
- // Auth debugger state
142
141
const [ authState , setAuthState ] =
143
142
useState < AuthDebuggerState > ( EMPTY_DEBUGGER_STATE ) ;
144
143
145
- // Helper function to update specific auth state properties
146
144
const updateAuthState = ( updates : Partial < AuthDebuggerState > ) => {
147
145
setAuthState ( ( prev ) => ( { ...prev , ...updates } ) ) ;
148
146
} ;
@@ -170,6 +168,19 @@ const App = () => {
170
168
const [ nextToolCursor , setNextToolCursor ] = useState < string | undefined > ( ) ;
171
169
const progressTokenRef = useRef ( 0 ) ;
172
170
171
+ const [ activeTab , setActiveTab ] = useState < string > ( ( ) => {
172
+ const hash = window . location . hash . slice ( 1 ) ;
173
+ const initialTab = hash || "resources" ;
174
+ return initialTab ;
175
+ } ) ;
176
+
177
+ const currentTabRef = useRef < string > ( activeTab ) ;
178
+ const lastToolCallOriginTabRef = useRef < string > ( activeTab ) ;
179
+
180
+ useEffect ( ( ) => {
181
+ currentTabRef . current = activeTab ;
182
+ } , [ activeTab ] ) ;
183
+
173
184
const { height : historyPaneHeight , handleDragStart } = useDraggablePane ( 300 ) ;
174
185
const {
175
186
width : sidebarWidth ,
@@ -213,6 +224,8 @@ const App = () => {
213
224
] ) ;
214
225
} ,
215
226
onElicitationRequest : ( request , resolve ) => {
227
+ const currentTab = lastToolCallOriginTabRef . current ;
228
+
216
229
setPendingElicitationRequests ( ( prev ) => [
217
230
...prev ,
218
231
{
@@ -222,16 +235,52 @@ const App = () => {
222
235
message : request . params . message ,
223
236
requestedSchema : request . params . requestedSchema ,
224
237
} ,
238
+ originatingTab : currentTab ,
225
239
resolve,
226
240
decline : ( error : Error ) => {
227
241
console . error ( "Elicitation request rejected:" , error ) ;
228
242
} ,
229
243
} ,
230
244
] ) ;
245
+
246
+ setActiveTab ( "elicitations" ) ;
247
+ window . location . hash = "elicitations" ;
231
248
} ,
232
249
getRoots : ( ) => rootsRef . current ,
233
250
} ) ;
234
251
252
+ useEffect ( ( ) => {
253
+ if ( serverCapabilities ) {
254
+ const hash = window . location . hash . slice ( 1 ) ;
255
+
256
+ const validTabs = [
257
+ ...( serverCapabilities ?. resources ? [ "resources" ] : [ ] ) ,
258
+ ...( serverCapabilities ?. prompts ? [ "prompts" ] : [ ] ) ,
259
+ ...( serverCapabilities ?. tools ? [ "tools" ] : [ ] ) ,
260
+ "ping" ,
261
+ "sampling" ,
262
+ "elicitations" ,
263
+ "roots" ,
264
+ "auth" ,
265
+ ] ;
266
+
267
+ const isValidTab = validTabs . includes ( hash ) ;
268
+
269
+ if ( ! isValidTab ) {
270
+ const defaultTab = serverCapabilities ?. resources
271
+ ? "resources"
272
+ : serverCapabilities ?. prompts
273
+ ? "prompts"
274
+ : serverCapabilities ?. tools
275
+ ? "tools"
276
+ : "ping" ;
277
+
278
+ setActiveTab ( defaultTab ) ;
279
+ window . location . hash = defaultTab ;
280
+ }
281
+ }
282
+ } , [ serverCapabilities ] ) ;
283
+
235
284
useEffect ( ( ) => {
236
285
localStorage . setItem ( "lastCommand" , command ) ;
237
286
} , [ command ] ) ;
@@ -260,7 +309,6 @@ const App = () => {
260
309
saveInspectorConfig ( CONFIG_LOCAL_STORAGE_KEY , config ) ;
261
310
} , [ config ] ) ;
262
311
263
- // Auto-connect to previously saved serverURL after OAuth callback
264
312
const onOAuthConnect = useCallback (
265
313
( serverUrl : string ) => {
266
314
setSseUrl ( serverUrl ) ;
@@ -270,7 +318,6 @@ const App = () => {
270
318
[ connectMcpServer ] ,
271
319
) ;
272
320
273
- // Update OAuth debug state during debug callback
274
321
const onOAuthDebugConnect = useCallback (
275
322
async ( {
276
323
authorizationCode,
@@ -291,7 +338,6 @@ const App = () => {
291
338
}
292
339
293
340
if ( restoredState && authorizationCode ) {
294
- // Restore the previous auth state and continue the OAuth flow
295
341
let currentState : AuthDebuggerState = {
296
342
...restoredState ,
297
343
authorizationCode,
@@ -302,12 +348,10 @@ const App = () => {
302
348
} ;
303
349
304
350
try {
305
- // Create a new state machine instance to continue the flow
306
351
const stateMachine = new OAuthStateMachine ( sseUrl , ( updates ) => {
307
352
currentState = { ...currentState , ...updates } ;
308
353
} ) ;
309
354
310
- // Continue stepping through the OAuth flow from where we left off
311
355
while (
312
356
currentState . oauthStep !== "complete" &&
313
357
currentState . oauthStep !== "authorization_code"
@@ -316,7 +360,6 @@ const App = () => {
316
360
}
317
361
318
362
if ( currentState . oauthStep === "complete" ) {
319
- // After the flow completes or reaches a user-input step, update the app state
320
363
updateAuthState ( {
321
364
...currentState ,
322
365
statusMessage : {
@@ -339,7 +382,6 @@ const App = () => {
339
382
} ) ;
340
383
}
341
384
} else if ( authorizationCode ) {
342
- // Fallback to the original behavior if no state was restored
343
385
updateAuthState ( {
344
386
authorizationCode,
345
387
oauthStep : "token_request" ,
@@ -349,7 +391,6 @@ const App = () => {
349
391
[ sseUrl ] ,
350
392
) ;
351
393
352
- // Load OAuth tokens when sseUrl changes
353
394
useEffect ( ( ) => {
354
395
const loadOAuthTokens = async ( ) => {
355
396
try {
@@ -408,6 +449,18 @@ const App = () => {
408
449
}
409
450
} , [ ] ) ;
410
451
452
+ useEffect ( ( ) => {
453
+ const handleHashChange = ( ) => {
454
+ const hash = window . location . hash . slice ( 1 ) ;
455
+ if ( hash && hash !== activeTab ) {
456
+ setActiveTab ( hash ) ;
457
+ }
458
+ } ;
459
+
460
+ window . addEventListener ( "hashchange" , handleHashChange ) ;
461
+ return ( ) => window . removeEventListener ( "hashchange" , handleHashChange ) ;
462
+ } , [ activeTab ] ) ;
463
+
411
464
const handleApproveSampling = ( id : number , result : CreateMessageResult ) => {
412
465
setPendingSampleRequests ( ( prev ) => {
413
466
const request = prev . find ( ( r ) => r . id === id ) ;
@@ -430,7 +483,34 @@ const App = () => {
430
483
) => {
431
484
setPendingElicitationRequests ( ( prev ) => {
432
485
const request = prev . find ( ( r ) => r . id === id ) ;
433
- request ?. resolve ( response ) ;
486
+ if ( request ) {
487
+ request . resolve ( response ) ;
488
+
489
+ if ( request . originatingTab ) {
490
+ const originatingTab = request . originatingTab ;
491
+
492
+ const validTabs = [
493
+ ...( serverCapabilities ?. resources ? [ "resources" ] : [ ] ) ,
494
+ ...( serverCapabilities ?. prompts ? [ "prompts" ] : [ ] ) ,
495
+ ...( serverCapabilities ?. tools ? [ "tools" ] : [ ] ) ,
496
+ "ping" ,
497
+ "sampling" ,
498
+ "elicitations" ,
499
+ "roots" ,
500
+ "auth" ,
501
+ ] ;
502
+
503
+ if ( validTabs . includes ( originatingTab ) ) {
504
+ setActiveTab ( originatingTab ) ;
505
+ window . location . hash = originatingTab ;
506
+
507
+ setTimeout ( ( ) => {
508
+ setActiveTab ( originatingTab ) ;
509
+ window . location . hash = originatingTab ;
510
+ } , 100 ) ;
511
+ }
512
+ }
513
+ }
434
514
return prev . filter ( ( r ) => r . id !== id ) ;
435
515
} ) ;
436
516
} ;
@@ -492,7 +572,23 @@ const App = () => {
492
572
setNextResourceTemplateCursor ( response . nextCursor ) ;
493
573
} ;
494
574
575
+ const getPrompt = async ( name : string , args : Record < string , string > = { } ) => {
576
+ lastToolCallOriginTabRef . current = currentTabRef . current ;
577
+
578
+ const response = await sendMCPRequest (
579
+ {
580
+ method : "prompts/get" as const ,
581
+ params : { name, arguments : args } ,
582
+ } ,
583
+ GetPromptResultSchema ,
584
+ "prompts" ,
585
+ ) ;
586
+ setPromptContent ( JSON . stringify ( response , null , 2 ) ) ;
587
+ } ;
588
+
495
589
const readResource = async ( uri : string ) => {
590
+ lastToolCallOriginTabRef . current = currentTabRef . current ;
591
+
496
592
const response = await sendMCPRequest (
497
593
{
498
594
method : "resources/read" as const ,
@@ -549,18 +645,6 @@ const App = () => {
549
645
setNextPromptCursor ( response . nextCursor ) ;
550
646
} ;
551
647
552
- const getPrompt = async ( name : string , args : Record < string , string > = { } ) => {
553
- const response = await sendMCPRequest (
554
- {
555
- method : "prompts/get" as const ,
556
- params : { name, arguments : args } ,
557
- } ,
558
- GetPromptResultSchema ,
559
- "prompts" ,
560
- ) ;
561
- setPromptContent ( JSON . stringify ( response , null , 2 ) ) ;
562
- } ;
563
-
564
648
const listTools = async ( ) => {
565
649
const response = await sendMCPRequest (
566
650
{
@@ -572,11 +656,12 @@ const App = () => {
572
656
) ;
573
657
setTools ( response . tools ) ;
574
658
setNextToolCursor ( response . nextCursor ) ;
575
- // Cache output schemas for validation
576
659
cacheToolOutputSchemas ( response . tools ) ;
577
660
} ;
578
661
579
662
const callTool = async ( name : string , params : Record < string , unknown > ) => {
663
+ lastToolCallOriginTabRef . current = currentTabRef . current ;
664
+
580
665
try {
581
666
const response = await sendMCPRequest (
582
667
{
@@ -592,6 +677,7 @@ const App = () => {
592
677
CompatibilityCallToolResultSchema ,
593
678
"tools" ,
594
679
) ;
680
+
595
681
setToolResult ( response ) ;
596
682
} catch ( e ) {
597
683
const toolResult : CompatibilityCallToolResult = {
@@ -626,7 +712,6 @@ const App = () => {
626
712
setStdErrNotifications ( [ ] ) ;
627
713
} ;
628
714
629
- // Helper component for rendering the AuthDebugger
630
715
const AuthDebuggerWrapper = ( ) => (
631
716
< TabsContent value = "auth" >
632
717
< AuthDebugger
@@ -638,7 +723,6 @@ const App = () => {
638
723
</ TabsContent >
639
724
) ;
640
725
641
- // Helper function to render OAuth callback components
642
726
if ( window . location . pathname === "/oauth/callback" ) {
643
727
const OAuthCallback = React . lazy (
644
728
( ) => import ( "./components/OAuthCallback" ) ,
@@ -698,7 +782,6 @@ const App = () => {
698
782
loggingSupported = { ! ! serverCapabilities ?. logging || false }
699
783
clearStdErrNotifications = { clearStdErrNotifications }
700
784
/>
701
- { /* Drag handle for resizing sidebar */ }
702
785
< div
703
786
onMouseDown = { handleSidebarDragStart }
704
787
style = { {
@@ -719,21 +802,12 @@ const App = () => {
719
802
< div className = "flex-1 overflow-auto" >
720
803
{ mcpClient ? (
721
804
< Tabs
722
- defaultValue = {
723
- Object . keys ( serverCapabilities ?? { } ) . includes (
724
- window . location . hash . slice ( 1 ) ,
725
- )
726
- ? window . location . hash . slice ( 1 )
727
- : serverCapabilities ?. resources
728
- ? "resources"
729
- : serverCapabilities ?. prompts
730
- ? "prompts"
731
- : serverCapabilities ?. tools
732
- ? "tools"
733
- : "ping"
734
- }
805
+ value = { activeTab }
735
806
className = "w-full p-4"
736
- onValueChange = { ( value ) => ( window . location . hash = value ) }
807
+ onValueChange = { ( value ) => {
808
+ setActiveTab ( value ) ;
809
+ window . location . hash = value ;
810
+ } }
737
811
>
738
812
< TabsList className = "mb-4 py-0" >
739
813
< TabsTrigger
@@ -895,7 +969,6 @@ const App = () => {
895
969
clearTools = { ( ) => {
896
970
setTools ( [ ] ) ;
897
971
setNextToolCursor ( undefined ) ;
898
- // Clear cached output schemas
899
972
cacheToolOutputSchemas ( [ ] ) ;
900
973
} }
901
974
callTool = { async ( name , params ) => {
0 commit comments