Skip to content

Commit 70dbe20

Browse files
committed
Rename LinkMemories -> ConnectMemories
Support linking multiple memories in one call.
1 parent e3d8351 commit 70dbe20

File tree

4 files changed

+164
-142
lines changed

4 files changed

+164
-142
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using System;
2+
using System.ComponentModel;
3+
using System.Linq;
4+
using Dapper;
5+
using Microsoft.Data.Sqlite;
6+
using ModelContextProtocol.Server;
7+
8+
namespace KnowledgeBaseServer.Tools;
9+
10+
[McpServerToolType]
11+
public static class ConnectMemoriesTool
12+
{
13+
[McpServerTool(
14+
Name = "ConnectMemories",
15+
ReadOnly = false,
16+
Destructive = false,
17+
Idempotent = true,
18+
OpenWorld = false
19+
)]
20+
[Description("Connects memories together to help find related memories in the future.")]
21+
public static string Handle(
22+
ConnectionString connectionString,
23+
[Description("Id of the parent memory (the older memory).")] Guid parentMemoryId,
24+
[Description("Id of the child memories (the newer memories).")] Guid[] childMemories
25+
)
26+
{
27+
var now = DateTimeOffset.UtcNow;
28+
var data = childMemories
29+
.Select(id => new
30+
{
31+
FromMemoryId = id,
32+
ToMemoryId = parentMemoryId,
33+
Created = now,
34+
})
35+
.ToArray();
36+
37+
using var connection = connectionString.CreateConnection();
38+
using var transaction = connection.BeginTransaction();
39+
40+
try
41+
{
42+
connection.Execute(
43+
sql: """
44+
insert into memory_links (from_memory_id, to_memory_id, created) values
45+
(@FromMemoryId, @ToMemoryId, @Created)
46+
""",
47+
data
48+
);
49+
transaction.Commit();
50+
}
51+
// FK constraint violation
52+
catch (SqliteException ex) when (ex is { SqliteErrorCode: 19, SqliteExtendedErrorCode: 787 })
53+
{
54+
return "Invalid memory ids provided.";
55+
}
56+
// PK constraint violation
57+
catch (SqliteException ex) when (ex is { SqliteErrorCode: 19, SqliteExtendedErrorCode: 1555 })
58+
{
59+
return "Some of the requested memories are already linked.";
60+
}
61+
62+
return "Memories linked successfully.";
63+
}
64+
}

src/KnowledgeBaseServer/Tools/LinkMemoriesTool.cs

Lines changed: 0 additions & 50 deletions
This file was deleted.
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
using System.Diagnostics;
2+
using System.Text.Json;
3+
using Bogus;
4+
using KnowledgeBaseServer.Dtos;
5+
using KnowledgeBaseServer.Tests.Data;
6+
using KnowledgeBaseServer.Tools;
7+
using Shouldly;
8+
using Xunit;
9+
10+
namespace KnowledgeBaseServer.Tests.Tools;
11+
12+
public class ConnectMemoriesToolTests : DatabaseTest
13+
{
14+
private readonly Faker _faker = new();
15+
16+
[Fact]
17+
public void ShouldReturnError_WhenIdsAreNotValid()
18+
{
19+
// arrange
20+
21+
// act
22+
var result = ConnectMemoriesTool.Handle(ConnectionString, _faker.Random.Guid(), [_faker.Random.Guid()]);
23+
24+
// assert
25+
using var connection = ConnectionString.CreateConnection();
26+
result.ShouldBe("Invalid memory ids provided.");
27+
connection.GetMemoryLinks().ShouldBeEmpty();
28+
}
29+
30+
[Fact]
31+
public void ShouldReturnNotModifyDatabase_WhenLinkExists()
32+
{
33+
// arrange
34+
var memories = JsonSerializer.Deserialize<CreatedMemoryDto[]>(
35+
CreateMemoriesTool.Handle(
36+
ConnectionString,
37+
JsonSerializerOptions.Default,
38+
_faker.Lorem.Word(),
39+
_faker.Lorem.Words(2)
40+
)
41+
);
42+
43+
Debug.Assert(memories is { Length: 2 });
44+
var parentMemoryId = memories[0].Id;
45+
var childMemoryId = memories[1].Id;
46+
47+
using (var seedConnection = ConnectionString.CreateConnection())
48+
{
49+
seedConnection.SeedMemoryLink(
50+
new MemoryLink
51+
{
52+
Created = _faker.Date.PastOffset(),
53+
FromMemoryId = childMemoryId,
54+
ToMemoryId = parentMemoryId,
55+
}
56+
);
57+
}
58+
59+
// act
60+
var result = ConnectMemoriesTool.Handle(ConnectionString, parentMemoryId, [childMemoryId]);
61+
62+
// assert
63+
using var connection = ConnectionString.CreateConnection();
64+
result.ShouldContain("already linked");
65+
connection.GetMemoryLinks().ShouldHaveSingleItem();
66+
}
67+
68+
[Fact]
69+
public void ShouldCreateLink()
70+
{
71+
// arrange
72+
var memories = JsonSerializer.Deserialize<CreatedMemoryDto[]>(
73+
CreateMemoriesTool.Handle(
74+
ConnectionString,
75+
JsonSerializerOptions.Default,
76+
_faker.Lorem.Word(),
77+
_faker.Lorem.Words()
78+
)
79+
);
80+
81+
Debug.Assert(memories is { Length: 3 });
82+
var parentMemoryId = memories[0].Id;
83+
var child1MemoryId = memories[1].Id;
84+
var child2MemoryId = memories[2].Id;
85+
86+
// act
87+
var result = ConnectMemoriesTool.Handle(ConnectionString, parentMemoryId, [child1MemoryId, child2MemoryId]);
88+
89+
using var connection = ConnectionString.CreateConnection();
90+
var actualMemoryLinks = connection.GetMemoryLinks();
91+
92+
// assert
93+
result.ShouldBe("Memories linked successfully.");
94+
actualMemoryLinks.ShouldSatisfyAllConditions(
95+
() => actualMemoryLinks.ShouldContain(ml => ml.ToMemoryId == parentMemoryId, expectedCount: 2),
96+
() => actualMemoryLinks.ShouldContain(ml => ml.FromMemoryId == child1MemoryId, expectedCount: 1),
97+
() => actualMemoryLinks.ShouldContain(ml => ml.FromMemoryId == child2MemoryId, expectedCount: 1)
98+
);
99+
}
100+
}

tests/KnowledgeBaseServer.Tests/Tools/LinkMemoriesToolTests.cs

Lines changed: 0 additions & 92 deletions
This file was deleted.

0 commit comments

Comments
 (0)