Forge
csharp3407750f
1using System.Net.Http.Json;
2using System.Security.Cryptography;
3using System.Text;
4using System.Text.Json;
5using AgentForge.Abstractions;
6using ModelContextProtocol.Protocol;
7using Tool = ModelContextProtocol.Protocol.Tool;
8
9namespace AgentForge.Mcp;
10
11internal static class ForgeMcpToolLoader
12{
13 internal static async Task<List<Tool>> LoadFromCapabilitiesAsync(
14 HttpClient http,
15 JsonSerializerOptions jsonOptions,
16 CancellationToken cancellationToken)
17 {
18 try
19 {
20 using var response = await http.GetAsync("/api/v1/capabilities", cancellationToken).ConfigureAwait(false);
21 if (!response.IsSuccessStatusCode)
22 {
23 var body = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
24 Console.Error.WriteLine(
25 $"Failed to load tools from forge: HTTP {(int)response.StatusCode} {response.ReasonPhrase}. {body}");
26 return [];
27 }
28
29 var caps = await response.Content.ReadFromJsonAsync<ForgeCapabilitiesResponse>(jsonOptions, cancellationToken)
30 .ConfigureAwait(false);
31 return caps?.Tools is not { Count: > 0 } descriptors
32 ? LogEmptyAndReturn()
33 : descriptors.Select(ToTool).ToList();
34 }
35 catch (Exception ex)
36 {
37 Console.Error.WriteLine($"Failed to load tools from forge: {ex.Message}");
38 return [];
39 }
40 }
41
42 internal static async Task<string?> FetchToolsFingerprintAsync(
43 HttpClient http,
44 JsonSerializerOptions jsonOptions,
45 CancellationToken cancellationToken)
46 {
47 try
48 {
49 using var response = await http.GetAsync("/api/v1/capabilities", cancellationToken).ConfigureAwait(false);
50 if (!response.IsSuccessStatusCode)
51 return null;
52
53 var caps = await response.Content.ReadFromJsonAsync<ForgeCapabilitiesResponse>(jsonOptions, cancellationToken)
54 .ConfigureAwait(false);
55 if (caps?.Tools is not { Count: > 0 })
56 return null;
57
58 var canonical = JsonSerializer.Serialize(
59 caps.Tools.OrderBy(t => t.Name, StringComparer.Ordinal),
60 jsonOptions);
61 return Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(canonical)));
62 }
63 catch (Exception ex)
64 {
65 Console.Error.WriteLine($"Forge MCP capabilities poll failed: {ex.Message}");
66 return null;
67 }
68 }
69
70 private static List<Tool> LogEmptyAndReturn()
71 {
72 Console.Error.WriteLine("Failed to load tools from forge: capabilities.tools is empty.");
73 return [];
74 }
75
76 private static Tool ToTool(ForgeMcpToolDescriptor descriptor) => new()
77 {
78 Name = descriptor.Name,
79 Description = descriptor.Description,
80 InputSchema = descriptor.InputSchema,
81 };
82}
83
View only · write via MCP/CIDE