agent: Add item to add custom MCP server in the panel's menu (#29091)
This is based on user feedback that the Agent Panel menu was only linking to extensions as a way to add MCP servers while we also support adding "custom" servers, too, which don't go through the extensions flow. Release Notes: - N/A
This commit is contained in:
parent
f0ef3110d3
commit
cc2fcb2f42
4 changed files with 59 additions and 23 deletions
|
@ -404,7 +404,7 @@ impl AssistantConfiguration {
|
||||||
.gap_2()
|
.gap_2()
|
||||||
.child(
|
.child(
|
||||||
h_flex().w_full().child(
|
h_flex().w_full().child(
|
||||||
Button::new("add-context-server", "Add MCPs Directly")
|
Button::new("add-context-server", "Add Custom Server")
|
||||||
.style(ButtonStyle::Filled)
|
.style(ButtonStyle::Filled)
|
||||||
.layer(ElevationIndex::ModalSurface)
|
.layer(ElevationIndex::ModalSurface)
|
||||||
.full_width()
|
.full_width()
|
||||||
|
|
|
@ -2,7 +2,7 @@ use context_server::{ContextServerSettings, ServerCommand, ServerConfig};
|
||||||
use gpui::{DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, WeakEntity, prelude::*};
|
use gpui::{DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, WeakEntity, prelude::*};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use settings::update_settings_file;
|
use settings::update_settings_file;
|
||||||
use ui::{Modal, ModalFooter, ModalHeader, Section, Tooltip, prelude::*};
|
use ui::{KeyBinding, Modal, ModalFooter, ModalHeader, Section, Tooltip, prelude::*};
|
||||||
use ui_input::SingleLineInput;
|
use ui_input::SingleLineInput;
|
||||||
use workspace::{ModalView, Workspace};
|
use workspace::{ModalView, Workspace};
|
||||||
|
|
||||||
|
@ -34,9 +34,9 @@ impl AddContextServerModal {
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let name_editor =
|
let name_editor =
|
||||||
cx.new(|cx| SingleLineInput::new(window, cx, "Your server name").label("Name"));
|
cx.new(|cx| SingleLineInput::new(window, cx, "my-custom-server").label("Name"));
|
||||||
let command_editor = cx.new(|cx| {
|
let command_editor = cx.new(|cx| {
|
||||||
SingleLineInput::new(window, cx, "Command").label("Command to run the context server")
|
SingleLineInput::new(window, cx, "Command").label("Command to run the MCP server")
|
||||||
});
|
});
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
@ -46,7 +46,7 @@ impl AddContextServerModal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn confirm(&mut self, cx: &mut Context<Self>) {
|
fn confirm(&mut self, _: &menu::Confirm, cx: &mut Context<Self>) {
|
||||||
let name = self
|
let name = self
|
||||||
.name_editor
|
.name_editor
|
||||||
.read(cx)
|
.read(cx)
|
||||||
|
@ -96,7 +96,7 @@ impl AddContextServerModal {
|
||||||
cx.emit(DismissEvent);
|
cx.emit(DismissEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cancel(&mut self, cx: &mut Context<Self>) {
|
fn cancel(&mut self, _: &menu::Cancel, cx: &mut Context<Self>) {
|
||||||
cx.emit(DismissEvent);
|
cx.emit(DismissEvent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -112,38 +112,68 @@ impl Focusable for AddContextServerModal {
|
||||||
impl EventEmitter<DismissEvent> for AddContextServerModal {}
|
impl EventEmitter<DismissEvent> for AddContextServerModal {}
|
||||||
|
|
||||||
impl Render for AddContextServerModal {
|
impl Render for AddContextServerModal {
|
||||||
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 is_name_empty = self.name_editor.read(cx).is_empty(cx);
|
let is_name_empty = self.name_editor.read(cx).is_empty(cx);
|
||||||
let is_command_empty = self.command_editor.read(cx).is_empty(cx);
|
let is_command_empty = self.command_editor.read(cx).is_empty(cx);
|
||||||
|
|
||||||
|
let focus_handle = self.focus_handle(cx);
|
||||||
|
|
||||||
div()
|
div()
|
||||||
.elevation_3(cx)
|
.elevation_3(cx)
|
||||||
.w(rems(34.))
|
.w(rems(34.))
|
||||||
.key_context("AddContextServerModal")
|
.key_context("AddContextServerModal")
|
||||||
.on_action(cx.listener(|this, _: &menu::Cancel, _window, cx| this.cancel(cx)))
|
.on_action(
|
||||||
.on_action(cx.listener(|this, _: &menu::Confirm, _window, cx| this.confirm(cx)))
|
cx.listener(|this, _: &menu::Cancel, _window, cx| this.cancel(&menu::Cancel, cx)),
|
||||||
|
)
|
||||||
|
.on_action(
|
||||||
|
cx.listener(|this, _: &menu::Confirm, _window, cx| {
|
||||||
|
this.confirm(&menu::Confirm, cx)
|
||||||
|
}),
|
||||||
|
)
|
||||||
.capture_any_mouse_down(cx.listener(|this, _, window, cx| {
|
.capture_any_mouse_down(cx.listener(|this, _, window, cx| {
|
||||||
this.focus_handle(cx).focus(window);
|
this.focus_handle(cx).focus(window);
|
||||||
}))
|
}))
|
||||||
.on_mouse_down_out(cx.listener(|_this, _, _, cx| cx.emit(DismissEvent)))
|
.on_mouse_down_out(cx.listener(|_this, _, _, cx| cx.emit(DismissEvent)))
|
||||||
.child(
|
.child(
|
||||||
Modal::new("add-context-server", None)
|
Modal::new("add-context-server", None)
|
||||||
.header(ModalHeader::new().headline("Add Context Server"))
|
.header(ModalHeader::new().headline("Add MCP Server"))
|
||||||
.section(
|
.section(
|
||||||
Section::new()
|
Section::new().child(
|
||||||
.child(self.name_editor.clone())
|
v_flex()
|
||||||
.child(self.command_editor.clone()),
|
.gap_2()
|
||||||
|
.child(self.name_editor.clone())
|
||||||
|
.child(self.command_editor.clone()),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.footer(
|
.footer(
|
||||||
ModalFooter::new()
|
ModalFooter::new()
|
||||||
.start_slot(
|
.start_slot(
|
||||||
Button::new("cancel", "Cancel").on_click(
|
Button::new("cancel", "Cancel")
|
||||||
cx.listener(|this, _event, _window, cx| this.cancel(cx)),
|
.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.cancel(&menu::Cancel, cx)
|
||||||
|
})),
|
||||||
)
|
)
|
||||||
.end_slot(
|
.end_slot(
|
||||||
Button::new("add-server", "Add Server")
|
Button::new("add-server", "Add Server")
|
||||||
.disabled(is_name_empty || is_command_empty)
|
.disabled(is_name_empty || is_command_empty)
|
||||||
|
.key_binding(
|
||||||
|
KeyBinding::for_action_in(
|
||||||
|
&menu::Confirm,
|
||||||
|
&focus_handle,
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
.map(|kb| kb.size(rems_from_px(12.))),
|
||||||
|
)
|
||||||
.map(|button| {
|
.map(|button| {
|
||||||
if is_name_empty {
|
if is_name_empty {
|
||||||
button.tooltip(Tooltip::text("Name is required"))
|
button.tooltip(Tooltip::text("Name is required"))
|
||||||
|
@ -153,9 +183,9 @@ impl Render for AddContextServerModal {
|
||||||
button
|
button
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.on_click(
|
.on_click(cx.listener(|this, _event, _window, cx| {
|
||||||
cx.listener(|this, _event, _window, cx| this.confirm(cx)),
|
this.confirm(&menu::Confirm, cx)
|
||||||
),
|
})),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
@ -47,7 +47,7 @@ use crate::thread_history::{PastContext, PastThread, ThreadHistory};
|
||||||
use crate::thread_store::ThreadStore;
|
use crate::thread_store::ThreadStore;
|
||||||
use crate::ui::UsageBanner;
|
use crate::ui::UsageBanner;
|
||||||
use crate::{
|
use crate::{
|
||||||
AgentDiff, ExpandMessageEditor, InlineAssistant, NewTextThread, NewThread,
|
AddContextServer, AgentDiff, ExpandMessageEditor, InlineAssistant, NewTextThread, NewThread,
|
||||||
OpenActiveThreadAsMarkdown, OpenAgentDiff, OpenHistory, ThreadEvent, ToggleContextPicker,
|
OpenActiveThreadAsMarkdown, OpenAgentDiff, OpenHistory, ThreadEvent, ToggleContextPicker,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1123,14 +1123,16 @@ impl AssistantPanel {
|
||||||
.action("Prompt Library", Box::new(OpenPromptLibrary::default()))
|
.action("Prompt Library", Box::new(OpenPromptLibrary::default()))
|
||||||
.action("Settings", Box::new(OpenConfiguration))
|
.action("Settings", Box::new(OpenConfiguration))
|
||||||
.separator()
|
.separator()
|
||||||
|
.header("MCPs")
|
||||||
.action(
|
.action(
|
||||||
"Install MCPs",
|
"View Server Extensions",
|
||||||
Box::new(zed_actions::Extensions {
|
Box::new(zed_actions::Extensions {
|
||||||
category_filter: Some(
|
category_filter: Some(
|
||||||
zed_actions::ExtensionCategoryFilter::ContextServers,
|
zed_actions::ExtensionCategoryFilter::ContextServers,
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
.action("Add Custom Server", Box::new(AddContextServer))
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -249,10 +249,14 @@ impl ModalFooter {
|
||||||
impl RenderOnce for ModalFooter {
|
impl RenderOnce for ModalFooter {
|
||||||
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
|
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||||
h_flex()
|
h_flex()
|
||||||
.flex_none()
|
|
||||||
.w_full()
|
.w_full()
|
||||||
|
.mt_4()
|
||||||
.p(DynamicSpacing::Base08.rems(cx))
|
.p(DynamicSpacing::Base08.rems(cx))
|
||||||
.justify_between()
|
.flex_none()
|
||||||
|
.justify_end()
|
||||||
|
.gap_1()
|
||||||
|
.border_t_1()
|
||||||
|
.border_color(cx.theme().colors().border_variant)
|
||||||
.child(div().when_some(self.start_slot, |this, start_slot| this.child(start_slot)))
|
.child(div().when_some(self.start_slot, |this, start_slot| this.child(start_slot)))
|
||||||
.child(div().when_some(self.end_slot, |this, end_slot| this.child(end_slot)))
|
.child(div().when_some(self.end_slot, |this, end_slot| this.child(end_slot)))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue