@@ -136,6 +136,15 @@ export class OrgScopedThreadStorage {
136136 return this . inner . listByTriggerIds ( this . requireOrg ( ) , triggerIds , options ) ;
137137 }
138138
139+ findLastUsedByVirtualMcpIds (
140+ virtualMcpIds : string [ ] ,
141+ ) : Promise < Map < string , { last_used_at : string ; last_used_by : string } > > {
142+ return this . inner . findLastUsedByVirtualMcpIds (
143+ this . requireOrg ( ) ,
144+ virtualMcpIds ,
145+ ) ;
146+ }
147+
139148 addInflightAsyncJob ( taskId : string , entry : InflightAsyncJob ) : Promise < void > {
140149 return this . inner . addInflightAsyncJob ( taskId , this . requireOrg ( ) , entry ) ;
141150 }
@@ -508,6 +517,35 @@ export class SqlThreadStorage implements ThreadStoragePort {
508517 } ;
509518 }
510519
520+ async findLastUsedByVirtualMcpIds (
521+ organizationId : string ,
522+ virtualMcpIds : string [ ] ,
523+ ) : Promise < Map < string , { last_used_at : string ; last_used_by : string } > > {
524+ const result = new Map <
525+ string ,
526+ { last_used_at : string ; last_used_by : string }
527+ > ( ) ;
528+ if ( virtualMcpIds . length === 0 ) return result ;
529+
530+ const rows = await this . db
531+ . selectFrom ( "threads" )
532+ . distinctOn ( "virtual_mcp_id" )
533+ . select ( [ "virtual_mcp_id" , "created_by" , "created_at" ] )
534+ . where ( "organization_id" , "=" , organizationId )
535+ . where ( "virtual_mcp_id" , "in" , virtualMcpIds )
536+ . orderBy ( "virtual_mcp_id" )
537+ . orderBy ( "created_at" , "desc" )
538+ . execute ( ) ;
539+
540+ for ( const row of rows ) {
541+ result . set ( row . virtual_mcp_id , {
542+ last_used_at : toIsoString ( row . created_at ) ,
543+ last_used_by : row . created_by ,
544+ } ) ;
545+ }
546+ return result ;
547+ }
548+
511549 /**
512550 * Upserts thread messages by id.
513551 * Inserts new messages; updates existing rows (by id) with parts, metadata, role, updated_at.
0 commit comments