11import type { CustomHeader } from './CustomHeadersEditor'
2- import { CircleMinus , Cog , Copy , FileText , RotateCcw , Server , Shield } from 'lucide-react'
3- import { useState } from 'react'
2+ import { CircleMinus , Cog , Copy , FileText , RotateCcw , Shield } from 'lucide-react'
3+ import { useEffect , useRef , useState } from 'react'
44import { useNavigate } from 'react-router-dom'
55import { toast } from 'sonner'
66import { Badge } from '@/components/ui/badge'
77import { Button } from '@/components/ui/button'
8- import { Card , CardContent } from '@/components/ui/card'
98import { Dialog , DialogContent , DialogHeader , DialogTitle , DialogTrigger } from '@/components/ui/dialog'
109import { DropdownMenu , DropdownMenuContent , DropdownMenuItem , DropdownMenuTrigger } from '@/components/ui/dropdown-menu'
1110import { Input } from '@/components/ui/input'
1211import { Label } from '@/components/ui/label'
12+ import { NotFound } from '@/components/ui/not-found'
1313import { RandomGradientBackground } from '@/components/ui/random-gradient-background'
1414import { Select , SelectContent , SelectItem , SelectTrigger , SelectValue } from '@/components/ui/select'
1515import { Tooltip , TooltipContent , TooltipTrigger } from '@/components/ui/tooltip'
1616import { useMcpContext } from '../context/McpContext'
1717import { CustomHeadersEditor } from './CustomHeadersEditor'
1818
1919export function InspectorDashboard ( ) {
20- const { connections, addConnection, removeConnection } = useMcpContext ( )
20+ const mcpContext = useMcpContext ( )
21+ const { connections, addConnection, removeConnection } = mcpContext
2122 const navigate = useNavigate ( )
2223
24+ // Log connections on every render to debug
25+ console . warn ( '[InspectorDashboard] Render - connections:' , connections . map ( c => ( { id : c . id , state : c . state } ) ) )
26+
2327 // Form state
2428 const [ transportType , setTransportType ] = useState ( 'SSE' )
2529 const [ url , setUrl ] = useState ( '' )
@@ -28,35 +32,100 @@ export function InspectorDashboard() {
2832 const [ requestTimeout , setRequestTimeout ] = useState ( '10000' )
2933 const [ resetTimeoutOnProgress , setResetTimeoutOnProgress ] = useState ( 'True' )
3034 const [ maxTotalTimeout , setMaxTotalTimeout ] = useState ( '60000' )
31- const [ proxyAddress , setProxyAddress ] = useState ( '' )
35+ const [ proxyAddress , setProxyAddress ] = useState ( ` ${ window . location . origin } /inspector/api/proxy` )
3236 const [ proxyToken , setProxyToken ] = useState ( 'c96aeb0c195aa9c7d3846b90aec9bc5fcdd5df97b3049aaede8f5dd1a15d2d87' )
3337
3438 // OAuth fields
3539 const [ clientId , setClientId ] = useState ( '' )
3640 const [ redirectUrl , setRedirectUrl ] = useState (
3741 typeof window !== 'undefined'
3842 ? new URL ( '/oauth/callback' , window . location . origin ) . toString ( )
39- : 'http://localhost:3000 /oauth/callback' ,
43+ : '/oauth/callback' ,
4044 )
4145 const [ scope , setScope ] = useState ( '' )
4246
4347 // UI state
4448 const [ headersDialogOpen , setHeadersDialogOpen ] = useState ( false )
4549 const [ authDialogOpen , setAuthDialogOpen ] = useState ( false )
4650 const [ configDialogOpen , setConfigDialogOpen ] = useState ( false )
51+ const [ isConnecting , setIsConnecting ] = useState ( false )
52+ const [ pendingConnectionUrl , setPendingConnectionUrl ] = useState < string | null > ( null )
53+ const hasShownToastRef = useRef ( false )
54+
55+ // Monitor the pending connection state
56+ useEffect ( ( ) => {
57+ if ( ! pendingConnectionUrl )
58+ return
59+
60+ const connection = connections . find ( c => c . id === pendingConnectionUrl )
61+ if ( ! connection ) {
62+ console . warn ( '[InspectorDashboard] Pending connection not found yet:' , pendingConnectionUrl )
63+ return
64+ }
65+
66+ console . warn ( '[InspectorDashboard] Connection state:' , connection . state , 'for' , pendingConnectionUrl )
67+
68+ // Skip if we've already shown a toast for this connection
69+ if ( hasShownToastRef . current )
70+ return
71+
72+ // Connection succeeded
73+ if ( connection . state === 'ready' ) {
74+ console . warn ( '[InspectorDashboard] Connection ready!' )
75+ hasShownToastRef . current = true
76+ setIsConnecting ( false )
77+ setPendingConnectionUrl ( null )
78+ toast . success ( 'Connection established successfully' )
79+
80+ // Reset form
81+ setUrl ( '' )
82+ setCustomHeaders ( [ ] )
83+ setClientId ( '' )
84+ setScope ( '' )
85+ }
86+ // Connection failed
87+ else if ( connection . state === 'failed' || connection . error ) {
88+ console . warn ( '[InspectorDashboard] Connection failed:' , connection . error )
89+ hasShownToastRef . current = true
90+ setIsConnecting ( false )
91+ setPendingConnectionUrl ( null )
92+ const errorMessage = connection . error || 'Failed to connect to server'
93+ toast . error ( errorMessage )
94+ // Don't remove the connection - let user see it in the list and manually remove if needed
95+ }
96+ } , [ connections , pendingConnectionUrl ] )
4797
4898 const handleAddConnection = ( ) => {
4999 if ( ! url . trim ( ) )
50100 return
51101
52- // For now, use URL as both ID and name - this will need proper implementation
53- addConnection ( url , url )
102+ setIsConnecting ( true )
103+ hasShownToastRef . current = false
104+ setPendingConnectionUrl ( url )
105+
106+ // Prepare proxy configuration if "Via Proxy" is selected
107+ const proxyConfig = connectionType === 'Via Proxy' && proxyAddress . trim ( )
108+ ? {
109+ proxyAddress : proxyAddress . trim ( ) ,
110+ proxyToken : proxyToken . trim ( ) ,
111+ customHeaders : customHeaders . reduce ( ( acc , header ) => {
112+ if ( header . name && header . value ) {
113+ acc [ header . name ] = header . value
114+ }
115+ return acc
116+ } , { } as Record < string , string > ) ,
117+ }
118+ : {
119+ customHeaders : customHeaders . reduce ( ( acc , header ) => {
120+ if ( header . name && header . value ) {
121+ acc [ header . name ] = header . value
122+ }
123+ return acc
124+ } , { } as Record < string , string > ) ,
125+ }
54126
55- // Reset form
56- setUrl ( '' )
57- setCustomHeaders ( [ ] )
58- setClientId ( '' )
59- setScope ( '' )
127+ // For now, use URL as both ID and name - this will need proper implementation
128+ addConnection ( url , url , proxyConfig )
60129 }
61130
62131 const handleClearAllConnections = ( ) => {
@@ -184,15 +253,7 @@ export function InspectorDashboard() {
184253 </ div >
185254 { connections . length === 0
186255 ? (
187- < Card >
188- < CardContent className = "flex flex-col items-center justify-center py-8" >
189- < Server className = "h-12 w-12 text-muted-foreground mb-4" />
190- < p className = "text-muted-foreground" > No servers connected yet</ p >
191- < p className = "text-sm text-muted-foreground" >
192- Add a server above to get started
193- </ p >
194- </ CardContent >
195- </ Card >
256+ < NotFound message = "No servers connected yet. Add a server above to get started." />
196257 )
197258 : (
198259 < div className = "grid gap-3" >
@@ -584,20 +645,28 @@ export function InspectorDashboard() {
584645 { /* Connect Button */ }
585646 < Button
586647 onClick = { handleAddConnection }
587- disabled = { ! url . trim ( ) }
648+ disabled = { ! url . trim ( ) || isConnecting }
588649 className = "w-full bg-white text-black hover:bg-white/90 font-semibold mt-4"
589650 >
590- < svg className = "w-4 h-4 mr-2" fill = "none" stroke = "currentColor" viewBox = "0 0 24 24" >
591- < path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = { 2 } d = "M13 10V3L4 14h7v7l9-11h-7z" />
592- </ svg >
593- Connect
651+ { isConnecting
652+ ? (
653+ < >
654+ < svg className = "w-4 h-4 mr-2 animate-spin" fill = "none" stroke = "currentColor" viewBox = "0 0 24 24" >
655+ < path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = { 2 } d = "M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
656+ </ svg >
657+ Connecting...
658+ </ >
659+ )
660+ : (
661+ < >
662+ < svg className = "w-4 h-4 mr-2" fill = "none" stroke = "currentColor" viewBox = "0 0 24 24" >
663+ < path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = { 2 } d = "M13 10V3L4 14h7v7l9-11h-7z" />
664+ </ svg >
665+ Connect
666+ </ >
667+ ) }
594668 </ Button >
595669
596- { /* Status */ }
597- < div className = "flex items-center justify-center gap-2 text-white/60 text-sm" >
598- < div className = "w-2 h-2 rounded-full bg-red-500 animate-status-pulse-red" > </ div >
599- Disconnected
600- </ div >
601670 </ div >
602671 < RandomGradientBackground className = "absolute inset-0" />
603672 </ div >
0 commit comments