Skip to content

Custom tools (searchTools and callTool) are being filtered out/not registered #1116

Description

@lzj960515

Bug Description

When creating custom tools and passing them to an Agent via the tools option, tools named callTool and searchTools are being silently filtered out and never registered with the agent.

This is a bug that should be fixed.

Reproduction

import { createTool } from 'ai';
import { Agent } from '@voltagent/core';
import { z } from 'zod';

const callTool = createTool({
  name: 'callTool',
  description: 'Call another tool dynamically',
  parameters: z.object({
    toolName: z.string(),
  }),
  execute: async ({ toolName }) => {
    return `Called tool: ${toolName}`;
  },
});

const searchTools = createTool({
  name: 'searchTools',
  description: 'Search available tools',
  parameters: z.object({
    query: z.string(),
  }),
  execute: async ({ query }) => {
    return `Searching for: ${query}`;
  },
});

const normalTool = createTool({
  name: 'normalTool',
  description: 'A normal tool',
  parameters: z.object({}),
  execute: async () => 'Done',
});

const agent = new Agent({
  name: 'test-agent',
  instructions: 'You are a helpful assistant.',
  model: someModel,
  tools: [callTool, searchTools, normalTool],
});

Expected Behavior

All 3 tools should be registered and available. User-defined tools should take priority over any internal/default tools with the same name.

Actual Behavior

  • normalTool ✅ works
  • callTool ❌ silently dropped
  • searchTools ❌ silently dropped

Environment

  • @voltagent/core: 2.6.1

Request

Please fix this bug. Custom tools provided by the user should override any internal tools with the same name, not be silently discarded.


Why I Need Custom searchTools

The built-in searchTools has a generic description:

Search available tools and inspect their schemas. Always call this before callTool when tool routing is enabled.

This means the AI must call searchTools first just to discover what tools are available.

My Approach

My custom searchTools embeds a list of all available tools with short descriptions directly in its description:

description: `Search available local tools and inspect their details.

Available tools:
- toolA: Short description A
- toolB: Short description B  
- toolC: Short description C

When you plan to call another tool, call searchTools with that tool's name to get its full description and parameter schema.`,

Benefits

  1. AI sees the full picture at decision time - No need to call first just to "explore" available tools
  2. Targeted calls - AI can directly call searchTools(toolName) with the correct tool name to get full details
  3. Fewer wasted calls - AI won't blindly try tools without knowing what's available

Key Design

I introduced a shortDescription field for each tool:

  • shortDescription: Brief summary, embedded in searchTools description for quick browsing
  • description: Full details, returned when calling searchTools with the tool name

The AI still calls searchTools to get complete information, but the purpose shifts from "discovering what tools exist" to "getting details for a specific tool" - making the behavior more precise.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions