Files
LazyBearWorks/LazyBear.MCP/Services/Kubernetes/K8sConfigTools.cs
Shahovalov MIkhail 879becadfe feat: внедрение RazorConsole TUI с runtime-управлением MCP-инструментами
- Добавлен RazorConsole.Core для интерактивного TUI-дашборда
- ToolRegistryService: живое включение/отключение модулей и отдельных методов
- InMemoryLogSink: кольцевой буфер логов с фильтрацией по модулю
- TUI: 3 таба (Overview, Logs, Settings)
- IToolModule: generic-интерфейс для легкого добавления новых MCP-модулей
- Guard-проверка TryCheckEnabled() во всех существующих инструментах
2026-04-13 17:31:28 +03:00

179 lines
6.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.ComponentModel;
using k8s;
using k8s.Autorest;
using LazyBear.MCP.Services.ToolRegistry;
using ModelContextProtocol.Server;
namespace LazyBear.MCP.Services.Kubernetes;
[McpServerToolType]
public sealed class K8sConfigTools(
K8sClientProvider clientProvider,
IConfiguration configuration,
ToolRegistryService registry,
ILogger<K8sConfigTools>? logger = null)
: KubernetesToolsBase(clientProvider, configuration, registry, logger)
{
private const int MaxSecretKeyLimit = 100;
[McpServerTool, Description("Список ConfigMap в namespace")]
public async Task<string> ListConfigMaps(
[Description("Namespace Kubernetes")] string? @namespace = null,
CancellationToken cancellationToken = default)
{
if (!TryCheckEnabled("ListConfigMaps", out var enabledError)) return enabledError;
ValidateNamespace(@namespace ?? _defaultNamespace, nameof(@namespace));
var ns = ResolveNamespace(@namespace);
if (!TryGetClient(out var client, out var clientError))
{
return clientError;
}
try
{
var configMaps = await client.CoreV1.ListNamespacedConfigMapAsync(ns, cancellationToken: cancellationToken);
if (configMaps.Items.Count == 0)
{
return $"В namespace '{ns}' configMap не найдены.";
}
var lines = configMaps.Items.Select(cm =>
{
var name = cm.Metadata?.Name ?? "unknown";
var keyCount = cm.Data?.Count ?? 0;
return $"{name}: keys={keyCount}";
});
return $"ConfigMap namespace '{ns}':\n{string.Join('\n', lines)}";
}
catch (Exception ex)
{
return FormatError("list_config_maps", ns, ex);
}
}
[McpServerTool, Description("Получить данные ConfigMap")]
public async Task<string> GetConfigMapData(
[Description("Имя ConfigMap")] string name,
[Description("Namespace Kubernetes")] string? @namespace = null,
CancellationToken cancellationToken = default)
{
if (!TryCheckEnabled("GetConfigMapData", out var enabledError)) return enabledError;
ValidateResourceName(name, nameof(name));
ValidateNamespace(@namespace ?? _defaultNamespace, nameof(@namespace));
var ns = ResolveNamespace(@namespace);
if (!TryGetClient(out var client, out var clientError))
{
return clientError;
}
try
{
var configMap = await client.CoreV1.ReadNamespacedConfigMapAsync(name, ns, cancellationToken: cancellationToken);
if (configMap.Data is null || configMap.Data.Count == 0)
{
return $"ConfigMap '{name}' в namespace '{ns}' не содержит данных.";
}
var lines = configMap.Data.Select(item => $"{item.Key}={item.Value}");
return $"Данные ConfigMap '{name}' namespace '{ns}':\n{string.Join('\n', lines)}";
}
catch (Exception ex)
{
return FormatError("get_config_map_data", ns, ex, name);
}
}
[McpServerTool, Description("Список Secret в namespace (без значений)")]
public async Task<string> ListSecrets(
[Description("Namespace Kubernetes")] string? @namespace = null,
CancellationToken cancellationToken = default)
{
if (!TryCheckEnabled("ListSecrets", out var enabledError)) return enabledError;
ValidateNamespace(@namespace ?? _defaultNamespace, nameof(@namespace));
var ns = ResolveNamespace(@namespace);
if (!TryGetClient(out var client, out var clientError))
{
return clientError;
}
try
{
var secrets = await client.CoreV1.ListNamespacedSecretAsync(ns, cancellationToken: cancellationToken);
if (secrets.Items.Count == 0)
{
return $"В namespace '{ns}' secret не найдены.";
}
var lines = secrets.Items.Select(secret =>
{
var name = secret.Metadata?.Name ?? "unknown";
var type = secret.Type ?? "Opaque";
return $"{name}: type={type}";
});
return $"Secret namespace '{ns}':\n{string.Join('\n', lines)}";
}
catch (Exception ex)
{
return FormatError("list_secrets", ns, ex);
}
}
[McpServerTool, Description("Ключи Secret без значений")]
public async Task<string> GetSecretKeys(
[Description("Имя Secret")] string name,
[Description("Namespace Kubernetes")] string? @namespace = null,
CancellationToken cancellationToken = default)
{
if (!TryCheckEnabled("GetSecretKeys", out var enabledError)) return enabledError;
ValidateResourceName(name, nameof(name));
ValidateNamespace(@namespace ?? _defaultNamespace, nameof(@namespace));
var ns = ResolveNamespace(@namespace);
if (!TryGetClient(out var client, out var clientError))
{
return clientError;
}
try
{
var secret = await client.CoreV1.ReadNamespacedSecretAsync(name, ns, cancellationToken: cancellationToken);
var keys = new HashSet<string>(StringComparer.Ordinal);
if (secret.Data is not null)
{
foreach (var key in secret.Data.Keys)
{
keys.Add(key);
}
}
if (secret.StringData is not null)
{
foreach (var key in secret.StringData.Keys)
{
keys.Add(key);
}
}
if (keys.Count == 0)
{
return $"Secret '{name}' в namespace '{ns}' не содержит ключей.";
}
return $"Ключи Secret '{name}' namespace '{ns}': {string.Join(", ", keys.OrderBy(k => k))}";
}
catch (Exception ex)
{
return FormatError("get_secret_keys", ns, ex, name);
}
}
}