context_server_configuration.rs

  1use std::sync::Arc;
  2
  3use anyhow::Context as _;
  4use context_server::ContextServerId;
  5use extension::{ContextServerConfiguration, ExtensionManifest};
  6use gpui::Task;
  7use language::LanguageRegistry;
  8use project::context_server_store::registry::ContextServerDescriptorRegistry;
  9use ui::prelude::*;
 10use util::ResultExt;
 11use workspace::Workspace;
 12
 13use crate::agent_configuration::ConfigureContextServerModal;
 14
 15pub(crate) fn init(language_registry: Arc<LanguageRegistry>, cx: &mut App) {
 16    cx.observe_new(move |_: &mut Workspace, window, cx| {
 17        let Some(window) = window else {
 18            return;
 19        };
 20
 21        if let Some(extension_events) = extension::ExtensionEvents::try_global(cx).as_ref() {
 22            cx.subscribe_in(extension_events, window, {
 23                let language_registry = language_registry.clone();
 24                move |workspace, _, event, window, cx| match event {
 25                    extension::Event::ExtensionInstalled(manifest) => {
 26                        show_configure_mcp_modal(
 27                            language_registry.clone(),
 28                            manifest,
 29                            workspace,
 30                            window,
 31                            cx,
 32                        );
 33                    }
 34                    extension::Event::ConfigureExtensionRequested(manifest) => {
 35                        if !manifest.context_servers.is_empty() {
 36                            show_configure_mcp_modal(
 37                                language_registry.clone(),
 38                                manifest,
 39                                workspace,
 40                                window,
 41                                cx,
 42                            );
 43                        }
 44                    }
 45                    _ => {}
 46                }
 47            })
 48            .detach();
 49        } else {
 50            log::info!(
 51                "No extension events global found. Skipping context server configuration wizard"
 52            );
 53        }
 54    })
 55    .detach();
 56}
 57
 58pub enum Configuration {
 59    NotAvailable(ContextServerId, Option<SharedString>),
 60    Required(
 61        ContextServerId,
 62        Option<SharedString>,
 63        ContextServerConfiguration,
 64    ),
 65}
 66
 67fn show_configure_mcp_modal(
 68    language_registry: Arc<LanguageRegistry>,
 69    manifest: &Arc<ExtensionManifest>,
 70    workspace: &mut Workspace,
 71    window: &mut Window,
 72    cx: &mut Context<'_, Workspace>,
 73) {
 74    let context_server_store = workspace.project().read(cx).context_server_store();
 75    let repository: Option<SharedString> = manifest.repository.as_ref().map(|s| s.clone().into());
 76
 77    let registry = ContextServerDescriptorRegistry::default_global(cx).read(cx);
 78    let worktree_store = workspace.project().read(cx).worktree_store();
 79    let configuration_tasks = manifest
 80        .context_servers
 81        .keys()
 82        .cloned()
 83        .map({
 84            |key| {
 85                let Some(descriptor) = registry.context_server_descriptor(&key) else {
 86                    return Task::ready(Configuration::NotAvailable(
 87                        ContextServerId(key),
 88                        repository.clone(),
 89                    ));
 90                };
 91                cx.spawn({
 92                    let repository_url = repository.clone();
 93                    let worktree_store = worktree_store.clone();
 94                    async move |_, cx| {
 95                        let configuration = descriptor
 96                            .configuration(worktree_store.clone(), &cx)
 97                            .await
 98                            .context("Failed to resolve context server configuration")
 99                            .log_err()
100                            .flatten();
101
102                        match configuration {
103                            Some(config) => Configuration::Required(
104                                ContextServerId(key),
105                                repository_url,
106                                config,
107                            ),
108                            None => {
109                                Configuration::NotAvailable(ContextServerId(key), repository_url)
110                            }
111                        }
112                    }
113                })
114            }
115        })
116        .collect::<Vec<_>>();
117
118    let jsonc_language = language_registry.language_for_name("jsonc");
119
120    cx.spawn_in(window, async move |this, cx| {
121        let configurations = futures::future::join_all(configuration_tasks).await;
122        let jsonc_language = jsonc_language.await.ok();
123
124        this.update_in(cx, |this, window, cx| {
125            let workspace = cx.entity().downgrade();
126            this.toggle_modal(window, cx, |window, cx| {
127                ConfigureContextServerModal::new(
128                    configurations.into_iter(),
129                    context_server_store,
130                    jsonc_language,
131                    language_registry,
132                    workspace,
133                    window,
134                    cx,
135                )
136            });
137        })
138    })
139    .detach();
140}