fix: close stale reconnect results without racing re-enable

Stale connections closed by the disabled-state guard now go through
client.cleanup() so their caches are cleared by the close handler at
close time, never by a post-close invalidation that could hit a
connection created by a concurrent re-enable. The post-OAuth reconnect
gets the same disabled check, with the config check and disabled state
write kept synchronous (no await in between) so a later enable cannot
be overwritten by the stale OAuth result.
This commit is contained in:
vzionv
2026-09-02 10:55:11 +08:00
parent 65a9fedcfe
commit 51d4a52832
2 changed files with 51 additions and 2 deletions
+48
View File
@@ -3458,6 +3458,54 @@ function runHeadlessStreaming(
serverName,
config,
)
// If the server was disabled while the reconnect was in
// flight, close any fresh connection and keep the disabled
// state
if (isMcpServerDisabled(serverName)) {
if (result.client.type === 'connected') {
// Close the fresh connection in the background; its
// caches are cleared by the close handler at close
// time, so no post-close invalidation can hit a
// connection created by a concurrent re-enable.
void result.client.cleanup()
}
const prefix = getMcpPrefix(serverName)
setAppState(prev => ({
...prev,
mcp: {
...prev.mcp,
clients: prev.mcp.clients.map(c =>
c.name === serverName
? {
name: serverName,
type: 'disabled' as const,
config,
}
: c,
),
tools: reject(prev.mcp.tools, t =>
t.name?.startsWith(prefix),
),
commands: reject(prev.mcp.commands, c =>
commandBelongsToServer(c, serverName),
),
resources: omit(prev.mcp.resources, serverName),
},
}))
dynamicMcpState = {
...dynamicMcpState,
clients: [
...dynamicMcpState.clients.filter(
c => c.name !== serverName,
),
{ name: serverName, type: 'disabled' as const, config },
],
tools: dynamicMcpState.tools.filter(
t => !t.name?.startsWith(prefix),
),
}
return
}
const prefix = getMcpPrefix(serverName)
setAppState(prev => ({
...prev,
+3 -2
View File
@@ -327,10 +327,11 @@ export function useManageMCPConnections(
}) => {
// A connect that completes after the server was disabled (e.g. disable
// during a reconnect request) must not resurrect the server: close any
// fresh connection, clear its caches, and mark it disabled instead.
// fresh connection (its caches are cleared by the close handler) and
// mark it disabled instead.
if (isMcpServerDisabled(client.name)) {
if (client.type === 'connected') {
void clearServerCache(client.name, client.config)
void client.cleanup()
}
updateServer({
name: client.name,