feat: внедрение RazorConsole TUI с runtime-управлением MCP-инструментами

- Добавлен RazorConsole.Core для интерактивного TUI-дашборда
- ToolRegistryService: живое включение/отключение модулей и отдельных методов
- InMemoryLogSink: кольцевой буфер логов с фильтрацией по модулю
- TUI: 3 таба (Overview, Logs, Settings)
- IToolModule: generic-интерфейс для легкого добавления новых MCP-модулей
- Guard-проверка TryCheckEnabled() во всех существующих инструментах
This commit is contained in:
2026-04-13 17:31:28 +03:00
parent c117d928b0
commit 879becadfe
24 changed files with 826 additions and 11 deletions

View File

@@ -1,13 +1,17 @@
using System.ComponentModel;
using System.Text;
using System.Text.Json;
using LazyBear.MCP.Services.ToolRegistry;
using ModelContextProtocol.Server;
using RestSharp;
namespace LazyBear.MCP.Services.Confluence;
[McpServerToolType]
public sealed class ConfluencePagesTools(ConfluenceClientProvider provider, IConfiguration configuration)
public sealed class ConfluencePagesTools(
ConfluenceClientProvider provider,
IConfiguration configuration,
ToolRegistryService registry)
{
private readonly RestClient? _client = provider.Client;
private readonly string? _clientInitializationError = provider.InitializationError;
@@ -15,11 +19,26 @@ public sealed class ConfluencePagesTools(ConfluenceClientProvider provider, ICon
private readonly string _username = configuration["Confluence:Username"] ?? string.Empty;
private readonly string _spaceKey = configuration["Confluence:SpaceKey"] ?? string.Empty;
private const string ModuleName = "Confluence";
private bool TryCheckEnabled(string toolName, out string error)
{
if (!registry.IsToolEnabled(ModuleName, toolName))
{
error = $"Инструмент '{toolName}' модуля Confluence отключён в TUI.";
return false;
}
error = string.Empty;
return true;
}
[McpServerTool, Description("Получить страницу Confluence по ID")]
public async Task<string> GetPage(
[Description("ID страницы Confluence")] string pageId,
CancellationToken cancellationToken = default)
{
if (!TryCheckEnabled("GetPage", out var enabledError)) return enabledError;
if (string.IsNullOrWhiteSpace(pageId))
{
return "ID страницы Confluence не задан.";
@@ -64,6 +83,8 @@ public sealed class ConfluencePagesTools(ConfluenceClientProvider provider, ICon
[Description("Максимум страниц в ответе")] int maxResults = 20,
CancellationToken cancellationToken = default)
{
if (!TryCheckEnabled("SearchPages", out var enabledError)) return enabledError;
if (!TryGetClient(out var client, out var error))
{
return error;
@@ -122,6 +143,8 @@ public sealed class ConfluencePagesTools(ConfluenceClientProvider provider, ICon
[Description("ID родительской страницы. Опционально")] string? parentId = null,
CancellationToken cancellationToken = default)
{
if (!TryCheckEnabled("CreatePage", out var enabledError)) return enabledError;
if (!TryGetClient(out var client, out var error))
{
return error;
@@ -188,6 +211,8 @@ public sealed class ConfluencePagesTools(ConfluenceClientProvider provider, ICon
[Description("Новое название страницы. Если пусто, не меняется")] string? title = null,
CancellationToken cancellationToken = default)
{
if (!TryCheckEnabled("UpdatePage", out var enabledError)) return enabledError;
if (!TryGetClient(out var client, out var error))
{
return error;
@@ -250,6 +275,8 @@ public sealed class ConfluencePagesTools(ConfluenceClientProvider provider, ICon
[Description("ID страницы для удаления")] string pageId,
CancellationToken cancellationToken = default)
{
if (!TryCheckEnabled("DeletePage", out var enabledError)) return enabledError;
if (!TryGetClient(out var client, out var error))
{
return error;
@@ -279,6 +306,8 @@ public sealed class ConfluencePagesTools(ConfluenceClientProvider provider, ICon
[Description("Ключ пространства")] string spaceKey,
CancellationToken cancellationToken = default)
{
if (!TryCheckEnabled("GetSpace", out var enabledError)) return enabledError;
if (!TryGetClient(out var client, out var error))
{
return error;