-
Notifications
You must be signed in to change notification settings - Fork 3k
Description
ExtensionManager::add_extension has a name-only dedup guard at extension_manager.rs:540-542:
if self.extensions.lock().await.contains_key(&sanitized_name) {
return Ok(());
}If an extension with the same name is already loaded, the call silently returns Ok(()) regardless of whether the config differs. This means callers cannot update an extension's envs, args, or other config without first calling remove_extension — which kills the subprocess and adds latency.
Impact: In goosed-slackbot, we inject a per-session env var (SLACK_ORIGIN_CHANNEL) into the slack MCP extension config via add_extension. But goosed's background load_extensions_from_session typically wins the race and loads the extension from stored session config (without the env var) before our call arrives. Our add_extension with the updated envs is silently dropped, and the MCP subprocess never sees the env var.
The workaround is calling remove_extension before add_extension, but this restarts the subprocess on every session setup — adding latency and unnecessary churn under heavy load.
Proposed fix: When add_extension is called with a name that's already loaded, compare the incoming config against the loaded config. If they differ (different envs, args, cmd, etc.), remove the old extension and re-add with the new config. If identical, keep the current no-op behavior.
let mut extensions = self.extensions.lock().await;
if let Some(existing) = extensions.get(&sanitized_name) {
if existing.config_matches(&config) {
return Ok(()); // truly identical — safe to skip
}
// Config changed — need to restart with new config
extensions.remove(&sanitized_name);
self.invalidate_tools_cache_and_bump_version().await;
}
drop(extensions);
// ... proceed to spawn new subprocess with updated configThis preserves the optimization (no restart when nothing changed) while allowing legitimate config updates to take effect.