From 09a9263bffb0976e2bc7541f6fa8ac20c9180ed5 Mon Sep 17 00:00:00 2001
From: Will Pfleger <pfleger.will@gmail.com>
Date: Sun, 12 Jul 2026 20:09:29 -0400
Subject: [PATCH 1/5] feat(desktop): surface read-only MCP servers

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
---
 .../src/managed_agents/config_bridge/codex.rs |   2 +-
 .../src/managed_agents/config_bridge/goose.rs |   2 +-
 .../managed_agents/config_bridge/reader.rs    |  17 +++
 .../config_bridge/reader_tests.rs             |  75 ++++++++-
 .../src/managed_agents/config_bridge/types.rs |   2 +
 .../features/agents/ui/AgentConfigPanel.tsx   |  12 +-
 .../features/agents/ui/McpServersSection.tsx  | 144 ++++++++++++++++++
 desktop/src/shared/api/types.ts               |   4 +
 desktop/src/testing/e2eBridge.ts              |  23 +++
 9 files changed, 277 insertions(+), 4 deletions(-)
 create mode 100644 desktop/src/features/agents/ui/McpServersSection.tsx

diff --git a/desktop/src-tauri/src/managed_agents/config_bridge/codex.rs b/desktop/src-tauri/src/managed_agents/config_bridge/codex.rs
index 5ef8677c64..c7c7135ccb 100644
--- a/desktop/src-tauri/src/managed_agents/config_bridge/codex.rs
+++ b/desktop/src-tauri/src/managed_agents/config_bridge/codex.rs
@@ -122,7 +122,7 @@ fn toml_string(table: &toml::Table, key: &str) -> Option<String> {
         .map(str::to_string)
 }
 
-fn codex_config_path() -> Option<std::path::PathBuf> {
+pub(crate) fn codex_config_path() -> Option<std::path::PathBuf> {
     if let Ok(home) = std::env::var("CODEX_HOME") {
         return Some(std::path::PathBuf::from(home).join("config.toml"));
     }
diff --git a/desktop/src-tauri/src/managed_agents/config_bridge/goose.rs b/desktop/src-tauri/src/managed_agents/config_bridge/goose.rs
index 914acf3f59..fce54edc40 100644
--- a/desktop/src-tauri/src/managed_agents/config_bridge/goose.rs
+++ b/desktop/src-tauri/src/managed_agents/config_bridge/goose.rs
@@ -154,7 +154,7 @@ fn mapping_string(map: &serde_yaml::Mapping, key: &str) -> Option<String> {
         .map(str::to_string)
 }
 
-fn goose_config_path() -> Option<PathBuf> {
+pub(crate) fn goose_config_path() -> Option<PathBuf> {
     if let Ok(root) = std::env::var("GOOSE_PATH_ROOT") {
         return Some(PathBuf::from(root).join("config").join("config.yaml"));
     }
diff --git a/desktop/src-tauri/src/managed_agents/config_bridge/reader.rs b/desktop/src-tauri/src/managed_agents/config_bridge/reader.rs
index 3564b19423..372d2cfde1 100644
--- a/desktop/src-tauri/src/managed_agents/config_bridge/reader.rs
+++ b/desktop/src-tauri/src/managed_agents/config_bridge/reader.rs
@@ -167,6 +167,8 @@ pub(crate) fn read_config_surface(
     let config_file_path = runtime_meta
         .and_then(|m| m.config_file_path)
         .map(resolve_tilde);
+    let mcp_config_file_path = runtime_meta.and_then(mcp_config_file_path_for_runtime);
+    let extensions = file_config.extensions.clone();
 
     let sources = ConfigSourceReport {
         acp_native: if supports_acp_native {
@@ -197,6 +199,7 @@ pub(crate) fn read_config_surface(
             ConfigTierStatus::NotApplicable
         },
         config_file_path,
+        mcp_config_file_path,
     };
 
     RuntimeConfigSurface {
@@ -205,10 +208,24 @@ pub(crate) fn read_config_surface(
         is_pre_spawn,
         normalized,
         advanced,
+        extensions,
         sources,
     }
 }
 
+fn mcp_config_file_path_for_runtime(runtime: &KnownAcpRuntime) -> Option<String> {
+    match runtime.id {
+        "goose" => {
+            super::goose::goose_config_path().map(|path| path.to_string_lossy().into_owned())
+        }
+        "claude" => Some(resolve_tilde("~/.claude.json")),
+        "codex" => {
+            super::codex::codex_config_path().map(|path| path.to_string_lossy().into_owned())
+        }
+        _ => None,
+    }
+}
+
 #[allow(clippy::too_many_arguments)]
 fn build_model_field(
     record_model: &Option<String>,
diff --git a/desktop/src-tauri/src/managed_agents/config_bridge/reader_tests.rs b/desktop/src-tauri/src/managed_agents/config_bridge/reader_tests.rs
index 1444bc71d6..367168896a 100644
--- a/desktop/src-tauri/src/managed_agents/config_bridge/reader_tests.rs
+++ b/desktop/src-tauri/src/managed_agents/config_bridge/reader_tests.rs
@@ -2,12 +2,31 @@
 //! `reader.rs` stays under the 1000-line budget; `#[path]`-included from
 //! there).
 
-use std::collections::BTreeMap;
+use std::{collections::BTreeMap, sync::Mutex};
 
 use super::*;
 use crate::managed_agents::discovery::KnownAcpRuntime;
 use crate::managed_agents::types::ManagedAgentRecord;
 
+static GOOSE_PATH_ROOT_LOCK: Mutex<()> = Mutex::new(());
+
+fn with_goose_path_root<T>(value: Option<&str>, body: impl FnOnce() -> T) -> T {
+    let _guard = GOOSE_PATH_ROOT_LOCK
+        .lock()
+        .unwrap_or_else(|err| err.into_inner());
+    let prior = std::env::var_os("GOOSE_PATH_ROOT");
+    match value {
+        Some(value) => std::env::set_var("GOOSE_PATH_ROOT", value),
+        None => std::env::remove_var("GOOSE_PATH_ROOT"),
+    }
+    let output = body();
+    match prior {
+        Some(value) => std::env::set_var("GOOSE_PATH_ROOT", value),
+        None => std::env::remove_var("GOOSE_PATH_ROOT"),
+    }
+    output
+}
+
 fn test_runtime() -> &'static KnownAcpRuntime {
     &KnownAcpRuntime {
         id: "goose",
@@ -111,6 +130,60 @@ fn pre_spawn_surface_reports_pending_acp_tiers() {
     assert_eq!(surface.sources.env_vars, ConfigTierStatus::Available);
 }
 
+#[test]
+fn surface_reports_mcp_specific_config_path() {
+    let record = test_record();
+    let runtime = test_runtime();
+    let surface = with_goose_path_root(None, || {
+        read_config_surface(&record, Some(runtime), None, None)
+    });
+
+    let path = surface
+        .sources
+        .mcp_config_file_path
+        .expect("mcp config path");
+    assert!(
+        path.ends_with(".config/goose/config.yaml"),
+        "unexpected MCP config path: {path}"
+    );
+}
+
+#[test]
+fn goose_mcp_config_path_follows_path_root_override() {
+    let record = test_record();
+    let runtime = test_runtime();
+    let surface = with_goose_path_root(Some("/tmp/buzz-goose-root"), || {
+        read_config_surface(&record, Some(runtime), None, None)
+    });
+
+    assert_eq!(
+        surface.sources.mcp_config_file_path.as_deref(),
+        Some("/tmp/buzz-goose-root/config/config.yaml")
+    );
+}
+
+#[test]
+fn claude_surface_uses_mcp_config_path_not_settings_path() {
+    let record = test_record();
+    let runtime = &KnownAcpRuntime {
+        id: "claude",
+        config_file_path: Some("~/.claude/settings.json"),
+        ..*test_runtime()
+    };
+    let surface = read_config_surface(&record, Some(runtime), None, None);
+
+    assert!(surface
+        .sources
+        .config_file_path
+        .as_deref()
+        .is_some_and(|path| path.ends_with(".claude/settings.json")));
+    assert!(surface
+        .sources
+        .mcp_config_file_path
+        .as_deref()
+        .is_some_and(|path| path.ends_with(".claude.json")));
+}
+
 #[test]
 fn record_model_overrides_file_model() {
     let mut record = test_record();
diff --git a/desktop/src-tauri/src/managed_agents/config_bridge/types.rs b/desktop/src-tauri/src/managed_agents/config_bridge/types.rs
index 17589bad53..15ccb718e7 100644
--- a/desktop/src-tauri/src/managed_agents/config_bridge/types.rs
+++ b/desktop/src-tauri/src/managed_agents/config_bridge/types.rs
@@ -123,6 +123,7 @@ pub struct ConfigSourceReport {
     pub env_vars: ConfigTierStatus,
     pub config_file: ConfigTierStatus,
     pub config_file_path: Option<String>,
+    pub mcp_config_file_path: Option<String>,
 }
 
 /// Full config surface returned to the frontend.
@@ -134,6 +135,7 @@ pub struct RuntimeConfigSurface {
     pub is_pre_spawn: bool,
     pub normalized: NormalizedConfig,
     pub advanced: Vec<ConfigField>,
+    pub extensions: Vec<ExtensionEntry>,
     pub sources: ConfigSourceReport,
 }
 
diff --git a/desktop/src/features/agents/ui/AgentConfigPanel.tsx b/desktop/src/features/agents/ui/AgentConfigPanel.tsx
index df314a6bf8..950df90cb9 100644
--- a/desktop/src/features/agents/ui/AgentConfigPanel.tsx
+++ b/desktop/src/features/agents/ui/AgentConfigPanel.tsx
@@ -17,6 +17,7 @@ import { useAgentConfigSurface } from "../hooks";
 import { cn } from "@/shared/lib/cn";
 import { copyTextToClipboard } from "@/shared/lib/clipboard";
 import { Spinner } from "@/shared/ui/spinner";
+import { McpServersSection } from "./McpServersSection";
 import type {
   ConfigField,
   ConfigOrigin,
@@ -365,8 +366,10 @@ export function AgentConfigPanel({
     );
   }
 
-  const { normalized, advanced, sources, isPreSpawn } = data;
+  const { normalized, advanced, extensions, runtimeId, sources, isPreSpawn } =
+    data;
   const configFilePath = sources.configFilePath;
+  const mcpConfigFilePath = sources.mcpConfigFilePath;
 
   const normalizedEntries = (
     Object.entries(normalized) as [
@@ -416,6 +419,13 @@ export function AgentConfigPanel({
         )}
       </div>
 
+      <McpServersSection
+        configFilePath={mcpConfigFilePath}
+        extensions={extensions}
+        runtimeId={runtimeId}
+        variant={advancedMode === "flat" ? "profile" : "compact"}
+      />
+
       {advanced.length > 0 && advancedMode === "flat" ? (
         <div className="divide-y divide-border/50 border-t border-border/50">
           {advanced.map((field) => (
diff --git a/desktop/src/features/agents/ui/McpServersSection.tsx b/desktop/src/features/agents/ui/McpServersSection.tsx
new file mode 100644
index 0000000000..521d80979f
--- /dev/null
+++ b/desktop/src/features/agents/ui/McpServersSection.tsx
@@ -0,0 +1,144 @@
+import * as React from "react";
+import { CheckCircle2, CircleSlash, FolderOpen, Plug } from "lucide-react";
+import { revealItemInDir } from "@tauri-apps/plugin-opener";
+import type { ExtensionEntry } from "@/shared/api/types";
+import { cn } from "@/shared/lib/cn";
+
+type McpServersSectionProps = {
+  configFilePath: string | null;
+  extensions: ExtensionEntry[];
+  runtimeId: string | null;
+  variant?: "compact" | "profile";
+  buzzAgentSlot?: React.ReactNode;
+};
+
+export function McpServersSection({
+  buzzAgentSlot,
+  configFilePath,
+  extensions,
+  runtimeId,
+  variant = "compact",
+}: McpServersSectionProps) {
+  const isBuzzAgent = runtimeId === "buzz-agent";
+  const shouldRender =
+    isBuzzAgent || extensions.length > 0 || configFilePath !== null;
+
+  const revealConfigFile = React.useCallback(() => {
+    if (!configFilePath) {
+      return;
+    }
+    void revealItemInDir(configFilePath);
+  }, [configFilePath]);
+
+  if (!shouldRender) {
+    return null;
+  }
+
+  return (
+    <div
+      className={cn(
+        "border-t border-border/50",
+        variant === "compact" ? "mt-3 pt-2" : "divide-y divide-border/50",
+      )}
+    >
+      <div
+        className={cn(
+          "flex min-w-0 items-start gap-3",
+          variant === "compact" ? "py-2" : "px-4 py-3",
+        )}
+      >
+        <span className="flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-muted/60">
+          <Plug className="h-4 w-4 text-muted-foreground" />
+        </span>
+        <div className="min-w-0 flex-1">
+          <div className="flex min-w-0 items-center justify-between gap-3">
+            <div className="min-w-0">
+              <div className="truncate text-xs font-medium text-foreground">
+                MCP Servers
+              </div>
+              <div className="mt-0.5 truncate text-sm text-muted-foreground">
+                {extensions.length > 0
+                  ? `${extensions.length} configured`
+                  : isBuzzAgent
+                    ? "No custom servers configured"
+                    : "No servers found"}
+              </div>
+            </div>
+            {configFilePath ? (
+              <button
+                aria-label="Reveal MCP config file"
+                className="inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring"
+                onClick={revealConfigFile}
+                title="Reveal MCP config file"
+                type="button"
+              >
+                <FolderOpen className="h-4 w-4" />
+              </button>
+            ) : null}
+          </div>
+          {configFilePath ? (
+            <div className="mt-1 truncate text-2xs text-muted-foreground/70">
+              Edit in {configFilePath}
+            </div>
+          ) : null}
+        </div>
+      </div>
+
+      {isBuzzAgent && buzzAgentSlot ? buzzAgentSlot : null}
+
+      {extensions.length > 0 ? (
+        <div
+          className={cn(
+            "divide-y divide-border/50",
+            variant === "compact" ? "mt-1" : "",
+          )}
+        >
+          {extensions.map((extension) => (
+            <McpServerRow
+              extension={extension}
+              key={`${extension.kind}:${extension.name}`}
+              variant={variant}
+            />
+          ))}
+        </div>
+      ) : null}
+    </div>
+  );
+}
+
+function McpServerRow({
+  extension,
+  variant,
+}: {
+  extension: ExtensionEntry;
+  variant: "compact" | "profile";
+}) {
+  const StatusIcon = extension.enabled ? CheckCircle2 : CircleSlash;
+
+  return (
+    <div
+      className={cn(
+        "flex min-w-0 items-center gap-3",
+        variant === "compact" ? "py-2" : "px-4 py-3",
+      )}
+    >
+      <span className="flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-muted/50">
+        <StatusIcon
+          className={cn(
+            "h-4 w-4",
+            extension.enabled ? "text-emerald-600" : "text-muted-foreground",
+          )}
+        />
+      </span>
+      <span className="min-w-0 flex-1">
+        <span className="block truncate text-sm font-medium text-foreground">
+          {extension.name}
+        </span>
+        <span className="mt-0.5 block truncate text-2xs text-muted-foreground/70">
+          {extension.kind}
+          {extension.enabled ? " enabled" : " disabled"}
+        </span>
+      </span>
+    </div>
+  );
+}
diff --git a/desktop/src/shared/api/types.ts b/desktop/src/shared/api/types.ts
index 2156325634..5066b2a370 100644
--- a/desktop/src/shared/api/types.ts
+++ b/desktop/src/shared/api/types.ts
@@ -679,8 +679,11 @@ export type ConfigSourceReport = {
   envVars: ConfigTierStatus;
   configFile: ConfigTierStatus;
   configFilePath: string | null;
+  mcpConfigFilePath: string | null;
 };
 
+export type ExtensionEntry = { name: string; kind: string; enabled: boolean };
+
 export type NormalizedConfig = {
   model: NormalizedField | null;
   provider: NormalizedField | null;
@@ -697,6 +700,7 @@ export type RuntimeConfigSurface = {
   isPreSpawn: boolean;
   normalized: NormalizedConfig;
   advanced: ConfigField[];
+  extensions: ExtensionEntry[];
   sources: ConfigSourceReport;
 };
 
diff --git a/desktop/src/testing/e2eBridge.ts b/desktop/src/testing/e2eBridge.ts
index 9b96c36d35..b8b244bb72 100644
--- a/desktop/src/testing/e2eBridge.ts
+++ b/desktop/src/testing/e2eBridge.ts
@@ -1265,6 +1265,7 @@ function buildMockConfigSurface(pubkey: string): {
   isPreSpawn: boolean;
   normalized: Record<string, unknown>;
   advanced: unknown[];
+  extensions: unknown[];
   sources: Record<string, unknown>;
 } {
   // Goose running — mixed origins, override on model
@@ -1350,12 +1351,18 @@ function buildMockConfigSurface(pubkey: string): {
         },
       },
     ],
+    extensions: [
+      { name: "developer", kind: "stdio", enabled: true },
+      { name: "web_search", kind: "stdio", enabled: true },
+      { name: "memory", kind: "stdio", enabled: false },
+    ],
     sources: {
       acpNative: "available",
       acpConfigOptions: "available",
       envVars: "available",
       configFile: "available",
       configFilePath: "~/.config/goose/config.yaml",
+      mcpConfigFilePath: "~/.config/goose/config.yaml",
     },
   };
 
@@ -1409,12 +1416,17 @@ function buildMockConfigSurface(pubkey: string): {
       systemPrompt: null,
     },
     advanced: [],
+    extensions: [
+      { name: "filesystem", kind: "mcp", enabled: true },
+      { name: "github", kind: "mcp", enabled: true },
+    ],
     sources: {
       acpNative: "available",
       acpConfigOptions: "available",
       envVars: "notApplicable",
       configFile: "available",
       configFilePath: "~/.claude/settings.json",
+      mcpConfigFilePath: "~/.claude.json",
     },
   };
 
@@ -1464,12 +1476,14 @@ function buildMockConfigSurface(pubkey: string): {
       systemPrompt: null,
     },
     advanced: [],
+    extensions: [{ name: "developer", kind: "stdio", enabled: true }],
     sources: {
       acpNative: "pending",
       acpConfigOptions: "pending",
       envVars: "available",
       configFile: "available",
       configFilePath: "~/.config/goose/config.yaml",
+      mcpConfigFilePath: "~/.config/goose/config.yaml",
     },
   };
 
@@ -1538,12 +1552,17 @@ function buildMockConfigSurface(pubkey: string): {
         writeVia: { type: "respawnWithEnvVar", envKey: "GOOSE_SANDBOX_MODE" },
       },
     ],
+    extensions: [
+      { name: "filesystem", kind: "mcp", enabled: true },
+      { name: "github", kind: "mcp", enabled: true },
+    ],
     sources: {
       acpNative: "notApplicable",
       acpConfigOptions: "notApplicable",
       envVars: "available",
       configFile: "available",
       configFilePath: "~/.codex/config.toml",
+      mcpConfigFilePath: "~/.codex/config.toml",
     },
   };
 
@@ -1595,12 +1614,14 @@ function buildMockConfigSurface(pubkey: string): {
       systemPrompt: null,
     },
     advanced: [],
+    extensions: [{ name: "web_search", kind: "stdio", enabled: true }],
     sources: {
       acpNative: "available",
       acpConfigOptions: "available",
       envVars: "available",
       configFile: "available",
       configFilePath: "~/.config/goose/config.yaml",
+      mcpConfigFilePath: "~/.config/goose/config.yaml",
     },
   };
 
@@ -1653,12 +1674,14 @@ function buildMockConfigSurface(pubkey: string): {
       systemPrompt: null,
     },
     advanced: [],
+    extensions: [],
     sources: {
       acpNative: "available",
       acpConfigOptions: "available",
       envVars: "available",
       configFile: "available",
       configFilePath: "~/.config/goose/config.yaml",
+      mcpConfigFilePath: "~/.config/goose/config.yaml",
     },
   };
 

From 42959fa8d1f2487f261987b8542952a418d5a946 Mon Sep 17 00:00:00 2001
From: Will Pfleger <pfleger.will@gmail.com>
Date: Sun, 12 Jul 2026 21:14:40 -0400
Subject: [PATCH 2/5] test(desktop): make MCP config path assertions portable

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
---
 desktop/scripts/check-file-sizes.mjs             |  3 ++-
 .../managed_agents/config_bridge/reader_tests.rs | 16 ++++++++++++----
 2 files changed, 14 insertions(+), 5 deletions(-)

diff --git a/desktop/scripts/check-file-sizes.mjs b/desktop/scripts/check-file-sizes.mjs
index f8746b1d8b..2d59948983 100644
--- a/desktop/scripts/check-file-sizes.mjs
+++ b/desktop/scripts/check-file-sizes.mjs
@@ -198,7 +198,8 @@ const overrides = new Map([
   // loginHint fields on AcpRuntimeCatalogEntry (+14 lines). Load-bearing new feature.
   // agent-lifecycle-fixes: GlobalAgentConfigSaveResult type grows with
   // failed_restart_count (+2 lines). Queued to split with the rest of this list.
-  ["src/shared/api/types.ts", 1030],
+  // mcp-readonly-view rebase: PR2 MCP config surface FE-type fields force +1 over the grandfathered ceiling.
+  ["src/shared/api/types.ts", 1031],
   // readiness-gate: PersonaDialog.tsx threads computeLocalModeGate +
   // requiredCredentialEnvKeys + RequiredFieldLabel so the "New agent" dialog
   // shows required markers and credential amber rows (parity with
diff --git a/desktop/src-tauri/src/managed_agents/config_bridge/reader_tests.rs b/desktop/src-tauri/src/managed_agents/config_bridge/reader_tests.rs
index 367168896a..b7829ff111 100644
--- a/desktop/src-tauri/src/managed_agents/config_bridge/reader_tests.rs
+++ b/desktop/src-tauri/src/managed_agents/config_bridge/reader_tests.rs
@@ -2,7 +2,7 @@
 //! `reader.rs` stays under the 1000-line budget; `#[path]`-included from
 //! there).
 
-use std::{collections::BTreeMap, sync::Mutex};
+use std::{collections::BTreeMap, path::Path, sync::Mutex};
 
 use super::*;
 use crate::managed_agents::discovery::KnownAcpRuntime;
@@ -142,8 +142,9 @@ fn surface_reports_mcp_specific_config_path() {
         .sources
         .mcp_config_file_path
         .expect("mcp config path");
+    let expected_suffix = Path::new(".config").join("goose").join("config.yaml");
     assert!(
-        path.ends_with(".config/goose/config.yaml"),
+        Path::new(&path).ends_with(&expected_suffix),
         "unexpected MCP config path: {path}"
     );
 }
@@ -156,9 +157,16 @@ fn goose_mcp_config_path_follows_path_root_override() {
         read_config_surface(&record, Some(runtime), None, None)
     });
 
+    let expected_path = Path::new("/tmp/buzz-goose-root")
+        .join("config")
+        .join("config.yaml");
     assert_eq!(
-        surface.sources.mcp_config_file_path.as_deref(),
-        Some("/tmp/buzz-goose-root/config/config.yaml")
+        surface
+            .sources
+            .mcp_config_file_path
+            .as_deref()
+            .map(Path::new),
+        Some(expected_path.as_path())
     );
 }
 

From 0ea6188a6d7d2a058389fb9990b1eb78089dd38a Mon Sep 17 00:00:00 2001
From: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7
 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
Date: Mon, 13 Jul 2026 13:44:51 -0400
Subject: [PATCH 3/5] fix(desktop): trim duplicate MCP server rows

Render only server rows and retain the buzz-agent empty-state message.
Keep the screenshot fixture aligned with parser output so it cannot recreate duplicate rows.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
---
 .../features/agents/ui/AgentConfigPanel.tsx   |  2 -
 .../features/agents/ui/McpServersSection.tsx  | 79 ++++---------------
 desktop/src/testing/e2eBridge.ts              | 56 ++++++-------
 .../e2e/config-bridge-screenshots.spec.ts     | 33 +++++++-
 4 files changed, 69 insertions(+), 101 deletions(-)

diff --git a/desktop/src/features/agents/ui/AgentConfigPanel.tsx b/desktop/src/features/agents/ui/AgentConfigPanel.tsx
index 950df90cb9..792db2e587 100644
--- a/desktop/src/features/agents/ui/AgentConfigPanel.tsx
+++ b/desktop/src/features/agents/ui/AgentConfigPanel.tsx
@@ -369,7 +369,6 @@ export function AgentConfigPanel({
   const { normalized, advanced, extensions, runtimeId, sources, isPreSpawn } =
     data;
   const configFilePath = sources.configFilePath;
-  const mcpConfigFilePath = sources.mcpConfigFilePath;
 
   const normalizedEntries = (
     Object.entries(normalized) as [
@@ -420,7 +419,6 @@ export function AgentConfigPanel({
       </div>
 
       <McpServersSection
-        configFilePath={mcpConfigFilePath}
         extensions={extensions}
         runtimeId={runtimeId}
         variant={advancedMode === "flat" ? "profile" : "compact"}
diff --git a/desktop/src/features/agents/ui/McpServersSection.tsx b/desktop/src/features/agents/ui/McpServersSection.tsx
index 521d80979f..b4d92af1ce 100644
--- a/desktop/src/features/agents/ui/McpServersSection.tsx
+++ b/desktop/src/features/agents/ui/McpServersSection.tsx
@@ -1,11 +1,9 @@
-import * as React from "react";
-import { CheckCircle2, CircleSlash, FolderOpen, Plug } from "lucide-react";
-import { revealItemInDir } from "@tauri-apps/plugin-opener";
+import type * as React from "react";
+import { CheckCircle2, CircleSlash } from "lucide-react";
 import type { ExtensionEntry } from "@/shared/api/types";
 import { cn } from "@/shared/lib/cn";
 
 type McpServersSectionProps = {
-  configFilePath: string | null;
   extensions: ExtensionEntry[];
   runtimeId: string | null;
   variant?: "compact" | "profile";
@@ -14,23 +12,13 @@ type McpServersSectionProps = {
 
 export function McpServersSection({
   buzzAgentSlot,
-  configFilePath,
   extensions,
   runtimeId,
   variant = "compact",
 }: McpServersSectionProps) {
   const isBuzzAgent = runtimeId === "buzz-agent";
-  const shouldRender =
-    isBuzzAgent || extensions.length > 0 || configFilePath !== null;
 
-  const revealConfigFile = React.useCallback(() => {
-    if (!configFilePath) {
-      return;
-    }
-    void revealItemInDir(configFilePath);
-  }, [configFilePath]);
-
-  if (!shouldRender) {
+  if (!isBuzzAgent && extensions.length === 0) {
     return null;
   }
 
@@ -41,58 +29,10 @@ export function McpServersSection({
         variant === "compact" ? "mt-3 pt-2" : "divide-y divide-border/50",
       )}
     >
-      <div
-        className={cn(
-          "flex min-w-0 items-start gap-3",
-          variant === "compact" ? "py-2" : "px-4 py-3",
-        )}
-      >
-        <span className="flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-muted/60">
-          <Plug className="h-4 w-4 text-muted-foreground" />
-        </span>
-        <div className="min-w-0 flex-1">
-          <div className="flex min-w-0 items-center justify-between gap-3">
-            <div className="min-w-0">
-              <div className="truncate text-xs font-medium text-foreground">
-                MCP Servers
-              </div>
-              <div className="mt-0.5 truncate text-sm text-muted-foreground">
-                {extensions.length > 0
-                  ? `${extensions.length} configured`
-                  : isBuzzAgent
-                    ? "No custom servers configured"
-                    : "No servers found"}
-              </div>
-            </div>
-            {configFilePath ? (
-              <button
-                aria-label="Reveal MCP config file"
-                className="inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring"
-                onClick={revealConfigFile}
-                title="Reveal MCP config file"
-                type="button"
-              >
-                <FolderOpen className="h-4 w-4" />
-              </button>
-            ) : null}
-          </div>
-          {configFilePath ? (
-            <div className="mt-1 truncate text-2xs text-muted-foreground/70">
-              Edit in {configFilePath}
-            </div>
-          ) : null}
-        </div>
-      </div>
-
       {isBuzzAgent && buzzAgentSlot ? buzzAgentSlot : null}
 
       {extensions.length > 0 ? (
-        <div
-          className={cn(
-            "divide-y divide-border/50",
-            variant === "compact" ? "mt-1" : "",
-          )}
-        >
+        <div className="divide-y divide-border/50">
           {extensions.map((extension) => (
             <McpServerRow
               extension={extension}
@@ -101,7 +41,16 @@ export function McpServersSection({
             />
           ))}
         </div>
-      ) : null}
+      ) : (
+        <p
+          className={cn(
+            "text-sm text-muted-foreground",
+            variant === "compact" ? "py-2" : "px-4 py-3",
+          )}
+        >
+          No custom servers configured
+        </p>
+      )}
     </div>
   );
 }
diff --git a/desktop/src/testing/e2eBridge.ts b/desktop/src/testing/e2eBridge.ts
index b8b244bb72..6cc0cace1c 100644
--- a/desktop/src/testing/e2eBridge.ts
+++ b/desktop/src/testing/e2eBridge.ts
@@ -1318,37 +1318,12 @@ function buildMockConfigSurface(pubkey: string): {
     },
     advanced: [
       {
-        key: "extensions.developer",
-        label: "Extension: developer",
-        value: "enabled",
-        origin: "configFile",
-        schemaType: { type: "enum", options: ["enabled", "disabled"] },
-        writeVia: {
-          type: "gooseNativeConfigWrite",
-          configKey: "goose.extensions.developer",
-        },
-      },
-      {
-        key: "extensions.web_search",
-        label: "Extension: web_search",
-        value: "enabled",
-        origin: "configFile",
-        schemaType: { type: "enum", options: ["enabled", "disabled"] },
-        writeVia: {
-          type: "gooseNativeConfigWrite",
-          configKey: "goose.extensions.web_search",
-        },
-      },
-      {
-        key: "extensions.memory",
-        label: "Extension: memory",
-        value: "disabled",
+        key: "active_provider",
+        label: "active_provider",
+        value: "openai",
         origin: "configFile",
-        schemaType: { type: "enum", options: ["enabled", "disabled"] },
-        writeVia: {
-          type: "gooseNativeConfigWrite",
-          configKey: "goose.extensions.memory",
-        },
+        schemaType: { type: "string" },
+        writeVia: { type: "readOnly" },
       },
     ],
     extensions: [
@@ -1685,10 +1660,25 @@ function buildMockConfigSurface(pubkey: string): {
     },
   };
 
-  // Map well-known test pubkeys to specific fixtures
-  // Synthetic agent for the multi-origin provenance showcase (not a TEST_IDENTITY).
+  const buzzAgentSurface = {
+    ...gooseSurface,
+    runtimeId: "buzz-agent",
+    runtimeLabel: "Buzz Agent",
+    advanced: [],
+    extensions: [],
+    sources: {
+      ...gooseSurface.sources,
+      configFilePath: null,
+      mcpConfigFilePath: null,
+    },
+  };
+
+  // Map well-known test pubkeys to specific fixtures.
+  // Synthetic agents are intentionally not TEST_IDENTITIES.
   const PUBKEY_MULTI_ORIGIN =
     "abc1230000000000000000000000000000000000000000000000000000000def";
+  const PUBKEY_BUZZ_AGENT =
+    "b0220000000000000000000000000000000000000000000000000000000000a9";
 
   switch (pubkey) {
     case ALICE_PUBKEY:
@@ -1701,6 +1691,8 @@ function buildMockConfigSurface(pubkey: string): {
       return runtimeOverrideSurface;
     case PUBKEY_MULTI_ORIGIN:
       return multiOriginSurface;
+    case PUBKEY_BUZZ_AGENT:
+      return buzzAgentSurface;
     default:
       return gooseSurface;
   }
diff --git a/desktop/tests/e2e/config-bridge-screenshots.spec.ts b/desktop/tests/e2e/config-bridge-screenshots.spec.ts
index 415d55a7fb..754c79f9c5 100644
--- a/desktop/tests/e2e/config-bridge-screenshots.spec.ts
+++ b/desktop/tests/e2e/config-bridge-screenshots.spec.ts
@@ -12,6 +12,8 @@ const RUNTIME_OVERRIDE_PUBKEY = TEST_IDENTITIES.outsider.pubkey;
 // (matches PUBKEY_MULTI_ORIGIN in e2eBridge buildMockConfigSurface).
 const MULTI_ORIGIN_PUBKEY =
   "abc1230000000000000000000000000000000000000000000000000000000def";
+const BUZZ_AGENT_PUBKEY =
+  "b0220000000000000000000000000000000000000000000000000000000000a9";
 
 const MANAGED_AGENTS = [
   {
@@ -244,13 +246,40 @@ test.describe("config bridge screenshots", () => {
     const panel = await openAgentProfileFromChannel(page, "Goose Agent");
 
     // Advanced runtime fields render directly in the profile panel's flat list.
-    await expect(panel.getByText("Extension: developer")).toBeVisible();
+    await expect(panel.getByText("active_provider")).toBeVisible();
+    // MCP servers render once each, without the removed summary row.
+    await expect(panel.getByText("developer", { exact: true })).toHaveCount(1);
+    await expect(panel.getByText("MCP Servers", { exact: true })).toHaveCount(
+      0,
+    );
     await settleAnimations(panel);
 
     await panel.screenshot({ path: `${SHOTS}/05-advanced-expanded.png` });
   });
 
-  test("06 — profile side panel — Configuration section", async ({ page }) => {
+  test("06 — buzz-agent empty MCP servers", async ({ page }) => {
+    await installMockBridge(page, {
+      managedAgents: [
+        {
+          pubkey: BUZZ_AGENT_PUBKEY,
+          name: "Buzz Agent",
+          status: "running" as const,
+          channelNames: ["agents"],
+        },
+      ],
+    });
+
+    const panel = await openAgentProfileFromChannel(page, "Buzz Agent");
+
+    await expect(
+      panel.getByText("No custom servers configured", { exact: true }),
+    ).toBeVisible();
+    await expect(panel.getByText("MCP Servers", { exact: true })).toHaveCount(
+      0,
+    );
+  });
+
+  test("07 — profile side panel — Configuration section", async ({ page }) => {
     // charlie (554cef…) is the well-known test pubkey that the mock bridge
     // seeds as a bot owned by the test viewer, so isBot + isOwner + managedAgent
     // are all true — the Configuration section renders in the profile panel.

From ec6e3d9d82b0f91a5f8007745006dd74b4cfd01d Mon Sep 17 00:00:00 2001
From: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7
 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
Date: Mon, 13 Jul 2026 14:38:55 -0400
Subject: [PATCH 4/5] fix(desktop): restore the MCP Servers group label

The compact rows expose raw transport kinds, so restore a text-only group label that identifies them as MCP servers.

Keep screenshot coverage aligned for populated and empty buzz-agent sections.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
---
 desktop/src/features/agents/ui/McpServersSection.tsx | 9 +++++++++
 desktop/tests/e2e/config-bridge-screenshots.spec.ts  | 6 +++---
 2 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/desktop/src/features/agents/ui/McpServersSection.tsx b/desktop/src/features/agents/ui/McpServersSection.tsx
index b4d92af1ce..3e6c93ca5d 100644
--- a/desktop/src/features/agents/ui/McpServersSection.tsx
+++ b/desktop/src/features/agents/ui/McpServersSection.tsx
@@ -29,6 +29,15 @@ export function McpServersSection({
         variant === "compact" ? "mt-3 pt-2" : "divide-y divide-border/50",
       )}
     >
+      <p
+        className={cn(
+          "text-xs font-medium text-foreground",
+          variant === "compact" ? "py-2" : "px-4 py-3",
+        )}
+      >
+        MCP Servers
+      </p>
+
       {isBuzzAgent && buzzAgentSlot ? buzzAgentSlot : null}
 
       {extensions.length > 0 ? (
diff --git a/desktop/tests/e2e/config-bridge-screenshots.spec.ts b/desktop/tests/e2e/config-bridge-screenshots.spec.ts
index 754c79f9c5..ef20e7b4a6 100644
--- a/desktop/tests/e2e/config-bridge-screenshots.spec.ts
+++ b/desktop/tests/e2e/config-bridge-screenshots.spec.ts
@@ -247,10 +247,10 @@ test.describe("config bridge screenshots", () => {
 
     // Advanced runtime fields render directly in the profile panel's flat list.
     await expect(panel.getByText("active_provider")).toBeVisible();
-    // MCP servers render once each, without the removed summary row.
+    // MCP servers render once each under their group label.
     await expect(panel.getByText("developer", { exact: true })).toHaveCount(1);
     await expect(panel.getByText("MCP Servers", { exact: true })).toHaveCount(
-      0,
+      1,
     );
     await settleAnimations(panel);
 
@@ -275,7 +275,7 @@ test.describe("config bridge screenshots", () => {
       panel.getByText("No custom servers configured", { exact: true }),
     ).toBeVisible();
     await expect(panel.getByText("MCP Servers", { exact: true })).toHaveCount(
-      0,
+      1,
     );
   });
 

From 2a801018079f31fbbabbc739c42c5f8f0b9c050e Mon Sep 17 00:00:00 2001
From: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7
 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
Date: Mon, 13 Jul 2026 15:40:53 -0400
Subject: [PATCH 5/5] fix(desktop): label the flat advanced-fields block as
 Advanced

The flat profile panel's advanced-fields block (active_provider etc.)
rendered directly beneath the new MCP Servers label with no header of
its own, so it visually read as part of the MCP Servers group. Add a
matching text-only 'Advanced' header so the block reads as its own
group, mirroring the label style already used for MCP Servers.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
---
 desktop/src/features/agents/ui/AgentConfigPanel.tsx | 3 +++
 desktop/tests/e2e/config-bridge-screenshots.spec.ts | 4 +++-
 2 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/desktop/src/features/agents/ui/AgentConfigPanel.tsx b/desktop/src/features/agents/ui/AgentConfigPanel.tsx
index 792db2e587..26ecab3c04 100644
--- a/desktop/src/features/agents/ui/AgentConfigPanel.tsx
+++ b/desktop/src/features/agents/ui/AgentConfigPanel.tsx
@@ -426,6 +426,9 @@ export function AgentConfigPanel({
 
       {advanced.length > 0 && advancedMode === "flat" ? (
         <div className="divide-y divide-border/50 border-t border-border/50">
+          <p className="px-4 py-3 text-xs font-medium text-foreground">
+            Advanced
+          </p>
           {advanced.map((field) => (
             <AdvancedRow
               key={field.key}
diff --git a/desktop/tests/e2e/config-bridge-screenshots.spec.ts b/desktop/tests/e2e/config-bridge-screenshots.spec.ts
index ef20e7b4a6..11d4a73fa3 100644
--- a/desktop/tests/e2e/config-bridge-screenshots.spec.ts
+++ b/desktop/tests/e2e/config-bridge-screenshots.spec.ts
@@ -245,8 +245,10 @@ test.describe("config bridge screenshots", () => {
 
     const panel = await openAgentProfileFromChannel(page, "Goose Agent");
 
-    // Advanced runtime fields render directly in the profile panel's flat list.
+    // Advanced runtime fields render directly in the profile panel's flat list,
+    // grouped under their own "Advanced" header.
     await expect(panel.getByText("active_provider")).toBeVisible();
+    await expect(panel.getByText("Advanced", { exact: true })).toHaveCount(1);
     // MCP servers render once each under their group label.
     await expect(panel.getByText("developer", { exact: true })).toHaveCount(1);
     await expect(panel.getByText("MCP Servers", { exact: true })).toHaveCount(
