Merge branch 'mcp' of https://github.com/Ai4c-AI/BotSharp
This commit is contained in:
commit
213d39349e
13
BotSharp.sln
13
BotSharp.sln
|
|
@ -127,7 +127,9 @@ 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.Core.Realtime", "src\Infrastructure\BotSharp.Core.Realtime\BotSharp.Core.Realtime.csproj", "{7ACD8C95-C66B-436A-80E7-541A57D8C3F4}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.MCP", "src\Infrastructure\BotSharp.MCP\BotSharp.MCP.csproj", "{8ED8EEF4-06DD-45F5-AA91-BD2395E901B5}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.PizzaBot.MCPServer", "tests\BotSharp.PizzaBot.MCPServer\BotSharp.PizzaBot.MCPServer.csproj", "{AD91B4ED-0623-4710-913E-6D7C893E76EF}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
|
@ -521,14 +523,6 @@ 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
|
||||
{7ACD8C95-C66B-436A-80E7-541A57D8C3F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7ACD8C95-C66B-436A-80E7-541A57D8C3F4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7ACD8C95-C66B-436A-80E7-541A57D8C3F4}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{7ACD8C95-C66B-436A-80E7-541A57D8C3F4}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{7ACD8C95-C66B-436A-80E7-541A57D8C3F4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7ACD8C95-C66B-436A-80E7-541A57D8C3F4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7ACD8C95-C66B-436A-80E7-541A57D8C3F4}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{7ACD8C95-C66B-436A-80E7-541A57D8C3F4}.Release|x64.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
@ -590,7 +584,6 @@ 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}
|
||||
{7ACD8C95-C66B-436A-80E7-541A57D8C3F4} = {E29DC6C4-5E57-48C5-BCB0-6B8F84782749}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {A9969D89-C98B-40A5-A12B-FC87E55B3A19}
|
||||
|
|
|
|||
|
|
@ -57,5 +57,11 @@ public abstract class AgentHookBase : IAgentHook
|
|||
|
||||
public virtual void OnAgentUtilityLoaded(Agent agent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual void OnAgentMCPToolLoaded(Agent agent)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ public enum AgentField
|
|||
Sample,
|
||||
LlmConfig,
|
||||
Utility,
|
||||
Acp,
|
||||
KnowledgeBase,
|
||||
Rule,
|
||||
MaxMessageCount
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@ public interface IAgentHook
|
|||
|
||||
void OnAgentUtilityLoaded(Agent agent);
|
||||
|
||||
void OnAgentMCPToolLoaded(Agent agent);
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when agent is loaded completely.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -95,15 +95,20 @@ public class Agent
|
|||
public List<string> Labels { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Merge utilities from entry agent
|
||||
/// Merge Utility from entry agent
|
||||
/// </summary>
|
||||
public bool MergeUtility { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Agent utilities
|
||||
/// Agent Utility
|
||||
/// </summary>
|
||||
public List<AgentUtility> Utilities { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Agent MCP Tools
|
||||
/// </summary>
|
||||
public List<MCPTool> McpTools { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Agent rules
|
||||
/// </summary>
|
||||
|
|
@ -157,6 +162,7 @@ public class Agent
|
|||
Responses = agent.Responses,
|
||||
Samples = agent.Samples,
|
||||
Utilities = agent.Utilities,
|
||||
McpTools = agent.McpTools,
|
||||
Knowledges = agent.Knowledges,
|
||||
IsPublic = agent.IsPublic,
|
||||
Disabled = agent.Disabled,
|
||||
|
|
@ -298,4 +304,10 @@ public class Agent
|
|||
LlmConfig = llmConfig;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Agent SetMcps(List<MCPTool> mcps)
|
||||
{
|
||||
McpTools = mcps ?? [];
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
namespace BotSharp.Abstraction.Agents.Models;
|
||||
|
||||
public class MCPTool
|
||||
{
|
||||
public string ServerId { get; set; }
|
||||
|
||||
public bool Disabled { get; set; }
|
||||
|
||||
public IEnumerable<MCPFunction> Functions { get; set; } = [];
|
||||
|
||||
public MCPTool()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public MCPTool(
|
||||
IEnumerable<MCPFunction>? functions = null)
|
||||
{
|
||||
Functions = functions ?? [];
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return ServerId;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class MCPFunction
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public MCPFunction(string name)
|
||||
{
|
||||
this.Name = name;
|
||||
}
|
||||
}
|
||||
|
|
@ -38,6 +38,7 @@ public class BasicAgentHook : AgentHookBase
|
|||
agent.SecondaryInstructions.Add(prompt);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private (IEnumerable<FunctionDef>, IEnumerable<string>) GetUtilityContent(Agent agent)
|
||||
{
|
||||
|
|
@ -81,5 +82,5 @@ public class BasicAgentHook : AgentHookBase
|
|||
.Distinct().ToList();
|
||||
|
||||
return (functionNames, templateNames);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,6 +71,11 @@ public partial class AgentService
|
|||
{
|
||||
hook.OnAgentUtilityLoaded(agent);
|
||||
}
|
||||
|
||||
if(agent.McpTools != null && agent.McpTools.Count >0)
|
||||
{
|
||||
hook.OnAgentMCPToolLoaded(agent);
|
||||
}
|
||||
|
||||
hook.OnAgentLoaded(agent);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ public partial class AgentService
|
|||
record.Responses = agent.Responses ?? [];
|
||||
record.Samples = agent.Samples ?? [];
|
||||
record.Utilities = agent.Utilities ?? [];
|
||||
record.McpTools = agent.McpTools ?? [];
|
||||
record.KnowledgeBases = agent.KnowledgeBases ?? [];
|
||||
record.Rules = agent.Rules ?? [];
|
||||
if (agent.LlmConfig != null && !agent.LlmConfig.IsInherit)
|
||||
|
|
@ -106,6 +107,7 @@ public partial class AgentService
|
|||
.SetResponses(foundAgent.Responses)
|
||||
.SetSamples(foundAgent.Samples)
|
||||
.SetUtilities(foundAgent.Utilities)
|
||||
.SetMcps(foundAgent.McpTools)
|
||||
.SetKnowledgeBases(foundAgent.KnowledgeBases)
|
||||
.SetRules(foundAgent.Rules)
|
||||
.SetLlmConfig(foundAgent.LlmConfig);
|
||||
|
|
|
|||
|
|
@ -60,6 +60,9 @@ namespace BotSharp.Core.Repository
|
|||
case AgentField.Utility:
|
||||
UpdateAgentUtilities(agent.Id, agent.MergeUtility, agent.Utilities);
|
||||
break;
|
||||
case AgentField.Acp:
|
||||
UpdateAgentMcpTools(agent.Id, agent.McpTools);
|
||||
break;
|
||||
case AgentField.KnowledgeBase:
|
||||
UpdateAgentKnowledgeBases(agent.Id, agent.KnowledgeBases);
|
||||
break;
|
||||
|
|
@ -191,6 +194,20 @@ namespace BotSharp.Core.Repository
|
|||
File.WriteAllText(agentFile, json);
|
||||
}
|
||||
|
||||
private void UpdateAgentMcpTools(string agentId, List<MCPTool> mcptools)
|
||||
{
|
||||
if (mcptools == null) return;
|
||||
|
||||
var (agent, agentFile) = GetAgentFromFile(agentId);
|
||||
if (agent == null) return;
|
||||
|
||||
|
||||
agent.McpTools = mcptools;
|
||||
agent.UpdatedDateTime = DateTime.UtcNow;
|
||||
var json = JsonSerializer.Serialize(agent, _options);
|
||||
File.WriteAllText(agentFile, json);
|
||||
}
|
||||
|
||||
private void UpdateAgentKnowledgeBases(string agentId, List<AgentKnowledgeBase> knowledgeBases)
|
||||
{
|
||||
if (knowledgeBases == null) return;
|
||||
|
|
@ -360,6 +377,7 @@ namespace BotSharp.Core.Repository
|
|||
agent.Profiles = inputAgent.Profiles;
|
||||
agent.Labels = inputAgent.Labels;
|
||||
agent.Utilities = inputAgent.Utilities;
|
||||
agent.McpTools = inputAgent.McpTools;
|
||||
agent.KnowledgeBases = inputAgent.KnowledgeBases;
|
||||
agent.RoutingRules = inputAgent.RoutingRules;
|
||||
agent.Rules = inputAgent.Rules;
|
||||
|
|
|
|||
37
src/Infrastructure/BotSharp.MCP/AIFunctionUtilities.cs
Normal file
37
src/Infrastructure/BotSharp.MCP/AIFunctionUtilities.cs
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
using BotSharp.Abstraction.Functions.Models;
|
||||
using McpDotNet.Protocol.Types;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace BotSharp.Core.MCP;
|
||||
|
||||
internal static class AIFunctionUtilities
|
||||
{
|
||||
public static FunctionDef MapToFunctionDef(Tool tool)
|
||||
{
|
||||
if (tool == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(tool));
|
||||
}
|
||||
|
||||
var properties = tool.InputSchema?.Properties;
|
||||
var required = tool.InputSchema?.Required ?? new List<string>();
|
||||
|
||||
FunctionDef funDef = new FunctionDef
|
||||
{
|
||||
Name = tool.Name,
|
||||
Description = tool.Description?? string.Empty,
|
||||
Type = "function",
|
||||
Parameters = new FunctionParametersDef
|
||||
{
|
||||
Type = "object",
|
||||
Properties = properties != null ? JsonSerializer.SerializeToDocument(properties) : JsonDocument.Parse("{}"),
|
||||
Required = required
|
||||
}
|
||||
};
|
||||
|
||||
return funDef;
|
||||
}
|
||||
}
|
||||
18
src/Infrastructure/BotSharp.MCP/BotSharp.MCP.csproj
Normal file
18
src/Infrastructure/BotSharp.MCP/BotSharp.MCP.csproj
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>$(TargetFramework)</TargetFramework>
|
||||
<LangVersion>$(LangVersion)</LangVersion>
|
||||
<VersionPrefix>$(BotSharpVersion)</VersionPrefix>
|
||||
<GeneratePackageOnBuild>$(GeneratePackageOnBuild)</GeneratePackageOnBuild>
|
||||
<OutputPath>$(SolutionDir)packages</OutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="mcpdotnet" Version="1.1.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\BotSharp.Abstraction\BotSharp.Abstraction.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
107
src/Infrastructure/BotSharp.MCP/Functions/McpToolFunction.cs
Normal file
107
src/Infrastructure/BotSharp.MCP/Functions/McpToolFunction.cs
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
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 Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BotSharp.Core.Mcp.Functions;
|
||||
|
||||
public class McpToolFunction : IFunctionCallback
|
||||
{
|
||||
private readonly Tool _tool;
|
||||
private readonly MCPClientManager _clientManager;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
public McpToolFunction(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.Factory.GetClientAsync(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 = JsonSerializer.Deserialize(json,typeof(object));
|
||||
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.")
|
||||
};
|
||||
|
||||
}
|
||||
69
src/Infrastructure/BotSharp.MCP/Hooks/MCPToolAgentHook.cs
Normal file
69
src/Infrastructure/BotSharp.MCP/Hooks/MCPToolAgentHook.cs
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
using BotSharp.Abstraction.Agents;
|
||||
using BotSharp.Abstraction.Agents.Enums;
|
||||
using BotSharp.Abstraction.Agents.Models;
|
||||
using BotSharp.Abstraction.Agents.Settings;
|
||||
using BotSharp.Abstraction.Conversations;
|
||||
using BotSharp.Abstraction.Functions.Models;
|
||||
using BotSharp.Core.Mcp;
|
||||
using BotSharp.Core.MCP;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BotSharp.MCP.Hooks;
|
||||
|
||||
public class MCPToolAgentHook : AgentHookBase
|
||||
{
|
||||
public override string SelfId => string.Empty;
|
||||
|
||||
public MCPToolAgentHook(IServiceProvider services, AgentSettings settings)
|
||||
: base(services, settings)
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnAgentMCPToolLoaded(Agent agent)
|
||||
{
|
||||
if (agent.Type == AgentType.Routing)
|
||||
return;
|
||||
var conv = _services.GetRequiredService<IConversationService>();
|
||||
var isConvMode = conv.IsConversationMode();
|
||||
if (!isConvMode) return;
|
||||
|
||||
agent.SecondaryFunctions ??= [];
|
||||
|
||||
var functions = GetMCPContent(agent).Result;
|
||||
|
||||
foreach (var fn in functions)
|
||||
{
|
||||
if (!agent.SecondaryFunctions.Any(x => x.Name.Equals(fn.Name, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
agent.SecondaryFunctions.Add(fn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<FunctionDef>> GetMCPContent(Agent agent)
|
||||
{
|
||||
List<FunctionDef> functionDefs = new List<FunctionDef>();
|
||||
var mcpClientManager = _services.GetRequiredService<MCPClientManager>();
|
||||
var mcps = agent.McpTools;
|
||||
foreach (var item in mcps)
|
||||
{
|
||||
var mcpClient = await mcpClientManager.Factory.GetClientAsync(item.ServerId);
|
||||
if (mcpClient != null)
|
||||
{
|
||||
var tools = await mcpClient.ListToolsAsync();
|
||||
var funcnames = item.Functions.Select(x => x.Name).ToList();
|
||||
foreach (var tool in tools.Tools.Where(x => funcnames.Contains(x.Name, StringComparer.OrdinalIgnoreCase)))
|
||||
{
|
||||
var funDef = AIFunctionUtilities.MapToFunctionDef(tool);
|
||||
functionDefs.Add(funDef);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return functionDefs;
|
||||
}
|
||||
}
|
||||
33
src/Infrastructure/BotSharp.MCP/MCPClientManager.cs
Normal file
33
src/Infrastructure/BotSharp.MCP/MCPClientManager.cs
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
using BotSharp.Core.Mcp.Settings;
|
||||
using McpDotNet.Client;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
|
||||
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 void Dispose()
|
||||
{
|
||||
LoggerFactory?.Dispose();
|
||||
}
|
||||
}
|
||||
50
src/Infrastructure/BotSharp.MCP/McpPlugin.cs
Normal file
50
src/Infrastructure/BotSharp.MCP/McpPlugin.cs
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
using BotSharp.Abstraction.Agents;
|
||||
using BotSharp.Abstraction.Conversations;
|
||||
using BotSharp.Abstraction.Functions;
|
||||
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;
|
||||
|
||||
namespace BotSharp.Core.Mcp;
|
||||
|
||||
public class McpPlugin : IBotSharpPlugin
|
||||
{
|
||||
public string Id => "5d779611-0012-46cb-a754-4ca4770e88ac";
|
||||
public string Name => "MCP Plugin";
|
||||
public string Description => "Integrated MCP tools";
|
||||
|
||||
private MCPClientManager clientManager;
|
||||
|
||||
public void RegisterDI(IServiceCollection services, IConfiguration config)
|
||||
{
|
||||
var settings = config.GetSection("MCPSettings").Get<MCPSettings>();
|
||||
services.AddScoped<MCPSettings>(provider => { return settings; });
|
||||
|
||||
clientManager = new MCPClientManager(settings, NullLoggerFactory.Instance);
|
||||
services.AddSingleton(clientManager);
|
||||
|
||||
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<IFunctionCallback>( provider => {
|
||||
var funcTool = new McpToolFunction( provider, tool, clientManager);
|
||||
return funcTool;
|
||||
});
|
||||
}
|
||||
}
|
||||
// Register hooks
|
||||
services.AddScoped<IAgentHook, MCPToolAgentHook>();
|
||||
}
|
||||
}
|
||||
13
src/Infrastructure/BotSharp.MCP/Settings/MCPSettings.cs
Normal file
13
src/Infrastructure/BotSharp.MCP/Settings/MCPSettings.cs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
using McpDotNet.Client;
|
||||
using McpDotNet.Configuration;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BotSharp.Core.Mcp.Settings;
|
||||
|
||||
public class MCPSettings
|
||||
{
|
||||
public McpClientOptions McpClientOptions { get; set; }
|
||||
|
||||
public List<McpServerConfig> McpServerConfigs { get; set; }
|
||||
|
||||
}
|
||||
|
|
@ -55,6 +55,7 @@ public class AgentCreationModel
|
|||
public int? MaxMessageCount { get; set; }
|
||||
|
||||
public List<AgentUtility> Utilities { get; set; } = new();
|
||||
public List<MCPTool> McpTools { get; set; } = new();
|
||||
public List<RoutingRuleUpdateModel> RoutingRules { get; set; } = new();
|
||||
public List<AgentKnowledgeBase> KnowledgeBases { get; set; } = new();
|
||||
public List<AgentRule> Rules { get; set; } = new();
|
||||
|
|
@ -73,6 +74,7 @@ public class AgentCreationModel
|
|||
Responses = Responses,
|
||||
Samples = Samples,
|
||||
Utilities = Utilities,
|
||||
McpTools = McpTools,
|
||||
IsPublic = IsPublic,
|
||||
Type = Type,
|
||||
Disabled = Disabled,
|
||||
|
|
|
|||
|
|
@ -39,6 +39,11 @@ public class AgentUpdateModel
|
|||
/// </summary>
|
||||
public List<AgentUtility>? Utilities { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// McpTools
|
||||
/// </summary>
|
||||
public List<MCPTool>? McpTools { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// knowledge bases
|
||||
/// </summary>
|
||||
|
|
@ -103,6 +108,7 @@ public class AgentUpdateModel
|
|||
Functions = Functions ?? [],
|
||||
Responses = Responses ?? [],
|
||||
Utilities = Utilities ?? [],
|
||||
McpTools = McpTools ?? [],
|
||||
KnowledgeBases = KnowledgeBases ?? [],
|
||||
Rules = Rules ?? [],
|
||||
LlmConfig = LlmConfig ?? new()
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ public class AgentViewModel
|
|||
[JsonPropertyName("merge_utility")]
|
||||
public bool MergeUtility { get; set; }
|
||||
public List<AgentUtility> Utilities { get; set; }
|
||||
public List<MCPTool> Acps { get; set; }
|
||||
|
||||
[JsonPropertyName("knowledge_bases")]
|
||||
public List<AgentKnowledgeBase> KnowledgeBases { get; set; }
|
||||
|
|
@ -86,6 +87,7 @@ public class AgentViewModel
|
|||
Responses = agent.Responses ?? [],
|
||||
Samples = agent.Samples ?? [],
|
||||
Utilities = agent.Utilities ?? [],
|
||||
Acps = agent.McpTools ?? [],
|
||||
KnowledgeBases = agent.KnowledgeBases ?? [],
|
||||
IsPublic= agent.IsPublic,
|
||||
Disabled = agent.Disabled,
|
||||
|
|
|
|||
|
|
@ -12,17 +12,18 @@ public class AgentDocument : MongoBase
|
|||
public bool Disabled { get; set; }
|
||||
public bool MergeUtility { get; set; }
|
||||
public int? MaxMessageCount { get; set; }
|
||||
public List<ChannelInstructionMongoElement> ChannelInstructions { get; set; } = [];
|
||||
public List<AgentTemplateMongoElement> Templates { get; set; } = [];
|
||||
public List<FunctionDefMongoElement> Functions { get; set; } = [];
|
||||
public List<AgentResponseMongoElement> Responses { get; set; } = [];
|
||||
public List<string> Samples { get; set; } = [];
|
||||
public List<AgentUtilityMongoElement> Utilities { get; set; } = [];
|
||||
public List<AgentKnowledgeBaseMongoElement> KnowledgeBases { get; set; } = [];
|
||||
public List<string> Profiles { get; set; } = [];
|
||||
public List<string> Labels { get; set; } = [];
|
||||
public List<RoutingRuleMongoElement> RoutingRules { get; set; } = [];
|
||||
public List<AgentRuleMongoElement> Rules { get; set; } = [];
|
||||
public List<ChannelInstructionMongoElement> ChannelInstructions { get; set; }
|
||||
public List<AgentTemplateMongoElement> Templates { get; set; }
|
||||
public List<FunctionDefMongoElement> Functions { get; set; }
|
||||
public List<AgentResponseMongoElement> Responses { get; set; }
|
||||
public List<string> Samples { get; set; }
|
||||
public List<AgentUtilityMongoElement> Utilities { get; set; }
|
||||
public List<AgentMCPToolMongoElement> McpTools { get; set; }
|
||||
public List<AgentKnowledgeBaseMongoElement> KnowledgeBases { get; set; }
|
||||
public List<string> Profiles { get; set; }
|
||||
public List<string> Labels { get; set; }
|
||||
public List<RoutingRuleMongoElement> RoutingRules { get; set; }
|
||||
public List<AgentRuleMongoElement> Rules { get; set; }
|
||||
public AgentLlmConfigMongoElement? LlmConfig { get; set; }
|
||||
|
||||
public DateTime CreatedTime { get; set; }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
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;
|
||||
|
||||
[BsonIgnoreExtraElements(Inherited = true)]
|
||||
public class AgentMCPToolMongoElement
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public string ServerId { get; set; }
|
||||
|
||||
public bool Disabled { get; set; }
|
||||
public List<McpFunctionMongoElement> Functions { get; set; } = [];
|
||||
public List<McpTemplateMongoElement> Templates { get; set; } = [];
|
||||
|
||||
public static AgentMCPToolMongoElement ToMongoElement(MCPTool utility)
|
||||
{
|
||||
return new AgentMCPToolMongoElement
|
||||
{
|
||||
Disabled = utility.Disabled,
|
||||
Functions = utility.Functions?.Select(x => new McpFunctionMongoElement(x.Name))?.ToList() ?? [],
|
||||
};
|
||||
}
|
||||
|
||||
public static MCPTool ToDomainElement(AgentMCPToolMongoElement utility)
|
||||
{
|
||||
return new MCPTool
|
||||
{
|
||||
Disabled = utility.Disabled,
|
||||
Functions = utility.Functions?.Select(x => new MCPFunction(x.Name))?.ToList() ?? [],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class McpFunctionMongoElement
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public McpFunctionMongoElement()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public McpFunctionMongoElement(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
}
|
||||
|
||||
public class McpTemplateMongoElement
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public McpTemplateMongoElement()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public McpTemplateMongoElement(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -61,6 +61,9 @@ public partial class MongoRepository
|
|||
case AgentField.Utility:
|
||||
UpdateAgentUtilities(agent.Id, agent.MergeUtility, agent.Utilities);
|
||||
break;
|
||||
case AgentField.Acp:
|
||||
UpdateAgentMcps(agent.Id, agent.McpTools);
|
||||
break;
|
||||
case AgentField.KnowledgeBase:
|
||||
UpdateAgentKnowledgeBases(agent.Id, agent.KnowledgeBases);
|
||||
break;
|
||||
|
|
@ -261,6 +264,19 @@ public partial class MongoRepository
|
|||
_dc.Agents.UpdateOne(filter, update);
|
||||
}
|
||||
|
||||
private void UpdateAgentMcps(string agentId, List<MCPTool> mcps)
|
||||
{
|
||||
if (mcps == null) return;
|
||||
|
||||
var elements = mcps?.Select(x => AgentMCPToolMongoElement.ToMongoElement(x))?.ToList() ?? [];
|
||||
|
||||
var filter = Builders<AgentDocument>.Filter.Eq(x => x.Id, agentId);
|
||||
var update = Builders<AgentDocument>.Update
|
||||
.Set(x => x.McpTools, elements)
|
||||
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
||||
|
||||
_dc.Agents.UpdateOne(filter, update);
|
||||
}
|
||||
private void UpdateAgentKnowledgeBases(string agentId, List<AgentKnowledgeBase> knowledgeBases)
|
||||
{
|
||||
if (knowledgeBases == null) return;
|
||||
|
|
@ -330,6 +346,7 @@ public partial class MongoRepository
|
|||
.Set(x => x.Responses, agent.Responses.Select(r => AgentResponseMongoElement.ToMongoElement(r)).ToList())
|
||||
.Set(x => x.Samples, agent.Samples)
|
||||
.Set(x => x.Utilities, agent.Utilities.Select(u => AgentUtilityMongoElement.ToMongoElement(u)).ToList())
|
||||
.Set(x => x.McpTools, agent.McpTools.Select(u => AgentMCPToolMongoElement.ToMongoElement(u)).ToList())
|
||||
.Set(x => x.KnowledgeBases, agent.KnowledgeBases.Select(u => AgentKnowledgeBaseMongoElement.ToMongoElement(u)).ToList())
|
||||
.Set(x => x.Rules, agent.Rules.Select(e => AgentRuleMongoElement.ToMongoElement(e)).ToList())
|
||||
.Set(x => x.LlmConfig, AgentLlmConfigMongoElement.ToMongoElement(agent.LlmConfig))
|
||||
|
|
@ -510,6 +527,7 @@ public partial class MongoRepository
|
|||
Responses = x.Responses?.Select(r => AgentResponseMongoElement.ToMongoElement(r))?.ToList() ?? [],
|
||||
RoutingRules = x.RoutingRules?.Select(r => RoutingRuleMongoElement.ToMongoElement(r))?.ToList() ?? [],
|
||||
Utilities = x.Utilities?.Select(u => AgentUtilityMongoElement.ToMongoElement(u))?.ToList() ?? [],
|
||||
McpTools = x.McpTools?.Select(u => AgentMCPToolMongoElement.ToMongoElement(u))?.ToList() ?? [],
|
||||
KnowledgeBases = x.KnowledgeBases?.Select(k => AgentKnowledgeBaseMongoElement.ToMongoElement(k))?.ToList() ?? [],
|
||||
Rules = x.Rules?.Select(e => AgentRuleMongoElement.ToMongoElement(e))?.ToList() ?? [],
|
||||
CreatedTime = x.CreatedDateTime,
|
||||
|
|
@ -604,6 +622,7 @@ public partial class MongoRepository
|
|||
Responses = agentDoc.Responses?.Select(r => AgentResponseMongoElement.ToDomainElement(r))?.ToList() ?? [],
|
||||
RoutingRules = agentDoc.RoutingRules?.Select(r => RoutingRuleMongoElement.ToDomainElement(agentDoc.Id, agentDoc.Name, r))?.ToList() ?? [],
|
||||
Utilities = agentDoc.Utilities?.Select(u => AgentUtilityMongoElement.ToDomainElement(u))?.ToList() ?? [],
|
||||
McpTools = agentDoc.McpTools?.Select(u => AgentMCPToolMongoElement.ToDomainElement(u))?.ToList() ?? [],
|
||||
KnowledgeBases = agentDoc.KnowledgeBases?.Select(x => AgentKnowledgeBaseMongoElement.ToDomainElement(x))?.ToList() ?? [],
|
||||
Rules = agentDoc.Rules?.Select(e => AgentRuleMongoElement.ToDomainElement(e))?.ToList() ?? []
|
||||
};
|
||||
|
|
|
|||
|
|
@ -27,8 +27,10 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\tests\BotSharp.PizzaBot.MCPServer\BotSharp.PizzaBot.MCPServer.csproj" />
|
||||
<ProjectReference Include="..\..\tests\BotSharp.Plugin.PizzaBot\BotSharp.Plugin.PizzaBot.csproj" />
|
||||
<ProjectReference Include="..\BotSharp.ServiceDefaults\BotSharp.ServiceDefaults.csproj" />
|
||||
<ProjectReference Include="..\Infrastructure\BotSharp.MCP\BotSharp.MCP.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="$(SolutionName)==BotSharp">
|
||||
|
|
|
|||
|
|
@ -50,10 +50,7 @@
|
|||
"Name": "gpt-35-turbo",
|
||||
"Version": "1106",
|
||||
"ApiKey": "",
|
||||
"Endpoint": "https://gpt-35-turbo-instruct.openai.azure.com/",
|
||||
"Type": "chat",
|
||||
"PromptCost": 0.0015,
|
||||
"CompletionCost": 0.002
|
||||
"Endpoint": "https://gpt-35-turbo-instruct.openai.azure.com/"
|
||||
},
|
||||
{
|
||||
"Name": "gpt-35-turbo-instruct",
|
||||
|
|
@ -170,11 +167,29 @@
|
|||
"HostAgentId": "01e2fc5c-2c89-4ec7-8470-7688608b496c",
|
||||
"EnableTranslator": false,
|
||||
"LlmConfig": {
|
||||
"Provider": "openai",
|
||||
"Provider": "azure-openai",
|
||||
"Model": "gpt-4o-mini"
|
||||
}
|
||||
},
|
||||
|
||||
"MCPSettings": {
|
||||
"McpClientOptions": {
|
||||
"ClientInfo": {
|
||||
"Name": "SimpleToolsBotsharp",
|
||||
"Version": "1.0.0"
|
||||
}
|
||||
},
|
||||
"McpServerConfigs": [
|
||||
{
|
||||
"Id": "PizzaServer",
|
||||
"Name": "PizzaServer",
|
||||
"TransportType": "stdio",
|
||||
"TransportOptions": {
|
||||
"command": "BotSharp.PizzaBot.MCPServer.exe",
|
||||
"workingDirectory": "E:\\GitHub\\BotSharp\\src\\WebStarter\\bin\\Debug\\net8.0"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Conversation": {
|
||||
"DataDir": "conversations",
|
||||
"ShowVerboseLog": false,
|
||||
|
|
@ -397,6 +412,7 @@
|
|||
"BotSharp.Core.Crontab",
|
||||
"BotSharp.Core.Realtime",
|
||||
"BotSharp.Logger",
|
||||
"BotSharp.MCP",
|
||||
"BotSharp.Plugin.MongoStorage",
|
||||
"BotSharp.Plugin.Dashboard",
|
||||
"BotSharp.Plugin.OpenAI",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<LangVersion>12.0</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="mcpdotnet" Version="1.0.1.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.0" />
|
||||
<PackageReference Include="Serilog.Extensions.Logging" Version="9.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
204
tests/BotSharp.PizzaBot.MCPServer/Program.cs
Normal file
204
tests/BotSharp.PizzaBot.MCPServer/Program.cs
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
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;
|
||||
|
||||
namespace BotSharp.PizzaBot.MCPServer
|
||||
{
|
||||
internal class Program
|
||||
{
|
||||
private static async Task Main(string[] args)
|
||||
{
|
||||
Console.WriteLine("Starting server...");
|
||||
|
||||
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();
|
||||
|
||||
Console.WriteLine("Server object created, registering handlers.");
|
||||
|
||||
#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<string, JsonSchemaProperty>()
|
||||
{
|
||||
["order_number"] = new JsonSchemaProperty() { Type = "string", Description = "order number." },
|
||||
["total_amount"] = new JsonSchemaProperty() { Type = "string", Description = "total amount." },
|
||||
},
|
||||
Required = new List<string>() { "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<string, JsonSchemaProperty>()
|
||||
{
|
||||
["pizza_type"] = new JsonSchemaProperty() { Type = "string", Description = "The pizza type." },
|
||||
["quantity"] = new JsonSchemaProperty() { Type = "string", Description = "quantity of pizza." },
|
||||
|
||||
},
|
||||
Required = new List<string>(){ "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<string, JsonSchemaProperty>()
|
||||
{
|
||||
["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" },
|
||||
|
||||
},
|
||||
Required = new List<string>(){"pizza_type", "quantity", "unit_price" }
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
};
|
||||
|
||||
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";
|
||||
|
||||
// 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 == "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
|
||||
|
||||
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()
|
||||
{
|
||||
// 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();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +1,21 @@
|
|||
using BotSharp.Abstraction.Conversations.Models;
|
||||
using System.Text.Json;
|
||||
//using BotSharp.Abstraction.Conversations.Models;
|
||||
//using System.Text.Json;
|
||||
|
||||
namespace BotSharp.Plugin.PizzaBot.Functions;
|
||||
//namespace BotSharp.Plugin.PizzaBot.Functions;
|
||||
|
||||
public class GetPizzaPricesFn : IFunctionCallback
|
||||
{
|
||||
public string Name => "get_pizza_price";
|
||||
//public class GetPizzaPricesFn : IFunctionCallback
|
||||
//{
|
||||
// public string Name => "get_pizza_price";
|
||||
|
||||
public async Task<bool> Execute(RoleDialogModel message)
|
||||
{
|
||||
message.Data = new
|
||||
{
|
||||
pepperoni_unit_price = 3.2,
|
||||
cheese_unit_price = 3.5,
|
||||
margherita_unit_price = 3.8,
|
||||
};
|
||||
message.Content = JsonSerializer.Serialize(message.Data);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// public async Task<bool> Execute(RoleDialogModel message)
|
||||
// {
|
||||
// message.Data = new
|
||||
// {
|
||||
// pepperoni_unit_price = 3.2,
|
||||
// cheese_unit_price = 3.5,
|
||||
// margherita_unit_price = 3.8,
|
||||
// };
|
||||
// message.Content = JsonSerializer.Serialize(message.Data);
|
||||
// return true;
|
||||
// }
|
||||
//}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ public class GetPizzaTypesFn : IFunctionCallback
|
|||
}).ToArray()
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
using BotSharp.Abstraction.Conversations.Models;
|
||||
//using BotSharp.Abstraction.Conversations.Models;
|
||||
|
||||
namespace BotSharp.Plugin.PizzaBot.Functions;
|
||||
//namespace BotSharp.Plugin.PizzaBot.Functions;
|
||||
|
||||
public class MakePaymentFn : IFunctionCallback
|
||||
{
|
||||
public string Name => "make_payment";
|
||||
//public class MakePaymentFn : IFunctionCallback
|
||||
//{
|
||||
// public string Name => "make_payment";
|
||||
|
||||
public async Task<bool> Execute(RoleDialogModel message)
|
||||
{
|
||||
message.Content = "Payment proceed successfully. Thank you for your business. Have a great day!";
|
||||
message.Data = new
|
||||
{
|
||||
Transaction = Guid.NewGuid().ToString(),
|
||||
Status = "Success"
|
||||
};
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// public async Task<bool> Execute(RoleDialogModel message)
|
||||
// {
|
||||
// message.Content = "Payment proceed successfully. Thank you for your business. Have a great day!";
|
||||
// message.Data = new
|
||||
// {
|
||||
// Transaction = Guid.NewGuid().ToString(),
|
||||
// Status = "Success"
|
||||
// };
|
||||
// return true;
|
||||
// }
|
||||
//}
|
||||
|
|
|
|||
|
|
@ -1,24 +1,24 @@
|
|||
using BotSharp.Abstraction.Conversations;
|
||||
using BotSharp.Abstraction.Conversations.Models;
|
||||
//using BotSharp.Abstraction.Conversations;
|
||||
//using BotSharp.Abstraction.Conversations.Models;
|
||||
|
||||
namespace BotSharp.Plugin.PizzaBot.Functions;
|
||||
//namespace BotSharp.Plugin.PizzaBot.Functions;
|
||||
|
||||
public class PlaceOrderFn : IFunctionCallback
|
||||
{
|
||||
public string Name => "place_an_order";
|
||||
//public class PlaceOrderFn : IFunctionCallback
|
||||
//{
|
||||
// public string Name => "place_an_order";
|
||||
|
||||
private readonly IServiceProvider _service;
|
||||
public PlaceOrderFn(IServiceProvider service)
|
||||
{
|
||||
_service = service;
|
||||
}
|
||||
// private readonly IServiceProvider _service;
|
||||
// public PlaceOrderFn(IServiceProvider service)
|
||||
// {
|
||||
// _service = service;
|
||||
// }
|
||||
|
||||
public async Task<bool> Execute(RoleDialogModel message)
|
||||
{
|
||||
message.Content = "The order number is P123-01";
|
||||
var state = _service.GetRequiredService<IConversationStateService>();
|
||||
state.SetState("order_number", "P123-01");
|
||||
// public async Task<bool> Execute(RoleDialogModel message)
|
||||
// {
|
||||
// message.Content = "The order number is P123-01";
|
||||
// var state = _service.GetRequiredService<IConversationStateService>();
|
||||
// state.SetState("order_number", "P123-01");
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// return true;
|
||||
// }
|
||||
//}
|
||||
|
|
|
|||
|
|
@ -7,5 +7,19 @@
|
|||
"disabled": false,
|
||||
"isPublic": true,
|
||||
"profiles": [ "pizza" ],
|
||||
"mcptools": [
|
||||
{
|
||||
"serverid": "PizzaServer",
|
||||
"disabled": false,
|
||||
"functions": [
|
||||
{
|
||||
"Name": "get_pizza_price"
|
||||
},
|
||||
{
|
||||
"Name": "place_an_order"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
"labels": [ "experiment" ]
|
||||
}
|
||||
|
|
@ -1,18 +1,18 @@
|
|||
{
|
||||
"name": "get_pizza_price",
|
||||
"description": "call this function to get the pizza price",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"pizza_type": {
|
||||
"type": "string",
|
||||
"description": "The pizza type."
|
||||
},
|
||||
"quantity": {
|
||||
"type": "string",
|
||||
"description": "quantity of pizza."
|
||||
}
|
||||
},
|
||||
"required": [ "pizza_type", "quantity" ]
|
||||
}
|
||||
}
|
||||
//{
|
||||
// "name": "get_pizza_price",
|
||||
// "description": "call this function to get the pizza price",
|
||||
// "parameters": {
|
||||
// "type": "object",
|
||||
// "properties": {
|
||||
// "pizza_type": {
|
||||
// "type": "string",
|
||||
// "description": "The pizza type."
|
||||
// },
|
||||
// "quantity": {
|
||||
// "type": "string",
|
||||
// "description": "quantity of pizza."
|
||||
// }
|
||||
// },
|
||||
// "required": [ "pizza_type", "quantity" ]
|
||||
// }
|
||||
//}
|
||||
|
|
@ -1,22 +1,22 @@
|
|||
{
|
||||
"name": "place_an_order",
|
||||
"description": "Place an order when user has confirmed the pizza type and quantity.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"pizza_type": {
|
||||
"type": "string",
|
||||
"description": "The pizza type."
|
||||
},
|
||||
"quantity": {
|
||||
"type": "number",
|
||||
"description": "quantity of pizza."
|
||||
},
|
||||
"unit_price": {
|
||||
"type": "number",
|
||||
"description": "unit price"
|
||||
}
|
||||
},
|
||||
"required": [ "pizza_type", "quantity", "unit_price" ]
|
||||
}
|
||||
}
|
||||
//{
|
||||
// "name": "place_an_order",
|
||||
// "description": "Place an order when user has confirmed the pizza type and quantity.",
|
||||
// "parameters": {
|
||||
// "type": "object",
|
||||
// "properties": {
|
||||
// "pizza_type": {
|
||||
// "type": "string",
|
||||
// "description": "The pizza type."
|
||||
// },
|
||||
// "quantity": {
|
||||
// "type": "number",
|
||||
// "description": "quantity of pizza."
|
||||
// },
|
||||
// "unit_price": {
|
||||
// "type": "number",
|
||||
// "description": "unit price"
|
||||
// }
|
||||
// },
|
||||
// "required": [ "pizza_type", "quantity", "unit_price" ]
|
||||
// }
|
||||
//}
|
||||
|
|
@ -7,6 +7,17 @@
|
|||
"disabled": false,
|
||||
"isPublic": true,
|
||||
"profiles": [ "pizza" ],
|
||||
"mcptools": [
|
||||
{
|
||||
"serverid": "PizzaServer",
|
||||
"disabled": false,
|
||||
"functions": [
|
||||
{
|
||||
"Name": "make_payment"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"labels": [ "experiment" ],
|
||||
"routingRules": [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,18 +1,18 @@
|
|||
{
|
||||
"name": "make_payment",
|
||||
"description": "call this function to make payment",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"order_number": {
|
||||
"type": "string",
|
||||
"description": "order number."
|
||||
},
|
||||
"total_amount": {
|
||||
"type": "string",
|
||||
"description": "total amount."
|
||||
}
|
||||
},
|
||||
"required": [ "order_number", "total_amount" ]
|
||||
}
|
||||
}
|
||||
//{
|
||||
// "name": "make_payment",
|
||||
// "description": "call this function to make payment",
|
||||
// "parameters": {
|
||||
// "type": "object",
|
||||
// "properties": {
|
||||
// "order_number": {
|
||||
// "type": "string",
|
||||
// "description": "order number."
|
||||
// },
|
||||
// "total_amount": {
|
||||
// "type": "string",
|
||||
// "description": "total amount."
|
||||
// }
|
||||
// },
|
||||
// "required": [ "order_number", "total_amount" ]
|
||||
// }
|
||||
//}
|
||||
Loading…
Reference in a new issue