@@ -798,8 +798,18 @@ describe('McpService.discoverTools per-server caching', () => {
798798 it ( 'does not return or cache tools discovered from a stale server configuration' , async ( ) => {
799799 mockGetWorkspaceServersRows . mockResolvedValue ( [ dbRow ( 'mcp-a' , 'A' ) ] )
800800 mockListTools . mockResolvedValueOnce ( [ tool ( 'stale-tool' , 'mcp-a' ) ] )
801+ const serverKey = `workspace:${ WORKSPACE_ID } :server:mcp-a`
801802 mockUpdateSet . mockReturnValueOnce ( {
802- where : vi . fn ( ) . mockReturnValue ( { returning : vi . fn ( ) . mockResolvedValue ( [ ] ) } ) ,
803+ where : vi . fn ( ) . mockReturnValue ( {
804+ returning : vi . fn ( ) . mockImplementation ( async ( ) => {
805+ // Connection-changing edits invalidate both cache entries and advance
806+ // mutation ownership after updating the database row.
807+ await mockCacheAdapter . beginMutation ( serverKey )
808+ cacheStore . delete ( serverKey )
809+ cacheStore . delete ( `${ serverKey } :failure` )
810+ return [ ]
811+ } ) ,
812+ } ) ,
803813 } )
804814
805815 const tools = await mcpService . discoverTools ( USER_ID , WORKSPACE_ID , true )
@@ -815,7 +825,31 @@ describe('McpService.discoverTools per-server caching', () => {
815825 } ,
816826 [ `workspace:${ WORKSPACE_ID } :server:mcp-a:failure` ]
817827 )
818- expect ( cacheStore . has ( `workspace:${ WORKSPACE_ID } :server:mcp-a` ) ) . toBe ( false )
828+ expect ( cacheStore . has ( serverKey ) ) . toBe ( false )
829+ } )
830+
831+ it ( 'keeps valid live tools when a metadata-only edit wins the database CAS' , async ( ) => {
832+ const serverKey = `workspace:${ WORKSPACE_ID } :server:mcp-a`
833+ mockGetWorkspaceServersRows . mockResolvedValue ( [ dbRow ( 'mcp-a' , 'A' ) ] )
834+ mockListTools . mockResolvedValueOnce ( [ tool ( 'still-valid' , 'mcp-a' ) ] )
835+ mockUpdateSet . mockReturnValueOnce ( {
836+ where : vi . fn ( ) . mockReturnValue ( { returning : vi . fn ( ) . mockResolvedValue ( [ ] ) } ) ,
837+ } )
838+
839+ await expect (
840+ mcpService . discoverServerToolsWithMetadata ( USER_ID , 'mcp-a' , WORKSPACE_ID , true )
841+ ) . resolves . toEqual ( {
842+ tools : [ tool ( 'still-valid' , 'mcp-a' ) ] ,
843+ state : 'unavailable' ,
844+ } )
845+
846+ expect ( cacheStore . get ( serverKey ) ?. tools ) . toEqual ( [ tool ( 'still-valid' , 'mcp-a' ) ] )
847+ expect ( mockCacheAdapter . applyMutationIfCurrent ) . toHaveBeenLastCalledWith (
848+ serverKey ,
849+ expect . any ( Number ) ,
850+ null ,
851+ [ ]
852+ )
819853 } )
820854
821855 it ( 'supersedes an older discovery before it can publish status' , async ( ) => {
0 commit comments