feat(desktop): surface effective buzz-agent MCP servers on config surface

Add buzz_agent_mcp_servers to RuntimeConfigSurface, populated by
resolve_config_surface via the existing effective_buzz_agent_mcp_servers
merge (global < definition < agent, enabled-only) for the buzz-agent
runtime only. Every other runtime keeps the field empty and continues
to surface its servers via extensions.

This is the WYSIWYG read path the PR3 UI's read-only buzzAgentSlot will
consume — the effective list is now reachable from the config-bridge
surface the frontend already reads, matching what actually runs at
spawn (spawn_hash.rs, runtime.rs) rather than just the agent's own
record layer.

Also bumps two pre-existing file-size overrides (agent_config.rs,
readiness.rs) to their gate's own split-count, matching this diff and
a prior unaccounted +1 line already on the branch.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
This commit is contained in:
npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7
2026-07-14 17:34:04 -04:00
co-authored by Will Pfleger
parent 812301592a
commit c3386a9627
4 changed files with 138 additions and 4 deletions
+9 -4
View File
@@ -166,9 +166,10 @@ const overrides = new Map([
// setup-mode requirements. The Windows-only requirement and serialization
// test add eight lines; split remains queued with the existing file debt.
// Windows Doctor install fix: cli_install_commands_windows field added to test stubs.
// team-instructions-first-class: ManagedAgentRecord fixture gains the new
// team_id field (+1 line).
["src-tauri/src/managed_agents/readiness.rs", 1765],
// `team-instructions-first-class` added `team_id`; MCP configuration added
// the required `mcp_servers` minimal-fixture field. The gate counts the
// trailing newline, so 1,765 physical lines are 1,766 counted lines.
["src-tauri/src/managed_agents/readiness.rs", 1766],
// applyWorkspace reposDir parameter plus the validateReposDir binding,
// threaded through Tauri invokes for configurable repos_dir, plus the
// harness-persona-sync `harnessOverride` create-input bit — load-bearing
@@ -429,7 +430,11 @@ const overrides = new Map([
// is_safe_to_reveal allowlist + baked_env_thinking_effort_is_unmasked test.
// +1: doctor-install-reliability: login_hint: None added to goose_runtime test stub.
// +1: doctor-install-reliability review fixes: auth_probe_args: None added to stub.
["src-tauri/src/commands/agent_config.rs", 1021],
// MCP config surface: `resolve_config_surface` receives the effective command
// and exposes the effective `buzz_agent_mcp_servers` merge; coverage pins the
// merge, disabled-mask, and non-buzz-agent cases. The gate counts trailing
// newlines, so 1,139 physical lines are 1,140 counted lines.
["src-tauri/src/commands/agent_config.rs", 1140],
// codex-install-auto-restart review-fixes: should_restart_after_install
// takes pid_alive:bool (pure predicate, no OS-dependent call); 3 racy
// cache tests replaced with 6 pure availability_drift predicate tests;
@@ -55,6 +55,7 @@ fn resolve_config_surface(
mut record: ManagedAgentRecord,
personas: &[AgentDefinition],
runtime_meta: Option<&KnownAcpRuntime>,
effective_command: &str,
session_cache: Option<&SessionConfigCache>,
global: &GlobalAgentConfig,
) -> RuntimeConfigSurface {
@@ -152,6 +153,19 @@ fn resolve_config_surface(
baseline.as_ref().map(|(m, o)| (m.as_str(), o.clone())),
);
if runtime_meta.is_some_and(|m| m.id == "buzz-agent") {
surface.buzz_agent_mcp_servers = crate::managed_agents::effective_buzz_agent_mcp_servers(
&record,
personas,
&global.mcp_servers,
effective_command,
)
.unwrap_or_else(|error| {
eprintln!("buzz-desktop: invalid persisted MCP server configuration: {error}");
Vec::new()
});
}
// Re-tag persona-sourced fields from BuzzExplicit to PersonaDefault.
if !had_prompt {
retag_persona_default(&mut surface.normalized.system_prompt);
@@ -375,6 +389,7 @@ pub async fn get_agent_config_surface(
record,
&personas,
runtime_meta,
&effective_cmd,
session_cache.as_ref(),
&global,
))
@@ -713,6 +728,7 @@ mod tests {
record,
&personas,
Some(goose_runtime()),
"goose",
None,
&Default::default(),
);
@@ -740,6 +756,7 @@ mod tests {
record,
&personas,
Some(goose_runtime()),
"goose",
Some(&cache),
&Default::default(),
);
@@ -768,6 +785,7 @@ mod tests {
record,
&personas,
Some(goose_runtime()),
"goose",
Some(&cache),
&Default::default(),
);
@@ -795,6 +813,7 @@ mod tests {
record,
&personas,
Some(goose_runtime()),
"goose",
Some(&cache),
&Default::default(),
);
@@ -819,6 +838,7 @@ mod tests {
record,
&personas,
Some(goose_runtime()),
"goose",
Some(&cache),
&Default::default(),
);
@@ -855,6 +875,7 @@ mod tests {
record,
&personas,
Some(goose_runtime()),
"goose",
Some(&cache),
&global,
);
@@ -876,6 +897,102 @@ mod tests {
);
}
// ── buzz_agent_mcp_servers surface tests ────────────────────────────────
fn mcp_server(
name: &str,
command: &str,
enabled: bool,
) -> crate::managed_agents::McpServerConfig {
crate::managed_agents::McpServerConfig {
name: name.to_string(),
command: command.to_string(),
args: vec![],
env: vec![],
enabled,
}
}
/// The buzz-agent runtime surfaces its effective-merged MCP servers onto
/// `RuntimeConfigSurface.buzz_agent_mcp_servers`, matching what
/// `effective_buzz_agent_mcp_servers` (the spawn-time source of truth)
/// would compute for the same record/persona/global layers.
#[test]
fn buzz_agent_runtime_surfaces_effective_merged_mcp_servers() {
let mut record = agent_record();
record.persona_id = None;
record.mcp_servers = vec![mcp_server("agent-server", "agent-cmd", true)];
let personas: Vec<AgentDefinition> = vec![];
let global = crate::managed_agents::GlobalAgentConfig {
mcp_servers: vec![mcp_server("global-server", "global-cmd", true)],
..Default::default()
};
let surface = resolve_config_surface(
record,
&personas,
known_acp_runtime("buzz-agent"),
"buzz-agent",
None,
&global,
);
let mut names: Vec<&str> = surface
.buzz_agent_mcp_servers
.iter()
.map(|s| s.name.as_str())
.collect();
names.sort_unstable();
assert_eq!(names, vec!["agent-server", "global-server"]);
}
/// A disabled agent-layer entry masks a same-named global entry — the
/// merged effective list must NOT include it. Fails against a variant
/// that surfaces the raw record layer instead of the merged result.
#[test]
fn buzz_agent_disabled_override_masks_inherited_server_in_surface() {
let mut record = agent_record();
record.persona_id = None;
record.mcp_servers = vec![mcp_server("shared", "", false)];
let personas: Vec<AgentDefinition> = vec![];
let global = crate::managed_agents::GlobalAgentConfig {
mcp_servers: vec![mcp_server("shared", "global-cmd", true)],
..Default::default()
};
let surface = resolve_config_surface(
record,
&personas,
known_acp_runtime("buzz-agent"),
"buzz-agent",
None,
&global,
);
assert!(surface.buzz_agent_mcp_servers.is_empty());
}
/// Non-buzz-agent runtimes (e.g. goose) must never populate
/// `buzz_agent_mcp_servers` — they surface their servers via `extensions`
/// instead. Fails against a variant that populates the field unconditionally.
#[test]
fn non_buzz_agent_runtime_leaves_mcp_surface_empty() {
let mut record = agent_record();
record.mcp_servers = vec![mcp_server("agent-server", "agent-cmd", true)];
let personas = vec![persona_with_model("persona-model")];
let surface = resolve_config_surface(
record,
&personas,
Some(goose_runtime()),
"goose",
None,
&Default::default(),
);
assert!(surface.buzz_agent_mcp_servers.is_empty());
}
// ── get_baked_build_env / is_secret_key tests ──────────────────────────
/// Build a `BakedEnvEntry` vec from a synthetic map, mirroring what
@@ -209,6 +209,10 @@ pub(crate) fn read_config_surface(
normalized,
advanced,
extensions,
// Populated by `resolve_config_surface` for the buzz-agent runtime only;
// this reader has no access to the global/persona MCP layers needed to
// compute the effective merge.
buzz_agent_mcp_servers: Vec::new(),
sources,
}
}
@@ -2,6 +2,8 @@ use std::collections::BTreeMap;
use serde::{Deserialize, Serialize};
use crate::managed_agents::types::McpServerConfig;
/// Where a config value came from — determines precedence and UI annotations.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
@@ -136,6 +138,12 @@ pub struct RuntimeConfigSurface {
pub normalized: NormalizedConfig,
pub advanced: Vec<ConfigField>,
pub extensions: Vec<ExtensionEntry>,
/// Effective (global < definition < agent merged, enabled-only) buzz-agent
/// MCP servers — "what runs." Populated only for the `buzz-agent` runtime;
/// empty for every other runtime, which surface their servers via
/// `extensions` instead.
#[serde(default)]
pub buzz_agent_mcp_servers: Vec<McpServerConfig>,
pub sources: ConfigSourceReport,
}