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

  1. Refresh the client's tool list (tools/list) after server restart
  2. Check the tool name spelling and exact casing
  3. Verify the extension providing the tool is installed and active
  4. 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

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


AI-assisted analysis of neoclide/coc.nvim@50e974d969 (2026-08-31). Data as JSON: /api/errors/e21d814961e4cb65. Report an issue: GitHub.