agent: Handle context servers that do not provide a configuration in MCP setup dialog (#30023)
<img width="674" alt="image" src="https://github.com/user-attachments/assets/0ccb89e2-1dc1-4caf-88a7-49159f43979f" /> <img width="675" alt="image" src="https://github.com/user-attachments/assets/790e5d45-905e-45da-affa-04ddd1d33c65" /> Release Notes: - N/A
This commit is contained in:
parent
80a85a31ab
commit
121e3b5dfd
@ -147,47 +147,50 @@ impl Render for AddContextServerModal {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
.footer(
|
.footer(
|
||||||
ModalFooter::new()
|
ModalFooter::new().end_slot(
|
||||||
.start_slot(
|
h_flex()
|
||||||
Button::new("cancel", "Cancel")
|
.gap_2()
|
||||||
.key_binding(
|
.child(
|
||||||
KeyBinding::for_action_in(
|
Button::new("cancel", "Cancel")
|
||||||
&menu::Cancel,
|
.key_binding(
|
||||||
&focus_handle,
|
KeyBinding::for_action_in(
|
||||||
window,
|
&menu::Cancel,
|
||||||
cx,
|
&focus_handle,
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
.map(|kb| kb.size(rems_from_px(12.))),
|
||||||
)
|
)
|
||||||
.map(|kb| kb.size(rems_from_px(12.))),
|
.on_click(cx.listener(|this, _event, _window, cx| {
|
||||||
)
|
this.cancel(&menu::Cancel, cx)
|
||||||
.on_click(cx.listener(|this, _event, _window, cx| {
|
})),
|
||||||
this.cancel(&menu::Cancel, cx)
|
)
|
||||||
})),
|
.child(
|
||||||
)
|
Button::new("add-server", "Add Server")
|
||||||
.end_slot(
|
.disabled(is_name_empty || is_command_empty)
|
||||||
Button::new("add-server", "Add Server")
|
.key_binding(
|
||||||
.disabled(is_name_empty || is_command_empty)
|
KeyBinding::for_action_in(
|
||||||
.key_binding(
|
&menu::Confirm,
|
||||||
KeyBinding::for_action_in(
|
&focus_handle,
|
||||||
&menu::Confirm,
|
window,
|
||||||
&focus_handle,
|
cx,
|
||||||
window,
|
)
|
||||||
cx,
|
.map(|kb| kb.size(rems_from_px(12.))),
|
||||||
)
|
)
|
||||||
.map(|kb| kb.size(rems_from_px(12.))),
|
.map(|button| {
|
||||||
)
|
if is_name_empty {
|
||||||
.map(|button| {
|
button.tooltip(Tooltip::text("Name is required"))
|
||||||
if is_name_empty {
|
} else if is_command_empty {
|
||||||
button.tooltip(Tooltip::text("Name is required"))
|
button.tooltip(Tooltip::text("Command is required"))
|
||||||
} else if is_command_empty {
|
} else {
|
||||||
button.tooltip(Tooltip::text("Command is required"))
|
button
|
||||||
} else {
|
}
|
||||||
button
|
})
|
||||||
}
|
.on_click(cx.listener(|this, _event, _window, cx| {
|
||||||
})
|
this.confirm(&menu::Confirm, cx)
|
||||||
.on_click(cx.listener(|this, _event, _window, cx| {
|
})),
|
||||||
this.confirm(&menu::Confirm, cx)
|
),
|
||||||
})),
|
),
|
||||||
),
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -19,18 +19,24 @@ use project::{
|
|||||||
};
|
};
|
||||||
use settings::{Settings as _, update_settings_file};
|
use settings::{Settings as _, update_settings_file};
|
||||||
use theme::ThemeSettings;
|
use theme::ThemeSettings;
|
||||||
use ui::{KeyBinding, Modal, ModalFooter, ModalHeader, Section, prelude::*};
|
use ui::{KeyBinding, Modal, ModalFooter, ModalHeader, Section, Tooltip, prelude::*};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
use workspace::{ModalView, Workspace};
|
use workspace::{ModalView, Workspace};
|
||||||
|
|
||||||
pub(crate) struct ConfigureContextServerModal {
|
pub(crate) struct ConfigureContextServerModal {
|
||||||
workspace: WeakEntity<Workspace>,
|
workspace: WeakEntity<Workspace>,
|
||||||
context_servers_to_setup: Vec<ConfigureContextServer>,
|
focus_handle: FocusHandle,
|
||||||
|
context_servers_to_setup: Vec<ContextServerSetup>,
|
||||||
context_server_store: Entity<ContextServerStore>,
|
context_server_store: Entity<ContextServerStore>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ConfigureContextServer {
|
#[allow(clippy::large_enum_variant)]
|
||||||
id: ContextServerId,
|
enum Configuration {
|
||||||
|
NotAvailable,
|
||||||
|
Required(ConfigurationRequiredState),
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ConfigurationRequiredState {
|
||||||
installation_instructions: Entity<markdown::Markdown>,
|
installation_instructions: Entity<markdown::Markdown>,
|
||||||
settings_validator: Option<jsonschema::Validator>,
|
settings_validator: Option<jsonschema::Validator>,
|
||||||
settings_editor: Entity<Editor>,
|
settings_editor: Entity<Editor>,
|
||||||
@ -38,64 +44,91 @@ struct ConfigureContextServer {
|
|||||||
waiting_for_context_server: bool,
|
waiting_for_context_server: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ContextServerSetup {
|
||||||
|
id: ContextServerId,
|
||||||
|
repository_url: Option<SharedString>,
|
||||||
|
configuration: Configuration,
|
||||||
|
}
|
||||||
|
|
||||||
impl ConfigureContextServerModal {
|
impl ConfigureContextServerModal {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
configurations: impl Iterator<Item = (ContextServerId, extension::ContextServerConfiguration)>,
|
configurations: impl Iterator<Item = crate::context_server_configuration::Configuration>,
|
||||||
context_server_store: Entity<ContextServerStore>,
|
context_server_store: Entity<ContextServerStore>,
|
||||||
jsonc_language: Option<Arc<Language>>,
|
jsonc_language: Option<Arc<Language>>,
|
||||||
language_registry: Arc<LanguageRegistry>,
|
language_registry: Arc<LanguageRegistry>,
|
||||||
workspace: WeakEntity<Workspace>,
|
workspace: WeakEntity<Workspace>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut Context<Self>,
|
||||||
) -> Option<Self> {
|
) -> Self {
|
||||||
let context_servers_to_setup = configurations
|
let context_servers_to_setup = configurations
|
||||||
.map(|(id, manifest)| {
|
.map(|config| match config {
|
||||||
let jsonc_language = jsonc_language.clone();
|
crate::context_server_configuration::Configuration::NotAvailable(
|
||||||
let settings_validator = jsonschema::validator_for(&manifest.settings_schema)
|
context_server_id,
|
||||||
.context("Failed to load JSON schema for context server settings")
|
repository_url,
|
||||||
.log_err();
|
) => ContextServerSetup {
|
||||||
ConfigureContextServer {
|
id: context_server_id,
|
||||||
id: id.clone(),
|
repository_url,
|
||||||
installation_instructions: cx.new(|cx| {
|
configuration: Configuration::NotAvailable,
|
||||||
Markdown::new(
|
},
|
||||||
manifest.installation_instructions.clone().into(),
|
crate::context_server_configuration::Configuration::Required(
|
||||||
Some(language_registry.clone()),
|
context_server_id,
|
||||||
None,
|
repository_url,
|
||||||
cx,
|
config,
|
||||||
)
|
) => {
|
||||||
}),
|
let jsonc_language = jsonc_language.clone();
|
||||||
settings_validator,
|
let settings_validator = jsonschema::validator_for(&config.settings_schema)
|
||||||
settings_editor: cx.new(|cx| {
|
.context("Failed to load JSON schema for context server settings")
|
||||||
let mut editor = Editor::auto_height(16, window, cx);
|
.log_err();
|
||||||
editor.set_text(manifest.default_settings.trim(), window, cx);
|
let state = ConfigurationRequiredState {
|
||||||
editor.set_show_gutter(false, cx);
|
installation_instructions: cx.new(|cx| {
|
||||||
editor.set_soft_wrap_mode(language::language_settings::SoftWrap::None, cx);
|
Markdown::new(
|
||||||
if let Some(buffer) = editor.buffer().read(cx).as_singleton() {
|
config.installation_instructions.clone().into(),
|
||||||
buffer.update(cx, |buffer, cx| buffer.set_language(jsonc_language, cx))
|
Some(language_registry.clone()),
|
||||||
}
|
None,
|
||||||
editor
|
cx,
|
||||||
}),
|
)
|
||||||
waiting_for_context_server: false,
|
}),
|
||||||
last_error: None,
|
settings_validator,
|
||||||
|
settings_editor: cx.new(|cx| {
|
||||||
|
let mut editor = Editor::auto_height(16, window, cx);
|
||||||
|
editor.set_text(config.default_settings.trim(), window, cx);
|
||||||
|
editor.set_show_gutter(false, cx);
|
||||||
|
editor.set_soft_wrap_mode(
|
||||||
|
language::language_settings::SoftWrap::None,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
if let Some(buffer) = editor.buffer().read(cx).as_singleton() {
|
||||||
|
buffer.update(cx, |buffer, cx| {
|
||||||
|
buffer.set_language(jsonc_language, cx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
editor
|
||||||
|
}),
|
||||||
|
waiting_for_context_server: false,
|
||||||
|
last_error: None,
|
||||||
|
};
|
||||||
|
ContextServerSetup {
|
||||||
|
id: context_server_id,
|
||||||
|
repository_url,
|
||||||
|
configuration: Configuration::Required(state),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
if context_servers_to_setup.is_empty() {
|
Self {
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(Self {
|
|
||||||
workspace,
|
workspace,
|
||||||
|
focus_handle: cx.focus_handle(),
|
||||||
context_servers_to_setup,
|
context_servers_to_setup,
|
||||||
context_server_store,
|
context_server_store,
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConfigureContextServerModal {
|
impl ConfigureContextServerModal {
|
||||||
pub fn confirm(&mut self, cx: &mut Context<Self>) {
|
pub fn confirm(&mut self, cx: &mut Context<Self>) {
|
||||||
if self.context_servers_to_setup.is_empty() {
|
if self.context_servers_to_setup.is_empty() {
|
||||||
|
self.dismiss(cx);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,7 +136,18 @@ impl ConfigureContextServerModal {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let configuration = &mut self.context_servers_to_setup[0];
|
let id = self.context_servers_to_setup[0].id.clone();
|
||||||
|
let configuration = match &mut self.context_servers_to_setup[0].configuration {
|
||||||
|
Configuration::NotAvailable => {
|
||||||
|
self.context_servers_to_setup.remove(0);
|
||||||
|
if self.context_servers_to_setup.is_empty() {
|
||||||
|
self.dismiss(cx);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Configuration::Required(state) => state,
|
||||||
|
};
|
||||||
|
|
||||||
configuration.last_error.take();
|
configuration.last_error.take();
|
||||||
if configuration.waiting_for_context_server {
|
if configuration.waiting_for_context_server {
|
||||||
return;
|
return;
|
||||||
@ -127,7 +171,7 @@ impl ConfigureContextServerModal {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let id = configuration.id.clone();
|
let id = id.clone();
|
||||||
|
|
||||||
let settings_changed = ProjectSettings::get_global(cx)
|
let settings_changed = ProjectSettings::get_global(cx)
|
||||||
.context_servers
|
.context_servers
|
||||||
@ -156,9 +200,14 @@ impl ConfigureContextServerModal {
|
|||||||
this.complete_setup(id, cx);
|
this.complete_setup(id, cx);
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
if let Some(configuration) = this.context_servers_to_setup.get_mut(0) {
|
if let Some(setup) = this.context_servers_to_setup.get_mut(0) {
|
||||||
configuration.last_error = Some(err.into());
|
match &mut setup.configuration {
|
||||||
configuration.waiting_for_context_server = false;
|
Configuration::NotAvailable => {}
|
||||||
|
Configuration::Required(state) => {
|
||||||
|
state.last_error = Some(err.into());
|
||||||
|
state.waiting_for_context_server = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this.dismiss(cx);
|
this.dismiss(cx);
|
||||||
}
|
}
|
||||||
@ -267,8 +316,8 @@ fn wait_for_context_server(
|
|||||||
|
|
||||||
impl Render for ConfigureContextServerModal {
|
impl Render for ConfigureContextServerModal {
|
||||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
let Some(configuration) = self.context_servers_to_setup.first() else {
|
let Some(setup) = self.context_servers_to_setup.first() else {
|
||||||
return div().child("No context servers to setup");
|
return div().into_any_element();
|
||||||
};
|
};
|
||||||
|
|
||||||
let focus_handle = self.focus_handle(cx);
|
let focus_handle = self.focus_handle(cx);
|
||||||
@ -277,6 +326,7 @@ impl Render for ConfigureContextServerModal {
|
|||||||
.elevation_3(cx)
|
.elevation_3(cx)
|
||||||
.w(rems(42.))
|
.w(rems(42.))
|
||||||
.key_context("ConfigureContextServerModal")
|
.key_context("ConfigureContextServerModal")
|
||||||
|
.track_focus(&focus_handle)
|
||||||
.on_action(cx.listener(|this, _: &menu::Confirm, _window, cx| this.confirm(cx)))
|
.on_action(cx.listener(|this, _: &menu::Confirm, _window, cx| this.confirm(cx)))
|
||||||
.on_action(cx.listener(|this, _: &menu::Cancel, _window, cx| this.dismiss(cx)))
|
.on_action(cx.listener(|this, _: &menu::Cancel, _window, cx| this.dismiss(cx)))
|
||||||
.capture_any_mouse_down(cx.listener(|this, _, window, cx| {
|
.capture_any_mouse_down(cx.listener(|this, _, window, cx| {
|
||||||
@ -284,9 +334,15 @@ impl Render for ConfigureContextServerModal {
|
|||||||
}))
|
}))
|
||||||
.child(
|
.child(
|
||||||
Modal::new("configure-context-server", None)
|
Modal::new("configure-context-server", None)
|
||||||
.header(ModalHeader::new().headline(format!("Configure {}", configuration.id)))
|
.header(ModalHeader::new().headline(format!("Configure {}", setup.id)))
|
||||||
.section(
|
.section(match &setup.configuration {
|
||||||
Section::new()
|
Configuration::NotAvailable => Section::new().child(
|
||||||
|
Label::new(
|
||||||
|
"No configuration options available for this context server. Visit the Repository for any further instructions.",
|
||||||
|
)
|
||||||
|
.color(Color::Muted),
|
||||||
|
),
|
||||||
|
Configuration::Required(configuration) => Section::new()
|
||||||
.child(div().pb_2().text_sm().child(MarkdownElement::new(
|
.child(div().pb_2().text_sm().child(MarkdownElement::new(
|
||||||
configuration.installation_instructions.clone(),
|
configuration.installation_instructions.clone(),
|
||||||
default_markdown_style(window, cx),
|
default_markdown_style(window, cx),
|
||||||
@ -370,45 +426,84 @@ impl Render for ConfigureContextServerModal {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
)
|
})
|
||||||
.footer(
|
.footer(
|
||||||
ModalFooter::new().end_slot(
|
ModalFooter::new()
|
||||||
h_flex()
|
.when_some(setup.repository_url.clone(), |this, repository_url| {
|
||||||
.gap_1()
|
this.start_slot(
|
||||||
.child(
|
h_flex().w_full().child(
|
||||||
Button::new("cancel", "Cancel")
|
Button::new("open-repository", "Open Repository")
|
||||||
.key_binding(
|
.icon(IconName::ArrowUpRight)
|
||||||
KeyBinding::for_action_in(
|
.icon_color(Color::Muted)
|
||||||
&menu::Cancel,
|
.icon_size(IconSize::XSmall)
|
||||||
&focus_handle,
|
.tooltip({
|
||||||
window,
|
let repository_url = repository_url.clone();
|
||||||
cx,
|
move |window, cx| {
|
||||||
)
|
Tooltip::with_meta(
|
||||||
.map(|kb| kb.size(rems_from_px(12.))),
|
"Open Repository",
|
||||||
)
|
None,
|
||||||
.on_click(cx.listener(|this, _event, _window, cx| {
|
repository_url.clone(),
|
||||||
this.dismiss(cx)
|
window,
|
||||||
})),
|
cx,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.on_click(move |_, _, cx| cx.open_url(&repository_url)),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.child(
|
})
|
||||||
Button::new("configure-server", "Configure MCP")
|
.end_slot(match &setup.configuration {
|
||||||
.disabled(configuration.waiting_for_context_server)
|
Configuration::NotAvailable => Button::new("dismiss", "Dismiss")
|
||||||
.key_binding(
|
.key_binding(
|
||||||
KeyBinding::for_action_in(
|
KeyBinding::for_action_in(
|
||||||
&menu::Confirm,
|
&menu::Cancel,
|
||||||
&focus_handle,
|
&focus_handle,
|
||||||
window,
|
window,
|
||||||
cx,
|
cx,
|
||||||
)
|
|
||||||
.map(|kb| kb.size(rems_from_px(12.))),
|
|
||||||
)
|
)
|
||||||
.on_click(cx.listener(|this, _event, _window, cx| {
|
.map(|kb| kb.size(rems_from_px(12.))),
|
||||||
this.confirm(cx)
|
)
|
||||||
})),
|
.on_click(
|
||||||
),
|
cx.listener(|this, _event, _window, cx| this.dismiss(cx)),
|
||||||
),
|
)
|
||||||
|
.into_any_element(),
|
||||||
|
Configuration::Required(state) => h_flex()
|
||||||
|
.gap_2()
|
||||||
|
.child(
|
||||||
|
Button::new("cancel", "Cancel")
|
||||||
|
.key_binding(
|
||||||
|
KeyBinding::for_action_in(
|
||||||
|
&menu::Cancel,
|
||||||
|
&focus_handle,
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
.map(|kb| kb.size(rems_from_px(12.))),
|
||||||
|
)
|
||||||
|
.on_click(cx.listener(|this, _event, _window, cx| {
|
||||||
|
this.dismiss(cx)
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
Button::new("configure-server", "Configure MCP")
|
||||||
|
.disabled(state.waiting_for_context_server)
|
||||||
|
.key_binding(
|
||||||
|
KeyBinding::for_action_in(
|
||||||
|
&menu::Confirm,
|
||||||
|
&focus_handle,
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
.map(|kb| kb.size(rems_from_px(12.))),
|
||||||
|
)
|
||||||
|
.on_click(cx.listener(|this, _event, _window, cx| {
|
||||||
|
this.confirm(cx)
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
.into_any_element(),
|
||||||
|
}),
|
||||||
),
|
),
|
||||||
)
|
).into_any_element()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -446,9 +541,14 @@ impl EventEmitter<DismissEvent> for ConfigureContextServerModal {}
|
|||||||
impl Focusable for ConfigureContextServerModal {
|
impl Focusable for ConfigureContextServerModal {
|
||||||
fn focus_handle(&self, cx: &App) -> FocusHandle {
|
fn focus_handle(&self, cx: &App) -> FocusHandle {
|
||||||
if let Some(current) = self.context_servers_to_setup.first() {
|
if let Some(current) = self.context_servers_to_setup.first() {
|
||||||
current.settings_editor.read(cx).focus_handle(cx)
|
match ¤t.configuration {
|
||||||
|
Configuration::NotAvailable => self.focus_handle.clone(),
|
||||||
|
Configuration::Required(configuration) => {
|
||||||
|
configuration.settings_editor.read(cx).focus_handle(cx)
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
cx.focus_handle()
|
self.focus_handle.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,8 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use anyhow::Context as _;
|
use anyhow::Context as _;
|
||||||
use context_server::ContextServerId;
|
use context_server::ContextServerId;
|
||||||
use extension::ExtensionManifest;
|
use extension::{ContextServerConfiguration, ExtensionManifest};
|
||||||
|
use gpui::Task;
|
||||||
use language::LanguageRegistry;
|
use language::LanguageRegistry;
|
||||||
use project::context_server_store::registry::ContextServerDescriptorRegistry;
|
use project::context_server_store::registry::ContextServerDescriptorRegistry;
|
||||||
use ui::prelude::*;
|
use ui::prelude::*;
|
||||||
@ -54,6 +55,15 @@ pub(crate) fn init(language_registry: Arc<LanguageRegistry>, cx: &mut App) {
|
|||||||
.detach();
|
.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum Configuration {
|
||||||
|
NotAvailable(ContextServerId, Option<SharedString>),
|
||||||
|
Required(
|
||||||
|
ContextServerId,
|
||||||
|
Option<SharedString>,
|
||||||
|
ContextServerConfiguration,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
fn show_configure_mcp_modal(
|
fn show_configure_mcp_modal(
|
||||||
language_registry: Arc<LanguageRegistry>,
|
language_registry: Arc<LanguageRegistry>,
|
||||||
manifest: &Arc<ExtensionManifest>,
|
manifest: &Arc<ExtensionManifest>,
|
||||||
@ -62,6 +72,7 @@ fn show_configure_mcp_modal(
|
|||||||
cx: &mut Context<'_, Workspace>,
|
cx: &mut Context<'_, Workspace>,
|
||||||
) {
|
) {
|
||||||
let context_server_store = workspace.project().read(cx).context_server_store();
|
let context_server_store = workspace.project().read(cx).context_server_store();
|
||||||
|
let repository: Option<SharedString> = manifest.repository.as_ref().map(|s| s.clone().into());
|
||||||
|
|
||||||
let registry = ContextServerDescriptorRegistry::default_global(cx).read(cx);
|
let registry = ContextServerDescriptorRegistry::default_global(cx).read(cx);
|
||||||
let worktree_store = workspace.project().read(cx).worktree_store();
|
let worktree_store = workspace.project().read(cx).worktree_store();
|
||||||
@ -69,21 +80,37 @@ fn show_configure_mcp_modal(
|
|||||||
.context_servers
|
.context_servers
|
||||||
.keys()
|
.keys()
|
||||||
.cloned()
|
.cloned()
|
||||||
.filter_map({
|
.map({
|
||||||
|key| {
|
|key| {
|
||||||
let descriptor = registry.context_server_descriptor(&key)?;
|
let Some(descriptor) = registry.context_server_descriptor(&key) else {
|
||||||
Some(cx.spawn({
|
return Task::ready(Configuration::NotAvailable(
|
||||||
|
ContextServerId(key),
|
||||||
|
repository.clone(),
|
||||||
|
));
|
||||||
|
};
|
||||||
|
cx.spawn({
|
||||||
|
let repository_url = repository.clone();
|
||||||
let worktree_store = worktree_store.clone();
|
let worktree_store = worktree_store.clone();
|
||||||
async move |_, cx| {
|
async move |_, cx| {
|
||||||
descriptor
|
let configuration = descriptor
|
||||||
.configuration(worktree_store.clone(), &cx)
|
.configuration(worktree_store.clone(), &cx)
|
||||||
.await
|
.await
|
||||||
.context("Failed to resolve context server configuration")
|
.context("Failed to resolve context server configuration")
|
||||||
.log_err()
|
.log_err()
|
||||||
.flatten()
|
.flatten();
|
||||||
.map(|config| (ContextServerId(key), config))
|
|
||||||
|
match configuration {
|
||||||
|
Some(config) => Configuration::Required(
|
||||||
|
ContextServerId(key),
|
||||||
|
repository_url,
|
||||||
|
config,
|
||||||
|
),
|
||||||
|
None => {
|
||||||
|
Configuration::NotAvailable(ContextServerId(key), repository_url)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}))
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
@ -91,22 +118,22 @@ fn show_configure_mcp_modal(
|
|||||||
let jsonc_language = language_registry.language_for_name("jsonc");
|
let jsonc_language = language_registry.language_for_name("jsonc");
|
||||||
|
|
||||||
cx.spawn_in(window, async move |this, cx| {
|
cx.spawn_in(window, async move |this, cx| {
|
||||||
let descriptors = futures::future::join_all(configuration_tasks).await;
|
let configurations = futures::future::join_all(configuration_tasks).await;
|
||||||
let jsonc_language = jsonc_language.await.ok();
|
let jsonc_language = jsonc_language.await.ok();
|
||||||
|
|
||||||
this.update_in(cx, |this, window, cx| {
|
this.update_in(cx, |this, window, cx| {
|
||||||
let modal = ConfigureContextServerModal::new(
|
let workspace = cx.entity().downgrade();
|
||||||
descriptors.into_iter().flatten(),
|
this.toggle_modal(window, cx, |window, cx| {
|
||||||
context_server_store,
|
ConfigureContextServerModal::new(
|
||||||
jsonc_language,
|
configurations.into_iter(),
|
||||||
language_registry,
|
context_server_store,
|
||||||
cx.entity().downgrade(),
|
jsonc_language,
|
||||||
window,
|
language_registry,
|
||||||
cx,
|
workspace,
|
||||||
);
|
window,
|
||||||
if let Some(modal) = modal {
|
cx,
|
||||||
this.toggle_modal(window, cx, |_, _| modal);
|
)
|
||||||
}
|
});
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
|
@ -253,7 +253,7 @@ impl RenderOnce for ModalFooter {
|
|||||||
.mt_4()
|
.mt_4()
|
||||||
.p(DynamicSpacing::Base08.rems(cx))
|
.p(DynamicSpacing::Base08.rems(cx))
|
||||||
.flex_none()
|
.flex_none()
|
||||||
.justify_end()
|
.justify_between()
|
||||||
.gap_1()
|
.gap_1()
|
||||||
.border_t_1()
|
.border_t_1()
|
||||||
.border_color(cx.theme().colors().border_variant)
|
.border_color(cx.theme().colors().border_variant)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user