mcp: Fix source property showing up as undefined in settings (#43417)
Follow up to #39021. <img width="576" height="141" alt="image" src="https://github.com/user-attachments/assets/c89885a4-e664-4614-9bb0-86442dff34ee" /> - Add migration to remove `source` tag because `ContextServerSettings` is now untagged - Fix typos in context server modal - PR seems to have removed the `test_action_namespaces` test, which I brought back in this PR Release Notes: - Fixed an issue where the `source` property of MCP settings would show up as unrecognised
This commit is contained in:
@@ -2553,7 +2553,7 @@ fn setup_context_server(
|
||||
let mut settings = ProjectSettings::get_global(cx).clone();
|
||||
settings.context_servers.insert(
|
||||
name.into(),
|
||||
project::project_settings::ContextServerSettings::Custom {
|
||||
project::project_settings::ContextServerSettings::Stdio {
|
||||
enabled: true,
|
||||
command: ContextServerCommand {
|
||||
path: "somebinary".into(),
|
||||
|
||||
@@ -182,7 +182,7 @@ impl ConfigurationSource {
|
||||
parse_input(&editor.read(cx).text(cx)).map(|(id, command)| {
|
||||
(
|
||||
id,
|
||||
ContextServerSettings::Custom {
|
||||
ContextServerSettings::Stdio {
|
||||
enabled: true,
|
||||
command,
|
||||
},
|
||||
@@ -403,7 +403,7 @@ impl ConfigureContextServerModal {
|
||||
|
||||
window.spawn(cx, async move |cx| {
|
||||
let target = match settings {
|
||||
ContextServerSettings::Custom {
|
||||
ContextServerSettings::Stdio {
|
||||
enabled: _,
|
||||
command,
|
||||
} => Some(ConfigurationTarget::Existing {
|
||||
@@ -635,7 +635,6 @@ impl ConfigureContextServerModal {
|
||||
}
|
||||
|
||||
fn render_modal_content(&self, cx: &App) -> AnyElement {
|
||||
// All variants now use single editor approach
|
||||
let editor = match &self.source {
|
||||
ConfigurationSource::New { editor, .. } => editor,
|
||||
ConfigurationSource::Existing { editor, .. } => editor,
|
||||
@@ -712,12 +711,12 @@ impl ConfigureContextServerModal {
|
||||
)
|
||||
} else if let ConfigurationSource::New { is_http, .. } = &self.source {
|
||||
let label = if *is_http {
|
||||
"Run command"
|
||||
"Configure Local"
|
||||
} else {
|
||||
"Connect via HTTP"
|
||||
"Configure Remote"
|
||||
};
|
||||
let tooltip = if *is_http {
|
||||
"Configure an MCP serevr that runs on stdin/stdout."
|
||||
"Configure an MCP server that runs on stdin/stdout."
|
||||
} else {
|
||||
"Configure an MCP server that you connect to over HTTP"
|
||||
};
|
||||
|
||||
@@ -972,7 +972,7 @@ impl ExtensionImports for WasmState {
|
||||
});
|
||||
|
||||
match settings {
|
||||
project::project_settings::ContextServerSettings::Custom {
|
||||
project::project_settings::ContextServerSettings::Stdio {
|
||||
enabled: _,
|
||||
command,
|
||||
} => Ok(serde_json::to_string(&settings::ContextServerSettings {
|
||||
|
||||
@@ -147,3 +147,9 @@ pub(crate) mod m_2025_11_20 {
|
||||
|
||||
pub(crate) use settings::SETTINGS_PATTERNS;
|
||||
}
|
||||
|
||||
pub(crate) mod m_2025_11_25 {
|
||||
mod settings;
|
||||
|
||||
pub(crate) use settings::remove_context_server_source;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
use anyhow::Result;
|
||||
use serde_json::Value;
|
||||
|
||||
pub fn remove_context_server_source(settings: &mut Value) -> Result<()> {
|
||||
if let Some(obj) = settings.as_object_mut() {
|
||||
if let Some(context_servers) = obj.get_mut("context_servers") {
|
||||
if let Some(servers) = context_servers.as_object_mut() {
|
||||
for (_, server) in servers.iter_mut() {
|
||||
if let Some(server_obj) = server.as_object_mut() {
|
||||
server_obj.remove("source");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -223,6 +223,7 @@ pub fn migrate_settings(text: &str) -> Result<Option<String>> {
|
||||
migrations::m_2025_11_20::SETTINGS_PATTERNS,
|
||||
&SETTINGS_QUERY_2025_11_20,
|
||||
),
|
||||
MigrationType::Json(migrations::m_2025_11_25::remove_context_server_source),
|
||||
];
|
||||
run_migrations(text, migrations)
|
||||
}
|
||||
@@ -1334,7 +1335,6 @@ mod tests {
|
||||
r#"{
|
||||
"context_servers": {
|
||||
"some-mcp-server": {
|
||||
"source": "custom",
|
||||
"command": {
|
||||
"path": "npx",
|
||||
"args": [
|
||||
@@ -1354,7 +1354,6 @@ mod tests {
|
||||
r#"{
|
||||
"context_servers": {
|
||||
"some-mcp-server": {
|
||||
"source": "custom",
|
||||
"command": "npx",
|
||||
"args": [
|
||||
"-y",
|
||||
@@ -1376,7 +1375,6 @@ mod tests {
|
||||
r#"{
|
||||
"context_servers": {
|
||||
"server-with-extras": {
|
||||
"source": "custom",
|
||||
"command": {
|
||||
"path": "/usr/bin/node",
|
||||
"args": ["server.js"]
|
||||
@@ -1389,7 +1387,6 @@ mod tests {
|
||||
r#"{
|
||||
"context_servers": {
|
||||
"server-with-extras": {
|
||||
"source": "custom",
|
||||
"command": "/usr/bin/node",
|
||||
"args": ["server.js"],
|
||||
"settings": {}
|
||||
@@ -1404,7 +1401,6 @@ mod tests {
|
||||
r#"{
|
||||
"context_servers": {
|
||||
"simple-server": {
|
||||
"source": "custom",
|
||||
"command": {
|
||||
"path": "simple-mcp-server"
|
||||
}
|
||||
@@ -1415,7 +1411,6 @@ mod tests {
|
||||
r#"{
|
||||
"context_servers": {
|
||||
"simple-server": {
|
||||
"source": "custom",
|
||||
"command": "simple-mcp-server"
|
||||
}
|
||||
}
|
||||
@@ -2311,4 +2306,52 @@ mod tests {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_remove_context_server_source() {
|
||||
assert_migrate_settings(
|
||||
&r#"
|
||||
{
|
||||
"context_servers": {
|
||||
"extension_server": {
|
||||
"source": "extension",
|
||||
"settings": {
|
||||
"foo": "bar"
|
||||
}
|
||||
},
|
||||
"custom_server": {
|
||||
"source": "custom",
|
||||
"command": "foo",
|
||||
"args": ["bar"],
|
||||
"env": {
|
||||
"FOO": "BAR"
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
"#
|
||||
.unindent(),
|
||||
Some(
|
||||
&r#"
|
||||
{
|
||||
"context_servers": {
|
||||
"extension_server": {
|
||||
"settings": {
|
||||
"foo": "bar"
|
||||
}
|
||||
},
|
||||
"custom_server": {
|
||||
"command": "foo",
|
||||
"args": ["bar"],
|
||||
"env": {
|
||||
"FOO": "BAR"
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
"#
|
||||
.unindent(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ impl ContextServerConfiguration {
|
||||
cx: &AsyncApp,
|
||||
) -> Option<Self> {
|
||||
match settings {
|
||||
ContextServerSettings::Custom {
|
||||
ContextServerSettings::Stdio {
|
||||
enabled: _,
|
||||
command,
|
||||
} => Some(ContextServerConfiguration::Custom { command }),
|
||||
@@ -1003,7 +1003,7 @@ mod tests {
|
||||
),
|
||||
(
|
||||
server_2_id.0.clone(),
|
||||
settings::ContextServerSettingsContent::Custom {
|
||||
settings::ContextServerSettingsContent::Stdio {
|
||||
enabled: true,
|
||||
command: ContextServerCommand {
|
||||
path: "somebinary".into(),
|
||||
@@ -1044,7 +1044,7 @@ mod tests {
|
||||
),
|
||||
(
|
||||
server_2_id.0.clone(),
|
||||
settings::ContextServerSettingsContent::Custom {
|
||||
settings::ContextServerSettingsContent::Stdio {
|
||||
enabled: true,
|
||||
command: ContextServerCommand {
|
||||
path: "somebinary".into(),
|
||||
@@ -1127,7 +1127,7 @@ mod tests {
|
||||
json!({"code.rs": ""}),
|
||||
vec![(
|
||||
SERVER_1_ID.into(),
|
||||
ContextServerSettings::Custom {
|
||||
ContextServerSettings::Stdio {
|
||||
enabled: true,
|
||||
command: ContextServerCommand {
|
||||
path: "somebinary".into(),
|
||||
@@ -1180,7 +1180,7 @@ mod tests {
|
||||
set_context_server_configuration(
|
||||
vec![(
|
||||
server_1_id.0.clone(),
|
||||
settings::ContextServerSettingsContent::Custom {
|
||||
settings::ContextServerSettingsContent::Stdio {
|
||||
enabled: false,
|
||||
command: ContextServerCommand {
|
||||
path: "somebinary".into(),
|
||||
@@ -1209,7 +1209,7 @@ mod tests {
|
||||
set_context_server_configuration(
|
||||
vec![(
|
||||
server_1_id.0.clone(),
|
||||
settings::ContextServerSettingsContent::Custom {
|
||||
settings::ContextServerSettingsContent::Stdio {
|
||||
enabled: true,
|
||||
command: ContextServerCommand {
|
||||
path: "somebinary".into(),
|
||||
@@ -1328,7 +1328,7 @@ mod tests {
|
||||
}
|
||||
|
||||
fn dummy_server_settings() -> ContextServerSettings {
|
||||
ContextServerSettings::Custom {
|
||||
ContextServerSettings::Stdio {
|
||||
enabled: true,
|
||||
command: ContextServerCommand {
|
||||
path: "somebinary".into(),
|
||||
|
||||
@@ -117,7 +117,7 @@ pub struct GlobalLspSettings {
|
||||
#[derive(Deserialize, Serialize, Clone, PartialEq, Eq, JsonSchema, Debug)]
|
||||
#[serde(tag = "source", rename_all = "snake_case")]
|
||||
pub enum ContextServerSettings {
|
||||
Custom {
|
||||
Stdio {
|
||||
/// Whether the context server is enabled.
|
||||
#[serde(default = "default_true")]
|
||||
enabled: bool,
|
||||
@@ -125,16 +125,6 @@ pub enum ContextServerSettings {
|
||||
#[serde(flatten)]
|
||||
command: ContextServerCommand,
|
||||
},
|
||||
Extension {
|
||||
/// Whether the context server is enabled.
|
||||
#[serde(default = "default_true")]
|
||||
enabled: bool,
|
||||
/// The settings for this context server specified by the extension.
|
||||
///
|
||||
/// Consult the documentation for the context server to see what settings
|
||||
/// are supported.
|
||||
settings: serde_json::Value,
|
||||
},
|
||||
Http {
|
||||
/// Whether the context server is enabled.
|
||||
#[serde(default = "default_true")]
|
||||
@@ -145,13 +135,23 @@ pub enum ContextServerSettings {
|
||||
#[serde(skip_serializing_if = "HashMap::is_empty", default)]
|
||||
headers: HashMap<String, String>,
|
||||
},
|
||||
Extension {
|
||||
/// Whether the context server is enabled.
|
||||
#[serde(default = "default_true")]
|
||||
enabled: bool,
|
||||
/// The settings for this context server specified by the extension.
|
||||
///
|
||||
/// Consult the documentation for the context server to see what settings
|
||||
/// are supported.
|
||||
settings: serde_json::Value,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<settings::ContextServerSettingsContent> for ContextServerSettings {
|
||||
fn from(value: settings::ContextServerSettingsContent) -> Self {
|
||||
match value {
|
||||
settings::ContextServerSettingsContent::Custom { enabled, command } => {
|
||||
ContextServerSettings::Custom { enabled, command }
|
||||
settings::ContextServerSettingsContent::Stdio { enabled, command } => {
|
||||
ContextServerSettings::Stdio { enabled, command }
|
||||
}
|
||||
settings::ContextServerSettingsContent::Extension { enabled, settings } => {
|
||||
ContextServerSettings::Extension { enabled, settings }
|
||||
@@ -171,8 +171,8 @@ impl From<settings::ContextServerSettingsContent> for ContextServerSettings {
|
||||
impl Into<settings::ContextServerSettingsContent> for ContextServerSettings {
|
||||
fn into(self) -> settings::ContextServerSettingsContent {
|
||||
match self {
|
||||
ContextServerSettings::Custom { enabled, command } => {
|
||||
settings::ContextServerSettingsContent::Custom { enabled, command }
|
||||
ContextServerSettings::Stdio { enabled, command } => {
|
||||
settings::ContextServerSettingsContent::Stdio { enabled, command }
|
||||
}
|
||||
ContextServerSettings::Extension { enabled, settings } => {
|
||||
settings::ContextServerSettingsContent::Extension { enabled, settings }
|
||||
@@ -200,17 +200,17 @@ impl ContextServerSettings {
|
||||
|
||||
pub fn enabled(&self) -> bool {
|
||||
match self {
|
||||
ContextServerSettings::Custom { enabled, .. } => *enabled,
|
||||
ContextServerSettings::Extension { enabled, .. } => *enabled,
|
||||
ContextServerSettings::Stdio { enabled, .. } => *enabled,
|
||||
ContextServerSettings::Http { enabled, .. } => *enabled,
|
||||
ContextServerSettings::Extension { enabled, .. } => *enabled,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_enabled(&mut self, enabled: bool) {
|
||||
match self {
|
||||
ContextServerSettings::Custom { enabled: e, .. } => *e = enabled,
|
||||
ContextServerSettings::Extension { enabled: e, .. } => *e = enabled,
|
||||
ContextServerSettings::Stdio { enabled: e, .. } => *e = enabled,
|
||||
ContextServerSettings::Http { enabled: e, .. } => *e = enabled,
|
||||
ContextServerSettings::Extension { enabled: e, .. } => *e = enabled,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,7 +192,7 @@ pub struct SessionSettingsContent {
|
||||
#[derive(Deserialize, Serialize, Clone, PartialEq, Eq, JsonSchema, MergeFrom, Debug)]
|
||||
#[serde(untagged, rename_all = "snake_case")]
|
||||
pub enum ContextServerSettingsContent {
|
||||
Custom {
|
||||
Stdio {
|
||||
/// Whether the context server is enabled.
|
||||
#[serde(default = "default_true")]
|
||||
enabled: bool,
|
||||
@@ -225,7 +225,7 @@ pub enum ContextServerSettingsContent {
|
||||
impl ContextServerSettingsContent {
|
||||
pub fn set_enabled(&mut self, enabled: bool) {
|
||||
match self {
|
||||
ContextServerSettingsContent::Custom {
|
||||
ContextServerSettingsContent::Stdio {
|
||||
enabled: custom_enabled,
|
||||
..
|
||||
} => {
|
||||
|
||||
@@ -568,7 +568,7 @@ impl VsCodeSettings {
|
||||
.filter_map(|(k, v)| {
|
||||
Some((
|
||||
k.clone().into(),
|
||||
ContextServerSettingsContent::Custom {
|
||||
ContextServerSettingsContent::Stdio {
|
||||
enabled: true,
|
||||
command: serde_json::from_value::<VsCodeContextServerCommand>(v.clone())
|
||||
.ok()
|
||||
|
||||
@@ -4686,6 +4686,133 @@ mod tests {
|
||||
});
|
||||
}
|
||||
|
||||
/// Checks that action namespaces are the expected set. The purpose of this is to prevent typos
|
||||
/// and let you know when introducing a new namespace.
|
||||
#[gpui::test]
|
||||
async fn test_action_namespaces(cx: &mut gpui::TestAppContext) {
|
||||
use itertools::Itertools;
|
||||
|
||||
init_keymap_test(cx);
|
||||
cx.update(|cx| {
|
||||
let all_actions = cx.all_action_names();
|
||||
|
||||
let mut actions_without_namespace = Vec::new();
|
||||
let all_namespaces = all_actions
|
||||
.iter()
|
||||
.filter_map(|action_name| {
|
||||
let namespace = action_name
|
||||
.split("::")
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter()
|
||||
.rev()
|
||||
.skip(1)
|
||||
.rev()
|
||||
.join("::");
|
||||
if namespace.is_empty() {
|
||||
actions_without_namespace.push(*action_name);
|
||||
}
|
||||
if &namespace == "test_only" || &namespace == "stories" {
|
||||
None
|
||||
} else {
|
||||
Some(namespace)
|
||||
}
|
||||
})
|
||||
.sorted()
|
||||
.dedup()
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(actions_without_namespace, Vec::<&str>::new());
|
||||
|
||||
let expected_namespaces = vec![
|
||||
"action",
|
||||
"activity_indicator",
|
||||
"agent",
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
"app_menu",
|
||||
"assistant",
|
||||
"assistant2",
|
||||
"auto_update",
|
||||
"bedrock",
|
||||
"branches",
|
||||
"buffer_search",
|
||||
"channel_modal",
|
||||
"cli",
|
||||
"client",
|
||||
"collab",
|
||||
"collab_panel",
|
||||
"command_palette",
|
||||
"console",
|
||||
"context_server",
|
||||
"copilot",
|
||||
"debug_panel",
|
||||
"debugger",
|
||||
"dev",
|
||||
"diagnostics",
|
||||
"edit_prediction",
|
||||
"editor",
|
||||
"feedback",
|
||||
"file_finder",
|
||||
"git",
|
||||
"git_onboarding",
|
||||
"git_panel",
|
||||
"go_to_line",
|
||||
"icon_theme_selector",
|
||||
"journal",
|
||||
"keymap_editor",
|
||||
"keystroke_input",
|
||||
"language_selector",
|
||||
"line_ending_selector",
|
||||
"lsp_tool",
|
||||
"markdown",
|
||||
"menu",
|
||||
"notebook",
|
||||
"notification_panel",
|
||||
"onboarding",
|
||||
"outline",
|
||||
"outline_panel",
|
||||
"pane",
|
||||
"panel",
|
||||
"picker",
|
||||
"project_panel",
|
||||
"project_search",
|
||||
"project_symbols",
|
||||
"projects",
|
||||
"repl",
|
||||
"rules_library",
|
||||
"search",
|
||||
"settings_editor",
|
||||
"settings_profile_selector",
|
||||
"snippets",
|
||||
"stash_picker",
|
||||
"supermaven",
|
||||
"svg",
|
||||
"syntax_tree_view",
|
||||
"tab_switcher",
|
||||
"task",
|
||||
"terminal",
|
||||
"terminal_panel",
|
||||
"theme_selector",
|
||||
"toast",
|
||||
"toolchain",
|
||||
"variable_list",
|
||||
"vim",
|
||||
"window",
|
||||
"workspace",
|
||||
"zed",
|
||||
"zed_actions",
|
||||
"zed_predict_onboarding",
|
||||
"zeta",
|
||||
];
|
||||
assert_eq!(
|
||||
all_namespaces,
|
||||
expected_namespaces
|
||||
.into_iter()
|
||||
.map(|namespace| namespace.to_string())
|
||||
.sorted()
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_bundled_settings_and_themes(cx: &mut App) {
|
||||
cx.text_system()
|
||||
|
||||
+2
-2
@@ -40,12 +40,12 @@ You can connect them by adding their commands directly to your `settings.json`,
|
||||
```json [settings]
|
||||
{
|
||||
"context_servers": {
|
||||
"run-command": {
|
||||
"local-mcp-server": {
|
||||
"command": "some-command",
|
||||
"args": ["arg-1", "arg-2"],
|
||||
"env": {}
|
||||
},
|
||||
"over-http": {
|
||||
"remote-mcp-server": {
|
||||
"url": "custom",
|
||||
"headers": { "Authorization": "Bearer <token>" }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user