Add SearchAll tool and update endpoints; enhance README documentation
This commit is contained in:
@@ -7,6 +7,7 @@ public static class Endpoints
|
||||
public static string RegionStores(int regionId) => $"{ApiBase}/api/regions/{regionId}/stores";
|
||||
public static string RegionUsers(int regionId) => $"{ApiBase}/api/regions/{regionId}/users";
|
||||
public static string StoreMembers(int storeId) => $"{ApiBase}/api/stores/{storeId}/members";
|
||||
public static string SearchAll(string q, bool? global = null) => $"{ApiBase}/api/search/all?q={Uri.EscapeDataString(q)}" + (global.HasValue ? $"&global={global.Value.ToString().ToLower()}" : "");
|
||||
public static string StoreLogActions(int storeId, string fromDate, string toDate, string storeLogActionIds) =>
|
||||
$"{ApiBase}/api/stores/{storeId}/log/{fromDate}/{toDate}/actions/{storeLogActionIds}";
|
||||
|
||||
|
||||
@@ -17,6 +17,6 @@ builder.Services
|
||||
.WithTools<RegionStoresTools>()
|
||||
.WithTools<StoreMembersTools>()
|
||||
.WithTools<FsMcp.Tools.RegionUsersTools>()
|
||||
.WithTools<StoreLogTools>();
|
||||
.WithTools<FsMcp.Tools.SearchAllTools>();
|
||||
|
||||
await builder.Build().RunAsync();
|
||||
|
||||
@@ -187,7 +187,23 @@ Notes for consumers:
|
||||
- `20` declined team invitations / `Team-Einladungen ablehnen`
|
||||
|
||||
The tool is strongly typed in `Tools/StoreLogTools.cs`, with per-field `Description` attributes so MCP clients can surface schema-aware guidance directly in tool UIs.
|
||||
### Search all tool
|
||||
|
||||
This server also exposes `get_search_all`.
|
||||
|
||||
- Purpose: Returns all kinds of searchable entry types from `GET /api/search/all`.
|
||||
- Input:
|
||||
- `q` (required, string; query to search for, must match `/^.+$/`)
|
||||
- `global` (optional, boolean; whether to broaden search globally)
|
||||
- Auth: Uses `USERNAME` and `PASSWORD` environment variables to log in and then sends the `X-CSRF-Token` header.
|
||||
- Output: Typed `MixedSearchResult` object with collections (`regions`, `workingGroups`, `stores`, `foodSharePoints`, `chats`, `threads`, `users`, `mails`, `events`, `polls`) of entities (`id`, `name`, `searchString`).
|
||||
|
||||
Notes for consumers:
|
||||
|
||||
- `q` cannot be empty.
|
||||
- To reduce verbosity, all sub-entity arrays map to a shared base search result object (`id`, `name`, `searchString`). Detailed payloads for each entity type are ignored for simplicity.
|
||||
|
||||
The tool is strongly typed in `Tools/SearchAllTools.cs`, with per-field `Description` attributes.
|
||||
## Publishing to NuGet.org
|
||||
|
||||
1. Run `dotnet pack -c Release` to create the NuGet package
|
||||
|
||||
85
FsMcp/Tools/SearchAllTools.cs
Normal file
85
FsMcp/Tools/SearchAllTools.cs
Normal file
@@ -0,0 +1,85 @@
|
||||
using System.ComponentModel;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.RegularExpressions;
|
||||
using FsMcp;
|
||||
using ModelContextProtocol.Server;
|
||||
|
||||
namespace FsMcp.Tools;
|
||||
|
||||
internal sealed class SearchAllTools
|
||||
{
|
||||
private readonly FoodsharingApiClient _apiClient;
|
||||
|
||||
public SearchAllTools(FoodsharingApiClient apiClient)
|
||||
{
|
||||
_apiClient = apiClient;
|
||||
}
|
||||
|
||||
[McpServerTool]
|
||||
[Description("General search endpoint returning all kinds of searchable entry types from GET /api/search/all.")]
|
||||
public async Task<MixedSearchResult> GetSearchAllAsync(
|
||||
[Description("Search query. Value must follow pattern /^.+$/ (cannot be empty).")]
|
||||
string q,
|
||||
[Description("Optional boolean to broaden the search to global results.")]
|
||||
bool? global = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(q) || !Regex.IsMatch(q, "^.+$"))
|
||||
{
|
||||
throw new ArgumentException("Search query 'q' must not be empty and must match pattern /^.+$/.", nameof(q));
|
||||
}
|
||||
|
||||
await _apiClient.EnsureLoginAsync();
|
||||
|
||||
var result = await _apiClient.HttpClient.GetFromJsonAsync<MixedSearchResult>(Endpoints.SearchAll(q, global));
|
||||
return result ?? new MixedSearchResult();
|
||||
}
|
||||
}
|
||||
|
||||
public sealed record MixedSearchResult
|
||||
{
|
||||
[JsonPropertyName("regions")]
|
||||
public IReadOnlyList<BaseSearchResult> Regions { get; init; } = Array.Empty<BaseSearchResult>();
|
||||
|
||||
[JsonPropertyName("workingGroups")]
|
||||
public IReadOnlyList<BaseSearchResult> WorkingGroups { get; init; } = Array.Empty<BaseSearchResult>();
|
||||
|
||||
[JsonPropertyName("stores")]
|
||||
public IReadOnlyList<BaseSearchResult> Stores { get; init; } = Array.Empty<BaseSearchResult>();
|
||||
|
||||
[JsonPropertyName("foodSharePoints")]
|
||||
public IReadOnlyList<BaseSearchResult> FoodSharePoints { get; init; } = Array.Empty<BaseSearchResult>();
|
||||
|
||||
[JsonPropertyName("chats")]
|
||||
public IReadOnlyList<BaseSearchResult> Chats { get; init; } = Array.Empty<BaseSearchResult>();
|
||||
|
||||
[JsonPropertyName("threads")]
|
||||
public IReadOnlyList<BaseSearchResult> Threads { get; init; } = Array.Empty<BaseSearchResult>();
|
||||
|
||||
[JsonPropertyName("users")]
|
||||
public IReadOnlyList<BaseSearchResult> Users { get; init; } = Array.Empty<BaseSearchResult>();
|
||||
|
||||
[JsonPropertyName("mails")]
|
||||
public IReadOnlyList<BaseSearchResult> Mails { get; init; } = Array.Empty<BaseSearchResult>();
|
||||
|
||||
[JsonPropertyName("events")]
|
||||
public IReadOnlyList<BaseSearchResult> Events { get; init; } = Array.Empty<BaseSearchResult>();
|
||||
|
||||
[JsonPropertyName("polls")]
|
||||
public IReadOnlyList<BaseSearchResult> Polls { get; init; } = Array.Empty<BaseSearchResult>();
|
||||
}
|
||||
|
||||
public sealed record BaseSearchResult
|
||||
{
|
||||
[Description("Unique identifier of the entity represented by the search result.")]
|
||||
[JsonPropertyName("id")]
|
||||
public int Id { get; init; }
|
||||
|
||||
[Description("Name of the entity represented by the search result.")]
|
||||
[JsonPropertyName("name")]
|
||||
public string Name { get; init; } = string.Empty;
|
||||
|
||||
[Description("Search criteria to test the search against.")]
|
||||
[JsonPropertyName("searchString")]
|
||||
public string? SearchString { get; init; }
|
||||
}
|
||||
Reference in New Issue
Block a user