This commit is contained in:
Wenbo Cao 2025-03-31 15:16:29 -05:00
commit f5bd6a9212
11 changed files with 75 additions and 22 deletions

View file

@ -19,8 +19,7 @@
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="8.0.1" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="System.ComponentModel.Annotations" Version="5.0.0" />
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="8.0.0" />
<PackageVersion Include="System.Linq.AsyncEnumerable" Version="10.0.0-preview.1.25080.5" />
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="8.0.0" />
<PackageVersion Include="System.Memory.Data" Version="8.0.0" />
<PackageVersion Include="System.Text.Json" Version="8.0.5" />
<PackageVersion Include="Serilog.Sinks.Console" Version="6.0.0" />
@ -110,8 +109,7 @@
<PackageVersion Include="MSTest.TestFramework" Version="3.1.1" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageVersion Include="ModelContextProtocol" Version="0.1.0-preview.2" />
<PackageVersion Include="System.Linq.Async" Version="6.0.1" />
<PackageVersion Include="ModelContextProtocol" Version="0.1.0-preview.2" />
</ItemGroup>
<ItemGroup>
<PackageVersion Include="BotSharp.Core" Version="$(BotSharpVersion)" />

View file

@ -7,5 +7,7 @@ public enum BroswerActionEnum
Typing = 3,
Hover = 4,
Scroll = 5,
DragAndDrop = 6
DragAndDrop = 6,
DropDown = 7,
FileUpload = 8
}

View file

@ -12,6 +12,8 @@ public class ElementActionArgs
[JsonPropertyName("content")]
public string? Content { get; set; }
[JsonPropertyName("file_urls")]
public string[] FileUrl { get; set; }
public ElementPosition? Position { get; set; }

View file

@ -1,4 +1,5 @@
using BotSharp.Abstraction.Functions.Models;
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol.Types;
using System;
using System.Collections.Generic;
@ -8,15 +9,15 @@ namespace BotSharp.Core.MCP;
internal static class AIFunctionUtilities
{
public static FunctionDef MapToFunctionDef(Tool tool)
public static FunctionDef MapToFunctionDef(McpClientTool tool)
{
if (tool == null)
{
throw new ArgumentNullException(nameof(tool));
}
var properties = tool.InputSchema.GetProperty("properties");
var required = tool.InputSchema.GetProperty("required");
var properties = tool.JsonSchema.GetProperty("properties");
var required = tool.JsonSchema.GetProperty("required");
FunctionDef funDef = new FunctionDef
{

View file

@ -10,7 +10,6 @@
<ItemGroup>
<PackageReference Include="ModelContextProtocol" />
<PackageReference Include="System.Linq.AsyncEnumerable" />
</ItemGroup>
<ItemGroup>

View file

@ -15,11 +15,11 @@ namespace BotSharp.Core.Mcp.Functions;
public class McpToolAdapter : IFunctionCallback
{
private readonly Tool _tool;
private readonly McpClientTool _tool;
private readonly MCPClientManager _clientManager;
private readonly IServiceProvider _serviceProvider;
public McpToolAdapter(IServiceProvider provider, Tool tool, MCPClientManager client)
public McpToolAdapter(IServiceProvider provider, McpClientTool tool, MCPClientManager client)
{
_serviceProvider = provider ?? throw new ArgumentNullException(nameof(provider));
_tool = tool ?? throw new ArgumentNullException(nameof(tool));

View file

@ -55,13 +55,13 @@ public class MCPToolAgentHook : AgentHookBase
var mcpClient = await mcpClientManager.GetMcpClientAsync(item.ServerId);
if (mcpClient != null)
{
/*var tools = await mcpClient.ListToolsAsync().ToListAsync();
var tools = await mcpClient.ListToolsAsync();
var toolnames = item.Functions.Select(x => x.Name).ToList();
foreach (var tool in tools.Where(x => toolnames.Contains(x.Name, StringComparer.OrdinalIgnoreCase)))
foreach (var tool in tools.ToList().Where(x => toolnames.Contains(x.Name, StringComparer.OrdinalIgnoreCase)))
{
var funDef = AIFunctionUtilities.MapToFunctionDef(tool);
functionDefs.Add(funDef);
}*/
}
}
}

View file

@ -45,7 +45,7 @@ public class McpPlugin : IBotSharpPlugin
private async Task RegisterFunctionCall(IServiceCollection services, McpServerConfig server)
{
var client = await clientManager.GetMcpClientAsync(server.Id);
/*var tools = await client.ListToolsAsync().ToListAsync();
var tools = await client.ListToolsAsync();
foreach (var tool in tools)
{
@ -56,6 +56,6 @@ public class McpPlugin : IBotSharpPlugin
var funcTool = new McpToolAdapter(provider, tool, clientManager);
return funcTool;
});
}*/
}
}
}

View file

@ -175,14 +175,13 @@ public class AgentController : ControllerBase
}
[HttpGet("/agent/mcp/tools")]
public async Task<IEnumerable<Tool>> GetMCPTools(string serverId)
public async Task<IEnumerable<McpClientTool>> GetMCPTools(string serverId)
{
var client = await _clientManager.GetMcpClientAsync(serverId);
throw new NotImplementedException();
/*var tools = await client.ListToolsAsync().ToListAsync();
var tools = await client.ListToolsAsync();
return tools.Where(x => !string.IsNullOrWhiteSpace(x.Name))
.OrderBy(x => x.Name).ToList();*/
.OrderBy(x => x.Name).ToList();
}
[HttpGet("/agent/labels")]

View file

@ -1,3 +1,6 @@
using System.IO;
using System.Net.Http;
namespace BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver;
public partial class PlaywrightWebDriver
@ -50,6 +53,18 @@ public partial class PlaywrightWebDriver
});
}
}
else if (action.Action == BroswerActionEnum.DropDown)
{
await locator.ClickAsync();
var optionLocator = page.Locator($"//div[text()='{action.Content}']");
var optionCount = await optionLocator.CountAsync(); ;
if (optionCount == 0)
{
Serilog.Log.Error($"Dropdown option not found: {action.Content}");
return;
}
await optionLocator.First.ClickAsync();
}
else if (action.Action == BroswerActionEnum.InputText)
{
await locator.FillAsync(action.Content);
@ -63,6 +78,36 @@ public partial class PlaywrightWebDriver
await locator.PressAsync(action.PressKey);
}
}
else if (action.Action == BroswerActionEnum.FileUpload)
{
if (action.FileUrl.Length == 0)
{
return;
}
var fileChooser = await page.RunAndWaitForFileChooserAsync(async () =>
{
await locator.ClickAsync();
});
var guid = Guid.NewGuid().ToString();
var directory = Path.Combine(Path.GetTempPath(), guid);
if (Directory.Exists(directory))
{
Directory.Delete(directory, true);
}
Directory.CreateDirectory(directory);
var localPaths = new List<string>();
using var httpClient = new HttpClient();
foreach (var fileUrl in action.FileUrl)
{
var bytes = await httpClient.GetByteArrayAsync(fileUrl);
var fileName = new Uri(fileUrl).AbsolutePath;
var localPath = Path.Combine(directory, Path.GetFileName(fileName));
await File.WriteAllBytesAsync(localPath, bytes);
await Task.Delay(2000);
localPaths.Add(localPath);
}
await fileChooser.SetFilesAsync(localPaths);
}
else if (action.Action == BroswerActionEnum.Typing)
{
await locator.PressSequentiallyAsync(action.Content);
@ -128,7 +173,6 @@ public partial class PlaywrightWebDriver
await Task.Delay(1000 * action.WaitTime);
}
}
public static List<int> GetVelocityTrack(float distance)
{
// Initialize the track list to store the movement distances

View file

@ -11,12 +11,20 @@
"action": {
"type": "string",
"description": "action to perform on element",
"enum": [ "Click", "InputText" ]
"enum": [ "Click", "InputText", "DropDown", "FileUpload" ]
},
"content": {
"type": "string",
"description": "content to input in element"
},
"file_urls": {
"type": "array",
"description": "The output format must be string array of file urls.",
"items": {
"type": "string",
"description": "file url"
}
},
"press_key": {
"type": "string",
"enum": [ "Enter" ],