diff --git a/BotSharp.sln b/BotSharp.sln
index d4890be1..d82f930c 100644
--- a/BotSharp.sln
+++ b/BotSharp.sln
@@ -127,6 +127,8 @@ 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}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -519,6 +521,14 @@ 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
+ {8ED8EEF4-06DD-45F5-AA91-BD2395E901B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8ED8EEF4-06DD-45F5-AA91-BD2395E901B5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8ED8EEF4-06DD-45F5-AA91-BD2395E901B5}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {8ED8EEF4-06DD-45F5-AA91-BD2395E901B5}.Debug|x64.Build.0 = Debug|Any CPU
+ {8ED8EEF4-06DD-45F5-AA91-BD2395E901B5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8ED8EEF4-06DD-45F5-AA91-BD2395E901B5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8ED8EEF4-06DD-45F5-AA91-BD2395E901B5}.Release|x64.ActiveCfg = Release|Any CPU
+ {8ED8EEF4-06DD-45F5-AA91-BD2395E901B5}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -580,6 +590,7 @@ 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}
+ {8ED8EEF4-06DD-45F5-AA91-BD2395E901B5} = {E29DC6C4-5E57-48C5-BCB0-6B8F84782749}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A9969D89-C98B-40A5-A12B-FC87E55B3A19}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/AgentHookBase.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/AgentHookBase.cs
index bec14b85..1103f2f5 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Agents/AgentHookBase.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Agents/AgentHookBase.cs
@@ -57,5 +57,11 @@ public abstract class AgentHookBase : IAgentHook
public virtual void OnAgentUtilityLoaded(Agent agent)
{
+
+ }
+
+ public virtual void OnAgentMCPToolLoaded(Agent agent)
+ {
+
}
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentField.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentField.cs
index 0c39a960..413f9eb8 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentField.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentField.cs
@@ -19,6 +19,7 @@ public enum AgentField
Sample,
LlmConfig,
Utility,
+ Acp,
KnowledgeBase,
Rule,
MaxMessageCount
diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentHook.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentHook.cs
index a3f53fb6..624616d7 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentHook.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentHook.cs
@@ -27,6 +27,8 @@ public interface IAgentHook
void OnAgentUtilityLoaded(Agent agent);
+ void OnAgentMCPToolLoaded(Agent agent);
+
///
/// Triggered when agent is loaded completely.
///
diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs
index 085b0f9d..fc28e571 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs
@@ -95,15 +95,20 @@ public class Agent
public List Labels { get; set; } = new();
///
- /// Merge utilities from entry agent
+ /// Merge Utility from entry agent
///
public bool MergeUtility { get; set; }
///
- /// Agent utilities
+ /// Agent Utility
///
public List Utilities { get; set; } = new();
+ ///
+ /// Agent MCP Tools
+ ///
+ public List McpTools { get; set; } = new();
+
///
/// Agent rules
///
@@ -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 mcps)
+ {
+ McpTools = mcps ?? [];
+ return this;
+ }
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/MCPTool.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/MCPTool.cs
new file mode 100644
index 00000000..fc8fbb62
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/MCPTool.cs
@@ -0,0 +1,41 @@
+namespace BotSharp.Abstraction.Agents.Models;
+
+public class MCPTool
+{
+ public string Name { get; set; }
+
+ public string ServerId { get; set; }
+
+ public bool Disabled { get; set; }
+
+ public IEnumerable Functions { get; set; } = [];
+
+ public MCPTool()
+ {
+
+ }
+
+ public MCPTool(
+ string name,
+ IEnumerable? functions = null)
+ {
+ Name = name;
+ Functions = functions ?? [];
+ }
+
+ public override string ToString()
+ {
+ return Name;
+ }
+}
+
+
+public class MCPFunction
+{
+ public string Name { get; set; }
+
+ public MCPFunction()
+ {
+
+ }
+}
diff --git a/src/Infrastructure/BotSharp.Core/Agents/Hooks/BasicAgentHook.cs b/src/Infrastructure/BotSharp.Core/Agents/Hooks/BasicAgentHook.cs
index 1101f160..5ab0ea03 100644
--- a/src/Infrastructure/BotSharp.Core/Agents/Hooks/BasicAgentHook.cs
+++ b/src/Infrastructure/BotSharp.Core/Agents/Hooks/BasicAgentHook.cs
@@ -38,6 +38,7 @@ public class BasicAgentHook : AgentHookBase
agent.SecondaryInstructions.Add(prompt);
}
}
+
private (IEnumerable, IEnumerable) GetUtilityContent(Agent agent)
{
@@ -81,5 +82,5 @@ public class BasicAgentHook : AgentHookBase
.Distinct().ToList();
return (functionNames, templateNames);
- }
+ }
}
diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs
index 15ba26a1..7fd94340 100644
--- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs
+++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs
@@ -71,6 +71,11 @@ public partial class AgentService
{
hook.OnAgentUtilityLoaded(agent);
}
+
+ if(agent.McpTools != null)
+ {
+ hook.OnAgentMCPToolLoaded(agent);
+ }
hook.OnAgentLoaded(agent);
}
diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs
index a9cc58d9..7e9da19c 100644
--- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs
+++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs
@@ -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);
diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs
index d7a6d121..ae401508 100644
--- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs
+++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs
@@ -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 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 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;
diff --git a/src/Infrastructure/BotSharp.MCP/AIFunctionUtilities.cs b/src/Infrastructure/BotSharp.MCP/AIFunctionUtilities.cs
new file mode 100644
index 00000000..e86cbf92
--- /dev/null
+++ b/src/Infrastructure/BotSharp.MCP/AIFunctionUtilities.cs
@@ -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();
+
+ 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;
+ }
+}
diff --git a/src/Infrastructure/BotSharp.MCP/BotSharp.MCP.csproj b/src/Infrastructure/BotSharp.MCP/BotSharp.MCP.csproj
new file mode 100644
index 00000000..c171a732
--- /dev/null
+++ b/src/Infrastructure/BotSharp.MCP/BotSharp.MCP.csproj
@@ -0,0 +1,18 @@
+
+
+
+ $(TargetFramework)
+ $(LangVersion)
+ $(BotSharpVersion)
+ $(GeneratePackageOnBuild)
+ $(SolutionDir)packages
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Infrastructure/BotSharp.MCP/Functions/McpToolFunction.cs b/src/Infrastructure/BotSharp.MCP/Functions/McpToolFunction.cs
new file mode 100644
index 00000000..5df8fb07
--- /dev/null
+++ b/src/Infrastructure/BotSharp.MCP/Functions/McpToolFunction.cs
@@ -0,0 +1,96 @@
+using BotSharp.Abstraction.Conversations.Models;
+using BotSharp.Abstraction.Functions;
+using McpDotNet.Client;
+using McpDotNet.Protocol.Types;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text.Json;
+using System.Threading.Tasks;
+
+namespace BotSharp.Core.Mcp.Functions;
+
+public class McpToolFunction : IFunctionCallback
+{
+ private readonly Tool _tool;
+ private readonly IMcpClient _client;
+
+ public McpToolFunction(Tool tool, IMcpClient client)
+ {
+ _tool = tool ?? throw new ArgumentNullException(nameof(tool));
+ _client = client ?? throw new ArgumentNullException(nameof(client));
+ }
+
+ public string Name => _tool.Name;
+
+ public async Task Execute(RoleDialogModel message)
+ {
+ // Convert arguments to dictionary format expected by mcpdotnet
+ Dictionary argDict = JsonToDictionary(message.FunctionArgs);
+
+ // Call the tool through mcpdotnet
+ var result = await _client.CallToolAsync(
+ _tool.Name,
+ argDict.Count == 0 ? new() : argDict
+ );
+
+ // Extract the text content from the result
+ // For simplicity in this sample, we'll just concatenate all text content
+ message.Content = string.Join("\n", result.Content
+ .Where(c => c.Type == "text")
+ .Select(c => c.Text));
+ return true;
+ }
+
+ private static Dictionary JsonToDictionary(string? json)
+ {
+ if (string.IsNullOrEmpty(json))
+ return [];
+
+ // 使用JsonDocument解析JSON字符串
+ using JsonDocument doc = JsonDocument.Parse(json);
+ JsonElement root = doc.RootElement;
+ return JsonElementToDictionary(root);
+ }
+
+ private static Dictionary JsonElementToDictionary(JsonElement element)
+ {
+ Dictionary 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.")
+ };
+
+}
diff --git a/src/Infrastructure/BotSharp.MCP/Hooks/MCPToolAgentHook.cs b/src/Infrastructure/BotSharp.MCP/Hooks/MCPToolAgentHook.cs
new file mode 100644
index 00000000..a4ac94b7
--- /dev/null
+++ b/src/Infrastructure/BotSharp.MCP/Hooks/MCPToolAgentHook.cs
@@ -0,0 +1,71 @@
+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;
+
+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();
+ var isConvMode = conv.IsConversationMode();
+ if (!isConvMode) return;
+
+ agent.SecondaryFunctions ??= [];
+ agent.SecondaryInstructions ??= [];
+ agent.McpTools ??= [];
+
+ var functions = GetMCPContent(agent);
+
+ foreach (var fn in functions)
+ {
+ if (!agent.SecondaryFunctions.Any(x => x.Name.Equals(fn.Name, StringComparison.OrdinalIgnoreCase)))
+ {
+ agent.SecondaryFunctions.Add(fn);
+ }
+ }
+ }
+
+ private IEnumerable GetMCPContent(Agent agent)
+ {
+ List functionDefs = new List();
+ var mcpClientManager = _services.GetRequiredService();
+ var mcps = agent.McpTools;
+ foreach (var item in mcps)
+ {
+ var ua = mcpClientManager.Factory.GetClientAsync(item.ServerId).Result;
+ if (ua != null)
+ {
+ var tools = ua.ListToolsAsync().Result;
+ 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;
+ }
+}
diff --git a/src/Infrastructure/BotSharp.MCP/MCPClientManager.cs b/src/Infrastructure/BotSharp.MCP/MCPClientManager.cs
new file mode 100644
index 00000000..bbe70205
--- /dev/null
+++ b/src/Infrastructure/BotSharp.MCP/MCPClientManager.cs
@@ -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();
+ }
+}
diff --git a/src/Infrastructure/BotSharp.MCP/McpPlugin.cs b/src/Infrastructure/BotSharp.MCP/McpPlugin.cs
new file mode 100644
index 00000000..c54a7050
--- /dev/null
+++ b/src/Infrastructure/BotSharp.MCP/McpPlugin.cs
@@ -0,0 +1,45 @@
+using BotSharp.Abstraction.Functions;
+using BotSharp.Abstraction.Plugins;
+using BotSharp.Core.Mcp.Functions;
+using BotSharp.Core.Mcp.Settings;
+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();
+ services.AddScoped(provider => { return settings; });
+ services.AddSingleton(provider =>
+ {
+ var loggerFactory = provider.GetService() ?? NullLoggerFactory.Instance;
+ return new MCPClientManager(settings, loggerFactory);
+ });
+
+ 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 =>
+ {
+ var func = new McpToolFunction(tool, client);
+ return func;
+ });
+ }
+ }
+ }
+ }
diff --git a/src/Infrastructure/BotSharp.MCP/Settings/MCPSettings.cs b/src/Infrastructure/BotSharp.MCP/Settings/MCPSettings.cs
new file mode 100644
index 00000000..34e408e3
--- /dev/null
+++ b/src/Infrastructure/BotSharp.MCP/Settings/MCPSettings.cs
@@ -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 McpServerConfigs { get; set; }
+
+}
diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentCreationModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentCreationModel.cs
index 8e4d915d..81ec1bc7 100644
--- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentCreationModel.cs
+++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentCreationModel.cs
@@ -55,6 +55,7 @@ public class AgentCreationModel
public int? MaxMessageCount { get; set; }
public List Utilities { get; set; } = new();
+ public List McpTools { get; set; } = new();
public List RoutingRules { get; set; } = new();
public List KnowledgeBases { get; set; } = new();
public List 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,
diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentUpdateModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentUpdateModel.cs
index ac2749b5..9f2925e5 100644
--- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentUpdateModel.cs
+++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentUpdateModel.cs
@@ -39,6 +39,11 @@ public class AgentUpdateModel
///
public List? Utilities { get; set; }
+ ///
+ /// McpTools
+ ///
+ public List? McpTools { get; set; }
+
///
/// knowledge bases
///
@@ -103,6 +108,7 @@ public class AgentUpdateModel
Functions = Functions ?? [],
Responses = Responses ?? [],
Utilities = Utilities ?? [],
+ McpTools = McpTools ?? [],
KnowledgeBases = KnowledgeBases ?? [],
Rules = Rules ?? [],
LlmConfig = LlmConfig ?? new()
diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs
index b4ba1e08..db3af34c 100644
--- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs
+++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs
@@ -24,6 +24,7 @@ public class AgentViewModel
[JsonPropertyName("merge_utility")]
public bool MergeUtility { get; set; }
public List Utilities { get; set; }
+ public List Acps { get; set; }
[JsonPropertyName("knowledge_bases")]
public List 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,
diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentDocument.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentDocument.cs
index bdfdbe80..a0d99e0d 100644
--- a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentDocument.cs
+++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentDocument.cs
@@ -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 ChannelInstructions { get; set; } = [];
- public List Templates { get; set; } = [];
- public List Functions { get; set; } = [];
- public List Responses { get; set; } = [];
- public List Samples { get; set; } = [];
- public List Utilities { get; set; } = [];
- public List KnowledgeBases { get; set; } = [];
- public List Profiles { get; set; } = [];
- public List Labels { get; set; } = [];
- public List RoutingRules { get; set; } = [];
- public List Rules { get; set; } = [];
+ public List ChannelInstructions { get; set; }
+ public List Templates { get; set; }
+ public List Functions { get; set; }
+ public List Responses { get; set; }
+ public List Samples { get; set; }
+ public List Utilities { get; set; }
+ public List McpTools { get; set; }
+ public List KnowledgeBases { get; set; }
+ public List Profiles { get; set; }
+ public List Labels { get; set; }
+ public List RoutingRules { get; set; }
+ public List Rules { get; set; }
public AgentLlmConfigMongoElement? LlmConfig { get; set; }
public DateTime CreatedTime { get; set; }
diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentMCPToolMongoElement.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentMCPToolMongoElement.cs
new file mode 100644
index 00000000..dae4c665
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentMCPToolMongoElement.cs
@@ -0,0 +1,73 @@
+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 Functions { get; set; } = [];
+ public List Templates { get; set; } = [];
+
+ public static AgentMCPToolMongoElement ToMongoElement(MCPTool utility)
+ {
+ return new AgentMCPToolMongoElement
+ {
+ Name = utility.Name,
+ Disabled = utility.Disabled,
+ Functions = utility.Functions?.Select(x => new McpFunctionMongoElement(x.Name))?.ToList() ?? [],
+ Templates = utility.Templates?.Select(x => new McpTemplateMongoElement(x.Name))?.ToList() ?? []
+ };
+ }
+
+ public static MCPTool ToDomainElement(AgentMCPToolMongoElement utility)
+ {
+ return new MCPTool
+ {
+ Name = utility.Name,
+ Disabled = utility.Disabled,
+ Functions = utility.Functions?.Select(x => new MCPFunction(x.Name))?.ToList() ?? [],
+ Templates = utility.Templates?.Select(x => new ACPTemplate(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;
+ }
+}
+
diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs
index 90dd9cdf..069845ef 100644
--- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs
+++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs
@@ -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 mcps)
+ {
+ if (mcps == null) return;
+
+ var elements = mcps?.Select(x => AgentMCPToolMongoElement.ToMongoElement(x))?.ToList() ?? [];
+
+ var filter = Builders.Filter.Eq(x => x.Id, agentId);
+ var update = Builders.Update
+ .Set(x => x.McpTools, elements)
+ .Set(x => x.UpdatedTime, DateTime.UtcNow);
+
+ _dc.Agents.UpdateOne(filter, update);
+ }
private void UpdateAgentKnowledgeBases(string agentId, List 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() ?? []
};
diff --git a/src/WebStarter/appsettings.json b/src/WebStarter/appsettings.json
index a0f02893..b8ab5f02 100644
--- a/src/WebStarter/appsettings.json
+++ b/src/WebStarter/appsettings.json
@@ -174,7 +174,23 @@
"Model": "gpt-4o-mini"
}
},
-
+ "MCPSettings": {
+ "McpClientOptions": {
+ "ClientInfo": {
+ "Name": "SimpleToolsBotsharp",
+ "Version": "1.0.0"
+ }
+ },
+ "McpServerOptions": [
+ {
+ "Id": "everything",
+ "Name": "Everything",
+ "TransportType": "sse",
+ "TransportOptions": {},
+ "Location": "http://localhost:5000/sse"
+ }
+ ]
+ },
"Conversation": {
"DataDir": "conversations",
"ShowVerboseLog": false,