Skip to main content
Glama
RelationService.cs5.58 kB
using Azure; using Azure.Data.Tables; using CentralMemoryMcp.Functions.Models; using CentralMemoryMcp.Functions.Storage; namespace CentralMemoryMcp.Functions.Services; public interface IRelationService { Task<RelationModel> UpsertRelationAsync(RelationModel model, CancellationToken ct = default); Task<RelationModel?> GetRelationAsync(string workspaceName, Guid relationId, CancellationToken ct = default); Task<List<RelationModel>> GetRelationsFromEntityAsync(string workspaceName, Guid fromEntityId, CancellationToken ct = default); Task<List<RelationModel>> GetRelationsForWorkspaceAsync(string workspaceName, CancellationToken ct = default); Task DeleteRelationAsync(string workspaceName, Guid relationId, CancellationToken ct = default); } public class RelationService(ITableStorageService storage) : IRelationService { public async Task<RelationModel> UpsertRelationAsync(RelationModel model, CancellationToken ct = default) { var table = await storage.GetRelationsTableAsync(ct); // Check for existing relation (same workspace, from, to, type) var filter = $"PartitionKey eq '{model.WorkspaceName}' and FromEntityId eq '{model.FromEntityId:N}' and ToEntityId eq '{model.ToEntityId:N}' and RelationType eq '{EscapeFilterValue(model.RelationType)}'"; await foreach(var e in table.QueryAsync<TableEntity>(filter: filter, maxPerPage:1, cancellationToken: ct)) { // Reuse its Id if (e.TryGetValue("Id", out var idObj) && idObj is string idStr && Guid.TryParse(idStr, out var existingId)) { model.Id = existingId; } break; } var entity = new TableEntity(model.PartitionKey, model.RowKey) { {"Id", model.Id.ToString("N")}, {"WorkspaceName", model.WorkspaceName}, {"FromEntityId", model.FromEntityId.ToString("N")}, {"ToEntityId", model.ToEntityId.ToString("N")}, {"RelationType", model.RelationType}, {"Metadata", model.Metadata ?? string.Empty} }; await table.UpsertEntityAsync(entity, TableUpdateMode.Replace, ct); return model; } public async Task<RelationModel?> GetRelationAsync(string workspaceName, Guid relationId, CancellationToken ct = default) { var table = await storage.GetRelationsTableAsync(ct); try { var response = await table.GetEntityAsync<TableEntity>(workspaceName, relationId.ToString("N"), cancellationToken: ct); var model = new RelationModel( response.Value.GetString("WorkspaceName")!, Guid.Parse(response.Value.GetString("FromEntityId")!), Guid.Parse(response.Value.GetString("ToEntityId")!), response.Value.GetString("RelationType")!, response.Value.GetString("Metadata")) { Id = relationId }; return model; } catch (RequestFailedException ex) when (ex.Status == 404) { return null; } } public async Task<List<RelationModel>> GetRelationsFromEntityAsync(string workspaceName, Guid fromEntityId, CancellationToken ct = default) { var table = await storage.GetRelationsTableAsync(ct); var results = new List<RelationModel>(); var fromIdStr = fromEntityId.ToString("N"); await foreach (var e in table.QueryAsync<TableEntity>( filter: $"PartitionKey eq '{workspaceName}' and FromEntityId eq '{fromIdStr}'", cancellationToken: ct)) { var relationId = Guid.TryParse(e.GetString("Id"), out var rid) ? rid : Guid.NewGuid(); var model = new RelationModel( e.GetString("WorkspaceName")!, Guid.Parse(e.GetString("FromEntityId")!), Guid.Parse(e.GetString("ToEntityId")!), e.GetString("RelationType")!, e.GetString("Metadata")); model.Id = relationId; results.Add(model); } return results; } public async Task<List<RelationModel>> GetRelationsForWorkspaceAsync(string workspaceName, CancellationToken ct = default) { var table = await storage.GetRelationsTableAsync(ct); var results = new List<RelationModel>(); await foreach (var e in table.QueryAsync<TableEntity>( filter: $"PartitionKey eq '{workspaceName}'", cancellationToken: ct)) { var relationId = Guid.TryParse(e.GetString("Id"), out var rid) ? rid : Guid.NewGuid(); var model = new RelationModel( e.GetString("WorkspaceName")!, Guid.Parse(e.GetString("FromEntityId")!), Guid.Parse(e.GetString("ToEntityId")!), e.GetString("RelationType")!, e.GetString("Metadata")); model.Id = relationId; results.Add(model); } return results; } public async Task DeleteRelationAsync(string workspaceName, Guid relationId, CancellationToken ct = default) { var table = await storage.GetRelationsTableAsync(ct); try { await table.DeleteEntityAsync(workspaceName, relationId.ToString("N"), cancellationToken: ct); } catch (RequestFailedException ex) when (ex.Status == 404) { // not found; ignore } } private static string EscapeFilterValue(string value) => value.Replace("'", "''"); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/MWG-Logan/Central-Memory-MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server