Make adding a remote MCP server more discoverable (#59359)
# Objective The reasoning for this is that it is currently rather hard to find the option to add a remote MCP in the app - there is no hint that this is supported and (without using the command palette) requires up to 5 clicks before the proper modal is shown. The idea for this here is to make adding a remote server the default and also make it more visibile throughout the app - the `AddContextServer` action will now default to remote and this also adds two context menu entries to directly open the modal with the remote option preselected. Open for feedback - I admit this might not be the most optiomal solution, but at least it surfaces the availablilty better and (hopefully) makes adding these easier. Release Notes: - Added an entry to the agent panel options menu to quickly add a remote MCP server
This commit is contained in:
1 parent
479bce0995
commit
61ce210ec2
4 files changed
+127
-47
No files matched your search
@@ -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, {
|
||||
|
||||
@@ -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<Editor>,
|
||||
is_http: bool,
|
||||
server_type: ContextServerType,
|
||||
},
|
||||
Existing {
|
||||
editor: Entity<Editor>,
|
||||
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>,
|
||||
) {
|
||||
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<Self>) -> Option<AnyElement> {
|
||||
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);
|
||||
});
|
||||
}
|
||||
})),
|
||||
)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)]
|
||||
|
||||
Reference in new issue
Block a user