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,23 +1,42 @@
using System.ComponentModel;
using System.Text.Json;
using LazyBear.MCP.Services.ToolRegistry;
using ModelContextProtocol.Server;
using RestSharp;
namespace LazyBear.MCP.Services.Jira;
[McpServerToolType]
public sealed class JiraIssueTools(JiraClientProvider provider, IConfiguration configuration)
public sealed class JiraIssueTools(
JiraClientProvider provider,
IConfiguration configuration,
ToolRegistryService registry)
{
private readonly RestClient? _client = provider.Client;
private readonly string? _clientInitializationError = provider.InitializationError;
private readonly string _token = configuration["Jira:Token"] ?? string.Empty;
private readonly string _defaultProject = configuration["Jira:Project"] ?? string.Empty;
private const string ModuleName = "Jira";
private bool TryCheckEnabled(string toolName, out string error)
{
if (!registry.IsToolEnabled(ModuleName, toolName))
{
error = $"Инструмент '{toolName}' модуля Jira отключён в TUI.";
return false;
}
error = string.Empty;
return true;
}
[McpServerTool, Description("Получить задачу Jira по ключу")]
public async Task<string> GetIssue(
[Description("Ключ задачи, например PROJ-123")] string issueKey,
CancellationToken cancellationToken = default)
{
if (!TryCheckEnabled("GetIssue", out var enabledError)) return enabledError;
if (string.IsNullOrWhiteSpace(issueKey))
{
return "Ключ задачи Jira не задан.";
@@ -60,6 +79,8 @@ public sealed class JiraIssueTools(JiraClientProvider provider, IConfiguration c
[Description("Максимум задач в ответе")] int maxResults = 20,
CancellationToken cancellationToken = default)
{
if (!TryCheckEnabled("ListIssues", out var enabledError)) return enabledError;
if (!TryGetClient(out var client, out var error))
{
return error;
@@ -118,6 +139,8 @@ public sealed class JiraIssueTools(JiraClientProvider provider, IConfiguration c
[Description("Описание задачи")] string? description = null,
CancellationToken cancellationToken = default)
{
if (!TryCheckEnabled("CreateIssue", out var enabledError)) return enabledError;
if (!TryGetClient(out var client, out var error))
{
return error;
@@ -190,6 +213,8 @@ public sealed class JiraIssueTools(JiraClientProvider provider, IConfiguration c
[Description("Новое описание")] string? description = null,
CancellationToken cancellationToken = default)
{
if (!TryCheckEnabled("UpdateIssue", out var enabledError)) return enabledError;
if (string.IsNullOrWhiteSpace(summary) && string.IsNullOrWhiteSpace(description))
{
return $"Нет полей для обновления задачи '{issueKey}'.";
@@ -256,6 +281,8 @@ public sealed class JiraIssueTools(JiraClientProvider provider, IConfiguration c
[Description("Ключ задачи")] string issueKey,
CancellationToken cancellationToken = default)
{
if (!TryCheckEnabled("GetIssueStatuses", out var enabledError)) return enabledError;
if (!TryGetClient(out var client, out var error))
{
return error;
@@ -301,6 +328,8 @@ public sealed class JiraIssueTools(JiraClientProvider provider, IConfiguration c
[Description("Максимум комментариев")] int limit = 20,
CancellationToken cancellationToken = default)
{
if (!TryCheckEnabled("ListIssueComments", out var enabledError)) return enabledError;
if (!TryGetClient(out var client, out var error))
{
return error;
@@ -349,6 +378,8 @@ public sealed class JiraIssueTools(JiraClientProvider provider, IConfiguration c
[Description("Текст комментария")] string body,
CancellationToken cancellationToken = default)
{
if (!TryCheckEnabled("AddComment", out var enabledError)) return enabledError;
if (string.IsNullOrWhiteSpace(body))
{
return "Текст комментария Jira не задан.";