-
Notifications
You must be signed in to change notification settings - Fork 175
Description
Feature Description
Add two new operations to edit_note: insert_before_section and insert_after_section. These insert content relative to a markdown section heading without consuming or modifying any existing content. The heading is used purely as a positional anchor.
Problem This Feature Solves
When using find_replace to insert content near a section heading, AI models (Claude, and likely others) consistently consume the heading itself. The str_replace pattern, which is trained extensively across all coding workflows, reinforces not including boundary text in replacements. Despite documentation warning against this, every Claude instance we've tested makes the same mistake repeatedly.
Example of the problem:
# Using find_replace to add content before ## Observations
find_text: "## Observations"
content: "New content here\n## Observations" # Must re-include the heading
# But trained str_replace behaviour produces:
find_text: "## Observations"
content: "New content here" # Heading consumed, it's gone
This is a structural problem, not a documentation problem. Advisory warnings don't fix trained behaviour. The solution is operations that make the error impossible.
Proposed Solution
Two new operations that use section headings as positional anchors:
insert_before_section: Insert content immediately before a section headinginsert_after_section: Insert content immediately after a section heading line
Both use the existing section parameter (same interface as replace_section). The heading is never in the find/replace path, it cannot be consumed.
Implementation
entity_service.py — New method insert_relative_to_section:
- Finds the section heading (same logic as
replace_section_content) - Inserts content before or after the heading line
- Raises
ValueErrorif section not found (unlikereplace_sectionwhich appends — insert operations require a target) - Raises
ValueErrorif multiple matching sections found
mcp/tools/edit_note.py — Updated validation and response formatting:
- Added to
valid_operationslist - Section validation covers all three section operations
- Response formatting reports lines inserted and target section
schemas/request.py — Updated Pydantic schema:
- Added to
Literaltype constraint onoperationfield - Updated section validator to cover new operations
User workflow
# Insert a new paragraph before the Observations section
edit_note("my-note", "insert_before_section",
"## New Section\nContent here",
section="## Observations")
# Insert content after a section heading
edit_note("my-note", "insert_after_section",
"First line under this heading",
section="## Implementation")Alternative Solutions
- Better documentation: We tried this. Every Claude instance reads the warning and makes the mistake anyway. The error is trained, not knowledge-based.
- Auto-include heading in find_replace: Would change existing behaviour and break other use cases where consuming text is intentional.
- Use
replace_sectionfor everything: Works but replaces content rather than inserting alongside it. Different semantics.
Additional Context
Relationship to existing Anthropic tools: Anthropic's own str_replace tool (used in Claude Code and computer use) is the root cause of the trained behaviour it replaces matched text, consuming the match. Anthropic's text_editor tool has an insert operation, but it works by line number, not by section heading. Line numbers are fragile in markdown notes where content shifts frequently. Our insert_before_section and insert_after_section use section headings as semantic anchors, stable positional references that work regardless of which line the section is on. This is a new approach specifically designed for markdown-structured notes.
Track record since implementation: Since deploying these operations to our fork, we have had zero instances of heading consumption. Prior to this change, every Claude instance (across multiple sessions, multiple model versions including Opus 4.5 and Opus 4.6, and both thinking-enabled and standard modes) made the heading consumption error despite explicit documentation warning against it. The structural fix eliminated the error class entirely, not reduced frequency, eliminated.
We've been using Basic Memory extensively with Claude for a knowledge base of 270+ notes. We implemented and tested these operations in our fork (Brennall/basic-memory) and have been using them in production for two days across multiple sessions.
The implementation follows existing patterns in the codebase — same section-finding logic as replace_section_content, same error handling, same parameter interface.
Impact
This would benefit any AI model using edit_note with find_replace near section headings. The problem is not Claude-specific it's a consequence of how all LLMs are trained on text replacement patterns. Providing section-aware insert operations eliminates the error class structurally rather than relying on each model to override its training.
Three files changed, ~60 lines of new code. Backward compatible, no changes to existing operations.