diff --git a/BotSharp.sln b/BotSharp.sln
index c6c75391..53eb3da9 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.Core.Realtime", "src\Infrastructure\BotSharp.Core.Realtime\BotSharp.Core.Realtime.csproj", "{781F1465-365C-0F22-1775-25025DAFA4C7}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.PizzaBot.MCPServer", "tests\BotSharp.PizzaBot.MCPServer\BotSharp.PizzaBot.MCPServer.csproj", "{8D2AD45F-836A-516F-DE6A-71443CEBB18A}"
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
+ {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
+ {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
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}
+ {781F1465-365C-0F22-1775-25025DAFA4C7} = {E29DC6C4-5E57-48C5-BCB0-6B8F84782749}
+ {8D2AD45F-836A-516F-DE6A-71443CEBB18A} = {32FAFFFE-A4CB-4FEE-BF7C-84518BBC6DCC}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A9969D89-C98B-40A5-A12B-FC87E55B3A19}
diff --git a/Directory.Packages.props b/Directory.Packages.props
index f49939eb..c56bade3 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -6,6 +6,7 @@
+
@@ -13,13 +14,16 @@
+
+
+
@@ -106,6 +110,8 @@
+
+
diff --git a/src/BotSharp.AppHost/BotSharp.AppHost.csproj b/src/BotSharp.AppHost/BotSharp.AppHost.csproj
index 68673f7d..26ff84a8 100644
--- a/src/BotSharp.AppHost/BotSharp.AppHost.csproj
+++ b/src/BotSharp.AppHost/BotSharp.AppHost.csproj
@@ -15,6 +15,7 @@
+
diff --git a/src/BotSharp.AppHost/Program.cs b/src/BotSharp.AppHost/Program.cs
index 9c371bf4..4c54ed11 100644
--- a/src/BotSharp.AppHost/Program.cs
+++ b/src/BotSharp.AppHost/Program.cs
@@ -2,6 +2,8 @@ var builder = DistributedApplication.CreateBuilder(args);
var apiService = builder.AddProject("apiservice")
.WithExternalHttpEndpoints();
+var mcpService = builder.AddProject("mcpservice")
+ .WithExternalHttpEndpoints();
builder.AddNpmApp("BotSharpUI", "../../../BotSharp-UI")
.WithReference(apiService)
diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentField.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentField.cs
index 413f9eb8..2ecddf0c 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentField.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentField.cs
@@ -19,7 +19,7 @@ public enum AgentField
Sample,
LlmConfig,
Utility,
- Acp,
+ McpTool,
KnowledgeBase,
Rule,
MaxMessageCount
diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs
index 7e9da19c..dd8743d4 100644
--- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs
+++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs
@@ -197,7 +197,7 @@ public partial class AgentService
var samples = GetSamplesFromFile(dir);
return agent.SetInstruction(defaultInstruction)
.SetChannelInstructions(channelInstructions)
- .SetTemplates(templates)
+ .SetTemplates(templates)
.SetFunctions(functions)
.SetResponses(responses)
.SetSamples(samples);
diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs
index ae401508..4da61828 100644
--- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs
+++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs
@@ -60,7 +60,7 @@ namespace BotSharp.Core.Repository
case AgentField.Utility:
UpdateAgentUtilities(agent.Id, agent.MergeUtility, agent.Utilities);
break;
- case AgentField.Acp:
+ case AgentField.McpTool:
UpdateAgentMcpTools(agent.Id, agent.McpTools);
break;
case AgentField.KnowledgeBase:
diff --git a/src/Infrastructure/BotSharp.MCP/AIFunctionUtilities.cs b/src/Infrastructure/BotSharp.MCP/AIFunctionUtilities.cs
index e86cbf92..8a409f67 100644
--- a/src/Infrastructure/BotSharp.MCP/AIFunctionUtilities.cs
+++ b/src/Infrastructure/BotSharp.MCP/AIFunctionUtilities.cs
@@ -1,9 +1,8 @@
using BotSharp.Abstraction.Functions.Models;
-using McpDotNet.Protocol.Types;
+using ModelContextProtocol.Protocol.Types;
using System;
using System.Collections.Generic;
using System.Text.Json;
-using System.Text.Json.Serialization;
namespace BotSharp.Core.MCP;
@@ -16,19 +15,19 @@ internal static class AIFunctionUtilities
throw new ArgumentNullException(nameof(tool));
}
- var properties = tool.InputSchema?.Properties;
- var required = tool.InputSchema?.Required ?? new List();
+ var properties = tool.InputSchema.GetProperty("properties");
+ var required = tool.InputSchema.GetProperty("required");
FunctionDef funDef = new FunctionDef
{
Name = tool.Name,
- Description = tool.Description?? string.Empty,
+ Description = tool.Description ?? string.Empty,
Type = "function",
Parameters = new FunctionParametersDef
{
Type = "object",
- Properties = properties != null ? JsonSerializer.SerializeToDocument(properties) : JsonDocument.Parse("{}"),
- Required = required
+ Properties = JsonDocument.Parse(properties.GetRawText()),
+ Required = JsonSerializer.Deserialize>(required.GetRawText())
}
};
diff --git a/src/Infrastructure/BotSharp.MCP/BotSharp.MCP.csproj b/src/Infrastructure/BotSharp.MCP/BotSharp.MCP.csproj
index e9727f7f..94aba37c 100644
--- a/src/Infrastructure/BotSharp.MCP/BotSharp.MCP.csproj
+++ b/src/Infrastructure/BotSharp.MCP/BotSharp.MCP.csproj
@@ -9,7 +9,8 @@
-
+
+
diff --git a/src/Infrastructure/BotSharp.MCP/Functions/McpToolFunction.cs b/src/Infrastructure/BotSharp.MCP/Functions/McpToolFunction.cs
index 97ec724f..1f6f2c60 100644
--- a/src/Infrastructure/BotSharp.MCP/Functions/McpToolFunction.cs
+++ b/src/Infrastructure/BotSharp.MCP/Functions/McpToolFunction.cs
@@ -1,11 +1,10 @@
using BotSharp.Abstraction.Agents;
-using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Conversations.Models;
using BotSharp.Abstraction.Functions;
-using McpDotNet.Client;
-using McpDotNet.Protocol.Types;
+using BotSharp.Abstraction.Utilities;
using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
+using ModelContextProtocol.Client;
+using ModelContextProtocol.Protocol.Types;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -38,7 +37,7 @@ public class McpToolFunction : IFunctionCallback
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.Factory.GetClientAsync(serverId);
+ var client = await _clientManager.GetMcpClientAsync(serverId);
// Call the tool through mcpdotnet
var result = await client.CallToolAsync(
_tool.Name,
@@ -50,7 +49,7 @@ public class McpToolFunction : IFunctionCallback
.Where(c => c.Type == "text")
.Select(c => c.Text));
message.Content = json;
- message.Data = JsonSerializer.Deserialize(json,typeof(object));
+ message.Data = 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..c5470582
--- /dev/null
+++ b/src/Infrastructure/BotSharp.MCP/Hooks/MCPResponseHook.cs
@@ -0,0 +1,36 @@
+using BotSharp.Abstraction.Agents;
+using BotSharp.Abstraction.Conversations;
+using BotSharp.Abstraction.Conversations.Models;
+using Microsoft.Extensions.DependencyInjection;
+using System;
+using System.Linq;
+using System.Text.Json;
+using System.Threading.Tasks;
+
+namespace BotSharp.MCP.Hooks;
+
+public class MCPResponseHook : ConversationHookBase
+{
+ private readonly IServiceProvider _services;
+ private readonly IConversationStateService _states;
+
+ public MCPResponseHook(IServiceProvider services,
+ IConversationStateService states)
+ {
+ _services = services;
+ _states = states;
+ }
+ public override async Task OnResponseGenerated(RoleDialogModel message)
+ {
+ var agentService = _services.GetRequiredService();
+ var state = _services.GetRequiredService();
+ var agent = await agentService.LoadAgent(message.CurrentAgentId);
+ if(agent.McpTools.Any(item => item.Functions.Any(x=> x.Name == message.FunctionName)))
+ {
+ var data = JsonDocument.Parse(JsonSerializer.Serialize(message.Data));
+ state.SaveStateByArgs(data);
+ }
+ await base.OnResponseGenerated(message);
+ }
+}
+
diff --git a/src/Infrastructure/BotSharp.MCP/Hooks/MCPToolAgentHook.cs b/src/Infrastructure/BotSharp.MCP/Hooks/MCPToolAgentHook.cs
index 32b10d0a..e56ab68e 100644
--- a/src/Infrastructure/BotSharp.MCP/Hooks/MCPToolAgentHook.cs
+++ b/src/Infrastructure/BotSharp.MCP/Hooks/MCPToolAgentHook.cs
@@ -7,6 +7,7 @@ using BotSharp.Abstraction.Functions.Models;
using BotSharp.Core.Mcp;
using BotSharp.Core.MCP;
using Microsoft.Extensions.DependencyInjection;
+using ModelContextProtocol.Client;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -51,12 +52,12 @@ public class MCPToolAgentHook : AgentHookBase
var mcps = agent.McpTools;
foreach (var item in mcps)
{
- var mcpClient = await mcpClientManager.Factory.GetClientAsync(item.ServerId);
+ var mcpClient = await mcpClientManager.GetMcpClientAsync(item.ServerId);
if (mcpClient != null)
{
- var tools = await mcpClient.ListToolsAsync();
+ var tools = await mcpClient.ListToolsAsync().ToListAsync();
var funcnames = item.Functions.Select(x => x.Name).ToList();
- foreach (var tool in tools.Tools.Where(x => funcnames.Contains(x.Name, StringComparer.OrdinalIgnoreCase)))
+ foreach (var tool in tools.Where(x => funcnames.Contains(x.Name, StringComparer.OrdinalIgnoreCase)))
{
var funDef = AIFunctionUtilities.MapToFunctionDef(tool);
functionDefs.Add(funDef);
diff --git a/src/Infrastructure/BotSharp.MCP/MCPClientManager.cs b/src/Infrastructure/BotSharp.MCP/MCPClientManager.cs
index bbe70205..78d94a1a 100644
--- a/src/Infrastructure/BotSharp.MCP/MCPClientManager.cs
+++ b/src/Infrastructure/BotSharp.MCP/MCPClientManager.cs
@@ -1,29 +1,28 @@
using BotSharp.Core.Mcp.Settings;
-using McpDotNet.Client;
using Microsoft.Extensions.Logging;
+using ModelContextProtocol.Client;
using System;
+using System.Linq;
+using System.Threading.Tasks;
namespace BotSharp.Core.Mcp;
public class MCPClientManager : IDisposable
{
public ILoggerFactory LoggerFactory { get; }
- public McpClientFactory Factory { get; }
-
-
+
private readonly MCPSettings mcpSettings;
public MCPClientManager(MCPSettings settings, ILoggerFactory loggerFactory)
{
mcpSettings = settings;
LoggerFactory = loggerFactory;
+ }
- // Inject the mock transport into the factory
- Factory = new McpClientFactory(
- settings.McpServerConfigs,
- settings.McpClientOptions,
- LoggerFactory
- );
+ public async Task GetMcpClientAsync(string serverId)
+ {
+ return await McpClientFactory.CreateAsync(mcpSettings.McpServerConfigs
+ .Where(x=> x.Name == serverId).First(), mcpSettings.McpClientOptions);
}
public void Dispose()
diff --git a/src/Infrastructure/BotSharp.MCP/McpPlugin.cs b/src/Infrastructure/BotSharp.MCP/McpPlugin.cs
index 3d8ec0c8..1780ff56 100644
--- a/src/Infrastructure/BotSharp.MCP/McpPlugin.cs
+++ b/src/Infrastructure/BotSharp.MCP/McpPlugin.cs
@@ -5,11 +5,13 @@ using BotSharp.Abstraction.Plugins;
using BotSharp.Core.Mcp.Functions;
using BotSharp.Core.Mcp.Settings;
using BotSharp.MCP.Hooks;
-using McpDotNet.Protocol.Types;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
+using ModelContextProtocol.Client;
+using ModelContextProtocol.Configuration;
+using System.Linq;
+using System.Threading.Tasks;
namespace BotSharp.Core.Mcp;
@@ -31,20 +33,30 @@ public class McpPlugin : IBotSharpPlugin
foreach (var server in settings.McpServerConfigs)
{
- var client = clientManager.Factory.GetClientAsync(server.Id).Result;
- var tools = client.ListToolsAsync().Result;
-
- foreach (var tool in tools.Tools)
- {
- services.AddScoped( provider => { return tool; });
-
- services.AddScoped( provider => {
- var funcTool = new McpToolFunction( provider, tool, clientManager);
- return funcTool;
- });
- }
+ RegisterFunctionCall(services, server)
+ .ConfigureAwait(false)
+ .GetAwaiter()
+ .GetResult();
}
// Register hooks
services.AddScoped();
+ services.AddScoped();
}
- }
+
+ private async Task RegisterFunctionCall(IServiceCollection services, McpServerConfig server)
+ {
+ var client = await clientManager.GetMcpClientAsync(server.Id);
+ var tools = await client.ListToolsAsync().ToListAsync();
+
+ foreach (var tool in tools)
+ {
+ services.AddScoped(provider => { return tool; });
+
+ services.AddScoped(provider =>
+ {
+ var funcTool = new McpToolFunction(provider, tool, clientManager);
+ return funcTool;
+ });
+ }
+ }
+}
diff --git a/src/Infrastructure/BotSharp.MCP/Settings/MCPSettings.cs b/src/Infrastructure/BotSharp.MCP/Settings/MCPSettings.cs
index 34e408e3..1279a08d 100644
--- a/src/Infrastructure/BotSharp.MCP/Settings/MCPSettings.cs
+++ b/src/Infrastructure/BotSharp.MCP/Settings/MCPSettings.cs
@@ -1,5 +1,5 @@
-using McpDotNet.Client;
-using McpDotNet.Configuration;
+using ModelContextProtocol.Client;
+using ModelContextProtocol.Configuration;
using System.Collections.Generic;
namespace BotSharp.Core.Mcp.Settings;
diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentMCPToolMongoElement.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentMCPToolMongoElement.cs
index ecad71f3..614683c3 100644
--- a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentMCPToolMongoElement.cs
+++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentMCPToolMongoElement.cs
@@ -1,9 +1,4 @@
using BotSharp.Abstraction.Agents.Models;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
namespace BotSharp.Plugin.MongoStorage.Models;
@@ -16,7 +11,6 @@ public class AgentMCPToolMongoElement
public bool Disabled { get; set; }
public List Functions { get; set; } = [];
- public List Templates { get; set; } = [];
public static AgentMCPToolMongoElement ToMongoElement(MCPTool utility)
{
@@ -51,19 +45,3 @@ public class McpFunctionMongoElement
Name = name;
}
}
-
-public class McpTemplateMongoElement
-{
- public string Name { get; set; }
-
- public McpTemplateMongoElement()
- {
-
- }
-
- public McpTemplateMongoElement(string name)
- {
- Name = name;
- }
-}
-
diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs
index 0d3a670b..ba4b3fde 100644
--- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs
+++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs
@@ -61,8 +61,8 @@ public partial class MongoRepository
case AgentField.Utility:
UpdateAgentUtilities(agent.Id, agent.MergeUtility, agent.Utilities);
break;
- case AgentField.Acp:
- UpdateAgentMcps(agent.Id, agent.McpTools);
+ case AgentField.McpTool:
+ UpdateAgentMcpTools(agent.Id, agent.McpTools);
break;
case AgentField.KnowledgeBase:
UpdateAgentKnowledgeBases(agent.Id, agent.KnowledgeBases);
@@ -264,7 +264,7 @@ public partial class MongoRepository
_dc.Agents.UpdateOne(filter, update);
}
- private void UpdateAgentMcps(string agentId, List mcps)
+ private void UpdateAgentMcpTools(string agentId, List mcps)
{
if (mcps == null) return;
diff --git a/src/WebStarter/WebStarter.csproj b/src/WebStarter/WebStarter.csproj
index 9cff5c73..c2f3f0ed 100644
--- a/src/WebStarter/WebStarter.csproj
+++ b/src/WebStarter/WebStarter.csproj
@@ -28,7 +28,6 @@
-
diff --git a/src/WebStarter/appsettings.json b/src/WebStarter/appsettings.json
index 3c2cd567..37952b50 100644
--- a/src/WebStarter/appsettings.json
+++ b/src/WebStarter/appsettings.json
@@ -182,11 +182,9 @@
{
"Id": "PizzaServer",
"Name": "PizzaServer",
- "TransportType": "stdio",
- "TransportOptions": {
- "command": "BotSharp.PizzaBot.MCPServer.exe",
- "workingDirectory": "E:\\GitHub\\BotSharp\\src\\WebStarter\\bin\\Debug\\net8.0"
- }
+ "TransportType": "sse",
+ "TransportOptions": [],
+ "Location": "http://localhost:58905/sse"
}
]
},
diff --git a/tests/BotSharp.PizzaBot.MCPServer/BotSharp.PizzaBot.MCPServer.csproj b/tests/BotSharp.PizzaBot.MCPServer/BotSharp.PizzaBot.MCPServer.csproj
index 1158e06a..319f10a1 100644
--- a/tests/BotSharp.PizzaBot.MCPServer/BotSharp.PizzaBot.MCPServer.csproj
+++ b/tests/BotSharp.PizzaBot.MCPServer/BotSharp.PizzaBot.MCPServer.csproj
@@ -1,16 +1,16 @@
-
+
- Exe
net8.0
enable
12.0
enable
-
-
-
-
+
+
+
+
+
diff --git a/tests/BotSharp.PizzaBot.MCPServer/McpEndpointRouteBuilderExtensions.cs b/tests/BotSharp.PizzaBot.MCPServer/McpEndpointRouteBuilderExtensions.cs
new file mode 100644
index 00000000..f2138444
--- /dev/null
+++ b/tests/BotSharp.PizzaBot.MCPServer/McpEndpointRouteBuilderExtensions.cs
@@ -0,0 +1,62 @@
+using Microsoft.Extensions.Options;
+using ModelContextProtocol.Protocol.Messages;
+using ModelContextProtocol.Server;
+using ModelContextProtocol.Utils.Json;
+
+namespace BotSharp.PizzaBot.MCPServer;
+
+public static class McpEndpointRouteBuilderExtensions
+{
+ public static IEndpointConventionBuilder MapMcpSse(this IEndpointRouteBuilder endpoints)
+ {
+ IMcpServer? server = null;
+ SseServerStreamTransport? transport = null;
+ var loggerFactory = endpoints.ServiceProvider.GetRequiredService();
+ var mcpServerOptions = endpoints.ServiceProvider.GetRequiredService>();
+
+ var routeGroup = endpoints.MapGroup("");
+
+ routeGroup.MapGet("/sse", async (HttpResponse response, CancellationToken requestAborted) =>
+ {
+ await using var localTransport = transport = new SseServerStreamTransport(response.Body);
+ await using var localServer = server = McpServerFactory.Create(transport, mcpServerOptions.Value, loggerFactory, endpoints.ServiceProvider);
+
+ await localServer.StartAsync(requestAborted);
+
+ response.Headers.ContentType = "text/event-stream";
+ response.Headers.CacheControl = "no-cache";
+
+ try
+ {
+ await transport.RunAsync(requestAborted);
+ }
+ catch (OperationCanceledException) when (requestAborted.IsCancellationRequested)
+ {
+ // RequestAborted always triggers when the client disconnects before a complete response body is written,
+ // but this is how SSE connections are typically closed.
+ }
+ });
+
+ routeGroup.MapPost("/message", async (HttpContext context) =>
+ {
+ if (transport is null)
+ {
+ await Results.BadRequest("Connect to the /sse endpoint before sending messages.").ExecuteAsync(context);
+ return;
+ }
+
+ var message = await context.Request.ReadFromJsonAsync(McpJsonUtilities.DefaultOptions, context.RequestAborted);
+ if (message is null)
+ {
+ await Results.BadRequest("No message in request body.").ExecuteAsync(context);
+ return;
+ }
+
+ await transport.OnMessageReceivedAsync(message, context.RequestAborted);
+ context.Response.StatusCode = StatusCodes.Status202Accepted;
+ await context.Response.WriteAsync("Accepted");
+ });
+
+ return routeGroup;
+ }
+}
diff --git a/tests/BotSharp.PizzaBot.MCPServer/Program.cs b/tests/BotSharp.PizzaBot.MCPServer/Program.cs
index 5e8633f8..fb39e6cd 100644
--- a/tests/BotSharp.PizzaBot.MCPServer/Program.cs
+++ b/tests/BotSharp.PizzaBot.MCPServer/Program.cs
@@ -1,204 +1,236 @@
-using McpDotNet.Protocol.Transport;
-using McpDotNet.Protocol.Types;
-using McpDotNet.Server;
-using Microsoft.Extensions.Logging;
-using Serilog;
-using System.Dynamic;
-using System.Text;
-using System.Text.Json;
+using BotSharp.PizzaBot.MCPServer;
+using ModelContextProtocol;
-namespace BotSharp.PizzaBot.MCPServer
-{
- internal class Program
- {
- private static async Task Main(string[] args)
- {
- Console.WriteLine("Starting server...");
+var builder = WebApplication.CreateBuilder(args);
+builder.Services.AddMcpServer().WithTools();
+var app = builder.Build();
- McpServerOptions options = new McpServerOptions()
- {
- ServerInfo = new Implementation() { Name = "PizzaServer", Version = "1.0.0" },
- Capabilities = new ServerCapabilities()
- {
- Tools = new(),
- Resources = new(),
- Prompts = new(),
- },
- ProtocolVersion = "2024-11-05"
- };
- var loggerFactory = CreateLoggerFactory();
- McpServerFactory factory = new McpServerFactory(new StdioServerTransport("PizzaServer", loggerFactory), options, loggerFactory,
- "This is a test server with only stub functionality");
- IMcpServer server = factory.CreateServer();
+app.MapGet("/", () => "This is a test server with only stub functionality!");
+app.MapMcpSse();
- Console.WriteLine("Server object created, registering handlers.");
+app.Run();
- #region Tools
- server.ListToolsHandler = (request, cancellationToken) =>
- {
- return Task.FromResult(new ListToolsResult()
- {
- Tools =
- [
- new Tool()
- {
- Name = "make_payment",
- Description = "call this function to make payment",
- InputSchema = new JsonSchema()
- {
- Type = "object",
- Properties = new Dictionary()
- {
- ["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()
- {
- Name = "get_pizza_prices",
- Description = "call this function to get pizza prices",
- InputSchema = new JsonSchema()
- {
- Type = "object",
- Properties = new Dictionary()
- {
- ["pizza_type"] = new JsonSchemaProperty() { Type = "string", Description = "The pizza type." },
- ["quantity"] = new JsonSchemaProperty() { Type = "string", Description = "quantity of pizza." },
- },
- Required = new List(){ "pizza_type", "quantity" }
- }
- },
- new Tool()
- {
- Name = "place_an_order",
- Description = "Place an order when user has confirmed the pizza type and quantity.",
- InputSchema = new JsonSchema()
- {
- 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" },
+//namespace BotSharp.PizzaBot.MCPServer
+//{
+// internal class Program
+// {
+// private static HashSet _subscribedResources = new();
+// private static readonly object _subscribedResourcesLock = new();
- },
- Required = new List(){"pizza_type", "quantity", "unit_price" }
- }
- }
- ]
- });
- };
+// private static async Task Main(string[] args)
+// {
+// Console.WriteLine("Starting server...");
- server.CallToolHandler = async (request, cancellationToken) =>
- {
- if (request.Name == "make_payment")
- {
- if (request.Arguments is null || !request.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))
- {
- throw new McpServerException("Missing required argument 'total_amount'");
- }
- dynamic message = new ExpandoObject();
- message.Transaction = Guid.NewGuid().ToString();
- message.Status = "Success";
+// McpServerOptions options = new McpServerOptions()
+// {
+// ServerInfo = new Implementation() { Name = "PizzaServer", Version = "1.0.0" },
+// Capabilities = new ServerCapabilities()
+// {
+// Tools = ConfigureTools(),
+// },
+// ProtocolVersion = "2024-11-05",
+// ServerInstructions = "This is a test server with only stub functionality"
+// };
+// var loggerFactory = CreateLoggerFactory();
+// await using IMcpServer server = McpServerFactory.Create(new StdioServerTransport("TestServer", loggerFactory), options, loggerFactory);
+// Log.Logger.Information("Server initialized.");
- // Serialize the message to JSON
- var jso = new JsonSerializerOptions() { WriteIndented = true };
- var jsonMessage = JsonSerializer.Serialize(message, jso);
+// await server.StartAsync();
- return new CallToolResponse()
- {
- Content = [new Content() { Text = jsonMessage , Type = "text" }]
- };
- }
- else if(request.Name == "get_pizza_prices")
- {
- if (request.Arguments is null || !request.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))
- {
- throw new McpServerException("Missing required argument 'quantity'");
- }
- dynamic message = new ExpandoObject();
- message.pepperoni_unit_price = 3.2;
- message.cheese_unit_price = 3.5;
- message.margherita_unit_price = 3.8;
- // Serialize the message to JSON
- var jso = new JsonSerializerOptions() { WriteIndented = true };
- var jsonMessage = JsonSerializer.Serialize(message, jso);
- return new CallToolResponse()
- {
- Content = [new Content() { Text = jsonMessage, Type = "text" }]
- };
- }
- else if (request.Name == "place_an_order")
- {
- if (request.Arguments is null || !request.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))
- {
- throw new McpServerException("Missing required argument 'quantity'");
- }
- if (request.Arguments is null || !request.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";
- // Serialize the message to JSON
- var jso = new JsonSerializerOptions() { WriteIndented = true };
- var jsonMessage = JsonSerializer.Serialize(message, jso);
- return new CallToolResponse()
- {
- Content = [new Content() { Text = jsonMessage, Type = "text" }]
- };
- }
- else
- {
- throw new McpServerException($"Unknown tool: {request.Name}");
- }
- };
- #endregion
+// Log.Logger.Information("Server started.");
- Console.WriteLine("Server initialized.");
+// // Run until process is stopped by the client (parent process)
+// while (true)
+// {
+// await Task.Delay(5000);
- await server.StartAsync();
+// // Snapshot the subscribed resources, rather than locking while sending notifications
+// List resources;
+// lock (_subscribedResourcesLock)
+// {
+// resources = _subscribedResources.ToList();
+// }
- Console.WriteLine("Server started.");
+// foreach (var resource in resources)
+// {
+// ResourceUpdatedNotificationParams notificationParams = new() { Uri = resource };
+// await server.SendMessageAsync(new JsonRpcNotification()
+// {
+// Method = NotificationMethods.ResourceUpdatedNotification,
+// Params = notificationParams
+// });
+// }
+// }
+// }
- // Run until process is stopped by the client (parent process)
- while (true)
- {
- await Task.Delay(1000);
- }
- }
+// private static ToolsCapability ConfigureTools()
+// {
+// return new()
+// {
+// ListToolsHandler = (request, cancellationToken) =>
+// {
+// return Task.FromResult(new ListToolsResult()
+// {
+// Tools = [
+// new Tool()
+// {
+// Name = "make_payment",
+// Description = "call this function to make payment",
+// InputSchema = new JsonSchema()
+// {
+// Type = "object",
+// Properties = new Dictionary()
+// {
+// ["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()
+// {
+// Name = "get_pizza_prices",
+// Description = "call this function to get pizza unit price",
+// InputSchema = new JsonSchema()
+// {
+// Type = "object",
+// Properties = new Dictionary()
+// {
+// ["pizza_type"] = new JsonSchemaProperty() { Type = "string", Description = "The pizza type." },
+// ["quantity"] = new JsonSchemaProperty() { Type = "string", Description = "quantity of pizza." },
- private static ILoggerFactory CreateLoggerFactory()
- {
- // Use serilog
- Log.Logger = new LoggerConfiguration()
- .MinimumLevel.Verbose() // Capture all log levels
- .WriteTo.File(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs", "TestServer_.log"),
- rollingInterval: RollingInterval.Day,
- outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}")
- .CreateLogger();
+// },
+// Required = new List(){ "pizza_type", "quantity" }
+// }
+// },
+// new Tool()
+// {
+// Name = "place_an_order",
+// Description = "Place an order when user has confirmed the pizza type and quantity.",
+// InputSchema = new JsonSchema()
+// {
+// 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 = "pizza unit price" },
- var logsPath = Path.Combine(AppContext.BaseDirectory, "testserver.log");
- return LoggerFactory.Create(builder =>
- {
- builder.AddSerilog();
- });
- }
- }
-}
+// },
+// Required = new List(){"pizza_type", "quantity", "unit_price" }
+// }
+// }
+// ]
+// });
+// },
+
+// CallToolHandler = async (request, cancellationToken) =>
+// {
+// if (request.Params.Name == "make_payment")
+// {
+// 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.Params.Arguments is null || !request.Params.Arguments.TryGetValue("total_amount", out var total_amount))
+// {
+// throw new McpServerException("Missing required argument 'total_amount'");
+// }
+// //dynamic message = new ExpandoObject();
+// //message.Transaction = Guid.NewGuid().ToString();
+// //message.Status = "Success";
+
+// //// Serialize the message to JSON
+// //var jso = new JsonSerializerOptions() { WriteIndented = true };
+// //var jsonMessage = JsonSerializer.Serialize(message, jso);
+
+// return new CallToolResponse()
+// {
+// Content = [new Content() { Text = "Payment proceed successfully. Thank you for your business. Have a great day!", Type = "text" }]
+// };
+// }
+// else if (request.Params.Name == "get_pizza_prices")
+// {
+// 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.Params.Arguments is null || !request.Params.Arguments.TryGetValue("quantity", out var quantity))
+// {
+// throw new McpServerException("Missing required argument 'quantity'");
+// }
+// double unit_price = 0;
+// if(pizza_type.ToString() == "Pepperoni Pizza")
+// {
+// unit_price = 3.2 * (int)quantity;
+// }
+// else if(pizza_type.ToString() == "Cheese Pizza")
+// {
+// unit_price = 3.5 * (int)quantity; ;
+// }
+// else if(pizza_type.ToString() == "Margherita Pizza")
+// {
+// unit_price = 3.8 * (int)quantity; ;
+// }
+// dynamic message = new ExpandoObject();
+// message.unit_price = unit_price;
+// var jso = new JsonSerializerOptions() { WriteIndented = true };
+// var jsonMessage = JsonSerializer.Serialize(message, jso);
+// return new CallToolResponse()
+// {
+// Content = [new Content() { Text = jsonMessage, Type = "text" }]
+// };
+// }
+// else if (request.Params.Name == "place_an_order")
+// {
+// 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.Params.Arguments is null || !request.Params.Arguments.TryGetValue("quantity", out var quantity))
+// {
+// throw new McpServerException("Missing required argument 'quantity'");
+// }
+// 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";
+// //// Serialize the message to JSON
+// //var jso = new JsonSerializerOptions() { WriteIndented = true };
+// //var jsonMessage = JsonSerializer.Serialize(message, jso);
+// return new CallToolResponse()
+// {
+// Content = [new Content() { Text = "The order number is P123-01: {order_number = \"P123-01\" }", Type = "text" }]
+// };
+// }
+// else
+// {
+// throw new McpServerException($"Unknown tool: {request.Params.Name}");
+// }
+// }
+// };
+// }
+
+
+// private static ILoggerFactory CreateLoggerFactory()
+// {
+// // Use serilog
+// Log.Logger = new LoggerConfiguration()
+// .MinimumLevel.Verbose() // Capture all log levels
+// .WriteTo.File(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs", "TestServer_.log"),
+// rollingInterval: RollingInterval.Day,
+// outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}")
+// .CreateLogger();
+
+// var logsPath = Path.Combine(AppContext.BaseDirectory, "testserver.log");
+// return LoggerFactory.Create(builder =>
+// {
+// builder.AddSerilog();
+// });
+// }
+// }
+//}
diff --git a/tests/BotSharp.PizzaBot.MCPServer/Properties/launchSettings.json b/tests/BotSharp.PizzaBot.MCPServer/Properties/launchSettings.json
new file mode 100644
index 00000000..617e672c
--- /dev/null
+++ b/tests/BotSharp.PizzaBot.MCPServer/Properties/launchSettings.json
@@ -0,0 +1,12 @@
+{
+ "profiles": {
+ "BotSharp.PizzaBot.MCPServer": {
+ "commandName": "Project",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "applicationUrl": "https://localhost:58904;http://localhost:58905"
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/BotSharp.PizzaBot.MCPServer/SseServerStreamTransport.cs b/tests/BotSharp.PizzaBot.MCPServer/SseServerStreamTransport.cs
new file mode 100644
index 00000000..7a7ce51a
--- /dev/null
+++ b/tests/BotSharp.PizzaBot.MCPServer/SseServerStreamTransport.cs
@@ -0,0 +1,84 @@
+using ModelContextProtocol.Protocol.Messages;
+using ModelContextProtocol.Protocol.Transport;
+using ModelContextProtocol.Utils.Json;
+using System.Buffers;
+using System.Net.ServerSentEvents;
+using System.Text.Json;
+using System.Threading.Channels;
+
+namespace BotSharp.PizzaBot.MCPServer;
+
+public class SseServerStreamTransport(Stream sseResponseStream) : ITransport
+{
+ private readonly Channel _incomingChannel = CreateSingleItemChannel();
+ private readonly Channel> _outgoingSseChannel = CreateSingleItemChannel>();
+
+ private Task? _sseWriteTask;
+ private Utf8JsonWriter? _jsonWriter;
+
+ public bool IsConnected => _sseWriteTask?.IsCompleted == false;
+
+ public Task RunAsync(CancellationToken cancellationToken)
+ {
+ void WriteJsonRpcMessageToBuffer(SseItem item, IBufferWriter writer)
+ {
+ if (item.EventType == "endpoint")
+ {
+ writer.Write("/message"u8);
+ return;
+ }
+
+ JsonSerializer.Serialize(GetUtf8JsonWriter(writer), item.Data, McpJsonUtilities.DefaultOptions);
+ }
+
+ // The very first SSE event isn't really an IJsonRpcMessage, but there's no API to write a single item of a different type,
+ // so we fib and special-case the "endpoint" event type in the formatter.
+ _outgoingSseChannel.Writer.TryWrite(new SseItem(null, "endpoint"));
+
+ var sseItems = _outgoingSseChannel.Reader.ReadAllAsync(cancellationToken);
+ return _sseWriteTask = SseFormatter.WriteAsync(sseItems, sseResponseStream, WriteJsonRpcMessageToBuffer, cancellationToken);
+ }
+
+ public ChannelReader MessageReader => _incomingChannel.Reader;
+
+ public ValueTask DisposeAsync()
+ {
+ _incomingChannel.Writer.TryComplete();
+ _outgoingSseChannel.Writer.TryComplete();
+ return new ValueTask(_sseWriteTask ?? Task.CompletedTask);
+ }
+
+ public Task SendMessageAsync(IJsonRpcMessage message, CancellationToken cancellationToken = default) =>
+ _outgoingSseChannel.Writer.WriteAsync(new SseItem(message), cancellationToken).AsTask();
+
+ public Task OnMessageReceivedAsync(IJsonRpcMessage message, CancellationToken cancellationToken)
+ {
+ if (!IsConnected)
+ {
+ throw new McpTransportException("Transport is not connected");
+ }
+
+ return _incomingChannel.Writer.WriteAsync(message, cancellationToken).AsTask();
+ }
+
+ private static Channel CreateSingleItemChannel() =>
+ Channel.CreateBounded(new BoundedChannelOptions(1)
+ {
+ SingleReader = true,
+ SingleWriter = false,
+ });
+
+ private Utf8JsonWriter GetUtf8JsonWriter(IBufferWriter writer)
+ {
+ if (_jsonWriter is null)
+ {
+ _jsonWriter = new Utf8JsonWriter(writer);
+ }
+ else
+ {
+ _jsonWriter.Reset(writer);
+ }
+
+ return _jsonWriter;
+ }
+}
diff --git a/tests/BotSharp.PizzaBot.MCPServer/Tools/MakePayment.cs b/tests/BotSharp.PizzaBot.MCPServer/Tools/MakePayment.cs
new file mode 100644
index 00000000..a236d874
--- /dev/null
+++ b/tests/BotSharp.PizzaBot.MCPServer/Tools/MakePayment.cs
@@ -0,0 +1,25 @@
+using ModelContextProtocol.Server;
+using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
+
+namespace BotSharp.PizzaBot.MCPServer.Tools;
+
+[McpToolType]
+public static class MakePayment
+{
+ [McpTool(name: "make_payment"), Description("call this function to make payment.")]
+ public static string Make_Payment(
+ [Description("order number"),Required] string order_number,
+ [Description("total amount"),Required] int total_amount)
+ {
+ if (order_number is null)
+ {
+ throw new McpServerException("Missing required argument 'order_number'");
+ }
+ if (order_number is null)
+ {
+ throw new McpServerException("Missing required argument 'total_amount'");
+ }
+ return "Payment proceed successfully. Thank you for your business. Have a great day!";
+ }
+}
diff --git a/tests/BotSharp.PizzaBot.MCPServer/Tools/PizzaPrices.cs b/tests/BotSharp.PizzaBot.MCPServer/Tools/PizzaPrices.cs
new file mode 100644
index 00000000..7ca766f3
--- /dev/null
+++ b/tests/BotSharp.PizzaBot.MCPServer/Tools/PizzaPrices.cs
@@ -0,0 +1,46 @@
+using ModelContextProtocol.Server;
+using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
+using System.Dynamic;
+using System.Text.Json;
+
+namespace BotSharp.PizzaBot.MCPServer.Tools;
+
+[McpToolType]
+public static class PizzaPrices
+{
+ [McpTool(name: "get_pizza_prices"), Description("call this function to get pizza unit price.")]
+ public static string GetPizzaPrices(
+ [Description("The pizza type."), Required] string pizza_type,
+ [Description("quantity of pizza"), Required] int quantity)
+ {
+ if (pizza_type is null)
+ {
+ throw new McpServerException("Missing required argument 'pizza_type'");
+ }
+ if (quantity <= 0)
+ {
+ throw new McpServerException("Missing required argument 'quantity'");
+ }
+ double unit_price = 0;
+ if (pizza_type.ToString() == "Pepperoni Pizza")
+ {
+ unit_price = 3.2 * (int)quantity;
+ }
+ else if (pizza_type.ToString() == "Cheese Pizza")
+ {
+ unit_price = 3.5 * (int)quantity; ;
+ }
+ else if (pizza_type.ToString() == "Margherita Pizza")
+ {
+ unit_price = 3.8 * (int)quantity; ;
+ }
+
+ dynamic message = new ExpandoObject();
+ message.unit_price = unit_price;
+ var jso = new JsonSerializerOptions() { WriteIndented = true };
+ var jsonMessage = JsonSerializer.Serialize(message, jso);
+
+ return jsonMessage;
+ }
+}
diff --git a/tests/BotSharp.PizzaBot.MCPServer/Tools/PlaceOrder.cs b/tests/BotSharp.PizzaBot.MCPServer/Tools/PlaceOrder.cs
new file mode 100644
index 00000000..a6237b6d
--- /dev/null
+++ b/tests/BotSharp.PizzaBot.MCPServer/Tools/PlaceOrder.cs
@@ -0,0 +1,31 @@
+using ModelContextProtocol.Server;
+using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
+
+namespace BotSharp.PizzaBot.MCPServer.Tools;
+
+[McpToolType]
+public static class PlaceOrder
+{
+ [McpTool(name: "place_an_order"), Description("Place an order when user has confirmed the pizza type and quantity.")]
+ public static string PlaceAnOrder(
+ [Description("The pizza type."), Required] string pizza_type,
+ [Description("quantity of pizza"), Required] int quantity,
+ [Description("pizza unit price"),Required] double unit_price)
+ {
+ if (pizza_type is null)
+ {
+ throw new McpServerException("Missing required argument 'pizza_type'");
+ }
+ if (quantity <= 0)
+ {
+ throw new McpServerException("Missing required argument 'quantity'");
+ }
+ if (unit_price <= 0)
+ {
+ throw new McpServerException("Missing required argument 'unit_price'");
+ }
+
+ return "The order number is P123-01: {order_number = \"P123-01\" }";
+ }
+}
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..2e40c4e0 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
@@ -1,6 +1,6 @@
{
"name": "Ordering",
- "description": "Provide types of pizza available, unit price, total cost and place the order.",
+ "description": "Provide types of pizza available, pizza unit price, total cost and place the order.",
"createdDateTime": "2023-07-26T02:29:25.123224Z",
"updatedDateTime": "2023-07-26T02:29:25.123274Z",
"id": "c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd",
@@ -20,6 +20,6 @@
}
]
}
- ]
+ ],
"labels": [ "experiment" ]
}
\ No newline at end of file