diff --git a/crates/agent_ui/src/agent_configuration.rs b/crates/agent_ui/src/agent_configuration.rs index 6ab5985646..6164fcf72b 100644 --- a/crates/agent_ui/src/agent_configuration.rs +++ b/crates/agent_ui/src/agent_configuration.rs @@ -538,7 +538,18 @@ impl AgentConfiguration { Some(ContextMenu::build(window, cx, |menu, _window, _cx| { menu.entry("Add Custom Server", None, { |window, cx| { - window.dispatch_action(crate::AddContextServer.boxed_clone(), cx) + window.dispatch_action( + crate::AddContextServer::local().boxed_clone(), + cx, + ) + } + }) + .entry("Add Remote Server", None, { + |window, cx| { + window.dispatch_action( + crate::AddContextServer::remote().boxed_clone(), + cx, + ) } }) .entry("Install from Extensions", None, { diff --git a/crates/agent_ui/src/agent_configuration/configure_context_server_modal.rs b/crates/agent_ui/src/agent_configuration/configure_context_server_modal.rs index 5ccc901b4a..5ceb13d594 100644 --- a/crates/agent_ui/src/agent_configuration/configure_context_server_modal.rs +++ b/crates/agent_ui/src/agent_configuration/configure_context_server_modal.rs @@ -31,10 +31,12 @@ use ui::{ use util::ResultExt as _; use workspace::{ModalView, Workspace}; -use crate::AddContextServer; +use crate::{AddContextServer, ContextServerType}; enum ConfigurationTarget { - New, + New { + server_type: ContextServerType, + }, Existing { id: ContextServerId, command: ContextServerCommand, @@ -56,11 +58,11 @@ enum ConfigurationTarget { enum ConfigurationSource { New { editor: Entity, - is_http: bool, + server_type: ContextServerType, }, Existing { editor: Entity, - is_http: bool, + server_type: ContextServerType, }, Extension { id: ContextServerId, @@ -106,9 +108,17 @@ impl ConfigurationSource { } match target { - ConfigurationTarget::New => ConfigurationSource::New { - editor: create_editor(context_server_input(None), jsonc_language, window, cx), - is_http: false, + ConfigurationTarget::New { server_type } => ConfigurationSource::New { + editor: create_editor( + match server_type { + ContextServerType::Remote => context_server_http_input(None), + ContextServerType::Local => context_server_input(None), + }, + jsonc_language, + window, + cx, + ), + server_type, }, ConfigurationTarget::Existing { id, command } => ConfigurationSource::Existing { editor: create_editor( @@ -117,7 +127,7 @@ impl ConfigurationSource { window, cx, ), - is_http: false, + server_type: ContextServerType::Local, }, ConfigurationTarget::ExistingHttp { id, @@ -131,7 +141,7 @@ impl ConfigurationSource { window, cx, ), - is_http: true, + server_type: ContextServerType::Remote, }, ConfigurationTarget::Extension { @@ -169,9 +179,15 @@ impl ConfigurationSource { fn output(&self, cx: &mut App) -> Result<(ContextServerId, ContextServerSettings)> { match self { - ConfigurationSource::New { editor, is_http } - | ConfigurationSource::Existing { editor, is_http } => { - if *is_http { + ConfigurationSource::New { + editor, + server_type, + } + | ConfigurationSource::Existing { + editor, + server_type, + } => match *server_type { + ContextServerType::Remote => { parse_http_input(&editor.read(cx).text(cx)).map(|(id, url, auth, oauth)| { ( id, @@ -184,7 +200,8 @@ impl ConfigurationSource { }, ) }) - } else { + } + ContextServerType::Local => { parse_input(&editor.read(cx).text(cx)).map(|(id, command)| { ( id, @@ -196,7 +213,7 @@ impl ConfigurationSource { ) }) } - } + }, ConfigurationSource::Extension { id, editor, @@ -440,7 +457,7 @@ impl ConfigureContextServerModal { ConfigurationTarget::Existing { id, .. } | ConfigurationTarget::ExistingHttp { id, .. } | ConfigurationTarget::Extension { id, .. } => Some(id), - ConfigurationTarget::New => None, + ConfigurationTarget::New { .. } => None, }) else { return State::Idle; }; @@ -474,13 +491,14 @@ impl ConfigureContextServerModal { _cx: &mut Context, ) { workspace.register_action({ - move |_workspace, _: &AddContextServer, window, cx| { + move |_workspace, action: &AddContextServer, window, cx| { let workspace_handle = cx.weak_entity(); let language_registry = language_registry.clone(); + let server_type = action.context_server_type; window .spawn(cx, async move |cx| { Self::show_modal( - ConfigurationTarget::New, + ConfigurationTarget::New { server_type }, language_registry, workspace_handle, cx, @@ -580,7 +598,7 @@ impl ConfigureContextServerModal { ConfigurationTarget::Existing { id, .. } => Some(id.clone()), ConfigurationTarget::ExistingHttp { id, .. } => Some(id.clone()), ConfigurationTarget::Extension { id, .. } => Some(id.clone()), - ConfigurationTarget::New => None, + ConfigurationTarget::New { .. } => None, }, source: ConfigurationSource::from_target( target, @@ -859,7 +877,9 @@ impl ConfigureContextServerModal { fn render_tab_bar(&self, cx: &mut Context) -> Option { let is_http = match &self.source { - ConfigurationSource::New { is_http, .. } => *is_http, + ConfigurationSource::New { server_type, .. } => { + *server_type == ContextServerType::Remote + } _ => return None, }; @@ -870,14 +890,15 @@ impl ConfigureContextServerModal { .p_1() .text_sm() .border_b_1() - .when(active, |this| { - this.border_color(cx.theme().colors().border_focused) - }) - .when(!active, |this| { - this.border_color(gpui::transparent_black()) - .text_color(cx.theme().colors().text_muted) - .hover(|s| s.text_color(cx.theme().colors().text)) - }) + .when_else( + active, + |this| this.border_color(cx.theme().colors().border_focused), + |this| { + this.border_color(gpui::transparent_black()) + .text_color(cx.theme().colors().text_muted) + .hover(|s| s.text_color(cx.theme().colors().text)) + }, + ) .child(label) }; @@ -890,27 +911,33 @@ impl ConfigureContextServerModal { .border_color(cx.theme().colors().border.opacity(0.5)) .child( tab("Local", !is_http).on_click(cx.listener(|this, _, window, cx| { - if let ConfigurationSource::New { editor, is_http } = &mut this.source { - if *is_http { - *is_http = false; - let new_text = context_server_input(None); - editor.update(cx, |editor, cx| { - editor.set_text(new_text, window, cx); - }); - } + if let ConfigurationSource::New { + editor, + server_type, + } = &mut this.source + && *server_type != ContextServerType::Local + { + *server_type = ContextServerType::Local; + let new_text = context_server_input(None); + editor.update(cx, |editor, cx| { + editor.set_text(new_text, window, cx); + }); } })), ) .child( tab("Remote", is_http).on_click(cx.listener(|this, _, window, cx| { - if let ConfigurationSource::New { editor, is_http } = &mut this.source { - if !*is_http { - *is_http = true; - let new_text = context_server_http_input(None); - editor.update(cx, |editor, cx| { - editor.set_text(new_text, window, cx); - }); - } + if let ConfigurationSource::New { + editor, + server_type, + } = &mut this.source + && *server_type != ContextServerType::Remote + { + *server_type = ContextServerType::Remote; + let new_text = context_server_http_input(None); + editor.update(cx, |editor, cx| { + editor.set_text(new_text, window, cx); + }); } })), ) diff --git a/crates/agent_ui/src/agent_panel.rs b/crates/agent_ui/src/agent_panel.rs index da5fa0ce17..6920e9b75a 100644 --- a/crates/agent_ui/src/agent_panel.rs +++ b/crates/agent_ui/src/agent_panel.rs @@ -5674,7 +5674,8 @@ impl AgentPanel { if !showing_terminal { menu = menu .header("MCP Servers") - .action("Add Custom Server…", Box::new(AddContextServer)) + .action("Add Custom Server…", Box::new(AddContextServer::local())) + .action("Add Remote Server…", Box::new(AddContextServer::remote())) .action( "Install New Servers…", Box::new(zed_actions::Extensions { diff --git a/crates/agent_ui/src/agent_ui.rs b/crates/agent_ui/src/agent_ui.rs index 6df1d366ea..7684376531 100644 --- a/crates/agent_ui/src/agent_ui.rs +++ b/crates/agent_ui/src/agent_ui.rs @@ -196,8 +196,6 @@ actions!( CycleFavoriteModels, /// Expands the message editor to full size. ExpandMessageEditor, - /// Adds a context server to the configuration. - AddContextServer, /// Archives the currently selected thread. ArchiveSelectedThread, /// Removes the currently selected thread. @@ -352,6 +350,49 @@ pub struct ToggleCommandPattern { #[serde(deny_unknown_fields)] pub struct NewThread; +/// The kind of context server to configure when adding a new one. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Deserialize, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum ContextServerType { + /// A context server that runs locally via stdin/stdout. + #[default] + Local, + /// A context server that is connected to over HTTP. + Remote, +} + +/// Adds a context server to the configuration. +#[derive(Clone, PartialEq, Deserialize, JsonSchema, Action)] +#[action(namespace = agent)] +#[serde(deny_unknown_fields)] +pub struct AddContextServer { + /// The kind of context server to add. + #[serde(default)] + pub context_server_type: ContextServerType, +} + +impl Default for AddContextServer { + fn default() -> Self { + Self::local() + } +} + +impl AddContextServer { + /// Returns an action that adds a local (stdin/stdout) context server. + pub fn local() -> Self { + Self { + context_server_type: ContextServerType::Local, + } + } + + /// Returns an action that adds a remote (HTTP) context server. + pub fn remote() -> Self { + Self { + context_server_type: ContextServerType::Remote, + } + } +} + /// Creates a new external agent conversation thread. #[derive(Clone, PartialEq, Deserialize, JsonSchema, Action)] #[action(namespace = agent)]