节点在线、应用在线、配置在线使用令牌查询
大石头 authored at 2021-12-16 19:49:30
2.27 KiB
Stardust
using System.Text;
using Microsoft.AspNetCore.Http;
using NewLife;
using Stardust.Extensions;
using Stardust.Server;
using Stardust.Web.Services;

namespace Stardust.Web.Mcp;

/// <summary>MCP 协议中间件。在 Cube Web 中间件之前短路 /mcp 请求,
/// 避免设备ID Cookie 解析等 Web 中间件逻辑影响这个机器API,并提升性能。</summary>
public class McpMiddleware
{
    private readonly RequestDelegate _next;

    public McpMiddleware(RequestDelegate next) => _next = next;

    public async Task InvokeAsync(HttpContext context, McpService mcpService, StarServerSetting setting)
    {
        // 仅处理 /mcp 路径,其余请求照常走后续管道
        if (!context.Request.Path.Equals("/mcp", StringComparison.OrdinalIgnoreCase))
        {
            await _next(context);
            return;
        }

        // MCP 总开关关闭时返回 404
        if (!setting.EnableMcp)
        {
            context.Response.StatusCode = 404;
            return;
        }

        // GET/DELETE 本服务为无状态,按 Streamable HTTP 规范返回 405
        if (HttpMethods.IsGet(context.Request.Method) || HttpMethods.IsDelete(context.Request.Method))
        {
            context.Response.StatusCode = 405;
            return;
        }

        // 读取请求体
        var body = String.Empty;
        using (var reader = new StreamReader(context.Request.Body, Encoding.UTF8))
            body = await reader.ReadToEndAsync();

        if (body.IsNullOrEmpty())
        {
            context.Response.ContentType = "application/json";
            await context.Response.WriteAsync(McpService.BuildError(null, -32700, "Parse error: empty body"));
            return;
        }

        // 提取调用方信息
        var ip = context.GetUserHost();
        var ua = context.Request.Headers.UserAgent.ToString();
        var authorization = context.Request.Headers.Authorization.ToString();

        // 客户端是否接受 SSE 流式响应
        var accept = context.Request.Headers.Accept.ToString();
        var acceptSse = accept.Contains("text/event-stream", StringComparison.OrdinalIgnoreCase);

        var result = await mcpService.HandleAsync(body, ip, ua, authorization, acceptSse);
        await McpService.WriteResponseAsync(context, result);
    }
}