Remove McpService and refactor dependency injection for MCP tools

- Deleted the McpService class, which was previously responsible for executing Model Context Protocol (MCP) tools.
- Updated the ApiBootstrap class to change the registration of IMcpService to the new Managing.Mcp.McpService implementation.
- Added new MCP tool implementations for DataTools, BotTools, and IndicatorTools to enhance functionality.
This commit is contained in:
2026-01-03 22:55:27 +07:00
parent 6f55566db3
commit 8ce7650bbf
12 changed files with 3234 additions and 238 deletions

View File

@@ -1,236 +0,0 @@
using Managing.Application.Abstractions.Services;
using Managing.Domain.Users;
using Managing.Mcp.Tools;
using Microsoft.Extensions.Logging;
using static Managing.Common.Enums;
namespace Managing.Application.LLM;
/// <summary>
/// Service for executing Model Context Protocol (MCP) tools
/// </summary>
public class McpService : IMcpService
{
private readonly BacktestTools _backtestTools;
private readonly ILogger<McpService> _logger;
public McpService(BacktestTools backtestTools, ILogger<McpService> logger)
{
_backtestTools = backtestTools;
_logger = logger;
}
public async Task<object> ExecuteToolAsync(User user, string toolName, Dictionary<string, object>? parameters = null)
{
_logger.LogInformation("Executing MCP tool: {ToolName} for user: {UserId}", toolName, user.Id);
try
{
return toolName.ToLowerInvariant() switch
{
"get_backtests_paginated" => await ExecuteGetBacktestsPaginated(user, parameters),
_ => throw new InvalidOperationException($"Unknown tool: {toolName}")
};
}
catch (Exception ex)
{
_logger.LogError(ex, "Error executing MCP tool {ToolName} for user {UserId}", toolName, user.Id);
throw;
}
}
public Task<IEnumerable<McpToolDefinition>> GetAvailableToolsAsync()
{
var tools = new List<McpToolDefinition>
{
new McpToolDefinition
{
Name = "get_backtests_paginated",
Description = "Retrieves paginated backtests with filtering and sorting capabilities. Supports filters for score, winrate, drawdown, tickers, indicators, duration, and trading type.",
Parameters = new Dictionary<string, McpParameterDefinition>
{
["page"] = new McpParameterDefinition
{
Type = "integer",
Description = "Page number (defaults to 1)",
Required = false,
DefaultValue = 1
},
["pageSize"] = new McpParameterDefinition
{
Type = "integer",
Description = "Number of items per page (defaults to 50, max 100)",
Required = false,
DefaultValue = 50
},
["sortBy"] = new McpParameterDefinition
{
Type = "string",
Description = "Field to sort by (Score, WinRate, GrowthPercentage, MaxDrawdown, SharpeRatio, FinalPnl, StartDate, EndDate, PositionCount)",
Required = false,
DefaultValue = "Score"
},
["sortOrder"] = new McpParameterDefinition
{
Type = "string",
Description = "Sort order - 'asc' or 'desc' (defaults to 'desc')",
Required = false,
DefaultValue = "desc"
},
["scoreMin"] = new McpParameterDefinition
{
Type = "number",
Description = "Minimum score filter (0-100)",
Required = false
},
["scoreMax"] = new McpParameterDefinition
{
Type = "number",
Description = "Maximum score filter (0-100)",
Required = false
},
["winrateMin"] = new McpParameterDefinition
{
Type = "integer",
Description = "Minimum winrate filter (0-100)",
Required = false
},
["winrateMax"] = new McpParameterDefinition
{
Type = "integer",
Description = "Maximum winrate filter (0-100)",
Required = false
},
["maxDrawdownMax"] = new McpParameterDefinition
{
Type = "number",
Description = "Maximum drawdown filter",
Required = false
},
["tickers"] = new McpParameterDefinition
{
Type = "string",
Description = "Comma-separated list of tickers to filter by (e.g., 'BTC,ETH,SOL')",
Required = false
},
["indicators"] = new McpParameterDefinition
{
Type = "string",
Description = "Comma-separated list of indicators to filter by",
Required = false
},
["durationMinDays"] = new McpParameterDefinition
{
Type = "number",
Description = "Minimum duration in days",
Required = false
},
["durationMaxDays"] = new McpParameterDefinition
{
Type = "number",
Description = "Maximum duration in days",
Required = false
},
["name"] = new McpParameterDefinition
{
Type = "string",
Description = "Filter by name (contains search)",
Required = false
},
["tradingType"] = new McpParameterDefinition
{
Type = "string",
Description = "Trading type filter (Spot, Futures, BacktestSpot, BacktestFutures, Paper)",
Required = false
}
}
}
};
return Task.FromResult<IEnumerable<McpToolDefinition>>(tools);
}
private async Task<object> ExecuteGetBacktestsPaginated(User user, Dictionary<string, object>? parameters)
{
var page = GetParameterValue<int>(parameters, "page", 1);
var pageSize = GetParameterValue<int>(parameters, "pageSize", 50);
var sortByString = GetParameterValue<string>(parameters, "sortBy", "Score");
var sortOrder = GetParameterValue<string>(parameters, "sortOrder", "desc");
var scoreMin = GetParameterValue<double?>(parameters, "scoreMin", null);
var scoreMax = GetParameterValue<double?>(parameters, "scoreMax", null);
var winrateMin = GetParameterValue<int?>(parameters, "winrateMin", null);
var winrateMax = GetParameterValue<int?>(parameters, "winrateMax", null);
var maxDrawdownMax = GetParameterValue<decimal?>(parameters, "maxDrawdownMax", null);
var tickers = GetParameterValue<string?>(parameters, "tickers", null);
var indicators = GetParameterValue<string?>(parameters, "indicators", null);
var durationMinDays = GetParameterValue<double?>(parameters, "durationMinDays", null);
var durationMaxDays = GetParameterValue<double?>(parameters, "durationMaxDays", null);
var name = GetParameterValue<string?>(parameters, "name", null);
var tradingTypeString = GetParameterValue<string?>(parameters, "tradingType", null);
// Parse sortBy enum
if (!Enum.TryParse<BacktestSortableColumn>(sortByString, true, out var sortBy))
{
sortBy = BacktestSortableColumn.Score;
}
// Parse tradingType enum
TradingType? tradingType = null;
if (!string.IsNullOrWhiteSpace(tradingTypeString) &&
Enum.TryParse<TradingType>(tradingTypeString, true, out var parsedTradingType))
{
tradingType = parsedTradingType;
}
return await _backtestTools.GetBacktestsPaginated(
user,
page,
pageSize,
sortBy,
sortOrder,
scoreMin,
scoreMax,
winrateMin,
winrateMax,
maxDrawdownMax,
tickers,
indicators,
durationMinDays,
durationMaxDays,
name,
tradingType);
}
private T GetParameterValue<T>(Dictionary<string, object>? parameters, string key, T defaultValue)
{
if (parameters == null || !parameters.ContainsKey(key))
{
return defaultValue;
}
try
{
var value = parameters[key];
if (value == null)
{
return defaultValue;
}
// Handle nullable types
var targetType = typeof(T);
var underlyingType = Nullable.GetUnderlyingType(targetType);
if (underlyingType != null)
{
// It's a nullable type
return (T)Convert.ChangeType(value, underlyingType);
}
return (T)Convert.ChangeType(value, targetType);
}
catch
{
return defaultValue;
}
}
}