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

107 lines
4.6 KiB
C#

using BotSharp.Abstraction.Agents;
using BotSharp.Abstraction.Conversations.Models;
using BotSharp.Abstraction.Functions;
using BotSharp.Abstraction.Utilities;
using Microsoft.Extensions.DependencyInjection;
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol.Types;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
namespace BotSharp.Core.Mcp.Functions;
public class McpToolAdapter : IFunctionCallback
{
private readonly Tool _tool;
private readonly MCPClientManager _clientManager;
private readonly IServiceProvider _serviceProvider;
public McpToolAdapter(IServiceProvider provider, Tool tool, MCPClientManager client)
{
_serviceProvider = provider ?? throw new ArgumentNullException(nameof(provider));
_tool = tool ?? throw new ArgumentNullException(nameof(tool));
_clientManager = client ?? throw new ArgumentNullException(nameof(client));
}
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);
var currentAgentId = message.CurrentAgentId;
var agentService = _serviceProvider.GetRequiredService<IAgentService>();
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);
// Call the tool through mcpdotnet
var result = await client.CallToolAsync(
_tool.Name,
argDict.Count == 0 ? new() : argDict
);
// Extract the text content from the result
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.")
};
}