neoclide/coc.nvim · error
Unknown tool: ${name}
Error message
Unknown tool: ${name} What it means
`ToolRegistry.call` looks up the tool by name via `get`; when no tool with that name is registered it throws 'Unknown tool' rather than silently returning an empty result. This is the dispatch-time counterpart of registration errors.
Source
Thrown at src/mcp/tools/index.ts:115
}
public has(name: string): boolean {
return this.isAllowed(name) && this.tools.has(name)
}
public list(): { tools: ToolInfo[] } {
let tools: ToolInfo[] = []
for (let tool of this.tools.values()) {
if (!this.isAllowed(tool.name)) continue
tools.push(toToolInfo(tool))
}
return { tools }
}
public async call(name: string, args: any, context: ToolContext): Promise<McpToolResult> {
let tool = this.get(name)
if (!tool) {
throw new Error(`Unknown tool: ${name}`)
}
return await Promise.resolve(tool.handler(args, context))
}
public dispose(): void {
this.tools.clear()
this._onDidChange.dispose()
}
}
View on GitHub (pinned to 50e974d969)
Solutions
- Refresh the client's tool list (tools/list) after server restart
- Check the tool name spelling and exact casing
- Verify the extension providing the tool is installed and active
- Confirm the tool wasn't unregistered (its Disposable disposed) before the call
Example fix
// before
await registry.call('Workspace_Search', args, ctx) // wrong casing
// after
await registry.call('workspace_search', args, ctx) Defensive patterns
Strategy: try-catch
Validate before calling
const names = (await registry.tools()).map(t => t.name)
if (!names.includes(name)) throw new Error(`'${name}' not offered; available: ${names.join(', ')}`) Try / catch
try {
const result = await registry.call(name, args, ctx)
} catch (e) {
if (/Unknown tool:/.test(e.message)) {
await refreshToolList() // re-fetch tools/list after server restart
throw new Error(`Tool '${name}' unavailable — refresh and retry`)
}
throw e
} Prevention
- Refresh the cached tools/list after any server restart
- Match tool names exactly, including casing
- Verify the providing extension is active before calling
- Track Disposable disposal so unregistered tools are removed from clients too
When it happens
Trigger: Calling a tool via the MCP `tools/call` path with a name that was never registered, was unregistered (its Disposable disposed), or is misspelled/differently-cased.
Common situations: Client caches the tool list, server restarted and the tool is no longer registered; client sends a name with different casing; extension providing the tool failed to load; stale tool name after a rename across versions.
Related errors
- Tool name is required
- ${ref.error}
- ${e instanceof Error ? e.message : String(e)}
- Resource not found: ${uri}
- Tool ${tool.name} already registered
AI-assisted analysis of neoclide/coc.nvim@50e974d969 (2026-08-31).
Data as JSON: /api/errors/e21d814961e4cb65.
Report an issue: GitHub.