Skip to content

feat(store): add find+replace params to mem_update for token-efficient partial content editsΒ #602

Description

@blak0p

✨ Feature Request

πŸ“‹ Pre-flight Checks

  • I have searched existing issues and this is not a duplicate
  • I understand this issue needs status:approved before a PR can be opened

πŸ” Problem Description

Editing a memory to fix a typo, update a file path, or rename a concept currently requires sending the entire content block. This wastes tokens and adds round-trips β€” especially painful for AI agents.

To fix a single word, the agent must either:

  • (a) remember the full content and re-send it, or
  • (b) call mem_get_observation first, then re-send the full body with the correction

Neither is token-efficient.

πŸ’‘ Proposed Solution

Add two optional, mutually-dependent string parameters to mem_update: find and replace.

When both are provided (neither alone is valid), the content field is modified in-place by replacing ALL occurrences of find with replace. This happens server-side without sending the full content.

Implementation: in internal/store/store.go:UpdateObservation, after the existing observation is read (which already happens in the transaction), apply strings.ReplaceAll to the content before the UPDATE query. The existing truncation, hash, revision_count, and sync logic apply automatically.

Example:

mem_update(
  id: 42,
  find: "mnemonic",
  replace: "memonic"
)

Validation rules:

  • find without replace β†’ error: "find and replace must be used together"
  • replace without find β†’ error: "find and replace must be used together"
  • find + replace with content β†’ error: "find/replace is mutually exclusive with content"
  • find is empty string β†’ no-op
  • find not found β†’ no-op, no error

πŸ“¦ Affected Area

  • Store (database, queries)
  • MCP Server (tools, transport)

πŸ”„ Alternatives Considered

Client-side approach: Read via mem_get_observation, modify locally, re-send via mem_update. Adds a round-trip and still sends the full content.

SQL-level REPLACE: Using SQLite REPLACE(content, ?, ?) directly. Viable but complicates content truncation logic currently handled in Go.

JSON string parameter: Single find_replace field as JSON. Rejected β€” cannot be validated by MCP tool schema, breaks flat-parameter pattern used by all other mem_update fields.

πŸ“Ž Additional Context

Token savings (1 token / 4 chars, conservative):

Scenario Current With find/replace Savings
Typo fix in ~300 char memory ~95 tok ~16 tok ~79 (83%)
Path update in ~800 char memory ~220 tok ~16 tok ~204 (93%)
Concept rename Γ— N memories ~150tok Γ— N ~20tok Γ— N ~130-200tok per edit

Implementation reference files:

  • Handler: internal/mcp/mcp.go:1055-1110
  • Store: internal/store/store.go:2349-2420
  • Params struct: internal/store/store.go:149-156

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions