diff --git a/libs/designer-v2/src/lib/core/state/designerOptions/designerOptionsInterfaces.ts b/libs/designer-v2/src/lib/core/state/designerOptions/designerOptionsInterfaces.ts
index 616e0db6d6f..777b83a05a7 100644
--- a/libs/designer-v2/src/lib/core/state/designerOptions/designerOptionsInterfaces.ts
+++ b/libs/designer-v2/src/lib/core/state/designerOptions/designerOptionsInterfaces.ts
@@ -54,6 +54,7 @@ export interface DesignerOptionsState {
enableNestedAgentLoops?: boolean; // allow agent loops to be added inside regular loops (requires bundle version >= 1.115.0)
disableMcpClientTools?: boolean; // hide MCP client tools from browse panel
disableNativeMcpClientTools?: boolean; // hide native (built-in) MCP client tools tab from browse panel
+ hiddenBrowseCategories?: string[]; // hide specific categories from browse panel by category key (e.g., ['aiAgent', 'humanInTheLoop', 'favorites'])
};
nodeSelectAdditionalCallback?: (nodeId: string) => any;
panelTabHideKeys?: PANEL_TAB_NAMES[];
diff --git a/libs/designer-v2/src/lib/core/state/designerOptions/designerOptionsSelectors.ts b/libs/designer-v2/src/lib/core/state/designerOptions/designerOptionsSelectors.ts
index 592bf2354e7..28c403fc62c 100644
--- a/libs/designer-v2/src/lib/core/state/designerOptions/designerOptionsSelectors.ts
+++ b/libs/designer-v2/src/lib/core/state/designerOptions/designerOptionsSelectors.ts
@@ -7,6 +7,8 @@ import { equals } from '@microsoft/logic-apps-shared';
import { getSupportedChannels } from '../../utils/agent';
import constants from '../../../common/constants';
+const EMPTY_ARRAY: string[] = [];
+
export const useReadOnly = () => {
return useSelector((state: RootState) => state.designerOptions.readOnly);
};
@@ -70,6 +72,10 @@ export const useDisableNativeMcpClientTools = () => {
return useSelector((state: RootState) => state.designerOptions.hostOptions?.disableNativeMcpClientTools ?? false);
};
+export const useHiddenBrowseCategories = () => {
+ return useSelector((state: RootState) => state.designerOptions.hostOptions?.hiddenBrowseCategories ?? EMPTY_ARRAY);
+};
+
export const useAreDesignerOptionsInitialized = () => {
return useSelector((state: RootState) => state.designerOptions?.designerOptionsInitialized ?? false);
};
diff --git a/libs/designer-v2/src/lib/ui/panel/recommendation/browse/__test__/browseView.spec.tsx b/libs/designer-v2/src/lib/ui/panel/recommendation/browse/__test__/browseView.spec.tsx
index 3361e7fac77..878417a95fa 100644
--- a/libs/designer-v2/src/lib/ui/panel/recommendation/browse/__test__/browseView.spec.tsx
+++ b/libs/designer-v2/src/lib/ui/panel/recommendation/browse/__test__/browseView.spec.tsx
@@ -317,7 +317,7 @@ describe('BrowseView', () => {
render(, { wrapper: createWrapper() });
// getActionCategories should be called with allowAgents=true
- expect(mockGetActionCategories).toHaveBeenCalledWith(true, false, false);
+ expect(mockGetActionCategories).toHaveBeenCalledWith(true, false, false, []);
});
test('should pass allowAgents as false when graphId is not root', () => {
@@ -330,7 +330,7 @@ describe('BrowseView', () => {
render(, { wrapper: createWrapper() });
// getActionCategories should be called with allowAgents=false
- expect(mockGetActionCategories).toHaveBeenCalledWith(false, false, false);
+ expect(mockGetActionCategories).toHaveBeenCalledWith(false, false, false, []);
});
test('should pass isAddingAgentTool to getActionCategories', () => {
@@ -338,7 +338,7 @@ describe('BrowseView', () => {
render(, { wrapper: createWrapper() });
- expect(mockGetActionCategories).toHaveBeenCalledWith(true, true, false);
+ expect(mockGetActionCategories).toHaveBeenCalledWith(true, true, false, []);
});
});
});
diff --git a/libs/designer-v2/src/lib/ui/panel/recommendation/browse/__test__/helper.spec.ts b/libs/designer-v2/src/lib/ui/panel/recommendation/browse/__test__/helper.spec.ts
index bf0325d3f02..3adca02c30d 100644
--- a/libs/designer-v2/src/lib/ui/panel/recommendation/browse/__test__/helper.spec.ts
+++ b/libs/designer-v2/src/lib/ui/panel/recommendation/browse/__test__/helper.spec.ts
@@ -166,4 +166,91 @@ describe('browse helper', () => {
expect(humanInTheLoop?.connectorFilters?.name).toContain('teams');
});
});
+
+ describe('hiddenBrowseCategories', () => {
+ describe('action categories', () => {
+ test('should hide aiAgent when included in hiddenCategories', () => {
+ const categories = getActionCategories(true, false, false, ['aiAgent']);
+ const aiAgent = categories.find((c) => c.key === 'aiAgent');
+ expect(aiAgent?.visible).toBe(false);
+ });
+
+ test('should hide humanInTheLoop when included in hiddenCategories', () => {
+ const categories = getActionCategories(false, false, false, ['humanInTheLoop']);
+ const humanInTheLoop = categories.find((c) => c.key === 'humanInTheLoop');
+ expect(humanInTheLoop?.visible).toBe(false);
+ });
+
+ test('should hide multiple categories', () => {
+ const categories = getActionCategories(true, false, false, ['aiAgent', 'humanInTheLoop', 'favorites']);
+ expect(categories.find((c) => c.key === 'aiAgent')?.visible).toBe(false);
+ expect(categories.find((c) => c.key === 'humanInTheLoop')?.visible).toBe(false);
+ expect(categories.find((c) => c.key === 'favorites')?.visible).toBe(false);
+ });
+
+ test('should not affect non-hidden categories', () => {
+ const categories = getActionCategories(true, false, false, ['aiAgent']);
+ const actionInApp = categories.find((c) => c.key === 'actionInApp');
+ expect(actionInApp?.visible).toBeUndefined(); // undefined = visible by default
+ });
+
+ test('should work with empty array', () => {
+ const categories = getActionCategories(true, false, false, []);
+ expect(categories.find((c) => c.key === 'aiAgent')?.visible).toBe(true);
+ });
+
+ test('should work with undefined', () => {
+ const categories = getActionCategories(true, false, false, undefined);
+ expect(categories.find((c) => c.key === 'aiAgent')?.visible).toBe(true);
+ });
+
+ test('should respect allowAgents flag even when not in hiddenCategories', () => {
+ const categoriesWithAgents = getActionCategories(true, false, false, []);
+ const categoriesWithoutAgents = getActionCategories(false, false, false, []);
+
+ expect(categoriesWithAgents.find((c) => c.key === 'aiAgent')?.visible).toBe(true);
+ expect(categoriesWithoutAgents.find((c) => c.key === 'aiAgent')?.visible).toBe(false);
+ });
+
+ test('should hide aiAgent via hiddenCategories even when allowAgents is true', () => {
+ const categories = getActionCategories(true, false, false, ['aiAgent']);
+ expect(categories.find((c) => c.key === 'aiAgent')?.visible).toBe(false);
+ });
+ });
+
+ describe('trigger categories', () => {
+ test('should hide manual trigger when included in hiddenCategories', () => {
+ const categories = getTriggerCategories(['manual']);
+ expect(categories.find((c) => c.key === 'manual')?.visible).toBe(false);
+ });
+
+ test('should hide schedule trigger when included in hiddenCategories', () => {
+ const categories = getTriggerCategories(['schedule']);
+ expect(categories.find((c) => c.key === 'schedule')?.visible).toBe(false);
+ });
+
+ test('should hide multiple trigger categories', () => {
+ const categories = getTriggerCategories(['manual', 'schedule', 'appEvent']);
+ expect(categories.find((c) => c.key === 'manual')?.visible).toBe(false);
+ expect(categories.find((c) => c.key === 'schedule')?.visible).toBe(false);
+ expect(categories.find((c) => c.key === 'appEvent')?.visible).toBe(false);
+ });
+
+ test('should not affect non-hidden trigger categories', () => {
+ const categories = getTriggerCategories(['manual']);
+ const schedule = categories.find((c) => c.key === 'schedule');
+ expect(schedule?.visible).toBeUndefined(); // undefined = visible by default
+ });
+
+ test('should work with empty array for triggers', () => {
+ const categories = getTriggerCategories([]);
+ expect(categories.find((c) => c.key === 'manual')?.visible).toBeUndefined();
+ });
+
+ test('should work with undefined for triggers', () => {
+ const categories = getTriggerCategories(undefined);
+ expect(categories.find((c) => c.key === 'manual')?.visible).toBeUndefined();
+ });
+ });
+ });
});
diff --git a/libs/designer-v2/src/lib/ui/panel/recommendation/browse/browseView.tsx b/libs/designer-v2/src/lib/ui/panel/recommendation/browse/browseView.tsx
index 58f9b8b9830..c412e318cf1 100644
--- a/libs/designer-v2/src/lib/ui/panel/recommendation/browse/browseView.tsx
+++ b/libs/designer-v2/src/lib/ui/panel/recommendation/browse/browseView.tsx
@@ -17,7 +17,7 @@ import type { AppDispatch } from '../../../../core';
import { equals, type DiscoveryOperation, type DiscoveryResultTypes } from '@microsoft/logic-apps-shared';
import { getNodeId } from '../helpers';
import { getTriggerCategories, getActionCategories, BrowseCategoryType } from './helper';
-import { useDisableMcpClientTools } from '../../../../core/state/designerOptions/designerOptionsSelectors';
+import { useDisableMcpClientTools, useHiddenBrowseCategories } from '../../../../core/state/designerOptions/designerOptionsSelectors';
interface BrowseViewProps {
isTrigger?: boolean;
@@ -35,8 +35,11 @@ export const BrowseView = ({ isTrigger = false, onOperationClick }: BrowseViewPr
const allowAgents = equals(relationshipIds.graphId, 'root');
const isAddingAgentTool = useIsAddingAgentTool();
const disableMcpClientTools = useDisableMcpClientTools();
+ const hiddenCategories = useHiddenBrowseCategories();
- const categories = isTrigger ? getTriggerCategories() : getActionCategories(allowAgents, isAddingAgentTool, disableMcpClientTools);
+ const categories = isTrigger
+ ? getTriggerCategories(hiddenCategories)
+ : getActionCategories(allowAgents, isAddingAgentTool, disableMcpClientTools, hiddenCategories);
const addTriggerOperation = useCallback(
(operation: DiscoveryOperation) => {
diff --git a/libs/designer-v2/src/lib/ui/panel/recommendation/browse/helper.ts b/libs/designer-v2/src/lib/ui/panel/recommendation/browse/helper.ts
index e891c71f1da..3dc1463635b 100644
--- a/libs/designer-v2/src/lib/ui/panel/recommendation/browse/helper.ts
+++ b/libs/designer-v2/src/lib/ui/panel/recommendation/browse/helper.ts
@@ -48,10 +48,15 @@ export interface BrowseCategoryConfig {
connectorFilters?: ConnectorFilterTypes;
}
-export const getTriggerCategories = (): BrowseCategoryConfig[] => {
+/**
+ * Get trigger categories for the browse panel.
+ * @param hiddenCategories - Array of category keys to hide. Available keys: 'manual', 'schedule', 'appEvent', 'azure', 'workflowExecution', 'chatMessage', 'evaluation', 'otherWays'
+ * @returns Array of trigger category configurations
+ */
+export const getTriggerCategories = (hiddenCategories?: string[]): BrowseCategoryConfig[] => {
const intl = getIntl();
- return [
+ const categories = [
{
key: 'manual',
text: intl.formatMessage({
@@ -183,16 +188,28 @@ export const getTriggerCategories = (): BrowseCategoryConfig[] => {
type: BrowseCategoryType.BROWSE,
},
];
+
+ // Apply hidden categories filter
+ return categories.map((category) => (hiddenCategories?.includes(category.key) ? { ...category, visible: false } : category));
};
+/**
+ * Get action categories for the browse panel.
+ * @param allowAgents - Whether to show AI agent category (based on graph root)
+ * @param isAddingAgentTool - Whether currently adding an agent tool
+ * @param disableMcpClientTools - Whether to disable MCP client tools category
+ * @param hiddenCategories - Array of category keys to hide. Available keys: 'favorites', 'mcpServers', 'aiAgent', 'actionInApp', 'dataTransformation', 'simpleOperations', 'humanInTheLoop'
+ * @returns Array of action category configurations
+ */
export const getActionCategories = (
allowAgents?: boolean,
isAddingAgentTool?: boolean,
- disableMcpClientTools?: boolean
+ disableMcpClientTools?: boolean,
+ hiddenCategories?: string[]
): BrowseCategoryConfig[] => {
const intl = getIntl();
- return [
+ const categories = [
{
key: 'favorites',
text: intl.formatMessage({
@@ -337,4 +354,7 @@ export const getActionCategories = (
},
},
];
+
+ // Apply hidden categories filter
+ return categories.map((category) => (hiddenCategories?.includes(category.key) ? { ...category, visible: false } : category));
};