diff --git a/docs/cli/settings.md b/docs/cli/settings.md index 174c8d2299..cafbc367a0 100644 --- a/docs/cli/settings.md +++ b/docs/cli/settings.md @@ -103,6 +103,7 @@ they appear in the UI. | -------------------------------- | ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | | Enable Interactive Shell | `tools.shell.enableInteractiveShell` | Use node-pty for an interactive shell experience. Fallback to child_process still applies. | `true` | | Show Color | `tools.shell.showColor` | Show color in shell output. | `false` | +| Enable Tool Discovery | `tools.enableToolDiscovery` | Enables the tool discovery feature using `tools.discoveryCommand`. | `false` | | Use Ripgrep | `tools.useRipgrep` | Use ripgrep for file content search instead of the fallback implementation. Provides faster search performance. | `true` | | Tool Output Truncation Threshold | `tools.truncateToolOutputThreshold` | Maximum characters to show when truncating large tool outputs. Set to 0 or negative to disable truncation. | `40000` | | Disable LLM Correction | `tools.disableLLMCorrection` | Disable LLM-based error correction for edit tools. When enabled, tools will fail immediately if exact string matches are not found, instead of attempting to self-correct. | `true` | diff --git a/docs/get-started/configuration.md b/docs/get-started/configuration.md index 61adb9e13c..612631b0e6 100644 --- a/docs/get-started/configuration.md +++ b/docs/get-started/configuration.md @@ -753,6 +753,12 @@ their corresponding top-level category object in your `settings.json` file. - **Default:** `undefined` - **Requires restart:** Yes +- **`tools.enableToolDiscovery`** (boolean): + - **Description:** Enables the tool discovery feature using + `tools.discoveryCommand`. + - **Default:** `false` + - **Requires restart:** Yes + - **`tools.callCommand`** (string): - **Description:** Defines a custom shell command for invoking discovered tools. The command must take the tool name as the first argument, read JSON diff --git a/packages/cli/src/config/settingsSchema.ts b/packages/cli/src/config/settingsSchema.ts index c6fa4c80ca..71376c3833 100644 --- a/packages/cli/src/config/settingsSchema.ts +++ b/packages/cli/src/config/settingsSchema.ts @@ -1190,6 +1190,17 @@ const SETTINGS_SCHEMA = { description: 'Command to run for tool discovery.', showInDialog: false, }, + enableToolDiscovery: { + type: 'boolean', + label: 'Enable Tool Discovery', + category: 'Tools', + requiresRestart: true, + default: false, + description: oneLine` + Enables the tool discovery feature using \`tools.discoveryCommand\`. + `, + showInDialog: true, + }, callCommand: { type: 'string', label: 'Tool Call Command', diff --git a/packages/core/src/config/config.ts b/packages/core/src/config/config.ts index 646e853b0f..9c2afe615f 100644 --- a/packages/core/src/config/config.ts +++ b/packages/core/src/config/config.ts @@ -388,6 +388,7 @@ export interface ConfigParameters { /** @deprecated Use Policy Engine instead */ excludeTools?: string[]; toolDiscoveryCommand?: string; + enableToolDiscovery?: boolean; toolCallCommand?: string; mcpServerCommand?: string; mcpServers?: Record; @@ -524,6 +525,7 @@ export class Config { /** @deprecated Use Policy Engine instead */ private readonly excludeTools: string[] | undefined; private readonly toolDiscoveryCommand: string | undefined; + private readonly enableToolDiscovery: boolean; private readonly toolCallCommand: string | undefined; private readonly mcpServerCommand: string | undefined; private readonly mcpEnabled: boolean; @@ -696,6 +698,7 @@ export class Config { this.allowedTools = params.allowedTools; this.excludeTools = params.excludeTools; this.toolDiscoveryCommand = params.toolDiscoveryCommand; + this.enableToolDiscovery = params.enableToolDiscovery ?? false; this.toolCallCommand = params.toolCallCommand; this.mcpServerCommand = params.mcpServerCommand; this.mcpServers = params.mcpServers; @@ -1534,6 +1537,10 @@ export class Config { return this.toolDiscoveryCommand; } + getEnableToolDiscovery(): boolean { + return this.enableToolDiscovery; + } + getToolCallCommand(): string | undefined { return this.toolCallCommand; } diff --git a/packages/core/src/tools/tool-registry.test.ts b/packages/core/src/tools/tool-registry.test.ts index 963830200d..b53112733f 100644 --- a/packages/core/src/tools/tool-registry.test.ts +++ b/packages/core/src/tools/tool-registry.test.ts @@ -22,6 +22,7 @@ import fs from 'node:fs'; import { MockTool } from '../test-utils/mock-tool.js'; import { ToolErrorType } from './tool-error.js'; import type { MessageBus } from '../confirmation-bus/message-bus.js'; +import { debugLogger } from '../utils/debugLogger.js'; vi.mock('node:fs'); @@ -216,6 +217,7 @@ describe('ToolRegistry', () => { unsubscribe: vi.fn(), } as unknown as MessageBus; let mockConfigGetToolDiscoveryCommand: ReturnType; + let mockConfigGetEnableToolDiscovery: ReturnType; let mockConfigGetExcludedTools: MockInstance< typeof Config.prototype.getExcludeTools >; @@ -242,6 +244,12 @@ describe('ToolRegistry', () => { config, 'getToolDiscoveryCommand', ); + mockConfigGetEnableToolDiscovery = vi.spyOn( + config, + 'getEnableToolDiscovery', + ); + mockConfigGetEnableToolDiscovery.mockReturnValue(true); + mockConfigGetExcludedTools = vi.spyOn(config, 'getExcludeTools'); vi.spyOn(config, 'getMcpServers'); vi.spyOn(config, 'getMcpServerCommand'); @@ -472,6 +480,22 @@ describe('ToolRegistry', () => { }); describe('discoverTools', () => { + it('should warn and skip discovery if enableToolDiscovery is false', async () => { + const discoveryCommand = 'my-discovery-command'; + mockConfigGetToolDiscoveryCommand.mockReturnValue(discoveryCommand); + mockConfigGetEnableToolDiscovery.mockReturnValue(false); + + const warnSpy = vi.spyOn(debugLogger, 'warn'); + const mockSpawn = vi.mocked(spawn); + + await toolRegistry.discoverAllTools(); + + expect(warnSpy).toHaveBeenCalledWith( + expect.stringContaining('Tool discovery is disabled by default'), + ); + expect(mockSpawn).not.toHaveBeenCalled(); + }); + it('should will preserve tool parametersJsonSchema during discovery from command', async () => { const discoveryCommand = 'my-discovery-command'; mockConfigGetToolDiscoveryCommand.mockReturnValue(discoveryCommand); diff --git a/packages/core/src/tools/tool-registry.ts b/packages/core/src/tools/tool-registry.ts index 60b1451838..96f30f6ce5 100644 --- a/packages/core/src/tools/tool-registry.ts +++ b/packages/core/src/tools/tool-registry.ts @@ -314,6 +314,13 @@ export class ToolRegistry { return; } + if (!this.config.getEnableToolDiscovery()) { + debugLogger.warn( + 'Tool discovery is disabled by default. Please enable it in settings to use discovered tools.', + ); + return; + } + try { const cmdParts = parse(discoveryCmd); if (cmdParts.length === 0) { diff --git a/schemas/settings.schema.json b/schemas/settings.schema.json index 10c5fa9627..b005670172 100644 --- a/schemas/settings.schema.json +++ b/schemas/settings.schema.json @@ -1251,6 +1251,13 @@ "markdownDescription": "Command to run for tool discovery.\n\n- Category: `Tools`\n- Requires restart: `yes`", "type": "string" }, + "enableToolDiscovery": { + "title": "Enable Tool Discovery", + "description": "Enables the tool discovery feature using `tools.discoveryCommand`.", + "markdownDescription": "Enables the tool discovery feature using `tools.discoveryCommand`.\n\n- Category: `Tools`\n- Requires restart: `yes`\n- Default: `false`", + "default": false, + "type": "boolean" + }, "callCommand": { "title": "Tool Call Command", "description": "Defines a custom shell command for invoking discovered tools. The command must take the tool name as the first argument, read JSON arguments from stdin, and emit JSON results on stdout.",