ui: Remove recommended MCP Servers + improve MCP Servers Settings UI/UX (#25535)
* fix: drop MCP recommendations auto-popup and silent preloads * feat: Add consent-driven MCP recommendations inside Add New Server dialog * refactor: Drop mcpDefaultServerOverrides for mcpServers[i].enabled * feat: Center the empty state on the MCP settings page * fix: keep existing MCP cards intact when adding a new server * fix: keep MCP cards stable when a new server is added * refactor: keep MCP server list in config insertion order * feat: shrink the recommended-MCP cards to two tools each and fit them in one row * feat: make recommended MCP cards click-to-fill and tighten copy * feat: highlight the selected MCP recommendation and stop auto-focus on dialog open * feat: derive MCP recommendation selection from the form URL * fix: make recommendation MCP cards fully non-focusable * fix: redirect focus from first card to the URL input on consent * chore: Formatting * refactor: Remove Recommended MCP Servers completely * fix: Preserve legacy mcpDefaultServerOverrides key after merge migration for downgrade compatibility
This commit is contained in:
@@ -522,7 +522,7 @@ const mcpDefaultEnabledMigration: Migration = {
|
||||
const config = configRaw ? JSON.parse(configRaw) : {};
|
||||
|
||||
// Don't overwrite an existing config entry — current data wins.
|
||||
if (SETTINGS_KEYS.MCP_DEFAULT_SERVER_OVERRIDES in config) {
|
||||
if (MCP_DEFAULT_OVERRIDES_LEGACY_KEY in config) {
|
||||
if (import.meta.env.DEV && import.meta.env.VITE_DEBUG)
|
||||
console.log('[Migration] MCP default enabled: config already has overrides, skipping');
|
||||
return;
|
||||
@@ -543,7 +543,7 @@ const mcpDefaultEnabledMigration: Migration = {
|
||||
return;
|
||||
}
|
||||
|
||||
config[SETTINGS_KEYS.MCP_DEFAULT_SERVER_OVERRIDES] = raw;
|
||||
config[MCP_DEFAULT_OVERRIDES_LEGACY_KEY] = raw;
|
||||
localStorage.setItem(CONFIG_LOCALSTORAGE_KEY, JSON.stringify(config));
|
||||
|
||||
if (import.meta.env.DEV && import.meta.env.VITE_DEBUG)
|
||||
@@ -586,6 +586,83 @@ const configTypesMigration: Migration = {
|
||||
}
|
||||
};
|
||||
|
||||
const MCP_DEFAULT_OVERRIDES_LEGACY_KEY = `${STORAGE_APP_NAME}.mcpDefaultServerOverrides`;
|
||||
const MCP_DEFAULT_OVERRIDES_MERGE_MIGRATION_ID = 'mcp-default-overrides-merge-v1';
|
||||
|
||||
/**
|
||||
* Folds `mcpDefaultServerOverrides` (the legacy "default for new chats" list,
|
||||
* JSON-encoded as `[{ serverId, enabled }, ...]`) into `mcpServers[i].enabled`.
|
||||
* The legacy override key is intentionally left in the config so a downgrade
|
||||
* keeps reading it. Runs after `mcpDefaultEnabledMigration` so any legacy
|
||||
* standalone overrides are already inside the config.
|
||||
*/
|
||||
const mcpDefaultOverridesMergeMigration: Migration = {
|
||||
id: MCP_DEFAULT_OVERRIDES_MERGE_MIGRATION_ID,
|
||||
description:
|
||||
'Merge mcpDefaultServerOverrides entries onto mcpServers[i].enabled (preserves legacy key)',
|
||||
|
||||
async run(): Promise<void> {
|
||||
const configRaw = localStorage.getItem(CONFIG_LOCALSTORAGE_KEY);
|
||||
if (configRaw === null) return;
|
||||
|
||||
const config = JSON.parse(configRaw);
|
||||
const raw = config[MCP_DEFAULT_OVERRIDES_LEGACY_KEY];
|
||||
|
||||
if (typeof raw !== 'string' || raw.length === 0) {
|
||||
if (import.meta.env.DEV && import.meta.env.VITE_DEBUG)
|
||||
console.log('[Migration] MCP default overrides merge: nothing to merge');
|
||||
return;
|
||||
}
|
||||
|
||||
let overrides: { serverId: string; enabled: boolean }[];
|
||||
try {
|
||||
const parsed = JSON.parse(raw);
|
||||
if (!Array.isArray(parsed)) return;
|
||||
overrides = parsed.filter(
|
||||
(o) =>
|
||||
typeof o === 'object' &&
|
||||
o !== null &&
|
||||
typeof (o as Record<string, unknown>).serverId === 'string' &&
|
||||
typeof (o as Record<string, unknown>).enabled === 'boolean'
|
||||
) as { serverId: string; enabled: boolean }[];
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
const serversRaw = config[SETTINGS_KEYS.MCP_SERVERS];
|
||||
let servers: { id: string; enabled?: boolean }[];
|
||||
try {
|
||||
servers = typeof serversRaw === 'string' ? JSON.parse(serversRaw) : [];
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Array.isArray(servers)) servers = [];
|
||||
|
||||
let serversChanged = false;
|
||||
const knownIds = new Set(servers.map((s) => s.id));
|
||||
for (const override of overrides) {
|
||||
if (!knownIds.has(override.serverId)) continue;
|
||||
const index = servers.findIndex((s) => s.id === override.serverId);
|
||||
|
||||
if (index >= 0 && servers[index].enabled !== override.enabled) {
|
||||
servers[index] = { ...servers[index], enabled: override.enabled };
|
||||
serversChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (serversChanged) {
|
||||
config[SETTINGS_KEYS.MCP_SERVERS] = JSON.stringify(servers);
|
||||
localStorage.setItem(CONFIG_LOCALSTORAGE_KEY, JSON.stringify(config));
|
||||
}
|
||||
|
||||
if (import.meta.env.DEV && import.meta.env.VITE_DEBUG)
|
||||
console.log(
|
||||
`[Migration] MCP default overrides merge: applied=${overrides.length} serversChanged=${serversChanged} (legacy key preserved)`
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const migrations: Migration[] = [
|
||||
localStorageMigration,
|
||||
idxdbMigration,
|
||||
@@ -593,6 +670,7 @@ const migrations: Migration[] = [
|
||||
themeMigration,
|
||||
customJsonKeyMigration,
|
||||
mcpDefaultEnabledMigration,
|
||||
mcpDefaultOverridesMergeMigration,
|
||||
configTypesMigration
|
||||
];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user