添加项目文件。
This commit is contained in:
parent
265f3abb97
commit
f13d3c9923
25
lazy52API.sln
Normal file
25
lazy52API.sln
Normal file
@ -0,0 +1,25 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.13.35931.197 d17.13
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "lazy52API", "lazy52API\lazy52API.csproj", "{13E3BF0F-6CBC-465C-A566-FB635D87DCA9}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{13E3BF0F-6CBC-465C-A566-FB635D87DCA9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{13E3BF0F-6CBC-465C-A566-FB635D87DCA9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{13E3BF0F-6CBC-465C-A566-FB635D87DCA9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{13E3BF0F-6CBC-465C-A566-FB635D87DCA9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {0CA45D20-65DD-42F9-9F9D-382FA58B5226}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
156
lazy52API/McpEndpointRouteBuilderExtensions.cs
Normal file
156
lazy52API/McpEndpointRouteBuilderExtensions.cs
Normal file
@ -0,0 +1,156 @@
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Routing.Patterns;
|
||||
using Microsoft.AspNetCore.WebUtilities;
|
||||
using Microsoft.Extensions.Options;
|
||||
using ModelContextProtocol.Protocol.Messages;
|
||||
using ModelContextProtocol.Protocol.Transport;
|
||||
using ModelContextProtocol.Server;
|
||||
using ModelContextProtocol.Utils.Json;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace WebSSE
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for <see cref="IEndpointRouteBuilder"/> to add MCP endpoints.
|
||||
/// https://github.com/modelcontextprotocol/csharp-sdk/tree/main
|
||||
/// </summary>
|
||||
public static class McpEndpointRouteBuilderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets up endpoints for handling MCP HTTP Streaming transport.
|
||||
/// </summary>
|
||||
/// <param name="endpoints">The web application to attach MCP HTTP endpoints.</param>
|
||||
/// <param name="pattern">The route pattern prefix to map to.</param>
|
||||
/// <param name="configureOptionsAsync">Configure per-session options.</param>
|
||||
/// <param name="runSessionAsync">Provides an optional asynchronous callback for handling new MCP sessions.</param>
|
||||
/// <returns>Returns a builder for configuring additional endpoint conventions like authorization policies.</returns>
|
||||
public static IEndpointConventionBuilder MapMcp(
|
||||
this IEndpointRouteBuilder endpoints,
|
||||
[StringSyntax("Route")] string pattern = "",
|
||||
Func<HttpContext, McpServerOptions, CancellationToken, Task>? configureOptionsAsync = null,
|
||||
Func<HttpContext, IMcpServer, CancellationToken, Task>? runSessionAsync = null)
|
||||
=> endpoints.MapMcp(RoutePatternFactory.Parse(pattern), configureOptionsAsync, runSessionAsync);
|
||||
|
||||
/// <summary>
|
||||
/// Sets up endpoints for handling MCP HTTP Streaming transport.
|
||||
/// </summary>
|
||||
/// <param name="endpoints">The web application to attach MCP HTTP endpoints.</param>
|
||||
/// <param name="pattern">The route pattern prefix to map to.</param>
|
||||
/// <param name="configureOptionsAsync">Configure per-session options.</param>
|
||||
/// <param name="runSessionAsync">Provides an optional asynchronous callback for handling new MCP sessions.</param>
|
||||
/// <returns>Returns a builder for configuring additional endpoint conventions like authorization policies.</returns>
|
||||
public static IEndpointConventionBuilder MapMcp(this IEndpointRouteBuilder endpoints,
|
||||
RoutePattern pattern,
|
||||
Func<HttpContext, McpServerOptions, CancellationToken, Task>? configureOptionsAsync = null,
|
||||
Func<HttpContext, IMcpServer, CancellationToken, Task>? runSessionAsync = null)
|
||||
{
|
||||
ConcurrentDictionary<string, SseResponseStreamTransport> _sessions = new(StringComparer.Ordinal);
|
||||
|
||||
var loggerFactory = endpoints.ServiceProvider.GetRequiredService<ILoggerFactory>();
|
||||
var optionsSnapshot = endpoints.ServiceProvider.GetRequiredService<IOptions<McpServerOptions>>();
|
||||
var optionsFactory = endpoints.ServiceProvider.GetRequiredService<IOptionsFactory<McpServerOptions>>();
|
||||
var hostApplicationLifetime = endpoints.ServiceProvider.GetRequiredService<IHostApplicationLifetime>();
|
||||
|
||||
var routeGroup = endpoints.MapGroup(pattern);
|
||||
|
||||
routeGroup.MapGet("/sse", async context =>
|
||||
{
|
||||
// If the server is shutting down, we need to cancel all SSE connections immediately without waiting for HostOptions.ShutdownTimeout
|
||||
// which defaults to 30 seconds.
|
||||
using var sseCts = CancellationTokenSource.CreateLinkedTokenSource(context.RequestAborted, hostApplicationLifetime.ApplicationStopping);
|
||||
var cancellationToken = sseCts.Token;
|
||||
|
||||
var response = context.Response;
|
||||
response.Headers.ContentType = "text/event-stream";
|
||||
response.Headers.CacheControl = "no-cache,no-store";
|
||||
|
||||
// Make sure we disable all response buffering for SSE
|
||||
context.Response.Headers.ContentEncoding = "identity";
|
||||
context.Features.GetRequiredFeature<IHttpResponseBodyFeature>().DisableBuffering();
|
||||
|
||||
var sessionId = MakeNewSessionId();
|
||||
await using var transport = new SseResponseStreamTransport(response.Body, $"/message?sessionId={sessionId}");
|
||||
if (!_sessions.TryAdd(sessionId, transport))
|
||||
{
|
||||
throw new Exception($"Unreachable given good entropy! Session with ID '{sessionId}' has already been created.");
|
||||
}
|
||||
|
||||
var options = optionsSnapshot.Value;
|
||||
if (configureOptionsAsync is not null)
|
||||
{
|
||||
options = optionsFactory.Create(Options.DefaultName);
|
||||
await configureOptionsAsync.Invoke(context, options, cancellationToken);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var transportTask = transport.RunAsync(cancellationToken);
|
||||
|
||||
try
|
||||
{
|
||||
await using var mcpServer = McpServerFactory.Create(transport, options, loggerFactory, endpoints.ServiceProvider);
|
||||
context.Features.Set(mcpServer);
|
||||
|
||||
runSessionAsync ??= RunSession;
|
||||
await runSessionAsync(context, mcpServer, cancellationToken);
|
||||
}
|
||||
finally
|
||||
{
|
||||
await transport.DisposeAsync();
|
||||
await transportTask;
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
// RequestAborted always triggers when the client disconnects before a complete response body is written,
|
||||
// but this is how SSE connections are typically closed.
|
||||
}
|
||||
finally
|
||||
{
|
||||
_sessions.TryRemove(sessionId, out _);
|
||||
}
|
||||
});
|
||||
|
||||
routeGroup.MapPost("/message", async context =>
|
||||
{
|
||||
if (!context.Request.Query.TryGetValue("sessionId", out var sessionId))
|
||||
{
|
||||
await Results.BadRequest("Missing sessionId query parameter.").ExecuteAsync(context);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_sessions.TryGetValue(sessionId.ToString(), out var transport))
|
||||
{
|
||||
await Results.BadRequest($"Session ID not found.").ExecuteAsync(context);
|
||||
return;
|
||||
}
|
||||
|
||||
var message = (IJsonRpcMessage?)await context.Request.ReadFromJsonAsync(McpJsonUtilities.DefaultOptions.GetTypeInfo(typeof(IJsonRpcMessage)), 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;
|
||||
}
|
||||
|
||||
private static Task RunSession(HttpContext httpContext, IMcpServer session, CancellationToken requestAborted)
|
||||
=> session.RunAsync(requestAborted);
|
||||
|
||||
private static string MakeNewSessionId()
|
||||
{
|
||||
// 128 bits
|
||||
Span<byte> buffer = stackalloc byte[16];
|
||||
RandomNumberGenerator.Fill(buffer);
|
||||
return WebEncoders.Base64UrlEncode(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
30
lazy52API/Program.cs
Normal file
30
lazy52API/Program.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using WebSSE;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Add services to the container.
|
||||
|
||||
builder.Services.AddControllers();
|
||||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen();
|
||||
builder.Services.AddMcpServer().WithToolsFromAssembly();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
//if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
app.MapControllers();
|
||||
//app.MapMcpSse();
|
||||
app.MapMcp();
|
||||
|
||||
app.Run();
|
||||
52
lazy52API/Properties/launchSettings.json
Normal file
52
lazy52API/Properties/launchSettings.json
Normal file
@ -0,0 +1,52 @@
|
||||
{
|
||||
"profiles": {
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"dotnetRunMessages": true,
|
||||
"applicationUrl": "http://localhost:5010"
|
||||
},
|
||||
"https": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"dotnetRunMessages": true,
|
||||
"applicationUrl": "https://localhost:7110;http://localhost:5010"
|
||||
},
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"Container (.NET SDK)": {
|
||||
"commandName": "SdkContainer",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_HTTPS_PORTS": "8081",
|
||||
"ASPNETCORE_HTTP_PORTS": "8080"
|
||||
},
|
||||
"publishAllPorts": true,
|
||||
"useSSL": true
|
||||
}
|
||||
},
|
||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:2486",
|
||||
"sslPort": 44340
|
||||
}
|
||||
}
|
||||
}
|
||||
53
lazy52API/Tool/doubao.cs
Normal file
53
lazy52API/Tool/doubao.cs
Normal file
@ -0,0 +1,53 @@
|
||||
using Flurl;
|
||||
using Flurl.Http;
|
||||
using ModelContextProtocol.Server;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace lazy52API.Tool
|
||||
{
|
||||
[McpServerToolType]
|
||||
public static class Painting
|
||||
{
|
||||
/// <summary>
|
||||
/// 调用豆包绘图接口
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[McpServerTool(Name = "DouBaoPainting"), Description("调用豆包绘图接口;输出image_url数组(一般是四个)")]
|
||||
public static string DouBao(
|
||||
[Description("要生成图片的描述")] string description,
|
||||
[Description("绘画风格")] string type,
|
||||
[Description("绘画比例,可选9:16,2:3,3:4,4:3,1:1,3:2,16:9。不填默认16:9")] string ratio = "16:9"
|
||||
)
|
||||
{
|
||||
Console.WriteLine($"接收到绘图任务:{description}");
|
||||
var Painting = new
|
||||
{
|
||||
description,
|
||||
type,
|
||||
ratio
|
||||
};
|
||||
var filteredPainting = new System.Collections.Generic.Dictionary<string, object>();
|
||||
foreach (var prop in Painting.GetType().GetProperties())
|
||||
{
|
||||
var value = prop.GetValue(Painting);
|
||||
if (value != null)
|
||||
{
|
||||
filteredPainting[prop.Name] = value;
|
||||
}
|
||||
}
|
||||
var request = new FlurlRequest("https://npi.lazy52.com/api/doubao")
|
||||
.WithTimeout(Timeout.InfiniteTimeSpan);
|
||||
var response = request
|
||||
.SetQueryParams(filteredPainting).GetAsync().GetAwaiter().GetResult();
|
||||
if (response != null)
|
||||
{
|
||||
var responseString = response.GetStringAsync().GetAwaiter().GetResult();
|
||||
//JToken rootToken = JToken.Parse(responseString);
|
||||
//var resultPath = "$.image_url[*]";
|
||||
//IEnumerable<JToken> resultTokens = rootToken.SelectTokens(resultPath);
|
||||
return responseString;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
8
lazy52API/appsettings.Development.json
Normal file
8
lazy52API/appsettings.Development.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
16
lazy52API/appsettings.json
Normal file
16
lazy52API/appsettings.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"Kestrel": {
|
||||
"EndPoints": {
|
||||
"Http": {
|
||||
"Url": "http://*:9090" //
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
31
lazy52API/lazy52API.csproj
Normal file
31
lazy52API/lazy52API.csproj
Normal file
@ -0,0 +1,31 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<RuntimeIdentifiers>linux-x64</RuntimeIdentifiers>
|
||||
<ContainerRuntimeIdentifier>linux-x64</ContainerRuntimeIdentifier>
|
||||
<EnableSdkContainerDebugging>True</EnableSdkContainerDebugging>
|
||||
<ContainerBaseImage>mcr.microsoft.com/dotnet/aspnet:8.0</ContainerBaseImage>
|
||||
<UserSecretsId>e1ff1491-e3f4-4af6-822c-2ca2d8aeb211</UserSecretsId>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
|
||||
<PackageReference Include="ModelContextProtocol" Version="0.1.0-preview.6" />
|
||||
<PackageReference Include="Flurl" Version="4.0.0" />
|
||||
<PackageReference Include="Flurl.Http" Version="4.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ContainerPort Include="8081" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="Properties\PublishProfiles\192.168.2.23_5002.pubxml.user" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Loading…
x
Reference in New Issue
Block a user