Commit c513ac51 authored by shrabbit's avatar shrabbit
Browse files

feat(NetworkTools): 增强 HttpGet 返回更多响应信息并修复默认 headers 参数错误

parent addd173d
Loading
Loading
Loading
Loading
+54 −8
Original line number Diff line number Diff line
using System.ComponentModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Text.Json;
using System.Text.Json.Nodes;
using JetBrains.Annotations;
using ModelContextProtocol.Server;
using RestSharp;
using shRabbit.Base;
@@ -9,11 +11,11 @@ namespace McpBase.McpServer.Tools;

public class NetworkTools(IRestClient client)
{
    [McpServerTool, Description("send a HTTP GET request.")]
    [McpServerTool, Description("Send an HTTP GET request to the specified URL and return the response. Provides status code, response body, headers, content length, elapsed time, final URI after redirects, and error info. Use this for fetching web pages, REST APIs, or any HTTP resource.")]
    public async Task<HttpResponse> HttpGet(
        [Description("request url")]string url,
        [Description("request headers(JSON format, like '{\"Content-Type\":\"application/json\"}').")] string headers = "{\"Content-Type\":\"application/json\"}s",
        [Description("timeout(ms)")]int timeout = 15000)
        [Description("Target request URL (must include scheme, e.g. https://example.com/api/resource)")] string url,
        [Description("Request headers as a JSON object string, e.g. {\"Content-Type\":\"application/json\",\"Authorization\":\"Bearer xxx\"}")] string headers = "{\"Content-Type\":\"application/json\"}",
        [Description("Request timeout in milliseconds (default 15000 = 15s)")] int timeout = 15000)
    {
        if (string.IsNullOrEmpty(url))
        {
@@ -51,40 +53,84 @@ public class NetworkTools(IRestClient client)
        request.AddHeaders(headerList);
        
        var ct = new CancellationTokenSource(timeout);
        var sw = Stopwatch.StartNew();
        try
        {
            var response = await client.ExecuteAsync(request, ct.Token);
            sw.Stop();
            if (response.IsSuccessful)
            {
                return new HttpResponse
                {
                    StatusCode = (int)response.StatusCode,
                    IsSuccess = true,
                    Content = response.Content,
                    ContentType = response.ContentType,
                    Headers = response.Headers?.ToDictionary(h => h.Name, h => h.Value.ToString()) ?? [],
                    Length = response.ContentLength is null ? "N/A" : new ByteSize(response.ContentLength ?? 0).ToString(),
                    LengthRaw = response.ContentLength ?? -1
                    LengthRaw = response.ContentLength ?? -1,
                    ElapsedMs = sw.ElapsedMilliseconds,
                    FinalUri = response.ResponseUri?.ToString() ?? url
                };
            }
            
            return new HttpResponse(response.ErrorMessage ?? response.ErrorException?.Message ?? "Unknown Error.")
            {
                StatusCode = (int)response.StatusCode,
                ElapsedMs = sw.ElapsedMilliseconds,
                FinalUri = response.ResponseUri?.ToString() ?? url
            };
        }
        catch (OperationCanceledException)
        {
            return new HttpResponse("Request timeout");
            sw.Stop();
            return new HttpResponse("Request timeout")
            {
                ElapsedMs = sw.ElapsedMilliseconds
            };
        }
    }
}

public class HttpResponse(string errorMessage = "No Errors.")
{
    [Description("Error message. \"No Errors.\" means success; otherwise contains failure reason (e.g. timeout, DNS failure, parse error).")]
    [UsedImplicitly]
    public string ErrorMessage { get; set; } = errorMessage;

    [Description("HTTP status code. -1 means no response received (e.g. timeout, DNS failure, connection refused).")]
    [UsedImplicitly]
    public int StatusCode { get; set; } = -1;

    [Description("Whether the request succeeded (true = HTTP 2xx response received and parsed).")]
    [UsedImplicitly]
    public bool IsSuccess { get; set; }

    [Description("Response body as a string. Null when no body or when request failed before receiving a response.")]
    [UsedImplicitly]
    public string? Content { get; set; } = null;

    [Description("Response Content-Type header value (e.g. application/json; charset=utf-8). Null if not provided by server.")]
    [UsedImplicitly]
    public string? ContentType { get; set; }

    [Description("Response body length as a human-readable string (e.g. 390.00B, 1.20MB). \"N/A\" when not reported by the server.")]
    [UsedImplicitly]
    public string Length { get; set; } = "N/A";

    [Description("Response body length in raw bytes. -1 when not reported by the server.")]
    [UsedImplicitly]
    public long LengthRaw { get; set; } = -1;

    [Description("Response headers as a dictionary (header name → value).")]
    [UsedImplicitly]
    public Dictionary<string, string> Headers { get; set; } = [];

    [Description("Elapsed time of the entire HTTP request in milliseconds (including DNS, connect, transfer).")]
    [UsedImplicitly]
    public long ElapsedMs { get; set; }

    [Description("Final URI after any redirects. If no redirect occurred, equals the original request URL.")]
    [UsedImplicitly]
    public string? FinalUri { get; set; }
}
 No newline at end of file