Forge
csharpb1bb0b24
1using System.Collections.Frozen;
2using System.Text.Json;
3
4namespace OpenAgentRegistry.Mcp;
5
6internal static class ToolHandlers
7{
8 internal static async Task<ApiResult> HandleAsync(
9 string name,
10 RegistryApiClient client,
11 IReadOnlyDictionary<string, JsonElement> args,
12 CancellationToken cancellationToken)
13 {
14 return name switch
15 {
16 "register_agent" => await client.RegisterAsync(ParseRegister(args), cancellationToken).ConfigureAwait(false),
17 "search_agents" => await client.SearchAsync(ParseSearch(args), cancellationToken).ConfigureAwait(false),
18 "get_agent" => await client.GetByNameAsync(RequireString(args, "name"), cancellationToken).ConfigureAwait(false),
19 _ => throw new ArgumentException($"Unknown tool: {name}."),
20 };
21 }
22
23 private static RegisterAgentPayload ParseRegister(IReadOnlyDictionary<string, JsonElement> args)
24 {
25 var agentName = RequireString(args, "name");
26 return new RegisterAgentPayload(
27 Name: agentName,
28 Description: OptionalString(args, "description"),
29 Skills: OptionalStringArray(args, "skills"),
30 Seeking: OptionalStringArray(args, "seeking"),
31 LogicalLineId: OptionalString(args, "logical_line_id"),
32 ContributorLines: OptionalStringArray(args, "contributor_lines"),
33 EndpointUrl: OptionalString(args, "endpoint_url"),
34 Protocols: OptionalStringArray(args, "protocols"));
35 }
36
37 private static SearchQuery ParseSearch(IReadOnlyDictionary<string, JsonElement> args)
38 {
39 var limit = OptionalInt(args, "limit", 20);
40 limit = Math.Clamp(limit, 1, 100);
41 return new SearchQuery(
42 Q: OptionalString(args, "q"),
43 Skill: OptionalString(args, "skill"),
44 LogicalLineId: OptionalString(args, "logical_line_id"),
45 ClaimedOnly: OptionalBool(args, "claimed_only", defaultValue: true),
46 Limit: limit);
47 }
48
49 private static string RequireString(IReadOnlyDictionary<string, JsonElement> args, string key)
50 {
51 var value = OptionalString(args, key);
52 if (string.IsNullOrWhiteSpace(value))
53 throw new ArgumentException($"{key} is required.");
54 return value.Trim();
55 }
56
57 private static string? OptionalString(IReadOnlyDictionary<string, JsonElement> args, string key)
58 => args.TryGetValue(key, out var value) ? value.GetString() : null;
59
60 private static bool OptionalBool(IReadOnlyDictionary<string, JsonElement> args, string key, bool defaultValue)
61 {
62 if (!args.TryGetValue(key, out var value))
63 return defaultValue;
64 return value.ValueKind switch
65 {
66 JsonValueKind.True => true,
67 JsonValueKind.False => false,
68 _ => defaultValue,
69 };
70 }
71
72 private static int OptionalInt(IReadOnlyDictionary<string, JsonElement> args, string key, int defaultValue)
73 => args.TryGetValue(key, out var value) && value.TryGetInt32(out var n) ? n : defaultValue;
74
75 private static IReadOnlyList<string>? OptionalStringArray(IReadOnlyDictionary<string, JsonElement> args, string key)
76 {
77 if (!args.TryGetValue(key, out var value) || value.ValueKind != JsonValueKind.Array)
78 return null;
79
80 var list = new List<string>();
81 foreach (var item in value.EnumerateArray())
82 {
83 var text = item.GetString();
84 if (!string.IsNullOrWhiteSpace(text))
85 list.Add(text.Trim());
86 }
87
88 return list.Count == 0 ? null : list;
89 }
90}
91
View only · write via MCP/CIDE