From bf6b30d3ceeffe45a57d554d88520f1c195b45d3 Mon Sep 17 00:00:00 2001 From: geffzhang Date: Tue, 18 Mar 2025 09:49:36 +0800 Subject: [PATCH] =?UTF-8?q?feat=EF=BC=9A=20use=20directory=20packages?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BotSharp.sln | 33 ++++++- Directory.Packages.props | 6 +- .../BotSharp.MCP/BotSharp.MCP.csproj | 2 +- .../BotSharp.MCP/Functions/McpToolFunction.cs | 5 +- .../BotSharp.MCP/Hooks/MCPResponseHook.cs | 41 ++++++++ .../BotSharp.PizzaBot.MCPServer.csproj | 9 +- tests/BotSharp.PizzaBot.MCPServer/Program.cs | 96 +++++++++---------- .../agent.json | 2 +- 8 files changed, 135 insertions(+), 59 deletions(-) create mode 100644 src/Infrastructure/BotSharp.MCP/Hooks/MCPResponseHook.cs diff --git a/BotSharp.sln b/BotSharp.sln index c6c75391..1e8ac13d 100644 --- a/BotSharp.sln +++ b/BotSharp.sln @@ -127,9 +127,11 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Core.Rules", "src\ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Plugin.DeepSeekAI", "src\Plugins\BotSharp.Plugin.DeepSeekAI\BotSharp.Plugin.DeepSeekAI.csproj", "{AF329442-B48E-4B48-A18A-1C869D1BA6F5}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.MCP", "src\Infrastructure\BotSharp.MCP\BotSharp.MCP.csproj", "{8ED8EEF4-06DD-45F5-AA91-BD2395E901B5}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.MCP", "src\Infrastructure\BotSharp.MCP\BotSharp.MCP.csproj", "{684781D5-3DD4-6A0B-B53F-0A362CD6BB0C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.PizzaBot.MCPServer", "tests\BotSharp.PizzaBot.MCPServer\BotSharp.PizzaBot.MCPServer.csproj", "{AD91B4ED-0623-4710-913E-6D7C893E76EF}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.PizzaBot.MCPServer", "tests\BotSharp.PizzaBot.MCPServer\BotSharp.PizzaBot.MCPServer.csproj", "{8D2AD45F-836A-516F-DE6A-71443CEBB18A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Core.Realtime", "src\Infrastructure\BotSharp.Core.Realtime\BotSharp.Core.Realtime.csproj", "{781F1465-365C-0F22-1775-25025DAFA4C7}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -523,6 +525,30 @@ Global {AF329442-B48E-4B48-A18A-1C869D1BA6F5}.Release|Any CPU.Build.0 = Release|Any CPU {AF329442-B48E-4B48-A18A-1C869D1BA6F5}.Release|x64.ActiveCfg = Release|Any CPU {AF329442-B48E-4B48-A18A-1C869D1BA6F5}.Release|x64.Build.0 = Release|Any CPU + {684781D5-3DD4-6A0B-B53F-0A362CD6BB0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {684781D5-3DD4-6A0B-B53F-0A362CD6BB0C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {684781D5-3DD4-6A0B-B53F-0A362CD6BB0C}.Debug|x64.ActiveCfg = Debug|Any CPU + {684781D5-3DD4-6A0B-B53F-0A362CD6BB0C}.Debug|x64.Build.0 = Debug|Any CPU + {684781D5-3DD4-6A0B-B53F-0A362CD6BB0C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {684781D5-3DD4-6A0B-B53F-0A362CD6BB0C}.Release|Any CPU.Build.0 = Release|Any CPU + {684781D5-3DD4-6A0B-B53F-0A362CD6BB0C}.Release|x64.ActiveCfg = Release|Any CPU + {684781D5-3DD4-6A0B-B53F-0A362CD6BB0C}.Release|x64.Build.0 = Release|Any CPU + {8D2AD45F-836A-516F-DE6A-71443CEBB18A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8D2AD45F-836A-516F-DE6A-71443CEBB18A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8D2AD45F-836A-516F-DE6A-71443CEBB18A}.Debug|x64.ActiveCfg = Debug|Any CPU + {8D2AD45F-836A-516F-DE6A-71443CEBB18A}.Debug|x64.Build.0 = Debug|Any CPU + {8D2AD45F-836A-516F-DE6A-71443CEBB18A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8D2AD45F-836A-516F-DE6A-71443CEBB18A}.Release|Any CPU.Build.0 = Release|Any CPU + {8D2AD45F-836A-516F-DE6A-71443CEBB18A}.Release|x64.ActiveCfg = Release|Any CPU + {8D2AD45F-836A-516F-DE6A-71443CEBB18A}.Release|x64.Build.0 = Release|Any CPU + {781F1465-365C-0F22-1775-25025DAFA4C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {781F1465-365C-0F22-1775-25025DAFA4C7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {781F1465-365C-0F22-1775-25025DAFA4C7}.Debug|x64.ActiveCfg = Debug|Any CPU + {781F1465-365C-0F22-1775-25025DAFA4C7}.Debug|x64.Build.0 = Debug|Any CPU + {781F1465-365C-0F22-1775-25025DAFA4C7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {781F1465-365C-0F22-1775-25025DAFA4C7}.Release|Any CPU.Build.0 = Release|Any CPU + {781F1465-365C-0F22-1775-25025DAFA4C7}.Release|x64.ActiveCfg = Release|Any CPU + {781F1465-365C-0F22-1775-25025DAFA4C7}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -584,6 +610,9 @@ Global {F812BAAE-5A7D-4DF7-8E71-70696B51C61F} = {E29DC6C4-5E57-48C5-BCB0-6B8F84782749} {AFD64412-4D6A-452E-82A2-79E5D8842E29} = {E29DC6C4-5E57-48C5-BCB0-6B8F84782749} {AF329442-B48E-4B48-A18A-1C869D1BA6F5} = {D5293208-2BEF-42FC-A64C-5954F61720BA} + {684781D5-3DD4-6A0B-B53F-0A362CD6BB0C} = {E29DC6C4-5E57-48C5-BCB0-6B8F84782749} + {8D2AD45F-836A-516F-DE6A-71443CEBB18A} = {32FAFFFE-A4CB-4FEE-BF7C-84518BBC6DCC} + {781F1465-365C-0F22-1775-25025DAFA4C7} = {E29DC6C4-5E57-48C5-BCB0-6B8F84782749} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {A9969D89-C98B-40A5-A12B-FC87E55B3A19} diff --git a/Directory.Packages.props b/Directory.Packages.props index aa76cf61..80231ce1 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -6,6 +6,7 @@ + @@ -13,13 +14,15 @@ - + + + @@ -106,6 +109,7 @@ + diff --git a/src/Infrastructure/BotSharp.MCP/BotSharp.MCP.csproj b/src/Infrastructure/BotSharp.MCP/BotSharp.MCP.csproj index e9727f7f..037ed776 100644 --- a/src/Infrastructure/BotSharp.MCP/BotSharp.MCP.csproj +++ b/src/Infrastructure/BotSharp.MCP/BotSharp.MCP.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/Infrastructure/BotSharp.MCP/Functions/McpToolFunction.cs b/src/Infrastructure/BotSharp.MCP/Functions/McpToolFunction.cs index 97ec724f..0e755df5 100644 --- a/src/Infrastructure/BotSharp.MCP/Functions/McpToolFunction.cs +++ b/src/Infrastructure/BotSharp.MCP/Functions/McpToolFunction.cs @@ -2,6 +2,7 @@ using BotSharp.Abstraction.Agents; using BotSharp.Abstraction.Agents.Models; using BotSharp.Abstraction.Conversations.Models; using BotSharp.Abstraction.Functions; +using BotSharp.Abstraction.Utilities; using McpDotNet.Client; using McpDotNet.Protocol.Types; using Microsoft.Extensions.DependencyInjection; @@ -49,8 +50,8 @@ public class McpToolFunction : IFunctionCallback var json = string.Join("\n", result.Content .Where(c => c.Type == "text") .Select(c => c.Text)); - message.Content = json; - message.Data = JsonSerializer.Deserialize(json,typeof(object)); + message.Content = json.JsonContent(); + return true; } diff --git a/src/Infrastructure/BotSharp.MCP/Hooks/MCPResponseHook.cs b/src/Infrastructure/BotSharp.MCP/Hooks/MCPResponseHook.cs new file mode 100644 index 00000000..0b939f2e --- /dev/null +++ b/src/Infrastructure/BotSharp.MCP/Hooks/MCPResponseHook.cs @@ -0,0 +1,41 @@ +using BotSharp.Abstraction.Agents.Enums; +using BotSharp.Abstraction.Agents.Settings; +using BotSharp.Abstraction.Agents; +using BotSharp.Abstraction.Conversations; +using BotSharp.Abstraction.Conversations.Models; +using BotSharp.Abstraction.Infrastructures.Enums; +using BotSharp.Abstraction.Translation; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Text.Json; + +namespace BotSharp.MCP.Hooks; + +public class MCPResponseHook : ConversationHookBase +{ + private readonly IServiceProvider _services; + private readonly IConversationStateService _states; + private const string AIAssistant = BuiltInAgentId.AIAssistant; + + public MCPResponseHook(IServiceProvider services, + IConversationStateService states) + { + _services = services; + _states = states; + } + public override async Task OnResponseGenerated(RoleDialogModel message) + { + var agentService = _services.GetRequiredService(); + var agent = await agentService.LoadAgent(message.CurrentAgentId); + if(agent.McpTools.Any(item => item.Functions.Any(x=> x.Name == message.FunctionName))) + { + message.Data = JsonSerializer.Deserialize(message.Content, typeof(object)); + } + await base.OnResponseGenerated(message); + } +} + diff --git a/tests/BotSharp.PizzaBot.MCPServer/BotSharp.PizzaBot.MCPServer.csproj b/tests/BotSharp.PizzaBot.MCPServer/BotSharp.PizzaBot.MCPServer.csproj index 1158e06a..03b092b1 100644 --- a/tests/BotSharp.PizzaBot.MCPServer/BotSharp.PizzaBot.MCPServer.csproj +++ b/tests/BotSharp.PizzaBot.MCPServer/BotSharp.PizzaBot.MCPServer.csproj @@ -8,9 +8,10 @@ enable - - - - + + + + + diff --git a/tests/BotSharp.PizzaBot.MCPServer/Program.cs b/tests/BotSharp.PizzaBot.MCPServer/Program.cs index 5e8633f8..b2477c99 100644 --- a/tests/BotSharp.PizzaBot.MCPServer/Program.cs +++ b/tests/BotSharp.PizzaBot.MCPServer/Program.cs @@ -1,3 +1,4 @@ +using McpDotNet.Protocol.Messages; using McpDotNet.Protocol.Transport; using McpDotNet.Protocol.Types; using McpDotNet.Server; @@ -20,30 +21,42 @@ namespace BotSharp.PizzaBot.MCPServer ServerInfo = new Implementation() { Name = "PizzaServer", Version = "1.0.0" }, Capabilities = new ServerCapabilities() { - Tools = new(), - Resources = new(), - Prompts = new(), + //Tools = ConfigureTools(), }, - ProtocolVersion = "2024-11-05" + ProtocolVersion = "2024-11-05", + ServerInstructions = "This is a test server with only stub functionality" }; var loggerFactory = CreateLoggerFactory(); - McpServerFactory factory = new McpServerFactory(new StdioServerTransport("PizzaServer", loggerFactory), options, loggerFactory, - "This is a test server with only stub functionality"); + McpServerFactory factory = new McpServerFactory(new StdioServerTransport("PizzaServer", loggerFactory), options, loggerFactory); IMcpServer server = factory.CreateServer(); + ConfigureTools(server); Console.WriteLine("Server object created, registering handlers."); - #region Tools - server.ListToolsHandler = (request, cancellationToken) => + Console.WriteLine("Server initialized."); + + await server.StartAsync(); + + Console.WriteLine("Server started."); + + // Run until process is stopped by the client (parent process) + while (true) { - return Task.FromResult(new ListToolsResult() + await Task.Delay(1000); + } + } + + private static void ConfigureTools(IMcpServer server) + { + server.ListToolsHandler = (request, cancellationToken) => { - Tools = - [ - new Tool() + return Task.FromResult(new ListToolsResult() + { + Tools = [ + new Tool() { Name = "make_payment", - Description = "call this function to make payment", + Description = "call this function to make payment", InputSchema = new JsonSchema() { Type = "object", @@ -51,9 +64,9 @@ namespace BotSharp.PizzaBot.MCPServer { ["order_number"] = new JsonSchemaProperty() { Type = "string", Description = "order number." }, ["total_amount"] = new JsonSchemaProperty() { Type = "string", Description = "total amount." }, - }, + }, Required = new List() { "order_number", "total_amount" } - }, + }, }, new Tool() { @@ -63,7 +76,7 @@ namespace BotSharp.PizzaBot.MCPServer { Type = "object", Properties = new Dictionary() - { + { ["pizza_type"] = new JsonSchemaProperty() { Type = "string", Description = "The pizza type." }, ["quantity"] = new JsonSchemaProperty() { Type = "string", Description = "quantity of pizza." }, @@ -79,7 +92,7 @@ namespace BotSharp.PizzaBot.MCPServer { Type = "object", Properties = new Dictionary() - { + { ["pizza_type"] = new JsonSchemaProperty() { Type = "string", Description = "The pizza type." }, ["quantity"] = new JsonSchemaProperty() { Type = "number", Description = "quantity of pizza." }, ["unit_price"] = new JsonSchemaProperty() { Type = "number", Description = "unit price" }, @@ -88,19 +101,19 @@ namespace BotSharp.PizzaBot.MCPServer Required = new List(){"pizza_type", "quantity", "unit_price" } } } - ] - }); - }; + ] + }); + }; server.CallToolHandler = async (request, cancellationToken) => { - if (request.Name == "make_payment") + if (request.Params.Name == "make_payment") { - if (request.Arguments is null || !request.Arguments.TryGetValue("order_number", out var order_number)) + if (request.Params.Arguments is null || !request.Params.Arguments.TryGetValue("order_number", out var order_number)) { throw new McpServerException("Missing required argument 'order_number'"); } - if (request.Arguments is null || !request.Arguments.TryGetValue("total_amount", out var total_amount)) + if (request.Params.Arguments is null || !request.Params.Arguments.TryGetValue("total_amount", out var total_amount)) { throw new McpServerException("Missing required argument 'total_amount'"); } @@ -113,17 +126,17 @@ namespace BotSharp.PizzaBot.MCPServer var jsonMessage = JsonSerializer.Serialize(message, jso); return new CallToolResponse() - { - Content = [new Content() { Text = jsonMessage , Type = "text" }] + { + Content = [new Content() { Text = jsonMessage, Type = "text" }] }; } - else if(request.Name == "get_pizza_prices") + else if (request.Params.Name == "get_pizza_prices") { - if (request.Arguments is null || !request.Arguments.TryGetValue("pizza_type", out var pizza_type)) + if (request.Params.Arguments is null || !request.Params.Arguments.TryGetValue("pizza_type", out var pizza_type)) { throw new McpServerException("Missing required argument 'pizza_type'"); } - if (request.Arguments is null || !request.Arguments.TryGetValue("quantity", out var quantity)) + if (request.Params.Arguments is null || !request.Params.Arguments.TryGetValue("quantity", out var quantity)) { throw new McpServerException("Missing required argument 'quantity'"); } @@ -139,23 +152,23 @@ namespace BotSharp.PizzaBot.MCPServer Content = [new Content() { Text = jsonMessage, Type = "text" }] }; } - else if (request.Name == "place_an_order") + else if (request.Params.Name == "place_an_order") { - if (request.Arguments is null || !request.Arguments.TryGetValue("pizza_type", out var pizza_type)) + if (request.Params.Arguments is null || !request.Params.Arguments.TryGetValue("pizza_type", out var pizza_type)) { throw new McpServerException("Missing required argument 'pizza_type'"); } - if (request.Arguments is null || !request.Arguments.TryGetValue("quantity", out var quantity)) + if (request.Params.Arguments is null || !request.Params.Arguments.TryGetValue("quantity", out var quantity)) { throw new McpServerException("Missing required argument 'quantity'"); } - if (request.Arguments is null || !request.Arguments.TryGetValue("unit_price", out var unit_price)) + if (request.Params.Arguments is null || !request.Params.Arguments.TryGetValue("unit_price", out var unit_price)) { throw new McpServerException("Missing required argument 'unit_price'"); } dynamic message = new ExpandoObject(); message.order_number = "P123-01"; - message.Content = "The order number is P123-01"; + message.Content = "The order number is P123-01"; // Serialize the message to JSON var jso = new JsonSerializerOptions() { WriteIndented = true }; var jsonMessage = JsonSerializer.Serialize(message, jso); @@ -166,22 +179,9 @@ namespace BotSharp.PizzaBot.MCPServer } else { - throw new McpServerException($"Unknown tool: {request.Name}"); + throw new McpServerException($"Unknown tool: {request.Params.Name}"); } - }; - #endregion - - Console.WriteLine("Server initialized."); - - await server.StartAsync(); - - Console.WriteLine("Server started."); - - // Run until process is stopped by the client (parent process) - while (true) - { - await Task.Delay(1000); - } + }; } private static ILoggerFactory CreateLoggerFactory() diff --git a/tests/BotSharp.Plugin.PizzaBot/data/agents/c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd/agent.json b/tests/BotSharp.Plugin.PizzaBot/data/agents/c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd/agent.json index 7ec18d46..9699d5ab 100644 --- a/tests/BotSharp.Plugin.PizzaBot/data/agents/c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd/agent.json +++ b/tests/BotSharp.Plugin.PizzaBot/data/agents/c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd/agent.json @@ -20,6 +20,6 @@ } ] } - ] + ], "labels": [ "experiment" ] } \ No newline at end of file