@@ -129,6 +129,25 @@ function ServerListItem({
129129 )
130130}
131131
132+ function buildEditInitialData ( server : McpServer ) {
133+ const entries : { key : string ; value : string } [ ] = server . headers
134+ ? Object . entries ( server . headers ) . map ( ( [ key , value ] ) => ( { key, value } ) )
135+ : [ ]
136+ if ( entries . length === 0 ) entries . push ( { key : '' , value : '' } )
137+ const last = entries [ entries . length - 1 ]
138+ if ( last . key !== '' || last . value !== '' ) entries . push ( { key : '' , value : '' } )
139+
140+ return {
141+ name : server . name || '' ,
142+ transport : ( server . transport as McpTransport ) || 'streamable-http' ,
143+ url : server . url || '' ,
144+ timeout : 30000 ,
145+ headers : entries ,
146+ oauthClientId : server . oauthClientId || undefined ,
147+ hasOauthClientSecret : server . hasOauthClientSecret === true ,
148+ }
149+ }
150+
132151interface MCPProps {
133152 initialServerId ?: string | null
134153}
@@ -165,19 +184,7 @@ export function MCP({ initialServerId }: MCPProps) {
165184 const { data : allowedMcpDomains = null } = useAllowedMcpDomains ( )
166185
167186 const [ showAddModal , setShowAddModal ] = useState ( false )
168- const [ showEditModal , setShowEditModal ] = useState ( false )
169- const [ editInitialData , setEditInitialData ] = useState <
170- | {
171- name : string
172- transport : McpTransport
173- url ?: string
174- timeout ?: number
175- headers ?: { key : string ; value : string } [ ]
176- oauthClientId ?: string
177- hasOauthClientSecret ?: boolean
178- }
179- | undefined
180- > ( undefined )
187+ const [ editingServerId , setEditingServerId ] = useState < string | null > ( null )
181188
182189 const [ searchTerm , setSearchTerm ] = useState ( '' )
183190 const [ deletingServers , setDeletingServers ] = useState < Set < string > > ( ( ) => new Set ( ) )
@@ -192,8 +199,8 @@ export function MCP({ initialServerId }: MCPProps) {
192199 }
193200 } , [ ] )
194201
195- const [ showDeleteDialog , setShowDeleteDialog ] = useState ( false )
196202 const [ serverToDeleteId , setServerToDeleteId ] = useState < string | null > ( null )
203+ const showDeleteDialog = serverToDeleteId !== null
197204
198205 const [ selectedServerId , setSelectedServerId ] = useState < string | null > ( initialServerId ?? null )
199206
@@ -238,9 +245,6 @@ export function MCP({ initialServerId }: MCPProps) {
238245 return ( ) => window . removeEventListener ( 'message' , onMessage )
239246 } , [ queryClient , workspaceId ] )
240247
241- const [ refreshingServers , setRefreshingServers ] = useState <
242- Record < string , { status : 'refreshing' | 'refreshed' ; workflowsUpdated ?: number } >
243- > ( { } )
244248 const [ expandedTools , setExpandedTools ] = useState < Set < string > > ( ( ) => new Set ( ) )
245249
246250 const startOauthForServer = async ( serverId : string ) => {
@@ -277,13 +281,11 @@ export function MCP({ initialServerId }: MCPProps) {
277281
278282 const handleRemoveServer = ( serverId : string ) => {
279283 setServerToDeleteId ( serverId )
280- setShowDeleteDialog ( true )
281284 }
282285
283286 const confirmDeleteServer = async ( ) => {
284287 if ( ! serverToDeleteId ) return
285288
286- setShowDeleteDialog ( false )
287289 const serverId = serverToDeleteId
288290 setServerToDeleteId ( null )
289291
@@ -344,7 +346,6 @@ export function MCP({ initialServerId }: MCPProps) {
344346
345347 const handleRefreshServer = async ( serverId : string ) => {
346348 try {
347- setRefreshingServers ( ( prev ) => ( { ...prev , [ serverId ] : { status : 'refreshing' } } ) )
348349 const result = await refreshServerMutation . mutateAsync ( { workspaceId, serverId } )
349350 logger . info (
350351 `Refreshed MCP server: ${ serverId } , workflows updated: ${ result . workflowsUpdated } `
@@ -369,50 +370,30 @@ export function MCP({ initialServerId }: MCPProps) {
369370 logger . warn ( 'Failed to reload workflow subblock values:' , reloadError )
370371 }
371372 }
372-
373- setRefreshingServers ( ( prev ) => ( {
374- ...prev ,
375- [ serverId ] : { status : 'refreshed' , workflowsUpdated : result . workflowsUpdated } ,
376- } ) )
377- setTimeout ( ( ) => {
378- setRefreshingServers ( ( prev ) => {
379- const newState = { ...prev }
380- delete newState [ serverId ]
381- return newState
382- } )
383- } , 3000 )
384373 } catch ( error ) {
385374 logger . error ( 'Failed to refresh MCP server:' , error )
386- setRefreshingServers ( ( prev ) => {
387- const newState = { ...prev }
388- delete newState [ serverId ]
389- return newState
390- } )
391375 }
392376 }
393377
394- const handleOpenEditModal = ( server : McpServer ) => {
395- const headers : { key : string ; value : string } [ ] = server . headers
396- ? Object . entries ( server . headers ) . map ( ( [ key , value ] ) => ( { key, value } ) )
397- : [ { key : '' , value : '' } ]
398- if ( headers . length === 0 ) headers . push ( { key : '' , value : '' } )
399-
400- const lastHeader = headers [ headers . length - 1 ]
401- if ( lastHeader . key !== '' || lastHeader . value !== '' ) {
402- headers . push ( { key : '' , value : '' } )
403- }
404-
405- setEditInitialData ( {
406- name : server . name || '' ,
407- transport : ( server . transport as McpTransport ) || 'streamable-http' ,
408- url : server . url || '' ,
409- timeout : 30000 ,
410- headers,
411- oauthClientId : server . oauthClientId || undefined ,
412- hasOauthClientSecret : server . hasOauthClientSecret === true ,
413- } )
414- setShowEditModal ( true )
415- }
378+ useEffect ( ( ) => {
379+ if ( ! refreshServerMutation . isSuccess ) return
380+ const timeout = window . setTimeout ( ( ) => refreshServerMutation . reset ( ) , 3000 )
381+ return ( ) => window . clearTimeout ( timeout )
382+ // eslint-disable-next-line react-hooks/exhaustive-deps -- mutation object is unstable; isSuccess flag is the trigger
383+ } , [ refreshServerMutation . isSuccess ] )
384+
385+ const refreshingServerId = refreshServerMutation . isPending
386+ ? refreshServerMutation . variables ?. serverId
387+ : null
388+ const refreshedServerId = refreshServerMutation . isSuccess
389+ ? refreshServerMutation . variables ?. serverId
390+ : null
391+ const refreshedWorkflowsUpdated = refreshServerMutation . data ?. workflowsUpdated
392+
393+ const editingServer = editingServerId
394+ ? ( servers . find ( ( s ) => s . id === editingServerId ) as McpServer | undefined )
395+ : undefined
396+ const editInitialData = editingServer ? buildEditInitialData ( editingServer ) : undefined
416397
417398 const selectedServer = ( ( ) => {
418399 if ( ! selectedServerId ) return null
@@ -547,11 +528,12 @@ export function MCP({ initialServerId }: MCPProps) {
547528 key = { tool . name }
548529 className = 'overflow-hidden rounded-md border bg-[var(--surface-3)]'
549530 >
550- < button
531+ < Button
551532 type = 'button'
533+ variant = 'ghost'
552534 onClick = { ( ) => hasParams && toggleToolExpanded ( tool . name ) }
553535 className = { cn (
554- 'flex w-full items-start justify-between px-2.5 py-2 text-left' ,
536+ 'flex h-auto w-full items-start justify-between rounded-none px-2.5 py-2 text-left text-sm ' ,
555537 hasParams && 'cursor-pointer hover-hover:bg-[var(--surface-4)]'
556538 ) }
557539 disabled = { ! hasParams }
@@ -593,7 +575,7 @@ export function MCP({ initialServerId }: MCPProps) {
593575 ) }
594576 />
595577 ) }
596- </ button >
578+ </ Button >
597579
598580 { isExpanded && hasParams && (
599581 < div className = 'border-[var(--border-1)] border-t bg-[var(--surface-2)] px-2.5 py-2' >
@@ -660,25 +642,27 @@ export function MCP({ initialServerId }: MCPProps) {
660642 < Button
661643 onClick = { ( ) => handleRefreshServer ( server . id ) }
662644 variant = 'default'
663- disabled = { ! ! refreshingServers [ server . id ] }
645+ disabled = { refreshingServerId === server . id || refreshedServerId === server . id }
664646 >
665- { refreshingServers [ server . id ] ?. status === 'refreshing'
647+ { refreshingServerId === server . id
666648 ? 'Refreshing...'
667- : refreshingServers [ server . id ] ?. status === 'refreshed'
668- ? refreshingServers [ server . id ] . workflowsUpdated
669- ? `Synced (${ refreshingServers [ server . id ] . workflowsUpdated } workflow${ refreshingServers [ server . id ] . workflowsUpdated === 1 ? '' : 's' } )`
649+ : refreshedServerId === server . id
650+ ? refreshedWorkflowsUpdated
651+ ? `Synced (${ refreshedWorkflowsUpdated } workflow${ refreshedWorkflowsUpdated === 1 ? '' : 's' } )`
670652 : 'Refreshed'
671653 : 'Refresh Tools' }
672654 </ Button >
673- < Button onClick = { ( ) => handleOpenEditModal ( server ) } variant = 'default' >
655+ < Button onClick = { ( ) => setEditingServerId ( server . id ) } variant = 'default' >
674656 Edit
675657 </ Button >
676658 </ div >
677659 </ div >
678660
679661 < McpServerFormModal
680- open = { showEditModal }
681- onOpenChange = { setShowEditModal }
662+ open = { editingServerId !== null }
663+ onOpenChange = { ( open ) => {
664+ if ( ! open ) setEditingServerId ( null )
665+ } }
682666 mode = 'edit'
683667 initialData = { editInitialData }
684668 onSubmit = { async ( config ) => {
@@ -753,7 +737,7 @@ export function MCP({ initialServerId }: MCPProps) {
753737 tools = { tools }
754738 isDeleting = { deletingServers . has ( server . id ) }
755739 isLoadingTools = { isLoadingTools }
756- isRefreshing = { refreshingServers [ server . id ] ?. status === 'refreshing' }
740+ isRefreshing = { refreshingServerId === server . id }
757741 onRemove = { ( ) => handleRemoveServer ( server . id ) }
758742 onViewDetails = { ( ) => handleViewDetails ( server . id ) }
759743 />
@@ -787,7 +771,12 @@ export function MCP({ initialServerId }: MCPProps) {
787771 allowedMcpDomains = { allowedMcpDomains }
788772 />
789773
790- < Modal open = { showDeleteDialog } onOpenChange = { setShowDeleteDialog } >
774+ < Modal
775+ open = { showDeleteDialog }
776+ onOpenChange = { ( open ) => {
777+ if ( ! open ) setServerToDeleteId ( null )
778+ } }
779+ >
791780 < ModalContent size = 'sm' >
792781 < ModalHeader > Delete MCP Server</ ModalHeader >
793782 < ModalBody >
@@ -800,7 +789,7 @@ export function MCP({ initialServerId }: MCPProps) {
800789 </ p >
801790 </ ModalBody >
802791 < ModalFooter >
803- < Button variant = 'default' onClick = { ( ) => setShowDeleteDialog ( false ) } >
792+ < Button variant = 'default' onClick = { ( ) => setServerToDeleteId ( null ) } >
804793 Cancel
805794 </ Button >
806795 < Button variant = 'destructive' onClick = { confirmDeleteServer } >
0 commit comments