feat(core): Allow filtering workflows by availableInMCP (#18646)

This commit is contained in:
Ricardo Espinoza
2025-09-02 16:21:12 -04:00
committed by GitHub
parent a12e782225
commit 6432555082
5 changed files with 46 additions and 0 deletions

View File

@@ -435,6 +435,30 @@ export class WorkflowRepository extends Repository<WorkflowEntity> {
this.applyTagsFilter(qb, filter);
this.applyProjectFilter(qb, filter);
this.applyParentFolderFilter(qb, filter);
this.applyAvailableInMCPFilter(qb, filter);
}
private applyAvailableInMCPFilter(
qb: SelectQueryBuilder<WorkflowEntity>,
filter: ListQuery.Options['filter'],
): void {
if (typeof filter?.availableInMCP === 'boolean') {
const dbType = this.globalConfig.database.type;
if (['postgresdb'].includes(dbType)) {
qb.andWhere("workflow.settings ->> 'availableInMCP' = :availableInMCP", {
availableInMCP: filter.availableInMCP.toString(),
});
} else if (['mysqldb', 'mariadb'].includes(dbType)) {
qb.andWhere("JSON_EXTRACT(workflow.settings, '$.availableInMCP') = :availableInMCP", {
availableInMCP: filter.availableInMCP,
});
} else if (dbType === 'sqlite') {
qb.andWhere("JSON_EXTRACT(workflow.settings, '$.availableInMCP') = :availableInMCP", {
availableInMCP: filter.availableInMCP ? 1 : 0, // SQLite stores booleans as 0/1
});
}
}
}
private applyNameFilter(

View File

@@ -35,6 +35,11 @@ export class WorkflowFilter extends BaseFilter {
@Expose()
parentFolderId?: string;
@IsBoolean()
@IsOptional()
@Expose()
availableInMCP?: boolean;
static async fromString(rawFilter: string) {
return await this.toFilter(rawFilter, WorkflowFilter);
}

View File

@@ -53,6 +53,7 @@ export declare namespace WorkflowRequest {
includeScopes?: string;
includeFolders?: string;
onlySharedWithMe?: string;
availableInMCP?: string;
}
> & {
listQueryOptions: ListQuery.Options;

View File

@@ -859,6 +859,21 @@ describe('GET /workflows', () => {
});
});
test('should filter workflows by field: availableInMCP', async () => {
const workflow1 = await createWorkflow({ settings: { availableInMCP: true } }, owner);
await createWorkflow({ settings: {} }, owner);
const response = await authOwnerAgent
.get('/workflows')
.query('filter={ "availableInMCP": true }')
.expect(200);
expect(response.body).toEqual({
count: 1,
data: [objectContaining({ id: workflow1.id })],
});
});
test('should filter workflows by field: tags (AND operator)', async () => {
const workflow1 = await createWorkflow({ name: 'First' }, owner);
const workflow2 = await createWorkflow({ name: 'Second' }, owner);

View File

@@ -2604,6 +2604,7 @@ export interface IWorkflowSettings {
executionTimeout?: number;
executionOrder?: 'v0' | 'v1';
timeSavedPerExecution?: number;
availableInMCP?: boolean;
}
export interface WorkflowFEMeta {