- { e.target.style.display = "none"; }} />
+ { e.target.style.display = "none"; }} loading="lazy" decoding="async" />
@@ -254,15 +253,17 @@ export default function KiloToolCard({ tool, isExpanded, onToggle, baseUrl, apiK
)}
-
setModalOpen(false)}
- onSelect={(model) => { setSelectedModel(model.value); setModalOpen(false); }}
- selectedModel={selectedModel}
- activeProviders={activeProviders}
- modelAliases={modelAliases}
- title="Select Model for Kilo Code"
- />
+ {modalOpen && (
+ setModalOpen(false)}
+ onSelect={(model) => { setSelectedModel(model.value); setModalOpen(false); }}
+ selectedModel={selectedModel}
+ activeProviders={activeProviders}
+ modelAliases={modelAliases}
+ title="Select Model for Kilo Code"
+ />
+ )}
{ e.target.style.display = "none"; }}
+ loading="lazy"
+ decoding="async"
/>
diff --git a/src/app/(dashboard)/dashboard/cli-tools/components/MitmToolCard.js b/src/app/(dashboard)/dashboard/cli-tools/components/MitmToolCard.js
index 82384292..3ca51d2e 100644
--- a/src/app/(dashboard)/dashboard/cli-tools/components/MitmToolCard.js
+++ b/src/app/(dashboard)/dashboard/cli-tools/components/MitmToolCard.js
@@ -143,6 +143,8 @@ export default function MitmToolCard({
className="size-8 object-contain rounded-lg"
sizes="32px"
onError={(e) => { e.target.style.display = "none"; }}
+ loading="lazy"
+ decoding="async"
/>
@@ -304,15 +306,17 @@ export default function MitmToolCard({
)}
{/* Model Select Modal */}
-
setModalOpen(false)}
- onSelect={handleModelSelect}
- selectedModel={currentEditingAlias ? modelMappings[currentEditingAlias] : null}
- activeProviders={activeProviders}
- modelAliases={modelAliases}
- title={`Select model for ${currentEditingAlias}`}
- />
+ {modalOpen && (
+ setModalOpen(false)}
+ onSelect={handleModelSelect}
+ selectedModel={currentEditingAlias ? modelMappings[currentEditingAlias] : null}
+ activeProviders={activeProviders}
+ modelAliases={modelAliases}
+ title={`Select model for ${currentEditingAlias}`}
+ />
+ )}
>
);
}
diff --git a/src/app/(dashboard)/dashboard/cli-tools/components/OpenClawToolCard.js b/src/app/(dashboard)/dashboard/cli-tools/components/OpenClawToolCard.js
index 88a73c63..b646a6eb 100644
--- a/src/app/(dashboard)/dashboard/cli-tools/components/OpenClawToolCard.js
+++ b/src/app/(dashboard)/dashboard/cli-tools/components/OpenClawToolCard.js
@@ -57,11 +57,10 @@ export default function OpenClawToolCard({
}, [initialStatus]);
useEffect(() => {
- if (isExpanded && !openclawStatus) {
- checkOpenclawStatus();
+ if (isExpanded) {
+ if (!openclawStatus) checkOpenclawStatus();
fetchModelAliases();
}
- if (isExpanded) fetchModelAliases();
}, [isExpanded]);
const fetchModelAliases = async () => {
@@ -233,7 +232,7 @@ export default function OpenClawToolCard({
- { e.target.style.display = "none"; }} />
+ { e.target.style.display = "none"; }} loading="lazy" decoding="async" />
@@ -367,15 +366,17 @@ export default function OpenClawToolCard({
)}
-
setModalOpen(false)}
- onSelect={handleModelSelect}
- selectedModel={selectedModel}
- activeProviders={activeProviders}
- modelAliases={modelAliases}
- title="Select Model for Open Claw"
- />
+ {modalOpen && (
+ setModalOpen(false)}
+ onSelect={handleModelSelect}
+ selectedModel={selectedModel}
+ activeProviders={activeProviders}
+ modelAliases={modelAliases}
+ title="Select Model for Open Claw"
+ />
+ )}
{
- if (isExpanded && !status) {
- checkStatus();
+ if (isExpanded) {
+ if (!status) checkStatus();
fetchModelAliases();
}
- if (isExpanded) fetchModelAliases();
}, [isExpanded]);
// Sync models from existing config
@@ -222,7 +221,7 @@ export default function OpenCodeToolCard({ tool, isExpanded, onToggle, baseUrl,
- { e.target.style.display = "none"; }} />
+ { e.target.style.display = "none"; }} loading="lazy" decoding="async" />
@@ -452,42 +451,46 @@ export default function OpenCodeToolCard({ tool, isExpanded, onToggle, baseUrl,
)}
-
{
- setModalOpen(false);
- saveModels(selectedModelsRef.current);
- }}
- onSelect={(model) => {
- if (!selectedModels.includes(model.value)) {
- setSelectedModels([...selectedModels, model.value]);
- if (!activeModel) setActiveModel(model.value);
- }
- }}
- onDeselect={(model) => {
- const remaining = selectedModels.filter(m => m !== model.value);
- setSelectedModels(remaining);
- if (activeModel === model.value) {
- setActiveModel(remaining[0] || "");
- }
- }}
- selectedModel={null}
- activeProviders={activeProviders}
- modelAliases={modelAliases}
- addedModelValues={selectedModels}
- closeOnSelect={false}
- title="Add Model for OpenCode"
- />
+ {modalOpen && (
+ {
+ setModalOpen(false);
+ saveModels(selectedModelsRef.current);
+ }}
+ onSelect={(model) => {
+ if (!selectedModels.includes(model.value)) {
+ setSelectedModels([...selectedModels, model.value]);
+ if (!activeModel) setActiveModel(model.value);
+ }
+ }}
+ onDeselect={(model) => {
+ const remaining = selectedModels.filter(m => m !== model.value);
+ setSelectedModels(remaining);
+ if (activeModel === model.value) {
+ setActiveModel(remaining[0] || "");
+ }
+ }}
+ selectedModel={null}
+ activeProviders={activeProviders}
+ modelAliases={modelAliases}
+ addedModelValues={selectedModels}
+ closeOnSelect={false}
+ title="Add Model for OpenCode"
+ />
+ )}
- setSubagentModalOpen(false)}
- onSelect={(model) => { setSubagentModel(model.value); setSubagentModalOpen(false); }}
- selectedModel={subagentModel}
- activeProviders={activeProviders}
- modelAliases={modelAliases}
- title="Select Subagent Model for OpenCode"
- />
+ {subagentModalOpen && (
+ setSubagentModalOpen(false)}
+ onSelect={(model) => { setSubagentModel(model.value); setSubagentModalOpen(false); }}
+ selectedModel={subagentModel}
+ activeProviders={activeProviders}
+ modelAliases={modelAliases}
+ title="Select Subagent Model for OpenCode"
+ />
+ )}
{tool.image ? (
- { e.target.style.display = "none"; }} />
+ { e.target.style.display = "none"; }} loading="lazy" decoding="async" />
) : tool.icon ? (
{tool.icon}
) : null}
diff --git a/src/app/(dashboard)/dashboard/combos/page.js b/src/app/(dashboard)/dashboard/combos/page.js
index abfc215c..fb4c17d6 100644
--- a/src/app/(dashboard)/dashboard/combos/page.js
+++ b/src/app/(dashboard)/dashboard/combos/page.js
@@ -7,6 +7,7 @@ import { CSS } from "@dnd-kit/utilities";
import { restrictToVerticalAxis, restrictToParentElement } from "@dnd-kit/modifiers";
import { Card, Button, Modal, Input, CardSkeleton, ModelSelectModal, ConfirmModal, CapacityBadges, Select } from "@/shared/components";
import { useCopyToClipboard } from "@/shared/hooks/useCopyToClipboard";
+import { useModelCaps } from "@/shared/hooks/useModelCaps";
import { isOpenAICompatibleProvider, isAnthropicCompatibleProvider } from "@/shared/constants/providers";
// Validate combo name: only a-z, A-Z, 0-9, -, _
@@ -19,7 +20,7 @@ export default function CombosPage() {
const [editingCombo, setEditingCombo] = useState(null);
const [activeProviders, setActiveProviders] = useState([]);
const [comboStrategies, setComboStrategies] = useState({});
- const [modelCaps, setModelCaps] = useState({});
+ const { getCaps } = useModelCaps();
const [confirmState, setConfirmState] = useState(null);
const { copied, copy } = useCopyToClipboard();
@@ -29,11 +30,10 @@ export default function CombosPage() {
const fetchData = async () => {
try {
- const [combosRes, providersRes, settingsRes, modelsRes] = await Promise.all([
+ const [combosRes, providersRes, settingsRes] = await Promise.all([
fetch("/api/combos"),
fetch("/api/providers"),
fetch("/api/settings"),
- fetch("/api/models"),
]);
const combosData = await combosRes.json();
const providersData = await providersRes.json();
@@ -44,13 +44,6 @@ export default function CombosPage() {
if (providersRes.ok) {
setActiveProviders(providersData.connections || []);
}
- if (modelsRes.ok) {
- const md = await modelsRes.json();
- // Build fullModel -> caps map for badge lookup
- const map = {};
- for (const m of md.models || []) if (m.caps) map[m.fullModel] = m.caps;
- setModelCaps(map);
- }
setComboStrategies(settingsData.comboStrategies || {});
} catch (error) {
console.log("Error fetching data:", error);
@@ -189,7 +182,7 @@ export default function CombosPage() {
setShowCreateModal(false)}
- onSave={handleCreate}
- activeProviders={activeProviders}
- />
+ {showCreateModal && (
+ setShowCreateModal(false)}
+ onSave={handleCreate}
+ activeProviders={activeProviders}
+ />
+ )}
- {/* Edit Modal - Use key to force remount and reset state */}
- setEditingCombo(null)}
- onSave={(data) => handleUpdate(editingCombo.id, data)}
- activeProviders={activeProviders}
- />
+ {editingCombo && (
+ setEditingCombo(null)}
+ onSave={(data) => handleUpdate(editingCombo.id, data)}
+ activeProviders={activeProviders}
+ />
+ )}
{/* Confirm Delete Modal */}
(
{model}
-
+
))
)}
@@ -340,15 +336,17 @@ function ComboCard({ combo, modelCaps = {}, activeProviders = [], copied, onCopy
{/* Judge model picker (single-select; combo members make natural judges too) */}
- setShowJudgeSelect(false)}
- onSelect={(m) => { onSetStrategy({ judgeModel: m?.value || "" }); setShowJudgeSelect(false); }}
- activeProviders={activeProviders}
- title="Select Judge Model"
- addedModelValues={judge ? [judge] : []}
- closeOnSelect={true}
- />
+ {showJudgeSelect && (
+ setShowJudgeSelect(false)}
+ onSelect={(m) => { onSetStrategy({ judgeModel: m?.value || "" }); setShowJudgeSelect(false); }}
+ activeProviders={activeProviders}
+ title="Select Judge Model"
+ addedModelValues={judge ? [judge] : []}
+ closeOnSelect={true}
+ />
+ )}
);
}
@@ -637,18 +635,20 @@ function ComboFormModal({ isOpen, combo, onClose, onSave, activeProviders, kindF
{/* Model Select Modal */}
- setShowModelSelect(false)}
- onSelect={handleAddModel}
- onDeselect={handleDeselectModel}
- activeProviders={activeProviders}
- modelAliases={modelAliases}
- title="Add Model to Combo"
- kindFilter={kindFilter}
- addedModelValues={models}
- closeOnSelect={false}
- />
+ {showModelSelect && (
+ setShowModelSelect(false)}
+ onSelect={handleAddModel}
+ onDeselect={handleDeselectModel}
+ activeProviders={activeProviders}
+ modelAliases={modelAliases}
+ title="Add Model to Combo"
+ kindFilter={kindFilter}
+ addedModelValues={models}
+ closeOnSelect={false}
+ />
+ )}
>
);
}
diff --git a/src/app/(dashboard)/dashboard/media-providers/[kind]/[id]/components/GenericExampleCard.js b/src/app/(dashboard)/dashboard/media-providers/[kind]/[id]/components/GenericExampleCard.js
index 815528d7..76c571b8 100644
--- a/src/app/(dashboard)/dashboard/media-providers/[kind]/[id]/components/GenericExampleCard.js
+++ b/src/app/(dashboard)/dashboard/media-providers/[kind]/[id]/components/GenericExampleCard.js
@@ -350,6 +350,8 @@ export function GenericExampleCard({ providerId, kind }) {
className="max-h-40 rounded-lg border border-border object-contain bg-sidebar"
onError={(e) => { e.currentTarget.style.display = "none"; }}
onLoad={(e) => { e.currentTarget.style.display = "block"; }}
+ loading="lazy"
+ decoding="async"
/>
)}
@@ -383,6 +385,8 @@ export function GenericExampleCard({ providerId, kind }) {
className="max-h-40 rounded-lg border border-border object-contain bg-sidebar"
onError={(e) => { e.currentTarget.style.display = "none"; }}
onLoad={(e) => { e.currentTarget.style.display = "block"; }}
+ loading="lazy"
+ decoding="async"
/>
)}
@@ -487,6 +491,8 @@ export function GenericExampleCard({ providerId, kind }) {
src={`data:image/png;base64,${partialImage.b64_json}`}
alt="Partial"
className="max-w-full rounded-lg border border-border mt-1.5 opacity-80"
+ loading="lazy"
+ decoding="async"
/>
)}
@@ -529,6 +535,8 @@ export function GenericExampleCard({ providerId, kind }) {
src={binaryImageUrl || (result?.data?.data?.[0]?.b64_json ? `data:image/png;base64,${result.data.data[0].b64_json}` : result?.data?.data?.[0]?.url)}
alt="Generated"
className="max-w-full rounded-lg border border-border"
+ loading="lazy"
+ decoding="async"
/>
)}
diff --git a/src/app/(dashboard)/dashboard/media-providers/combo/[id]/page.js b/src/app/(dashboard)/dashboard/media-providers/combo/[id]/page.js
index 45c15460..7ad8b673 100644
--- a/src/app/(dashboard)/dashboard/media-providers/combo/[id]/page.js
+++ b/src/app/(dashboard)/dashboard/media-providers/combo/[id]/page.js
@@ -357,7 +357,7 @@ export default function ComboDetailPage() {
Download
-

+
)}
{testResult.audioUrl && (
@@ -393,18 +393,20 @@ export default function ComboDetailPage() {
)}
- setShowPicker(false)}
- onSelect={handleAddModel}
- onDeselect={handleDeselectModel}
- activeProviders={connections}
- modelAliases={modelAliases}
- title={`Add ${kindLabel} Model`}
- kindFilter={combo.kind}
- addedModelValues={providers}
- closeOnSelect={false}
- />
+ {showPicker && (
+ setShowPicker(false)}
+ onSelect={handleAddModel}
+ onDeselect={handleDeselectModel}
+ activeProviders={connections}
+ modelAliases={modelAliases}
+ title={`Add ${kindLabel} Model`}
+ kindFilter={combo.kind}
+ addedModelValues={providers}
+ closeOnSelect={false}
+ />
+ )}
);
}
diff --git a/src/app/(dashboard)/dashboard/providers/components/ModelsCard.js b/src/app/(dashboard)/dashboard/providers/components/ModelsCard.js
index a6d9533e..9bdf4b41 100644
--- a/src/app/(dashboard)/dashboard/providers/components/ModelsCard.js
+++ b/src/app/(dashboard)/dashboard/providers/components/ModelsCard.js
@@ -116,26 +116,22 @@ export default function ModelsCard({ providerId, kindFilter, providerAliasOverri
const [testingModelId, setTestingModelId] = useState(null);
const [testError, setTestError] = useState("");
const [showAddCustomModel, setShowAddCustomModel] = useState(false);
- const [connections, setConnections] = useState([]);
const providerAlias = providerAliasOverride || getProviderAlias(providerId);
const effectiveType = kindFilter || "llm";
const fetchData = useCallback(async () => {
try {
- const [aliasRes, connRes, customRes] = await Promise.all([
+ const [aliasRes, customRes] = await Promise.all([
fetch("/api/models/alias"),
- fetch("/api/providers", { cache: "no-store" }),
fetch("/api/models/custom", { cache: "no-store" }),
]);
const aliasData = await aliasRes.json();
- const connData = await connRes.json();
const customData = await customRes.json();
if (aliasRes.ok) setModelAliases(aliasData.aliases || {});
- if (connRes.ok) setConnections((connData.connections || []).filter((c) => c.provider === providerId));
if (customRes.ok) setCustomModels(customData.models || []);
} catch (e) { console.log("ModelsCard fetch error:", e); }
- }, [providerId]);
+ }, []);
useEffect(() => { fetchData(); }, [fetchData]);
@@ -242,7 +238,7 @@ export default function ModelsCard({ providerId, kindFilter, providerAliasOverri
onSetAlias={(alias) => handleSetAlias(model.id, alias)}
onDeleteAlias={() => handleDeleteAlias(existingAlias)}
testStatus={modelTestResults[model.id]}
- onTest={connections.length > 0 ? () => handleTestModel(model.id) : undefined}
+ onTest={() => handleTestModel(model.id)}
isTesting={testingModelId === model.id}
isFree={model.isFree}
/>
@@ -259,7 +255,7 @@ export default function ModelsCard({ providerId, kindFilter, providerAliasOverri
onSetAlias={() => {}}
onDeleteAlias={() => handleDeleteCustomModel(model.id)}
testStatus={modelTestResults[model.id]}
- onTest={connections.length > 0 ? () => handleTestModel(model.id) : undefined}
+ onTest={() => handleTestModel(model.id)}
isTesting={testingModelId === model.id}
isCustom
/>
diff --git a/src/app/(dashboard)/dashboard/providers/page.js b/src/app/(dashboard)/dashboard/providers/page.js
index dd134f33..61461726 100644
--- a/src/app/(dashboard)/dashboard/providers/page.js
+++ b/src/app/(dashboard)/dashboard/providers/page.js
@@ -10,6 +10,7 @@ import {
Toggle,
} from "@/shared/components";
import ProviderIcon from "@/shared/components/ProviderIcon";
+import { getProviderIconSrc } from "@/shared/utils/providerIcon";
import { OAUTH_PROVIDERS, APIKEY_PROVIDERS } from "@/shared/constants/config";
import {
FREE_PROVIDERS,
@@ -756,12 +757,12 @@ function ApiKeyProviderCard({
};
const getIconPath = () => {
- if (isCompatible)
+ if (isCompatible && provider.apiType)
return provider.apiType === "responses"
? "/providers/oai-r.png"
: "/providers/oai-cc.png";
if (isAnthropicCompatible) return "/providers/anthropic-m.png";
- return `/providers/${provider.id}.png`;
+ return getProviderIconSrc(provider.id);
};
return (
diff --git a/src/app/(dashboard)/dashboard/usage/components/ProviderLimits/index.js b/src/app/(dashboard)/dashboard/usage/components/ProviderLimits/index.js
index babfa6e2..a0396897 100644
--- a/src/app/(dashboard)/dashboard/usage/components/ProviderLimits/index.js
+++ b/src/app/(dashboard)/dashboard/usage/components/ProviderLimits/index.js
@@ -1265,22 +1265,24 @@ export default function ProviderLimits() {
/>
)}
{hiddenQuotaRows.length > 0 && (
-
-
+
+
visibility_off
-
Hidden:
- {hiddenQuotaRows.map((quotaRow) => (
-
- ))}
+
Hidden:
+
+ {hiddenQuotaRows.map((quotaRow) => (
+
+ ))}
+
)}
diff --git a/src/app/(dashboard)/dashboard/usage/components/ProviderTopology.js b/src/app/(dashboard)/dashboard/usage/components/ProviderTopology.js
index 3827a0f8..57b217a3 100644
--- a/src/app/(dashboard)/dashboard/usage/components/ProviderTopology.js
+++ b/src/app/(dashboard)/dashboard/usage/components/ProviderTopology.js
@@ -10,6 +10,7 @@ import {
} from "@xyflow/react";
import "@xyflow/react/dist/style.css";
import { AI_PROVIDERS } from "@/shared/constants/providers";
+import { getProviderIconSrc, markProviderIconMissing } from "@/shared/utils/providerIcon";
// Force-stop FE animation if a provider stays active longer than this
const FE_ACTIVE_TIMEOUT_MS = 60000;
@@ -19,9 +20,8 @@ function getProviderConfig(providerId) {
return AI_PROVIDERS[providerId] || { color: "#6b7280", name: providerId };
}
-// Use local provider images from /public/providers/
function getProviderImageUrl(providerId) {
- return `/providers/${providerId}.png`;
+ return getProviderIconSrc(providerId);
}
// Custom provider node - rectangle with image + name
@@ -47,8 +47,19 @@ function ProviderNode({ data }) {
className="w-8 h-8 rounded-md flex items-center justify-center shrink-0"
style={{ backgroundColor: `${color}15` }}
>
- {!imgError ? (
-

setImgError(true)} />
+ {imageUrl && !imgError ? (
+

{
+ const m = imageUrl?.match(/^\/providers\/([^/]+)\.png$/i);
+ if (m) markProviderIconMissing(m[1]);
+ setImgError(true);
+ }}
+ />
) : (
{textIcon}
)}
@@ -86,7 +97,7 @@ function RouterNode({ data }) {
-

+
9Router
{data.activeCount > 0 && (
diff --git a/src/app/api/cli-tools/claude-settings/route.js b/src/app/api/cli-tools/claude-settings/route.js
index bb5fa06f..b831e395 100644
--- a/src/app/api/cli-tools/claude-settings/route.js
+++ b/src/app/api/cli-tools/claude-settings/route.js
@@ -6,15 +6,52 @@ import { promisify } from "util";
import fs from "fs/promises";
import path from "path";
import os from "os";
+import { DEFAULT_PLUGINS } from "@/shared/constants/coworkPlugins";
const execAsync = promisify(exec);
+// Exa MCP def — reuse from coworkPlugins (DRY).
+const EXA_PLUGIN = DEFAULT_PLUGINS.find((p) => p.name === "exa");
+const buildExaMcpEntry = () => ({
+ type: EXA_PLUGIN.transport,
+ url: EXA_PLUGIN.url,
+});
+
// Get claude settings path based on OS
const getClaudeSettingsPath = () => {
const homeDir = os.homedir();
return path.join(homeDir, ".claude", "settings.json");
};
+// Claude Code CLI reads mcpServers from ~/.claude.json (NOT settings.json).
+const getClaudeJsonPath = () => path.join(os.homedir(), ".claude.json");
+
+const readClaudeJson = async () => {
+ try {
+ const content = await fs.readFile(getClaudeJsonPath(), "utf-8");
+ return JSON.parse(content.replace(/,(\s*[}\]])/g, "$1"));
+ } catch {
+ return null;
+ }
+};
+
+const writeClaudeJsonMcp = async (mcpServers) => {
+ const filePath = getClaudeJsonPath();
+ let data = {};
+ try {
+ data = JSON.parse(await fs.readFile(filePath, "utf-8"));
+ } catch (error) {
+ if (error.code !== "ENOENT") throw error;
+ }
+ if (mcpServers && Object.keys(mcpServers).length > 0) {
+ data.mcpServers = { ...(data.mcpServers || {}), ...mcpServers };
+ } else if (data.mcpServers) {
+ delete data.mcpServers.exa;
+ if (Object.keys(data.mcpServers).length === 0) delete data.mcpServers;
+ }
+ await fs.writeFile(filePath, JSON.stringify(data, null, 2));
+};
+
// Check if claude CLI is installed (via which/where or config file exists)
const checkClaudeInstalled = async () => {
@@ -65,11 +102,13 @@ export async function GET() {
const settings = await readSettings();
const has9Router = !!(settings?.env?.ANTHROPIC_BASE_URL);
+ const claudeJson = await readClaudeJson();
return NextResponse.json({
installed: true,
settings: settings,
has9Router: has9Router,
+ exaMcpEnabled: !!claudeJson?.mcpServers?.exa,
settingsPath: getClaudeSettingsPath(),
});
} catch (error) {
@@ -84,7 +123,7 @@ export async function GET() {
// POST - Backup old fields and write new settings
export async function POST(request) {
try {
- const { env } = await request.json();
+ const { env, exaMcpEnabled } = await request.json();
if (!env || typeof env !== "object") {
return NextResponse.json(
@@ -130,6 +169,11 @@ export async function POST(request) {
// Write new settings
await fs.writeFile(settingsPath, JSON.stringify(newSettings, null, 2));
+ // Exa MCP toggle — write to ~/.claude.json (CLI reads mcpServers from here).
+ if (EXA_PLUGIN) {
+ await writeClaudeJsonMcp(exaMcpEnabled ? { exa: buildExaMcpEntry() } : null);
+ }
+
return NextResponse.json({
success: true,
message: "Settings updated successfully",
@@ -185,6 +229,9 @@ export async function DELETE() {
}
}
+ // Remove injected MCP servers (Exa) from ~/.claude.json
+ await writeClaudeJsonMcp(null);
+
// Write updated settings
await fs.writeFile(settingsPath, JSON.stringify(currentSettings, null, 2));
@@ -200,4 +247,3 @@ export async function DELETE() {
);
}
}
-
diff --git a/src/shared/components/ComboFormModal.js b/src/shared/components/ComboFormModal.js
index d80732da..ca022f80 100644
--- a/src/shared/components/ComboFormModal.js
+++ b/src/shared/components/ComboFormModal.js
@@ -166,11 +166,13 @@ export default function ComboFormModal({ isOpen, combo, onClose, onSave, activeP