Files
LazyBearWorks/LazyBear.MCP/Services/Kubernetes/K8sNetworkTools.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

152 lines
5.8 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 K8sNetworkTools(
K8sClientProvider clientProvider,
IConfiguration configuration,
ToolRegistryService registry,
ILogger<K8sNetworkTools>? logger = null)
: KubernetesToolsBase(clientProvider, configuration, registry, logger)
{
[McpServerTool, Description("Список service в namespace")]
public async Task<string> ListServices(
[Description("Namespace Kubernetes")] string? @namespace = null,
CancellationToken cancellationToken = default)
{
if (!TryCheckEnabled("ListServices", 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 services = await client.CoreV1.ListNamespacedServiceAsync(ns, cancellationToken: cancellationToken);
if (services.Items.Count == 0)
{
return $"В namespace '{ns}' service не найдены.";
}
var lines = services.Items.Select(svc =>
{
var name = svc.Metadata?.Name ?? "unknown";
var type = svc.Spec?.Type ?? "ClusterIP";
var clusterIp = svc.Spec?.ClusterIP ?? "-";
var ports = svc.Spec?.Ports is null
? "-"
: string.Join(",", svc.Spec.Ports.Select(p => $"{p.Port}/{p.Protocol}"));
return $"{name}: type={type}, clusterIp={clusterIp}, ports={ports}";
});
return $"Services namespace '{ns}':\n{string.Join('\n', lines)}";
}
catch (Exception ex)
{
return FormatError("list_services", ns, ex);
}
}
[McpServerTool, Description("Детали service")]
public async Task<string> GetServiceDetails(
[Description("Имя service")] string name,
[Description("Namespace Kubernetes")] string? @namespace = null,
CancellationToken cancellationToken = default)
{
if (!TryCheckEnabled("GetServiceDetails", 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 service = await client.CoreV1.ReadNamespacedServiceAsync(name, ns, cancellationToken: cancellationToken);
var type = service.Spec?.Type ?? "ClusterIP";
var clusterIp = service.Spec?.ClusterIP ?? "-";
var externalIps = service.Spec?.ExternalIPs is null ? "-" : string.Join(",", service.Spec.ExternalIPs);
var selector = service.Spec?.Selector is null
? "-"
: string.Join(",", service.Spec.Selector.Select(x => $"{x.Key}={x.Value}"));
var ports = service.Spec?.Ports is null
? "-"
: string.Join(",", service.Spec.Ports.Select(p => $"{p.Name ?? "port"}:{p.Port}->{p.TargetPort}"));
return $"Service '{name}' namespace '{ns}': type={type}, clusterIp={clusterIp}, externalIps={externalIps}, selector={selector}, ports={ports}";
}
catch (Exception ex)
{
return FormatError("get_service_details", ns, ex, name);
}
}
[McpServerTool, Description("Список ingress в namespace")]
public async Task<string> ListIngresses(
[Description("Namespace Kubernetes")] string? @namespace = null,
CancellationToken cancellationToken = default)
{
if (!TryCheckEnabled("ListIngresses", 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 ingresses = await client.NetworkingV1.ListNamespacedIngressAsync(ns, cancellationToken: cancellationToken);
if (ingresses.Items.Count == 0)
{
return $"В namespace '{ns}' ingress не найдены.";
}
var lines = ingresses.Items.Select(ing =>
{
var name = ing.Metadata?.Name ?? "unknown";
var rules = ing.Spec?.Rules is null
? "-"
: string.Join(";", ing.Spec.Rules.Select(r =>
{
var host = string.IsNullOrWhiteSpace(r.Host) ? "*" : r.Host;
var paths = r.Http?.Paths is null
? "-"
: string.Join(",", r.Http.Paths.Select(p =>
{
var serviceName = p.Backend?.Service?.Name ?? "-";
var servicePort = p.Backend?.Service?.Port?.Number?.ToString() ?? p.Backend?.Service?.Port?.Name ?? "-";
return $"{p.Path ?? "/"}->{serviceName}:{servicePort}";
}));
return $"{host}:{paths}";
}));
return $"{name}: rules={rules}";
});
return $"Ingress namespace '{ns}':\n{string.Join('\n', lines)}";
}
catch (Exception ex)
{
return FormatError("list_ingresses", ns, ex);
}
}
}