Skip to content

Commit 78f234b

Browse files
phernandezclaude
andcommitted
fix: preserve custom frontmatter fields when updating notes
Fixes #36 by modifying entity_service.update_entity() to read existing frontmatter from files before updating them. Custom metadata fields such as Status, Priority, and Version are now preserved when notes are updated through the write_note MCP tool. Added test case that verifies this behavior by creating a note with custom frontmatter and then updating it. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent cbe72be commit 78f234b

File tree

2 files changed

+84
-2
lines changed

2 files changed

+84
-2
lines changed

src/basic_memory/services/entity_service.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,10 +141,23 @@ async def update_entity(self, entity: EntityModel, schema: EntitySchema) -> Enti
141141
# Convert file path string to Path
142142
file_path = Path(entity.file_path)
143143

144+
# Read existing frontmatter from the file if it exists
145+
existing_markdown = await self.entity_parser.parse_file(file_path)
146+
147+
# Create post with new content from schema
144148
post = await schema_to_markdown(schema)
149+
150+
# Merge new metadata with existing metadata
151+
existing_markdown.frontmatter.metadata.update(post.metadata)
152+
153+
# Create a new post with merged metadata
154+
merged_post = frontmatter.Post(
155+
post.content,
156+
**existing_markdown.frontmatter.metadata
157+
)
145158

146159
# write file
147-
final_content = frontmatter.dumps(post, sort_keys=False)
160+
final_content = frontmatter.dumps(merged_post, sort_keys=False)
148161
checksum = await self.file_service.write_file(file_path, final_content)
149162

150163
# parse entity from file
@@ -309,4 +322,4 @@ async def update_entity_relations(
309322
)
310323
continue
311324

312-
return await self.repository.get_by_file_path(path)
325+
return await self.repository.get_by_file_path(path)

tests/mcp/test_tool_notes.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,3 +252,72 @@ async def test_write_note_verbose(app):
252252
""").strip()
253253
in result
254254
)
255+
256+
257+
@pytest.mark.asyncio
258+
async def test_write_note_preserves_custom_metadata(app, test_config):
259+
"""Test that updating a note preserves custom metadata fields.
260+
261+
Reproduces issue #36 where custom frontmatter fields like Status
262+
were being lost when updating notes with the write_note tool.
263+
264+
Should:
265+
- Create a note with custom frontmatter
266+
- Update the note with new content
267+
- Verify custom frontmatter is preserved
268+
"""
269+
# First, create a note with custom metadata using write_note
270+
await write_note(
271+
title="Custom Metadata Note",
272+
folder="test",
273+
content="# Initial content",
274+
tags=["test"],
275+
)
276+
277+
# Read the note to get its permalink
278+
content = await read_note("test/custom-metadata-note")
279+
280+
# Now directly update the file with custom frontmatter
281+
# We need to use a direct file update to add custom frontmatter
282+
from pathlib import Path
283+
import frontmatter
284+
285+
file_path = test_config.home / "test" / "Custom Metadata Note.md"
286+
post = frontmatter.load(file_path)
287+
288+
# Add custom frontmatter
289+
post["Status"] = "In Progress"
290+
post["Priority"] = "High"
291+
post["Version"] = "1.0"
292+
293+
# Write the file back
294+
with open(file_path, "w") as f:
295+
f.write(frontmatter.dumps(post))
296+
297+
# Now update the note using write_note
298+
result = await write_note(
299+
title="Custom Metadata Note",
300+
folder="test",
301+
content="# Updated content",
302+
tags=["test", "updated"],
303+
)
304+
305+
# Verify the update was successful
306+
assert "Updated test/Custom Metadata Note.md" in result
307+
308+
# Read the note back and check if custom frontmatter is preserved
309+
content = await read_note("test/custom-metadata-note")
310+
311+
# Custom frontmatter should be preserved
312+
assert "Status: In Progress" in content
313+
assert "Priority: High" in content
314+
# Version might be quoted as '1.0' due to YAML serialization
315+
assert "Version:" in content # Just check that the field exists
316+
assert "1.0" in content # And that the value exists somewhere
317+
318+
# And new content should be there
319+
assert "# Updated content" in content
320+
321+
# And tags should be updated
322+
assert "'#test'" in content
323+
assert "'#updated'" in content

0 commit comments

Comments
 (0)