13 changed files with 302 additions and 3 deletions
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -0,0 +1,30 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
|||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
|||
<xs:element name="Weavers"> |
|||
<xs:complexType> |
|||
<xs:all> |
|||
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
|||
<xs:complexType> |
|||
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:all> |
|||
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
|||
<xs:annotation> |
|||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:schema> |
|||
@ -0,0 +1,26 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\..\configureawait.props" /> |
|||
<Import Project="..\..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFrameworks>net8.0;net9.0;net10.0</TargetFrameworks> |
|||
<AssemblyName>LINGYUN.Abp.AI.Tools.Mcp</AssemblyName> |
|||
<PackageId>LINGYUN.Abp.AI.Tools.Mcp</PackageId> |
|||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> |
|||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> |
|||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> |
|||
<Nullable>enable</Nullable> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.Http.Client" /> |
|||
<PackageReference Include="ModelContextProtocol" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\LINGYUN.Abp.AI.Tools\LINGYUN.Abp.AI.Tools.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,22 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.Http.Client; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.AI.Tools.Mcp; |
|||
|
|||
[DependsOn( |
|||
typeof(AbpAIToolsModule), |
|||
typeof(AbpHttpClientModule))] |
|||
public class AbpAIToolsMcpModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.AddMcpAIToolClient(); |
|||
|
|||
Configure<AbpAIToolsOptions>(options => |
|||
{ |
|||
// Mcp工具
|
|||
options.AIToolProviders.Add<McpAIToolProvider>(); |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,150 @@ |
|||
using ModelContextProtocol.Client; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Volo.Abp; |
|||
|
|||
namespace LINGYUN.Abp.AI.Tools.Mcp; |
|||
public static class McpAIToolDefinitionExtenssions |
|||
{ |
|||
private const string Endpoint = "McpEndpoint"; |
|||
private const string Headers = "McpHeaders"; |
|||
private const string TransportMode = "McpTransportMode"; |
|||
private const string ConnectionTimeout = "McpConnectionTimeout"; |
|||
private const string MaxReconnectionAttempts = "McpMaxReconnectionAttempts"; |
|||
private const string CurrentAccessToken = "UseMcpCurrentAccessToken"; |
|||
|
|||
public static AIToolDefinition UseMcpCurrentAccessToken(this AIToolDefinition definition, bool useCurrentAccessToken = true) |
|||
{ |
|||
definition.WithProperty(CurrentAccessToken, useCurrentAccessToken); |
|||
|
|||
return definition; |
|||
} |
|||
|
|||
public static AIToolDefinition WithMcpEndpoint(this AIToolDefinition definition, string endPoint) |
|||
{ |
|||
Check.NotNullOrWhiteSpace(endPoint, nameof(endPoint)); |
|||
|
|||
definition.WithProperty(Endpoint, endPoint); |
|||
|
|||
return definition; |
|||
} |
|||
|
|||
public static AIToolDefinition WithMcpTransportMode(this AIToolDefinition definition, HttpTransportMode transportMode = HttpTransportMode.AutoDetect) |
|||
{ |
|||
definition.WithProperty(TransportMode, ((int)transportMode).ToString()); |
|||
|
|||
return definition; |
|||
} |
|||
|
|||
public static AIToolDefinition WithMcpConnectionTimeout(this AIToolDefinition definition, TimeSpan connectionTimeout) |
|||
{ |
|||
definition.WithProperty(ConnectionTimeout, connectionTimeout.ToString()); |
|||
|
|||
return definition; |
|||
} |
|||
|
|||
public static AIToolDefinition WithMcpMaxReconnectionAttempts(this AIToolDefinition definition, int maxReconnectionAttempts = 5) |
|||
{ |
|||
definition.WithProperty(MaxReconnectionAttempts, maxReconnectionAttempts); |
|||
|
|||
return definition; |
|||
} |
|||
|
|||
public static AIToolDefinition WithMcpHeaders(this AIToolDefinition definition, IDictionary<string, string> headers) |
|||
{ |
|||
Check.NotNullOrEmpty(headers, nameof(headers)); |
|||
|
|||
var currentHeaders = definition.GetMcpHeaders(); |
|||
|
|||
currentHeaders.AddIfNotContains(headers); |
|||
|
|||
definition.WithProperty(Headers, currentHeaders); |
|||
|
|||
return definition; |
|||
} |
|||
|
|||
public static AIToolDefinition WithMcpHeaders(this AIToolDefinition definition, string key, string value) |
|||
{ |
|||
Check.NotNullOrEmpty(key, nameof(key)); |
|||
Check.NotNullOrEmpty(value, nameof(value)); |
|||
|
|||
var currentHeaders = definition.GetMcpHeaders(); |
|||
|
|||
currentHeaders.TryAdd(key, value); |
|||
|
|||
definition.WithProperty(Headers, currentHeaders); |
|||
|
|||
return definition; |
|||
} |
|||
|
|||
public static bool IsUseMcpCurrentAccessToken(this AIToolDefinition definition) |
|||
{ |
|||
if (definition.Properties.TryGetValue(CurrentAccessToken, out var useCurrentAccessTokenObj) && useCurrentAccessTokenObj != null |
|||
&& bool.TryParse(useCurrentAccessTokenObj.ToString(), out var useCurrentAccessToken)) |
|||
{ |
|||
return useCurrentAccessToken; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
public static string GetMcpEndpoint(this AIToolDefinition definition) |
|||
{ |
|||
definition.Properties.TryGetValue(Endpoint, out var endpointObj); |
|||
|
|||
Check.NotNull(endpointObj, nameof(Endpoint)); |
|||
|
|||
return endpointObj.ToString()!; |
|||
} |
|||
|
|||
public static TimeSpan GetMcpConnectionTimeout(this AIToolDefinition definition, double defaultConnectionTimeoutSeconds = 30) |
|||
{ |
|||
definition.Properties.TryGetValue(ConnectionTimeout, out var connectionTimeoutObj); |
|||
|
|||
if (connectionTimeoutObj != null && |
|||
TimeSpan.TryParse(connectionTimeoutObj.ToString(), out var connectionTimeout)) |
|||
{ |
|||
return connectionTimeout; |
|||
} |
|||
|
|||
return TimeSpan.FromSeconds(defaultConnectionTimeoutSeconds); |
|||
} |
|||
|
|||
public static HttpTransportMode GetMcpTransportMode(this AIToolDefinition definition, HttpTransportMode defaultTransportMode = HttpTransportMode.AutoDetect) |
|||
{ |
|||
definition.Properties.TryGetValue(TransportMode, out var defaultTransportModeObj); |
|||
|
|||
if (defaultTransportModeObj != null && |
|||
Enum.TryParse<HttpTransportMode>(defaultTransportModeObj.ToString(), out var transportMode)) |
|||
{ |
|||
return transportMode; |
|||
} |
|||
|
|||
return defaultTransportMode; |
|||
} |
|||
public static int GetMcpMaxReconnectionAttempts(this AIToolDefinition definition, int defaultMaxReconnectionAttempts = 5) |
|||
{ |
|||
definition.Properties.TryGetValue(MaxReconnectionAttempts, out var maxReconnectionAttemptsObj); |
|||
|
|||
if (maxReconnectionAttemptsObj != null && |
|||
int.TryParse(maxReconnectionAttemptsObj.ToString(), out var maxReconnectionAttempts)) |
|||
{ |
|||
return maxReconnectionAttempts; |
|||
} |
|||
|
|||
return defaultMaxReconnectionAttempts; |
|||
} |
|||
|
|||
public static IDictionary<string, string> GetMcpHeaders(this AIToolDefinition definition) |
|||
{ |
|||
if (definition.Properties.TryGetValue(Headers, out var headersObj) && headersObj != null) |
|||
{ |
|||
if (headersObj is IDictionary<string, string> h) |
|||
{ |
|||
return h; |
|||
} |
|||
} |
|||
|
|||
return new Dictionary<string, string>(); |
|||
} |
|||
} |
|||
@ -0,0 +1,46 @@ |
|||
using Microsoft.Extensions.AI; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using ModelContextProtocol.Client; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Net.Http; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace LINGYUN.Abp.AI.Tools.Mcp; |
|||
public class McpAIToolProvider : IAIToolProvider, ITransientDependency |
|||
{ |
|||
public const string ProviderName = "Mcp"; |
|||
public string Name => ProviderName; |
|||
|
|||
protected IHttpClientFactory HttpClientFactory { get; } |
|||
public McpAIToolProvider(IHttpClientFactory httpClientFactory) |
|||
{ |
|||
HttpClientFactory = httpClientFactory; |
|||
} |
|||
|
|||
public async virtual Task<AITool[]> CreateToolsAsync(AIToolDefinition definition) |
|||
{ |
|||
var httpClient = HttpClientFactory.CreateMcpAIToolClient(); |
|||
var httpClientTransportOptions = new HttpClientTransportOptions |
|||
{ |
|||
Endpoint = new Uri(definition.GetMcpEndpoint()), |
|||
AdditionalHeaders = new Dictionary<string, string>(), |
|||
TransportMode = definition.GetMcpTransportMode(), |
|||
ConnectionTimeout = definition.GetMcpConnectionTimeout(), |
|||
MaxReconnectionAttempts = definition.GetMcpMaxReconnectionAttempts(), |
|||
}; |
|||
|
|||
var headers = definition.GetMcpHeaders(); |
|||
foreach (var header in headers) |
|||
{ |
|||
httpClientTransportOptions.AdditionalHeaders.TryAdd(header.Key, header.Value); |
|||
} |
|||
|
|||
var mcpClient = await McpClient.CreateAsync( |
|||
new HttpClientTransport(httpClientTransportOptions, httpClient)); |
|||
|
|||
return (await mcpClient.ListToolsAsync()).ToArray(); |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
using System.Net.Http; |
|||
|
|||
namespace Microsoft.Extensions.DependencyInjection; |
|||
internal static class HttpClientMcpAIToolExtenssions |
|||
{ |
|||
private const string McpAIToolClient = "__AbpAIMcpToolClient"; |
|||
public static IServiceCollection AddMcpAIToolClient(this IServiceCollection services) |
|||
{ |
|||
services.AddHttpClient(McpAIToolClient); |
|||
|
|||
return services; |
|||
} |
|||
|
|||
public static HttpClient CreateMcpAIToolClient(this IHttpClientFactory httpClientFactory) |
|||
{ |
|||
return httpClientFactory.CreateClient(McpAIToolClient); |
|||
} |
|||
} |
|||
Loading…
Reference in new issue