(Re-)register all of a client's tools in the QodeX registry. Removes stale entries from previous tool lists.
(client: MCPClient, destructive: boolean)
| 130 | |
| 131 | /** (Re-)register all of a client's tools in the QodeX registry. Removes stale entries from previous tool lists. */ |
| 132 | private registerTools(client: MCPClient, destructive: boolean): void { |
| 133 | const previouslyRegistered = this.registeredToolsByServer.get(client.name) ?? new Set<string>(); |
| 134 | const newNames = new Set<string>(); |
| 135 | |
| 136 | // Add / overwrite current tools |
| 137 | for (const toolDef of client.tools) { |
| 138 | const wrapper = new MCPToolWrapper(client, toolDef, destructive); |
| 139 | this.registry.register(wrapper); |
| 140 | newNames.add(wrapper.name); |
| 141 | } |
| 142 | |
| 143 | // Remove anything that was registered before this round but is not in the new tool list. |
| 144 | // Critical when a server's tools shrink across a restart or tools/list_changed event — |
| 145 | // leftover wrappers would point to a stale client and surface to the agent as ghost tools. |
| 146 | let removed = 0; |
| 147 | for (const stale of previouslyRegistered) { |
| 148 | if (!newNames.has(stale)) { |
| 149 | if (this.registry.unregister(stale)) removed++; |
| 150 | } |
| 151 | } |
| 152 | |
| 153 | this.registeredToolsByServer.set(client.name, newNames); |
| 154 | logger.debug(`MCP ${client.name}: registered ${newNames.size} tool(s)${removed > 0 ? `, removed ${removed} stale` : ''}`); |
| 155 | } |
| 156 | |
| 157 | /** Stop all servers (called on graceful shutdown). Also clears their tools from the registry. */ |
| 158 | async stopAll(): Promise<void> { |