@@ -8,6 +8,8 @@ export class IndexedDB {
88 private dbName = 'McpRegistryDB' ;
99 private settingsStore = 'user-settings' ;
1010 private serversStore = 'mcp-servers' ;
11+ private lastFetchKey = 'servers-last-fetch' ;
12+ private cacheMaxAge = 24 * 60 * 60 * 1000 ; // 24 hours
1113
1214 async init ( ) {
1315 if ( typeof window === 'undefined' ) return null ;
@@ -104,11 +106,26 @@ export class IndexedDB {
104106 await this . set ( 'mcp-servers-stack' , stack ) ;
105107 }
106108
109+ /** Get timestamp of last server fetch */
110+ async getLastFetchTime ( ) : Promise < number > {
111+ return ( await this . get < number > ( this . lastFetchKey ) ) || 0 ;
112+ }
113+
114+ /** Set timestamp of last server fetch */
115+ async setLastFetchTime ( timestamp : number ) : Promise < void > {
116+ await this . set ( this . lastFetchKey , timestamp ) ;
117+ }
118+
119+ /** Check if cached server data is stale */
120+ async isDataStale ( ) : Promise < boolean > {
121+ return Date . now ( ) - ( await this . getLastFetchTime ( ) ) > this . cacheMaxAge ;
122+ }
123+
107124 // NOTE: can use IndexedDB as a basic search store for MCP servers
108125
109126 /**
110127 * Initialize servers in IndexedDB.
111- * By default this will not re-fetch if the DB already contains servers.
128+ * By default this will not re-fetch if the DB already contains fresh servers.
112129 * Set `forceRefresh` to true to always re-fetch and replace stored servers.
113130 */
114131 async initServers ( registryUrl : string , forceRefresh = false ) {
@@ -117,11 +134,15 @@ export class IndexedDB {
117134 try {
118135 const db = await this . init ( ) ;
119136 if ( ! db ) return null ;
120- // If we already have servers stored and no forced refresh requested, skip fetching
121- if ( ! forceRefresh ) {
122- const has = await this . hasServers ( ) ;
123- if ( has ) return this . db ;
137+
138+ const hasServers = await this . hasServers ( ) ;
139+ const isStale = await this . isDataStale ( ) ;
140+
141+ // Skip fetching if: not forced, has servers, and data is fresh
142+ if ( ! forceRefresh && hasServers && ! isStale ) {
143+ return this . db ;
124144 }
145+
125146 // Attempt to fetch servers. On failure, preserve existing DB contents instead of clearing them.
126147 const docs = await fetchAllServers ( registryUrl ) . catch ( ( err ) => {
127148 console . warn ( 'Failed to fetch servers for initServers:' , err ) ;
@@ -131,13 +152,37 @@ export class IndexedDB {
131152 if ( ! docs ) return this . db ;
132153 // Save using the existing saveServers method which handles clearing and writing
133154 await this . saveServers ( docs || [ ] ) ;
155+ await this . setLastFetchTime ( Date . now ( ) ) ;
134156 return this . db ;
135157 } catch ( err ) {
136158 console . warn ( 'initServers failed:' , err ) ;
137159 return null ;
138160 }
139161 }
140162
163+ /**
164+ * Refresh servers in the background without blocking.
165+ * Dispatches 'servers-updated' event when complete.
166+ */
167+ async refreshInBackground ( registryUrl : string ) : Promise < void > {
168+ setTimeout ( async ( ) => {
169+ try {
170+ console . log ( 'Starting background server refresh...' ) ;
171+ const docs = await fetchAllServers ( registryUrl ) ;
172+ await this . saveServers ( docs ) ;
173+ await this . setLastFetchTime ( Date . now ( ) ) ;
174+ console . log ( 'IndexedDB refresh completed' ) ;
175+
176+ // Dispatch custom event to notify UI
177+ if ( typeof window !== 'undefined' ) {
178+ window . dispatchEvent ( new CustomEvent ( 'servers-updated' , { detail : { count : docs . length } } ) ) ;
179+ }
180+ } catch ( err ) {
181+ console . warn ( 'IndexedDB refresh failed:' , err ) ;
182+ }
183+ } , 0 ) ;
184+ }
185+
141186 async search ( term : string ) : Promise < McpServerItem [ ] > {
142187 if ( ! this . db ) return [ ] ;
143188 try {
0 commit comments