fix(coding-agent/acp): MCP tools from Zed not activated in ACP sessions

When Zed provisions MCP servers through `session/new.mcpServers`, the
ACP agent
correctly connects to them and registers their tools via
`refreshMCPTools`, but
the tools were never activated.  `refreshMCPTools` builds the next
active tool
set from `getSelectedMCPToolNames()`, which — with discovery disabled —
returns
only MCP tools already in the active set.  Since ACP sessions start with
no MCP
tools, this created a circular deadlock: tools could only become active
if they
were already active.

Added an optional `{ activateAll?: boolean }` parameter to
`refreshMCPTools`.
When true, every newly registered tool is force-activated regardless of
prior
selection.  `AcpAgent#configureMcpServers` passes `activateAll: true` on
both
call sites so client-provisioned tools are immediately usable.
This commit is contained in:
ian_p
2026-06-01 00:03:42 +08:00
parent 8b619a2f40
commit e9a7f1add9
3 changed files with 65 additions and 3 deletions
@@ -1981,7 +1981,7 @@ export class AcpAgent implements Agent {
}
if (servers.length === 0) {
record.mcpManager = undefined;
await record.session.refreshMCPTools([]);
await record.session.refreshMCPTools([], { activateAll: true });
return;
}
@@ -2008,7 +2008,7 @@ export class AcpAgent implements Agent {
}
record.mcpManager = manager;
await record.session.refreshMCPTools(result.tools);
await record.session.refreshMCPTools(result.tools, { activateAll: true });
}
#toMcpConfig(server: McpServer): MCPServerConfig {
@@ -3617,8 +3617,13 @@ export class AgentSession {
/**
* Replace MCP tools in the registry and recompute the visible MCP tool set immediately.
* This allows /mcp add/remove/reauth to take effect without restarting the session.
*
* @param mcpTools The new MCP tools to register.
* @param options.activateAll When true, force-activates every newly registered MCP tool
* regardless of prior selection state. Used when an ACP client provisions MCP servers
* for a session where MCP discovery is disabled.
*/
async refreshMCPTools(mcpTools: CustomTool[]): Promise<void> {
async refreshMCPTools(mcpTools: CustomTool[], options?: { activateAll?: boolean }): Promise<void> {
const previousSelectedMCPToolNames = this.getSelectedMCPToolNames();
const existingNames = Array.from(this.#toolRegistry.keys());
for (const name of existingNames) {
@@ -3659,6 +3664,18 @@ export class AgentSession {
this.#getConfiguredDefaultSelectedMCPToolNames(),
);
if (options?.activateAll) {
// Force-activate every newly registered MCP tool. This path is used
// when an ACP client provisions MCP servers for a session where MCP
// discovery is disabled — without it, getSelectedMCPToolNames()
// returns only already-active tools (circular deadlock: tools can
// only become active if they're already active).
const newMcpNames = mcpTools.map(t => t.name);
const nextActive = [...new Set([...this.#getActiveNonMCPToolNames(), ...newMcpNames])];
await this.#applyActiveToolsByName(nextActive, { previousSelectedMCPToolNames });
return;
}
const nextActive = [...this.#getActiveNonMCPToolNames(), ...this.getSelectedMCPToolNames()];
await this.#applyActiveToolsByName(nextActive, { previousSelectedMCPToolNames });
}