BotSharp/src/Infrastructure/BotSharp.Core.MCP/Functions/McpToolAdapter.cs

102 lines
4.4 KiB
C#
Raw Normal View History

using System.Text.Json;
2025-04-01 01:59:19 +00:00
using BotSharp.Core.MCP.Managers;
2025-03-31 21:43:42 +00:00
using ModelContextProtocol.Client;
2025-03-31 21:43:42 +00:00
namespace BotSharp.Core.MCP.Functions;
public class McpToolAdapter : IFunctionCallback
{
2025-04-15 16:28:37 +00:00
private readonly string _provider;
2025-03-29 02:05:52 +00:00
private readonly McpClientTool _tool;
2025-04-01 01:59:19 +00:00
private readonly McpClientManager _clientManager;
2025-03-31 21:43:42 +00:00
private readonly IServiceProvider _services;
2025-04-15 16:28:37 +00:00
public McpToolAdapter(
IServiceProvider services,
string serverName,
McpClientTool tool,
McpClientManager client)
{
2025-03-31 21:43:42 +00:00
_services = services ?? throw new ArgumentNullException(nameof(services));
_tool = tool ?? throw new ArgumentNullException(nameof(tool));
2025-02-27 04:39:07 +00:00
_clientManager = client ?? throw new ArgumentNullException(nameof(client));
2025-04-15 16:28:37 +00:00
_provider = serverName;
}
2025-04-15 16:28:37 +00:00
public string Provider => _provider;
public string Name => _tool.Name;
public async Task<bool> Execute(RoleDialogModel message)
{
// Convert arguments to dictionary format expected by mcpdotnet
Dictionary<string, object> argDict = JsonToDictionary(message.FunctionArgs);
2025-02-27 04:39:07 +00:00
var currentAgentId = message.CurrentAgentId;
2025-03-31 21:43:42 +00:00
var agentService = _services.GetRequiredService<IAgentService>();
2025-02-27 04:39:07 +00:00
var agent = await agentService.LoadAgent(currentAgentId);
var serverId = agent.McpTools.Where(t => t.Functions.Any(f => f.Name == Name)).FirstOrDefault().ServerId;
var client = await _clientManager.GetMcpClientAsync(serverId);
2025-03-31 21:43:42 +00:00
// Call the tool through mcpdotnet
2025-03-31 21:43:42 +00:00
var result = await client.CallToolAsync(_tool.Name, argDict.IsNullOrEmpty() ? new() : argDict);
// Extract the text content from the result
2025-03-31 21:43:42 +00:00
var json = string.Join("\n", result.Content.Where(c => c.Type == "text").Select(c => c.Text));
message.Content = json;
message.Data = json.JsonContent();
return true;
}
private static Dictionary<string, object> JsonToDictionary(string? json)
{
if (string.IsNullOrEmpty(json))
return [];
using JsonDocument doc = JsonDocument.Parse(json);
JsonElement root = doc.RootElement;
return JsonElementToDictionary(root);
}
private static Dictionary<string, object> JsonElementToDictionary(JsonElement element)
{
Dictionary<string, object> dictionary = [];
if (element.ValueKind == JsonValueKind.Object)
{
foreach (JsonProperty property in element.EnumerateObject())
{
dictionary[property.Name] = JsonElementToValue(property.Value);
}
}
return dictionary;
}
private static object? JsonElementToValue(JsonElement element) => element.ValueKind switch
{
JsonValueKind.Object => JsonElementToDictionary(element),
JsonValueKind.Array => element.EnumerateArray().Select(JsonElementToValue).ToList(),
JsonValueKind.String => element.GetString(),
JsonValueKind.Number when element.TryGetInt32(out int intValue) => intValue,
JsonValueKind.Number when element.TryGetInt64(out long longValue) => longValue,
JsonValueKind.Number when element.TryGetDouble(out double doubleValue) => doubleValue,
JsonValueKind.Number when element.TryGetDecimal(out decimal decimalValue) => decimalValue,
JsonValueKind.Number when element.TryGetByte(out byte byteValue) => byteValue,
JsonValueKind.Number when element.TryGetSByte(out sbyte sbyteValue) => sbyteValue,
JsonValueKind.Number when element.TryGetUInt16(out ushort uint16Value) => uint16Value,
JsonValueKind.Number when element.TryGetUInt32(out uint uint32Value) => uint32Value,
JsonValueKind.Number when element.TryGetUInt64(out ulong uint64Value) => uint64Value,
JsonValueKind.Number when element.TryGetDateTime(out DateTime dateTimeValue) => dateTimeValue,
JsonValueKind.Number when element.TryGetDateTimeOffset(out DateTimeOffset dateTimeOffsetValue) => dateTimeOffsetValue,
JsonValueKind.Number when element.TryGetGuid(out Guid guidValue) => guidValue,
JsonValueKind.Number => element.GetRawText(),
JsonValueKind.True => true,
JsonValueKind.False => false,
JsonValueKind.Null => null,
JsonValueKind.Undefined => string.Empty, // JsonElement is undefined (there is no value).
_ => throw new ArgumentOutOfRangeException(nameof(element.ValueKind), element.ValueKind, "Unexpected JsonValueKind encountered.")
};
}