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}