CherryHQ/cherry-studio · warning · Error

Tool not found

Error message

Tool not found

What it means

In BrowserServer's CallToolRequestSchema handler, the requested tool name is looked up in toolHandlers; if absent, it throws 'Tool not found'. Only the names exported from ./tools/registry (toolHandlers keys) are callable.

Source

Thrown at src/main/ai/mcp/servers/browser/server.ts:41

      {
        capabilities: {
          resources: {},
          tools: {}
        }
      }
    )

    this.mcpServer.server.setRequestHandler(ListToolsRequestSchema, async () => {
      return {
        tools: toolDefinitions
      }
    })

    this.mcpServer.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      const { name, arguments: args } = request.params
      const handler = toolHandlers[name]
      if (!handler) {
        throw new Error('Tool not found')
      }
      return handler(this.controller, args)
    })

    // Clean up browser controller when the MCP server connection closes
    // (triggered by McpRuntimeService.onStop() → client.close())
    this.server.onclose = () => {
      void this.controller.dispose()
    }
  }
}

export default BrowserServer

View on GitHub (pinned to 726446b54c)

Solutions

  1. Re-fetch the tool list via ListTools and only call names that appear in toolDefinitions.
  2. Verify exact tool name spelling and casing against registry.ts exports.
  3. If a tool seems missing, confirm it isn't gated/conditionally registered in ./tools/registry.

Example fix

// before
const handler = toolHandlers[name]
if (!handler) {
  throw new Error('Tool not found')
}

// after — return an MCP error result with the available tools for self-correction
const handler = toolHandlers[name]
if (!handler) {
  return {
    content: [{ type: 'text', text: `Unknown tool: ${name}. Available: ${Object.keys(toolHandlers).join(', ')}` }],
    isError: true
  }
}
Defensive patterns

Strategy: validation

Validate before calling

// Only call tool names that the server actually advertises
function assertToolKnown(name, toolHandlers) {
  if (!Object.prototype.hasOwnProperty.call(toolHandlers, name)) {
    throw new Error(`Unknown tool: ${name}. Available: ${Object.keys(toolHandlers).join(', ')}`)
  }
}

Type guard

function isRegisteredTool(name: string, handlers: Record<string, unknown>): name is string {
  return Object.prototype.hasOwnProperty.call(handlers, name)
}

Try / catch

const handler = toolHandlers[name]
if (!handler) {
  return { content: [{ type: 'text', text: `Unknown tool: ${name}. Available: ${Object.keys(toolHandlers).join(', ')}` }], isError: true }
}

Prevention

When it happens

Trigger: An MCP client calls a tool name that is not registered — typo in the tool name, an LLM hallucinating a tool, a stale client that predates a tool rename, or a tool that was conditionally excluded from toolDefinitions.

Common situations: LLM invents a tool like 'click_element' that isn't in the registry; client caches an old tool list after an upgrade; casing mismatch (Navigate vs navigate).

Related errors


AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12). Data as JSON: /api/errors/962c16eab9789d72. Report an issue: GitHub.