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
- Re-fetch the tool list via ListTools and only call names that appear in toolDefinitions.
- Verify exact tool name spelling and casing against registry.ts exports.
- 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
- Re-fetch ListTools and only call advertised names.
- Verify exact spelling and casing.
- Route calls to the correct server.
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
- No arguments provided
- Invalid command: command must be a non-empty string
- Invalid command: command cannot be empty
- Invalid args: must be an array
- Invalid args: argument at index ${index} must be a string
AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12).
Data as JSON: /api/errors/962c16eab9789d72.
Report an issue: GitHub.