forked from MWG-Logan/Central-Memory-MCP
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathGraphFunctions.cs
More file actions
183 lines (155 loc) · 7.38 KB
/
GraphFunctions.cs
File metadata and controls
183 lines (155 loc) · 7.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
using CentralMemoryMcp.Functions.Models;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Extensions.Mcp;
using System.ComponentModel;
using CentralMemoryMcp.Functions.Services;
namespace CentralMemoryMcp.Functions.Functions;
public class GraphFunctions(IKnowledgeGraphService graph, IRelationService relations)
{
private readonly IRelationService _relations = relations;
[Function(nameof(ReadGraph))]
public async Task<object> ReadGraph(
[McpToolTrigger("read_graph", "Reads the entire knowledge graph (entities and relations) for a workspace.")]
ToolInvocationContext context,
[McpToolProperty("workspaceName", "The unique identifier of the workspace.", isRequired: true)]
string workspaceName)
{
var entities = await graph.ReadGraphAsync(workspaceName);
var relations = await _relations.GetRelationsForWorkspaceAsync(workspaceName);
return new
{
WorkspaceName = workspaceName,
Entities = entities,
Relations = relations
};
}
[Function(nameof(UpsertEntity))]
public async Task<object> UpsertEntity(
[McpToolTrigger("upsert_entity", "Creates or updates an entity in the knowledge graph.")]
UpsertEntityRequest request,
ToolInvocationContext context)
{
if (string.IsNullOrWhiteSpace(request.WorkspaceName) ||
string.IsNullOrWhiteSpace(request.Name) ||
string.IsNullOrWhiteSpace(request.EntityType))
{
return new { success = false, message = "Invalid entity payload. Require workspaceName, name and entityType." };
}
var model = new EntityModel(
request.WorkspaceName,
request.Name,
request.EntityType,
request.Observations ?? [],
request.Metadata);
model = await graph.UpsertEntityAsync(model); // capture potentially reused Id
return new { success = true, id = model.Id, workspace = model.WorkspaceName, name = model.Name };
}
[Function(nameof(UpsertRelation))]
public async Task<object> UpsertRelation(
[McpToolTrigger("upsert_relation", "Creates or updates a relation between two entities in the knowledge graph.")]
UpsertRelationRequest request,
ToolInvocationContext context)
{
if (string.IsNullOrWhiteSpace(request.WorkspaceName) ||
string.IsNullOrWhiteSpace(request.RelationType))
{
return new { success = false, message = "Invalid relation payload. Require workspaceName and relationType." };
}
Guid fromId = request.FromEntityId.HasValue && request.FromEntityId.Value != Guid.Empty
? request.FromEntityId.Value
: Guid.Empty;
Guid toId = request.ToEntityId.HasValue && request.ToEntityId.Value != Guid.Empty
? request.ToEntityId.Value
: Guid.Empty;
if (fromId == Guid.Empty && !string.IsNullOrWhiteSpace(request.From))
{
var entity = await graph.GetEntityAsync(request.WorkspaceName, request.From);
if (entity is not null) fromId = entity.Id; else return new { success = false, message = $"Source entity '{request.From}' not found." };
}
if (toId == Guid.Empty && !string.IsNullOrWhiteSpace(request.To))
{
var entity = await graph.GetEntityAsync(request.WorkspaceName, request.To);
if (entity is not null) toId = entity.Id; else return new { success = false, message = $"Target entity '{request.To}' not found." };
}
if (fromId == Guid.Empty || toId == Guid.Empty)
{
return new { success = false, message = "Invalid relation payload. Provide fromEntityId/toEntityId or from/to names that exist." };
}
var model = new RelationModel(
request.WorkspaceName,
fromId,
toId,
request.RelationType,
request.Metadata);
model = await _relations.UpsertRelationAsync(model); // capture reused relation Id if existed
return new { success = true, relationId = model.Id, workspace = model.WorkspaceName, fromEntityId = model.FromEntityId, toEntityId = model.ToEntityId, relationType = model.RelationType };
}
[Function(nameof(GetEntityRelations))]
public async Task<object> GetEntityRelations(
[McpToolTrigger("get_entity_relations", "Gets all relations originating from a specific entity.")]
ToolInvocationContext context,
[McpToolProperty("workspaceName", "The workspace identifier.", isRequired: true)]
string workspaceName,
[McpToolProperty("entityId", "The GUID of the entity (preferred).", isRequired: false)]
Guid? entityId,
[McpToolProperty("entityName", "Legacy entity name (used if entityId not provided).", isRequired: false)]
string? entityName)
{
Guid resolvedId;
if (entityId.HasValue && entityId.Value != Guid.Empty)
{
resolvedId = entityId.Value;
}
else if (!string.IsNullOrWhiteSpace(entityName))
{
var entity = await graph.GetEntityAsync(workspaceName, entityName);
if (entity is null)
{
return new { success = false, message = $"Entity '{entityName}' not found in workspace '{workspaceName}'." };
}
resolvedId = entity.Id;
}
else
{
return new { success = false, message = "Provide either entityId (GUID) or entityName." };
}
var relations = await _relations.GetRelationsFromEntityAsync(workspaceName, resolvedId);
return new
{
success = true,
WorkspaceName = workspaceName,
EntityId = resolvedId,
Relations = relations
};
}
public class UpsertEntityRequest
{
[Description("The workspace name for the entity.")]
public required string WorkspaceName { get; set; }
[Description("The name of the entity.")]
public required string Name { get; set; }
[Description("The type/category of the entity.")]
public required string EntityType { get; set; }
[Description("List of observations about the entity.")]
public List<string>? Observations { get; set; }
[Description("Optional metadata as JSON string.")]
public string? Metadata { get; set; }
}
public class UpsertRelationRequest
{
[Description("The workspace name for the relation.")]
public required string WorkspaceName { get; set; }
[Description("The GUID of the source entity.")]
public Guid? FromEntityId { get; set; }
[Description("The GUID of the target entity.")]
public Guid? ToEntityId { get; set; }
[Description("Legacy source entity name (used if fromEntityId not provided).")]
public string? From { get; set; }
[Description("Legacy target entity name (used if toEntityId not provided).")]
public string? To { get; set; }
[Description("The type of relationship (e.g., 'knows', 'works_with', 'owns').")]
public required string RelationType { get; set; }
[Description("Optional metadata as JSON string.")]
public string? Metadata { get; set; }
}
}