context_store: Refactor state management (#29910)

Because we instantiated `ContextServerManager` both in `agent` and
`assistant-context-editor`, and these two entities track the running MCP
servers separately, we were effectively running every MCP server twice.

This PR moves the `ContextServerManager` into the project crate (now
called `ContextServerStore`). The store can be accessed via a project
instance. This ensures that we only instantiate one `ContextServerStore`
per project.

Also, this PR adds a bunch of tests to ensure that the
`ContextServerStore` behaves correctly (Previously there were none).

Closes #28714
Closes #29530

Release Notes:

- N/A
This commit is contained in:
Bennet Bo Fenner 2025-05-05 21:36:12 +02:00 committed by GitHub
parent 8199664a5a
commit 9cb5ffac25
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
43 changed files with 1570 additions and 1049 deletions

View file

@ -1,5 +1,6 @@
use anyhow::Context as _;
use collections::HashMap;
use context_server::ContextServerCommand;
use dap::adapters::DebugAdapterName;
use fs::Fs;
use futures::StreamExt as _;
@ -51,6 +52,10 @@ pub struct ProjectSettings {
#[serde(default)]
pub dap: HashMap<DebugAdapterName, DapSettings>,
/// Settings for context servers used for AI-related features.
#[serde(default)]
pub context_servers: HashMap<Arc<str>, ContextServerConfiguration>,
/// Configuration for Diagnostics-related features.
#[serde(default)]
pub diagnostics: DiagnosticsSettings,
@ -78,6 +83,19 @@ pub struct DapSettings {
pub binary: Option<String>,
}
#[derive(Deserialize, Serialize, Clone, PartialEq, Eq, JsonSchema, Debug, Default)]
pub struct ContextServerConfiguration {
/// The command to run this context server.
///
/// This will override the command set by an extension.
pub command: Option<ContextServerCommand>,
/// The settings for this context server.
///
/// Consult the documentation for the context server to see what settings
/// are supported.
pub settings: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct NodeBinarySettings {
/// The path to the Node binary.
@ -376,6 +394,40 @@ impl Settings for ProjectSettings {
}
}
#[derive(Deserialize)]
struct VsCodeContextServerCommand {
command: String,
args: Option<Vec<String>>,
env: Option<HashMap<String, String>>,
// note: we don't support envFile and type
}
impl From<VsCodeContextServerCommand> for ContextServerCommand {
fn from(cmd: VsCodeContextServerCommand) -> Self {
Self {
path: cmd.command,
args: cmd.args.unwrap_or_default(),
env: cmd.env,
}
}
}
if let Some(mcp) = vscode.read_value("mcp").and_then(|v| v.as_object()) {
current
.context_servers
.extend(mcp.iter().filter_map(|(k, v)| {
Some((
k.clone().into(),
ContextServerConfiguration {
command: Some(
serde_json::from_value::<VsCodeContextServerCommand>(v.clone())
.ok()?
.into(),
),
settings: None,
},
))
}));
}
// TODO: translate lsp settings for rust-analyzer and other popular ones to old.lsp
}
}