{"record":{"id":"ebd1090d4f37a0d2","repo":"neoclide/coc.nvim","slug":"tool-tool-name-already-registered","errorCode":null,"errorMessage":"Tool ${tool.name} already registered","messagePattern":"Tool (.+?) already registered","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/mcp/tools/index.ts","lineNumber":79,"sourceCode":"  public readonly onDidChange: Event<void> = this._onDidChange.event\n\n  /**\n   * Restrict exposed tools to a whitelist of names. `null` allows every\n   * registered tool; an empty set exposes none. Tools outside the whitelist\n   * are hidden from `tools/list` and rejected by `has`/`get`/`call`.\n   */\n  public setAllowedTools(names: string[] | null): void {\n    this.allowed = names ? new Set(names) : null\n    this._onDidChange.fire()\n  }\n\n  public isAllowed(name: string): boolean {\n    return this.allowed === null || this.allowed.has(name)\n  }\n\n  public register(tool: McpTool): Disposable {\n    if (this.tools.has(tool.name)) {\n      throw new Error(`Tool ${tool.name} already registered`)\n    }\n    this.tools.set(tool.name, tool)\n    this._onDidChange.fire()\n    return Disposable.create(() => {\n      this.unregister(tool.name)\n    })\n  }\n\n  public unregister(name: string): void {\n    if (this.tools.delete(name)) {\n      this._onDidChange.fire()\n    }\n  }\n\n  public get(name: string): McpTool | undefined {\n    let tool = this.tools.get(name)\n    return tool && this.isAllowed(name) ? tool : undefined\n  }","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/neoclide/coc.nvim/blob/50e974d9692461a69147d5cab146a8d3e439abe4/src/mcp/tools/index.ts#L61-L97","documentation":"`ToolRegistry.register` keeps a map keyed by tool name and refuses to overwrite an existing entry. Registering a tool whose name is already taken throws, forcing callers to unregister first or pick a unique name — this keeps `tools/list` unambiguous.","triggerScenarios":"Calling `registerTool` twice with tools sharing the same `name`; re-initializing/reloading the MCP server (initialize path) without disposing previously registered tools; two extensions registering tools with colliding names.","commonSituations":"Hot-reload or re-registration on config change where the old Disposable wasn't kept/disposed; duplicate tool definitions in configuration; name collision between built-in tools (workspace search etc.) and extension tools.","solutions":["Keep the Disposable returned by registerTool and dispose it before re-registering the same name","Choose a unique tool name (prefix with extension id)","Unregister the existing tool first if replacement is intended","Guard with registry `get(name)` / existence check before registering"],"exampleFix":"// before\nserver.registerTool({ name: 'search', ... })\nserver.registerTool({ name: 'search', ... }) // throws\n// after\nconst d = server.registerTool({ name: 'search', ... })\nd.dispose() // before re-registering\nserver.registerTool({ name: 'search', ... })","handlingStrategy":"try-catch","validationCode":"function registerUnique(server: McpServer, tool: McpTool): Disposable {\n  const disposables: Disposable[] = []\n  if (registeredNames.has(tool.name)) registeredNames.get(tool.name)!.dispose()\n  const d = server.registerTool(tool)\n  registeredNames.set(tool.name, d)\n  return d\n}","typeGuard":null,"tryCatchPattern":"let d: Disposable\ntry {\n  d = server.registerTool(tool)\n} catch (e) {\n  if (/already registered/.test(e.message)) {\n    logger.warn(`Tool '${tool.name}' already registered — skipping duplicate`)\n    return\n  }\n  throw e\n}","preventionTips":["Store and dispose the Disposable returned by registerTool on deactivation","Namespace tool names with your extension id to avoid collisions","Never call registerTool twice for the same name without disposing","Clear all registrations on config reload before re-registering"],"tags":["mcp","duplicate-key","registration"],"backgroundTag":"duplicate-registration","analyzedSha":"50e974d9692461a69147d5cab146a8d3e439abe4","analyzedAt":"2026-08-31T11:17:23.966Z","schemaVersion":2},"datasetVersion":"2026-09-01T08:17:40.651Z"}