@@ -222,6 +222,7 @@ pub struct ContextServerStore {
update_servers_task: Option<Task<Result<()>>>,
context_server_factory: Option<ContextServerFactory>,
needs_server_update: bool,
+ ai_disabled: bool,
_subscriptions: Vec<Subscription>,
}
@@ -377,23 +378,42 @@ impl ContextServerStore {
cx: &mut Context<Self>,
) -> Self {
let mut subscriptions = vec![cx.observe_global::<SettingsStore>(move |this, cx| {
+ let ai_disabled = DisableAiSettings::get_global(cx).disable_ai;
+ let ai_was_disabled = this.ai_disabled;
+ this.ai_disabled = ai_disabled;
+
let settings =
&Self::resolve_project_settings(&this.worktree_store, cx).context_servers;
- if &this.context_server_settings == settings {
+ let settings_changed = &this.context_server_settings != settings;
+
+ if settings_changed {
+ this.context_server_settings = settings.clone();
+ }
+
+ // When AI is disabled, stop all running servers
+ if ai_disabled {
+ let server_ids: Vec<_> = this.servers.keys().cloned().collect();
+ for id in server_ids {
+ this.stop_server(&id, cx).log_err();
+ }
return;
}
- this.context_server_settings = settings.clone();
- if maintain_server_loop {
+
+ // Trigger updates if AI was re-enabled or settings changed
+ if maintain_server_loop && (ai_was_disabled || settings_changed) {
this.available_context_servers_changed(cx);
}
})];
if maintain_server_loop {
subscriptions.push(cx.observe(®istry, |this, _registry, cx| {
- this.available_context_servers_changed(cx);
+ if !DisableAiSettings::get_global(cx).disable_ai {
+ this.available_context_servers_changed(cx);
+ }
}));
}
+ let ai_disabled = DisableAiSettings::get_global(cx).disable_ai;
let mut this = Self {
state,
_subscriptions: subscriptions,
@@ -404,12 +424,13 @@ impl ContextServerStore {
project: weak_project,
registry,
needs_server_update: false,
+ ai_disabled,
servers: HashMap::default(),
server_ids: Default::default(),
update_servers_task: None,
context_server_factory,
};
- if maintain_server_loop {
+ if maintain_server_loop && !DisableAiSettings::get_global(cx).disable_ai {
this.available_context_servers_changed(cx);
}
this
@@ -8,10 +8,11 @@ use project::context_server_store::*;
use project::project_settings::ContextServerSettings;
use project::worktree_store::WorktreeStore;
use project::{
- FakeFs, Project, context_server_store::registry::ContextServerDescriptor,
+ DisableAiSettings, FakeFs, Project, context_server_store::registry::ContextServerDescriptor,
project_settings::ProjectSettings,
};
use serde_json::json;
+use settings::settings_content::SaturatingBool;
use settings::{ContextServerCommand, Settings, SettingsStore};
use std::sync::Arc;
use std::{cell::RefCell, path::PathBuf, rc::Rc};
@@ -553,6 +554,116 @@ async fn test_context_server_enabled_disabled(cx: &mut TestAppContext) {
}
}
+#[gpui::test]
+async fn test_context_server_respects_disable_ai(cx: &mut TestAppContext) {
+ const SERVER_1_ID: &str = "mcp-1";
+
+ let server_1_id = ContextServerId(SERVER_1_ID.into());
+
+ // Set up SettingsStore with disable_ai: true in user settings BEFORE creating project
+ cx.update(|cx| {
+ let settings_store = SettingsStore::test(cx);
+ cx.set_global(settings_store);
+ DisableAiSettings::register(cx);
+ // Set disable_ai via user settings (not override_global) so it persists through recompute_values
+ SettingsStore::update_global(cx, |store, cx| {
+ store.update_user_settings(cx, |content| {
+ content.project.disable_ai = Some(SaturatingBool(true));
+ });
+ });
+ });
+
+ // Now create the project (ContextServerStore will see disable_ai = true)
+ let fs = FakeFs::new(cx.executor());
+ fs.insert_tree(path!("/test"), json!({"code.rs": ""})).await;
+ let project = Project::test(fs.clone(), [path!("/test").as_ref()], cx).await;
+
+ let executor = cx.executor();
+ let store = project.read_with(cx, |project, _| project.context_server_store());
+ store.update(cx, |store, _| {
+ store.set_context_server_factory(Box::new(move |id, _| {
+ Arc::new(ContextServer::new(
+ id.clone(),
+ Arc::new(create_fake_transport(id.0.to_string(), executor.clone())),
+ ))
+ }));
+ });
+
+ set_context_server_configuration(
+ vec![(
+ server_1_id.0.clone(),
+ settings::ContextServerSettingsContent::Stdio {
+ enabled: true,
+ remote: false,
+ command: ContextServerCommand {
+ path: "somebinary".into(),
+ args: vec!["arg".to_string()],
+ env: None,
+ timeout: None,
+ },
+ },
+ )],
+ cx,
+ );
+
+ cx.run_until_parked();
+
+ // Verify that no server started because AI is disabled
+ cx.update(|cx| {
+ assert_eq!(
+ store.read(cx).status_for_server(&server_1_id),
+ None,
+ "Server should not start when disable_ai is true"
+ );
+ });
+
+ // Enable AI and verify server starts
+ {
+ let _server_events = assert_server_events(
+ &store,
+ vec![
+ (server_1_id.clone(), ContextServerStatus::Starting),
+ (server_1_id.clone(), ContextServerStatus::Running),
+ ],
+ cx,
+ );
+ cx.update(|cx| {
+ SettingsStore::update_global(cx, |store, cx| {
+ store.update_user_settings(cx, |content| {
+ content.project.disable_ai = Some(SaturatingBool(false));
+ });
+ });
+ });
+ cx.run_until_parked();
+ }
+
+ // Disable AI again and verify server stops
+ {
+ let _server_events = assert_server_events(
+ &store,
+ vec![(server_1_id.clone(), ContextServerStatus::Stopped)],
+ cx,
+ );
+ cx.update(|cx| {
+ SettingsStore::update_global(cx, |store, cx| {
+ store.update_user_settings(cx, |content| {
+ content.project.disable_ai = Some(SaturatingBool(true));
+ });
+ });
+ });
+ cx.run_until_parked();
+ }
+
+ // Verify server is stopped
+ cx.update(|cx| {
+ assert_eq!(
+ store.read(cx).status_for_server(&server_1_id),
+ Some(ContextServerStatus::Stopped),
+ "Server should be stopped when disable_ai is true"
+ );
+ });
+}
+
#[gpui::test]
async fn test_server_ids_includes_disabled_servers(cx: &mut TestAppContext) {
const ENABLED_SERVER_ID: &str = "enabled-server";