@@ -72,7 +72,7 @@ const STATUS_UPDATE_CAS_ATTEMPTS = 3
7272const STATUS_METADATA_RACE_RETRY_ATTEMPTS = 3
7373
7474type CacheMutationResult = 'applied' | 'superseded' | 'unavailable'
75- type SuccessfulPublicationResult = 'published' | Exclude < CacheMutationResult , 'applied' >
75+ type StatusPublicationResult = 'published' | Exclude < CacheMutationResult , 'applied' >
7676
7777export type McpServerDiscoveryState =
7878 | 'cached'
@@ -763,7 +763,7 @@ class McpService {
763763 config : McpServerConfig ,
764764 mutation : CacheMutation | null ,
765765 tools : McpTool [ ]
766- ) : Promise < SuccessfulPublicationResult > {
766+ ) : Promise < StatusPublicationResult > {
767767 const cacheApplied = await this . applyServerCacheMutation (
768768 workspaceId ,
769769 config . id ,
@@ -786,75 +786,64 @@ class McpService {
786786 } )
787787 if ( statusApplied ) return 'published'
788788
789- // A connection-config edit advances mutation ownership, while metadata-only
790- // edits only bump updatedAt. Probe ownership without changing cache state:
791- // superseded results must reload the winner, but metadata races can keep
792- // and return these valid live tools without publishing stale DB status.
793- const ownership = await this . applyServerCacheMutation (
789+ const retryResult = await this . retryStatusPublicationAfterMetadataRace (
794790 workspaceId ,
795- config . id ,
791+ config ,
796792 mutation ,
797- null ,
798- [ ]
793+ ( configUpdatedAt ) =>
794+ this . updateServerStatus ( config . id , workspaceId , {
795+ outcome : 'connected' ,
796+ toolCount : tools . length ,
797+ configUpdatedAt,
798+ publicationOrder : new Date ( mutation . id ) ,
799+ } )
799800 )
800- if ( ownership === 'superseded' ) return 'superseded'
801-
802- const currentConfig = await this . getServerConfig ( config . id , workspaceId )
803- if (
804- ! currentConfig ||
805- ! config . discoveryRevision ||
806- currentConfig . discoveryRevision !== config . discoveryRevision
807- ) {
801+ if ( retryResult === 'superseded' ) {
808802 await this . applyServerCacheMutation ( workspaceId , config . id , mutation , null , [
809803 serverCacheKey ( workspaceId , config . id ) ,
810804 failureCacheKey ( workspaceId , config . id ) ,
811805 ] )
812- return 'superseded'
813806 }
814- return 'unavailable'
815- }
816-
817- private async getCurrentConfigForOwnedDiscoveryMutation (
818- workspaceId : string ,
819- config : McpServerConfig ,
820- mutation : CacheMutation
821- ) : Promise < McpServerConfig | null > {
822- const ownership = await this . applyServerCacheMutation (
823- workspaceId ,
824- config . id ,
825- mutation ,
826- null ,
827- [ ]
828- )
829- if ( ownership !== 'applied' ) return null
830-
831- const currentConfig = await this . getServerConfig ( config . id , workspaceId )
832- if (
833- ! currentConfig ||
834- ! config . discoveryRevision ||
835- currentConfig . discoveryRevision !== config . discoveryRevision
836- ) {
837- return null
838- }
839- return currentConfig
807+ return retryResult
840808 }
841809
842810 private async retryStatusPublicationAfterMetadataRace (
843811 workspaceId : string ,
844812 config : McpServerConfig ,
845813 mutation : CacheMutation ,
846814 publishStatus : ( configUpdatedAt : string ) => Promise < boolean >
847- ) : Promise < boolean > {
815+ ) : Promise < StatusPublicationResult > {
848816 for ( let attempt = 0 ; attempt < STATUS_METADATA_RACE_RETRY_ATTEMPTS ; attempt ++ ) {
849- const currentConfig = await this . getCurrentConfigForOwnedDiscoveryMutation (
817+ const ownership = await this . applyServerCacheMutation (
850818 workspaceId ,
851- config ,
852- mutation
819+ config . id ,
820+ mutation ,
821+ null ,
822+ [ ]
853823 )
854- if ( ! currentConfig ) return false
855- if ( await publishStatus ( currentConfig . updatedAt ! ) ) return true
824+ if ( ownership === 'superseded' ) return 'superseded'
825+ if ( ownership === 'unavailable' ) return 'unavailable'
826+
827+ let currentConfig : McpServerConfig | null
828+ try {
829+ currentConfig = await this . getServerConfig ( config . id , workspaceId )
830+ } catch ( error ) {
831+ logger . warn ( `Failed to reread server ${ config . id } for status publication` , {
832+ workspaceId,
833+ error : getMcpSafeErrorDiagnostics ( error ) ,
834+ } )
835+ return 'unavailable'
836+ }
837+ if (
838+ ! currentConfig ||
839+ ! config . discoveryRevision ||
840+ currentConfig . discoveryRevision !== config . discoveryRevision
841+ ) {
842+ return 'superseded'
843+ }
844+ if ( await publishStatus ( currentConfig . updatedAt ! ) ) return 'published'
856845 }
857- return false
846+ return 'unavailable'
858847 }
859848
860849 private async publishFailedDiscovery (
@@ -891,7 +880,7 @@ class McpService {
891880 // that affects discovery. Keep the failed publication only while this
892881 // mutation still owns the cache and the connection configuration is
893882 // unchanged, then retry the status CAS against the row's current token.
894- const retriedStatusApplied = await this . retryStatusPublicationAfterMetadataRace (
883+ const retryResult = await this . retryStatusPublicationAfterMetadataRace (
895884 workspaceId ,
896885 config ,
897886 mutation ,
@@ -903,7 +892,7 @@ class McpService {
903892 publicationOrder : new Date ( mutation . id ) ,
904893 } )
905894 )
906- if ( retriedStatusApplied ) return true
895+ if ( retryResult === 'published' ) return true
907896
908897 // Do not leave a negative-cache entry for a failure that lost the
909898 // database publication CAS.
@@ -938,13 +927,14 @@ class McpService {
938927 // Metadata-only edits share discovery state with the original row. Retry
939928 // only while this mutation still owns the cache and the discovery-relevant
940929 // configuration has not changed.
941- return this . retryStatusPublicationAfterMetadataRace (
930+ const retryResult = await this . retryStatusPublicationAfterMetadataRace (
942931 workspaceId ,
943932 config ,
944933 mutation ,
945934 ( configUpdatedAt ) =>
946935 this . markServerOauthPending ( config . id , workspaceId , configUpdatedAt , new Date ( mutation . id ) )
947936 )
937+ return retryResult === 'published'
948938 }
949939
950940 async discoverTools (
0 commit comments