fix: hide plugin tools from the marketplace when the tools capability is off

buildCatalog gated built-ins, MCP, and skills on their capabilities and
permissions but pushed regular plugin tools unconditionally, so deployments
that removed the tools capability still offered attachable tool cards in
the marketplace. The loop now requires AgentCapabilities.tools, matching
the old Add Tools gate.
This commit is contained in:
Marco Beretta 2026-07-03 19:24:55 +02:00
parent 0e262eda5b
commit cf00c4fd83
No known key found for this signature in database
GPG key ID: D918033D8E74CC11
3 changed files with 32 additions and 17 deletions

View file

@ -29,7 +29,7 @@ jest.mock('react-hook-form', () => ({
jest.mock('~/Providers', () => ({
useAgentPanelContext: () => ({
agentsConfig: { capabilities: ['execute_code'] },
agentsConfig: { capabilities: ['execute_code', 'tools'] },
regularTools: [{ pluginKey: 'dalle', name: 'DALL-E', description: 'Images' }],
mcpServersMap: new Map(),
actions: [],

View file

@ -12,6 +12,11 @@ const emptyInputs: BuildCatalogInputs = {
permissions: { mcp: true, skills: true },
};
const toolInputs: BuildCatalogInputs = {
...emptyInputs,
agentsConfig: { capabilities: [AgentCapabilities.tools] },
};
describe('buildCatalog', () => {
test('returns empty when nothing is enabled', () => {
expect(buildCatalog(emptyInputs)).toEqual([]);
@ -103,18 +108,26 @@ describe('buildCatalog', () => {
expect(skill?.name).toBe('Reviewer');
});
test('emits tool items', () => {
test('emits tool items when the tools capability is enabled', () => {
const items = buildCatalog({
...emptyInputs,
...toolInputs,
regularTools: [makePlugin({ pluginKey: 'dalle', name: 'DALL-E', description: 'Images' })],
});
const tool = items.find((i) => i.kind === 'tool');
expect(tool?.id).toBe('dalle');
});
test('marks an auth-requiring, unauthenticated tool as needs_setup', () => {
test('hides tool items when the admin disabled the tools capability', () => {
const items = buildCatalog({
...emptyInputs,
regularTools: [makePlugin({ pluginKey: 'dalle', name: 'DALL-E', description: 'Images' })],
});
expect(items.find((i) => i.kind === 'tool')).toBeUndefined();
});
test('marks an auth-requiring, unauthenticated tool as needs_setup', () => {
const items = buildCatalog({
...toolInputs,
regularTools: [
makePlugin({
pluginKey: 'serpapi',
@ -131,7 +144,7 @@ describe('buildCatalog', () => {
test('leaves status undefined for an authenticated auth-requiring tool', () => {
const items = buildCatalog({
...emptyInputs,
...toolInputs,
regularTools: [
makePlugin({
pluginKey: 'serpapi',
@ -148,7 +161,7 @@ describe('buildCatalog', () => {
test('leaves status undefined for a tool with no authConfig', () => {
const items = buildCatalog({
...emptyInputs,
...toolInputs,
regularTools: [makePlugin({ pluginKey: 'dalle', name: 'DALL-E', description: 'Images' })],
});
const tool = items.find((i) => i.kind === 'tool');
@ -211,7 +224,7 @@ describe('buildCatalog', () => {
map.set('srv', { serverName: 'srv', isConfigured: true, tools: [] });
const items = buildCatalog({
...emptyInputs,
agentsConfig: { capabilities: [AgentCapabilities.execute_code] },
agentsConfig: { capabilities: [AgentCapabilities.execute_code, AgentCapabilities.tools] },
regularTools: [makePlugin({ pluginKey: 't1', name: 'T1', description: '' })],
mcpServersMap: map,
skills: [makeSkill({ _id: 's1', name: 'S1', description: '' })],

View file

@ -148,16 +148,18 @@ export function buildCatalog(inputs: BuildCatalogInputs): AgentItem[] {
}
}
for (const plugin of inputs.regularTools) {
items.push({
kind: 'tool',
id: plugin.pluginKey,
name: plugin.name ?? plugin.pluginKey,
description: plugin.description ?? '',
iconKey: 'tool',
plugin,
status: pluginNeedsAuth(plugin) ? 'needs_setup' : undefined,
});
if (enabled.has(AgentCapabilities.tools)) {
for (const plugin of inputs.regularTools) {
items.push({
kind: 'tool',
id: plugin.pluginKey,
name: plugin.name ?? plugin.pluginKey,
description: plugin.description ?? '',
iconKey: 'tool',
plugin,
status: pluginNeedsAuth(plugin) ? 'needs_setup' : undefined,
});
}
}
if (inputs.permissions.skills) {