Files
MoneyMap/MoneyMap.Mcp/Tools/MerchantTools.cs
T

96 lines
3.5 KiB
C#

using System.ComponentModel;
using System.Text.Json;
using Microsoft.EntityFrameworkCore;
using ModelContextProtocol.Server;
using MoneyMap.Data;
namespace MoneyMap.Mcp.Tools;
[McpServerToolType]
public static class MerchantTools
{
private static readonly JsonSerializerOptions JsonOptions = new() { WriteIndented = true };
[McpServerTool(Name = "list_merchants"), Description("List all merchants with transaction counts and category mapping info.")]
public static async Task<string> ListMerchants(
[Description("Filter merchants by name (contains)")] string? query = null,
MoneyMapContext db = default!)
{
var q = db.Merchants
.Include(m => m.Transactions)
.Include(m => m.CategoryMappings)
.AsQueryable();
if (!string.IsNullOrWhiteSpace(query))
q = q.Where(m => m.Name.Contains(query));
var merchants = await q
.OrderBy(m => m.Name)
.Select(m => new
{
m.Id,
m.Name,
TransactionCount = m.Transactions.Count,
MappingCount = m.CategoryMappings.Count,
Categories = m.CategoryMappings.Select(cm => cm.Category).Distinct().ToList()
})
.ToListAsync();
return JsonSerializer.Serialize(new { Count = merchants.Count, Merchants = merchants }, JsonOptions);
}
[McpServerTool(Name = "merge_merchants"), Description("Merge duplicate merchants. Reassigns all transactions and category mappings from source to target, then deletes source.")]
public static async Task<string> MergeMerchants(
[Description("Merchant ID to merge FROM (will be deleted)")] int sourceMerchantId,
[Description("Merchant ID to merge INTO (will be kept)")] int targetMerchantId,
MoneyMapContext db = default!)
{
if (sourceMerchantId == targetMerchantId)
return "Source and target merchant cannot be the same";
var source = await db.Merchants.FindAsync(sourceMerchantId);
var target = await db.Merchants.FindAsync(targetMerchantId);
if (source == null)
return $"Source merchant {sourceMerchantId} not found";
if (target == null)
return $"Target merchant {targetMerchantId} not found";
var transactions = await db.Transactions
.Where(t => t.MerchantId == sourceMerchantId)
.ToListAsync();
foreach (var t in transactions)
t.MerchantId = targetMerchantId;
var sourceMappings = await db.CategoryMappings
.Where(cm => cm.MerchantId == sourceMerchantId)
.ToListAsync();
var targetMappingPatterns = await db.CategoryMappings
.Where(cm => cm.MerchantId == targetMerchantId)
.Select(cm => cm.Pattern)
.ToListAsync();
foreach (var mapping in sourceMappings)
{
if (targetMappingPatterns.Contains(mapping.Pattern))
db.CategoryMappings.Remove(mapping);
else
mapping.MerchantId = targetMerchantId;
}
db.Merchants.Remove(source);
await db.SaveChangesAsync();
return JsonSerializer.Serialize(new
{
Merged = true,
Source = new { source.Id, source.Name },
Target = new { target.Id, target.Name },
TransactionsReassigned = transactions.Count,
MappingsReassigned = sourceMappings.Count
}, JsonOptions);
}
}