first commit

This commit is contained in:
Beyhan Oğur
2026-04-26 21:52:23 +03:00
commit 880f412e2c
2662 changed files with 866266 additions and 0 deletions

View File

@@ -0,0 +1,334 @@
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Switch } from "@/components/ui/switch";
import {
getErrorMessage,
useGetCoreConfigQuery,
useUpdateCoreConfigMutation,
} from "@/lib/store";
import { CoreConfig, DefaultCoreConfig } from "@/lib/types/config";
import { RbacOperation, RbacResource, useRbac } from "@enterprise/lib";
import { useCallback, useEffect, useMemo, useState } from "react";
import { toast } from "sonner";
export default function MCPView() {
const hasSettingsUpdateAccess = useRbac(
RbacResource.Settings,
RbacOperation.Update,
);
const { data: bifrostConfig } = useGetCoreConfigQuery({ fromDB: true });
const config = bifrostConfig?.client_config;
const [updateCoreConfig, { isLoading }] = useUpdateCoreConfigMutation();
const [localConfig, setLocalConfig] = useState<CoreConfig>(DefaultCoreConfig);
const [localValues, setLocalValues] = useState<{
mcp_agent_depth: string;
mcp_tool_execution_timeout: string;
mcp_code_mode_binding_level: string;
mcp_tool_sync_interval: string;
}>({
mcp_agent_depth: "10",
mcp_tool_execution_timeout: "30",
mcp_code_mode_binding_level: "server",
mcp_tool_sync_interval: "10",
});
useEffect(() => {
if (bifrostConfig && config) {
setLocalConfig(config);
setLocalValues({
mcp_agent_depth: config?.mcp_agent_depth?.toString() || "10",
mcp_tool_execution_timeout:
config?.mcp_tool_execution_timeout?.toString() || "30",
mcp_code_mode_binding_level:
config?.mcp_code_mode_binding_level || "server",
mcp_tool_sync_interval:
config?.mcp_tool_sync_interval?.toString() || "10",
});
}
}, [config, bifrostConfig]);
const hasChanges = useMemo(() => {
if (!config) return false;
return (
localConfig.mcp_agent_depth !== config.mcp_agent_depth ||
localConfig.mcp_tool_execution_timeout !==
config.mcp_tool_execution_timeout ||
localConfig.mcp_code_mode_binding_level !==
(config.mcp_code_mode_binding_level || "server") ||
localConfig.mcp_tool_sync_interval !==
(config.mcp_tool_sync_interval ?? 10) ||
localConfig.mcp_disable_auto_tool_inject !==
(config.mcp_disable_auto_tool_inject ?? false)
);
}, [config, localConfig]);
const handleAgentDepthChange = useCallback((value: string) => {
setLocalValues((prev) => ({ ...prev, mcp_agent_depth: value }));
const numValue = Number.parseInt(value);
if (!isNaN(numValue) && numValue > 0) {
setLocalConfig((prev) => ({ ...prev, mcp_agent_depth: numValue }));
}
}, []);
const handleToolExecutionTimeoutChange = useCallback((value: string) => {
setLocalValues((prev) => ({ ...prev, mcp_tool_execution_timeout: value }));
const numValue = Number.parseInt(value);
if (!isNaN(numValue) && numValue > 0) {
setLocalConfig((prev) => ({
...prev,
mcp_tool_execution_timeout: numValue,
}));
}
}, []);
const handleCodeModeBindingLevelChange = useCallback((value: string) => {
setLocalValues((prev) => ({ ...prev, mcp_code_mode_binding_level: value }));
if (value === "server" || value === "tool") {
setLocalConfig((prev) => ({
...prev,
mcp_code_mode_binding_level: value,
}));
}
}, []);
const handleToolSyncIntervalChange = useCallback((value: string) => {
setLocalValues((prev) => ({ ...prev, mcp_tool_sync_interval: value }));
const numValue = Number.parseInt(value);
if (!isNaN(numValue) && numValue >= 0) {
setLocalConfig((prev) => ({ ...prev, mcp_tool_sync_interval: numValue }));
}
}, []);
const handleDisableAutoToolInjectChange = useCallback((checked: boolean) => {
setLocalConfig((prev) => ({
...prev,
mcp_disable_auto_tool_inject: checked,
}));
}, []);
const handleSave = useCallback(async () => {
try {
const agentDepth = Number.parseInt(localValues.mcp_agent_depth);
const toolTimeout = Number.parseInt(
localValues.mcp_tool_execution_timeout,
);
if (isNaN(agentDepth) || agentDepth <= 0) {
toast.error("Max agent depth must be a positive number.");
return;
}
if (isNaN(toolTimeout) || toolTimeout <= 0) {
toast.error("Tool execution timeout must be a positive number.");
return;
}
if (!bifrostConfig) {
toast.error("Configuration not loaded. Please refresh and try again.");
return;
}
await updateCoreConfig({
...bifrostConfig,
client_config: localConfig,
}).unwrap();
toast.success("MCP settings updated successfully.");
} catch (error) {
toast.error(getErrorMessage(error));
}
}, [bifrostConfig, localConfig, localValues, updateCoreConfig]);
return (
<div
className="mx-auto w-full max-w-7xl space-y-4"
data-testid="mcp-settings-view"
>
<div>
<h2 className="text-lg font-semibold tracking-tight">MCP Settings</h2>
<p className="text-muted-foreground text-sm">
Configure MCP (Model Context Protocol) agent and tool settings.
</p>
</div>
<div className="space-y-4">
{/* Max Agent Depth */}
<div className="flex items-center justify-between space-x-2 rounded-sm border p-4">
<div className="space-y-0.5">
<label htmlFor="mcp-agent-depth" className="text-sm font-medium">
Max Agent Depth
</label>
<p className="text-muted-foreground text-sm">
Maximum depth for MCP agent execution.
</p>
</div>
<Input
id="mcp-agent-depth"
data-testid="mcp-agent-depth-input"
type="number"
className="w-24"
value={localValues.mcp_agent_depth}
onChange={(e) => handleAgentDepthChange(e.target.value)}
min="1"
/>
</div>
{/* Tool Execution Timeout */}
<div className="flex items-center justify-between space-x-2 rounded-sm border p-4">
<div className="space-y-0.5">
<label
htmlFor="mcp-tool-execution-timeout"
className="text-sm font-medium"
>
Tool Execution Timeout (seconds)
</label>
<p className="text-muted-foreground text-sm">
Maximum time in seconds for tool execution.
</p>
</div>
<Input
id="mcp-tool-execution-timeout"
data-testid="mcp-tool-timeout-input"
type="number"
className="w-24"
value={localValues.mcp_tool_execution_timeout}
onChange={(e) => handleToolExecutionTimeoutChange(e.target.value)}
min="1"
/>
</div>
{/* Tool Sync Interval */}
<div className="flex items-center justify-between space-x-2 rounded-sm border p-4">
<div className="space-y-0.5">
<label
htmlFor="mcp-tool-sync-interval"
className="text-sm font-medium"
>
Tool Sync Interval (minutes)
</label>
<p className="text-muted-foreground text-sm">
How often to refresh tool lists from MCP servers. Set to 0 to
disable.
</p>
</div>
<Input
id="mcp-tool-sync-interval"
data-testid="mcp-tool-sync-interval-input"
type="number"
className="w-24"
value={localValues.mcp_tool_sync_interval}
onChange={(e) => handleToolSyncIntervalChange(e.target.value)}
min="0"
/>
</div>
{/* Disable Auto Tool Injection */}
<div className="flex items-center justify-between space-x-2 rounded-sm border p-4">
<div className="space-y-0.5">
<label
htmlFor="mcp-disable-auto-tool-inject"
className="text-sm font-medium"
>
Disable Auto Tool Injection
</label>
<p className="text-muted-foreground text-sm">
When enabled, MCP tools are not automatically included in every
request. Tools are only injected when explicitly specified via
request headers (
<code className="text-xs">x-bf-mcp-include-tools</code>) and still
must be allowed by the virtual key MCP configuration.
</p>
</div>
<Switch
id="mcp-disable-auto-tool-inject"
checked={localConfig.mcp_disable_auto_tool_inject ?? false}
onCheckedChange={handleDisableAutoToolInjectChange}
disabled={!hasSettingsUpdateAccess}
data-testid="mcp-disable-auto-tool-inject-switch"
/>
</div>
{/* Code Mode Binding Level */}
<div className="space-y-4 rounded-sm border p-4">
<div className="space-y-0.5">
<label htmlFor="mcp-binding-level" className="text-sm font-medium">
Code Mode Binding Level
</label>
<p className="text-muted-foreground text-sm">
How tools are exposed in the VFS: server-level (all tools per
server) or tool-level (individual tools).
</p>
</div>
<Select
value={localValues.mcp_code_mode_binding_level}
onValueChange={handleCodeModeBindingLevelChange}
>
<SelectTrigger
id="mcp-binding-level"
data-testid="mcp-binding-level"
className="w-56"
>
<SelectValue placeholder="Select binding level" />
</SelectTrigger>
<SelectContent>
<SelectItem value="server">Server-Level</SelectItem>
<SelectItem value="tool">Tool-Level</SelectItem>
</SelectContent>
</Select>
{/* Visual Example */}
<div className="mt-6 space-y-2">
<p className="text-foreground text-xs font-semibold tracking-wide uppercase">
VFS Structure:
</p>
{localValues.mcp_code_mode_binding_level === "server" ? (
<div className="bg-muted border-border rounded-sm border p-4">
<div className="text-foreground space-y-1 font-mono text-xs">
<div>servers/</div>
<div className="pl-3"> calculator.py</div>
<div className="pl-3"> youtube.py</div>
<div className="pl-3"> weather.py</div>
</div>
<p className="text-muted-foreground mt-3 text-xs">
All tools per server in a single .py file
</p>
</div>
) : (
<div className="bg-muted border-border rounded-sm border p-4">
<div className="text-foreground space-y-1 font-mono text-xs">
<div>servers/</div>
<div className="pl-3"> calculator/</div>
<div className="pl-6"> add.py</div>
<div className="pl-6"> subtract.py</div>
<div className="pl-3"> youtube/</div>
<div className="pl-6"> GET_CHANNELS.py</div>
<div className="pl-6"> SEARCH_VIDEOS.py</div>
<div className="pl-3"> weather/</div>
<div className="pl-6"> get_forecast.py</div>
</div>
<p className="text-muted-foreground mt-3 text-xs">
Individual .py file for each tool
</p>
</div>
)}
</div>
</div>
</div>
<div className="flex justify-end pt-2">
<Button
onClick={handleSave}
disabled={!hasChanges || isLoading || !hasSettingsUpdateAccess}
data-testid="mcp-settings-save-btn"
>
{isLoading ? "Saving..." : "Save Changes"}
</Button>
</div>
</div>
);
}