| | | 1 | | @using Shared.Contracts.Mcp |
| | | 2 | | @inherits RadzenComponent |
| | | 3 | | |
| | | 4 | | <RadzenStack Gap="0.5rem"> |
| | | 5 | | <RadzenStack Orientation="Orientation.Horizontal" AlignItems="AlignItems.Center" JustifyContent="JustifyContent.Spac |
| | | 6 | | <CheckBox Text="Enable MCP" @bind-Value="@McpSettingsProvider.Current.Enabled" @bind-Value:after="OnMcpEnabledCh |
| | | 7 | | <RadzenStack Orientation="Orientation.Horizontal" Gap="0.3rem" JustifyContent="JustifyContent.End"> |
| | | 8 | | <RadzenButton Icon="edit_note" ButtonStyle="ButtonStyle.Info" Size="ButtonSize.Small" Click="@OnOpenMcpSetti |
| | | 9 | | <RadzenButton Icon="refresh" ButtonStyle="ButtonStyle.Light" Size="ButtonSize.Small" Click="@OnReloadMcpSett |
| | | 10 | | </RadzenStack> |
| | | 11 | | </RadzenStack> |
| | | 12 | | |
| | 0 | 13 | | @if (McpSettingsProvider.Current.Enabled) |
| | | 14 | | { |
| | 0 | 15 | | @if (!McpSettingsProvider.Current.Servers.Any()) |
| | | 16 | | { |
| | | 17 | | <RadzenText TextStyle="TextStyle.Caption" class="server-error-text"> |
| | 0 | 18 | | @SharedResource.NoMCP |
| | | 19 | | </RadzenText> |
| | | 20 | | } |
| | | 21 | | |
| | 0 | 22 | | @foreach (var server in McpSettingsProvider.Current.Servers) |
| | | 23 | | { |
| | 0 | 24 | | var hasError = McpSettingsProvider.Current.ServerErrors.TryGetValue(server.Name, out var errorMsg); |
| | 0 | 25 | | var borderColor = hasError ? "var(--rz-error-color)" : "var(--rz-border-color)"; |
| | 0 | 26 | | var serverStyle = $"border: 1px solid {borderColor}; border-radius: var(--rz-border-radius);"; |
| | | 27 | | |
| | | 28 | | <RadzenStack Gap="0.5rem" Style="@serverStyle"> |
| | | 29 | | <RadzenStack Orientation="Orientation.Horizontal" AlignItems="AlignItems.Center" JustifyContent="Justify |
| | | 30 | | <RadzenStack Orientation="Orientation.Horizontal" Gap="0.5rem" AlignItems="AlignItems.Center"> |
| | | 31 | | <CheckBox Text="@server.Name" @bind-Value="@server.Enabled" @bind-Value:after="OnMcpToolSettingC |
| | | 32 | | </RadzenStack> |
| | | 33 | | <ApprovalModeSelector Value="@GetMcpServerApprovalMode(server)" Disabled="@(!server.Enabled)" |
| | 0 | 34 | | ValueChanged="@(mode => OnMcpServerApprovalChanged(server, mode))" /> |
| | | 35 | | </RadzenStack> |
| | | 36 | | |
| | 0 | 37 | | @if (hasError && !string.IsNullOrEmpty(errorMsg)) |
| | | 38 | | { |
| | | 39 | | <RadzenStack class="server-error-text"> |
| | 0 | 40 | | <strong>Error:</strong> @errorMsg |
| | | 41 | | </RadzenStack> |
| | | 42 | | } |
| | | 43 | | |
| | 0 | 44 | | @if (server.Enabled) |
| | | 45 | | { |
| | | 46 | | <RadzenStack Orientation="Orientation.Horizontal" Gap="0.5rem" Style="padding-left: 2rem;"> |
| | | 47 | | <RadzenBadge Text="@server.Transport" /> |
| | 0 | 48 | | @if (hasError) |
| | | 49 | | { |
| | | 50 | | <RadzenBadge Text="@SharedResource.Error" Style="font-size: 0.7rem; background: var(--rz-err |
| | | 51 | | } |
| | 0 | 52 | | else if (server.Enabled && server.Tools.Any()) |
| | | 53 | | { |
| | | 54 | | <RadzenBadge Text="@SharedResource.Ready" Style="font-size: 0.7rem; background: var(--rz-suc |
| | | 55 | | } |
| | | 56 | | </RadzenStack> |
| | | 57 | | <RadzenStack Gap="0.2rem" class="server-commands"> |
| | 0 | 58 | | @if (server.Transport == "stdio") |
| | | 59 | | { |
| | 0 | 60 | | <div>Command: @server.Command @(string.Join(" ", server.Args))</div> |
| | | 61 | | } |
| | | 62 | | else |
| | | 63 | | { |
| | 0 | 64 | | <div>URL: @server.Url</div> |
| | | 65 | | } |
| | 0 | 66 | | @if (server.Env.Any()) |
| | | 67 | | { |
| | 0 | 68 | | <div>Env: @server.Env.Count variable(s)</div> |
| | | 69 | | } |
| | | 70 | | </RadzenStack> |
| | | 71 | | |
| | | 72 | | <RadzenStack Orientation="Orientation.Horizontal" Gap="0.3rem" Style="padding-left: 2rem;"> |
| | | 73 | | <RadzenButton Text="@SharedResource.RefreshTools" Icon="sync" Size="ButtonSize.ExtraSmall" Butto |
| | 0 | 74 | | Click="@(() => OnRefreshMcpTools(server))" IsBusy="@(_refreshingServerId == server |
| | | 75 | | </RadzenStack> |
| | | 76 | | |
| | 0 | 77 | | @if (server.Tools.Any()) |
| | | 78 | | { |
| | | 79 | | <RadzenAccordion Style="margin-top: 0.3rem; border: none;"> |
| | | 80 | | <Items> |
| | | 81 | | <RadzenAccordionItem Text="@SharedResource.Tools" Icon="build"> |
| | | 82 | | <RadzenStack Gap="0.5rem"> |
| | 0 | 83 | | @foreach (var tool in server.Tools) |
| | | 84 | | { |
| | | 85 | | <RadzenStack Orientation="Orientation.Vertical" JustifyContent="JustifyConte |
| | | 86 | | <RadzenStack Orientation="Orientation.Horizontal" JustifyContent="Justif |
| | | 87 | | <CheckBox Text="@tool.Name" @bind-Value="@tool.Enabled" @bind-Value: |
| | 0 | 88 | | <RadzenButton Icon="science" Size="ButtonSize.ExtraSmall" ButtonStyl |
| | | 89 | | </RadzenStack> |
| | 0 | 90 | | <RadzenText TextStyle="TextStyle.Caption" class="tool-description">@tool |
| | | 91 | | </RadzenStack> |
| | | 92 | | } |
| | | 93 | | </RadzenStack> |
| | | 94 | | </RadzenAccordionItem> |
| | | 95 | | </Items> |
| | | 96 | | </RadzenAccordion> |
| | | 97 | | } |
| | | 98 | | } |
| | | 99 | | </RadzenStack> |
| | | 100 | | } |
| | | 101 | | } |
| | | 102 | | </RadzenStack> |
| | | 103 | | |
| | | 104 | | @code { |
| | 0 | 105 | | [Inject] IMcpSettingsProvider McpSettingsProvider { get; set; } = null!; |
| | 0 | 106 | | [Inject] NotificationService NotificationService { get; set; } = null!; |
| | 0 | 107 | | [Inject] DialogService DialogService { get; set; } = null!; |
| | 0 | 108 | | [Inject] IToolManager ToolManager { get; set; } = null!; |
| | | 109 | | |
| | | 110 | | private string? _refreshingServerId; |
| | | 111 | | |
| | | 112 | | private async Task OnMcpEnabledChanged() |
| | | 113 | | { |
| | 0 | 114 | | if (!McpSettingsProvider.Current.Enabled) |
| | | 115 | | { |
| | 0 | 116 | | await McpSettingsProvider.StopAllAsync(); |
| | | 117 | | } |
| | 0 | 118 | | await McpSettingsProvider.SaveAsync(); |
| | 0 | 119 | | } |
| | | 120 | | |
| | | 121 | | private async Task OnOpenMcpSettings() |
| | | 122 | | { |
| | 0 | 123 | | await McpSettingsProvider.OpenSettingsFileAsync(); |
| | 0 | 124 | | } |
| | | 125 | | |
| | | 126 | | private async Task OnReloadMcpSettings() |
| | | 127 | | { |
| | 0 | 128 | | await McpSettingsProvider.LoadMcpFileAsync(); |
| | 0 | 129 | | StateHasChanged(); |
| | 0 | 130 | | } |
| | | 131 | | |
| | | 132 | | private async Task OnRefreshMcpTools(McpServerConfig server) |
| | | 133 | | { |
| | 0 | 134 | | _refreshingServerId = server.Name; |
| | 0 | 135 | | StateHasChanged(); |
| | | 136 | | |
| | 0 | 137 | | var result = await McpSettingsProvider.RefreshToolsAsync(server); |
| | | 138 | | |
| | 0 | 139 | | _refreshingServerId = null; |
| | | 140 | | |
| | | 141 | | // Update error state |
| | 0 | 142 | | if (result.StartsWith("Success")) |
| | | 143 | | { |
| | 0 | 144 | | McpSettingsProvider.Current.ServerErrors.Remove(server.Name); |
| | | 145 | | } |
| | | 146 | | else |
| | | 147 | | { |
| | 0 | 148 | | McpSettingsProvider.Current.ServerErrors[server.Name] = result; |
| | | 149 | | } |
| | | 150 | | |
| | 0 | 151 | | NotificationService.Notify(new NotificationMessage |
| | 0 | 152 | | { |
| | 0 | 153 | | Severity = result.StartsWith("Success") ? NotificationSeverity.Success : NotificationSeverity.Error, |
| | 0 | 154 | | Summary = result, |
| | 0 | 155 | | Duration = 4000 |
| | 0 | 156 | | }); |
| | | 157 | | |
| | 0 | 158 | | await McpSettingsProvider.SaveAsync(); |
| | 0 | 159 | | StateHasChanged(); |
| | 0 | 160 | | } |
| | | 161 | | |
| | | 162 | | private ToolApprovalMode GetMcpServerApprovalMode(McpServerConfig server) |
| | | 163 | | { |
| | 0 | 164 | | return McpSettingsProvider.Current.ServerApprovalModes.TryGetValue(server.Name, out var mode) |
| | 0 | 165 | | ? mode |
| | 0 | 166 | | : ToolApprovalMode.Allow; |
| | | 167 | | } |
| | | 168 | | |
| | | 169 | | private async Task OnMcpServerApprovalChanged(McpServerConfig server, ToolApprovalMode mode) |
| | | 170 | | { |
| | 0 | 171 | | McpSettingsProvider.Current.ServerApprovalModes[server.Name] = mode; |
| | 0 | 172 | | await McpSettingsProvider.SaveAsync(); |
| | 0 | 173 | | } |
| | | 174 | | |
| | | 175 | | private void OnMcpToolSettingChanged() |
| | | 176 | | { |
| | | 177 | | // Update server enabled states |
| | 0 | 178 | | foreach (var server in McpSettingsProvider.Current.Servers) |
| | | 179 | | { |
| | 0 | 180 | | McpSettingsProvider.Current.ServerEnabledStates[server.Name] = server.Enabled; |
| | | 181 | | } |
| | | 182 | | |
| | 0 | 183 | | McpSettingsProvider.Current.ToolDisabledStates.Clear(); |
| | | 184 | | |
| | 0 | 185 | | foreach (var server in McpSettingsProvider.Current.Servers) |
| | | 186 | | { |
| | 0 | 187 | | foreach (var tool in server.Tools) |
| | | 188 | | { |
| | 0 | 189 | | var toolKey = $"{server.Name}:{tool.Name}"; |
| | 0 | 190 | | if (!tool.Enabled) |
| | | 191 | | { |
| | 0 | 192 | | McpSettingsProvider.Current.ToolDisabledStates.Add(toolKey); |
| | | 193 | | } |
| | | 194 | | } |
| | | 195 | | } |
| | | 196 | | |
| | 0 | 197 | | McpSettingsProvider.CallSaveTrigger(); |
| | 0 | 198 | | } |
| | | 199 | | |
| | | 200 | | private async Task TestRun(string serverName, McpToolConfig mcpTool) { |
| | 0 | 201 | | await DialogService.OpenAsync<MCPTestRun>( |
| | 0 | 202 | | $"{serverName} - {mcpTool.Name}", |
| | 0 | 203 | | new Dictionary<string, object?> { { "Tool", mcpTool }, { "ServerName", serverName } }, |
| | 0 | 204 | | new DialogOptions { |
| | 0 | 205 | | Resizable = true, |
| | 0 | 206 | | Icon = "science", |
| | 0 | 207 | | Top = "1em", |
| | 0 | 208 | | Width = "50em" |
| | 0 | 209 | | } |
| | 0 | 210 | | ); |
| | 0 | 211 | | } |
| | | 212 | | } |