Skip to content

Conversation

@frederikb96
Copy link
Contributor

Description

Adds metadata update capability to both mem0 core library and MCP API layer.

Context:

  • mem0's update() method currently only accepts memory_id and data parameters
  • Internal _update_memory() already supports metadata with proper protection logic
  • No way to update custom metadata fields via public API or MCP

Changes:

1. mem0 Core Library (mem0/memory/main.py)

  • Added metadata: Optional[Dict[str, Any]] = None parameter to both sync/async update() methods
  • Pass metadata parameter to existing _update_memory() call
  • Updated docstrings with parameter description and usage examples

2. MCP API Layer (openmemory/api/app/mcp_server.py)

  • New update_memory tool with memory_id, text, and optional metadata parameters
  • Uses public mem0 update() API (no direct internal method calls)
  • Validates permissions and accessibility before update
  • Merges custom metadata with existing DB metadata
  • Creates history and access log entries

Implementation Details:

The existing _update_memory() method (lines 1145-1162) already implements proper metadata handling:

  • Overwrites system-managed fields (data, hash, created_at, updated_at)
  • Preserves protected session identifiers (user_id, agent_id, run_id) from existing memory
  • Merges custom metadata fields naturally

This PR exposes that capability through the public API and makes it available via MCP, following the same pattern as add() which already accepts metadata.

Metadata Protection:

  • System fields auto-managed: data, hash, created_at, updated_at
  • Session identifiers preserved: user_id, agent_id, run_id
  • Custom metadata merged safely

Type of change

  • New feature (non-breaking change which adds functionality)

How Has This Been Tested?

  • Manual testing via MCP API integration

Test verification:

  • Updated memory content with custom metadata (category: "physics", topic: "quantum", updated_test: "true")
  • Verified custom metadata merged with existing
  • Confirmed system fields auto-managed correctly
  • Verified session identifiers preserved from existing memory
  • Backward compatible - existing code without metadata parameter works unchanged
  • Full test flow: add with infer=False → search → update with metadata → search (verified updated) → delete

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • I have checked my code and corrected any misspellings

@frederikb96 frederikb96 force-pushed the feature/update-memory-metadata branch from ad0163f to 876958b Compare October 18, 2025 09:08
@parshvadaftari
Copy link
Contributor

Good catch @frederikb96 can. You check for failing tests?? Really appreciate the effort!

@frederikb96
Copy link
Contributor Author

@parshvadaftari Thanks for fast reply 🙂

The test expected that there are exactly 3 arguments. The code is still backward compatible, but the test was just too strict. I adjusted it, should work now. All tests pass again.

@frederikb96
Copy link
Contributor Author

I added two more commits here:


Before this PR, update() in mem0 main.py didn't accept a metadata parameter, and internally all custom metadata was lost on every update.

This happened because the internal _update_memory() function treated metadata=None as "start with empty dict", clearing everything.

Now:

  • metadata parameter added to public update() API
  • When metadata=None (not provided): preserves existing custom fields
  • When metadata={} (explicit empty): clears custom fields as expected

This makes updates more intuitive - updating content no longer accidentally erases metadata. I suspect that was also the intention back then, but it was just a bug in the code.


Then I aligned the behavior of the new MCP update_memory function to handle metadata for the openmemory DB accordingly and fixed my code.

@parshvadaftari
Copy link
Contributor

parshvadaftari commented Oct 21, 2025

Hey @frederikb96 can you share response for the update and update with metadata? Also please update your branch with the latest main inorder for the tests to pass. Thanks!

@frederikb96 frederikb96 force-pushed the feature/update-memory-metadata branch from 7ad44bd to a5cecb2 Compare October 22, 2025 11:28
@frederikb96
Copy link
Contributor Author

Hi, I rebased it, streamlined it again, and adjusted one of my test scripts. I had to include some of my other feature branches code to be able to run it to query metadata via search to display in test. And the test doesnt behave exactly as it will be later since some other fixes from other PRs are not incorporated yet. But its the best I managed to test this individually 😅 e.g. normally the metadata:

        "source_app": "openmemory",
        "mcp_client": "claude-code"

is not lost thats just because of another db bug in the add memory function at moment, where I have not yet created PR for.

I tried to make little mini PRs for now, but maybe I will create more big once which fix multiple things at once since then testing here for PR is easier 🙂

Here the test:

❯ source .venv/bin/activate && python 01-test-mcp-basic.py 
================================================================================
MCP UPDATE_MEMORY METADATA TEST
================================================================================

=== STEP 1: Delete All Memories ===
Result: meta=None content=[TextContent(type='text', text='Successfully deleted all memories', annotations=None, meta=None)] structuredContent={'result': 'Successfully deleted all memories'} isError=False

=== STEP 2: Add Memory (no metadata) ===
Input: The user likes coding.
(No metadata parameter available on this feature branch)

Result:
{
  "results": [
    {
      "id": "a7938779-c89d-4ec2-8c2b-353a37f0d8fc",
      "memory": "Likes coding",
      "event": "ADD"
    }
  ]
}

=== STEP 3: Search with include_metadata=True ===
Query: coding
(Should find memory with no custom metadata)

Result:
{
  "results": [
    {
      "id": "a7938779-c89d-4ec2-8c2b-353a37f0d8fc",
      "memory": "Likes coding",
      "hash": "92852dc8b92358ffee81ce58fbc73578",
      "created_at": "2025-10-22T04:09:41.663371-07:00",
      "updated_at": null,
      "score": 0.50719327,
      "user_id": "frederik",
      "metadata": {
        "source_app": "openmemory",
        "mcp_client": "claude-code"
      }
    }
  ]
}

→ Extracted Memory ID: a7938779-c89d-4ec2-8c2b-353a37f0d8fc

=== STEP 4: Update Memory with Custom Metadata ===
Updated text: The user likes coding in Python.
Custom metadata: {
  "category": "preferences",
  "topic": "programming"
}

Result:
{
  "message": "Memory updated successfully!"
}

=== STEP 5: Search to Verify Metadata Added ===
Query: coding
(Should find memory with category='preferences', topic='programming')

Result:
{
  "results": [
    {
      "id": "a7938779-c89d-4ec2-8c2b-353a37f0d8fc",
      "memory": "The user likes coding in Python.",
      "hash": "27e46b0c3cb6c2df9919c3f08a78ed7b",
      "created_at": "2025-10-22T04:09:41.663371-07:00",
      "updated_at": "2025-10-22T04:09:46.168849-07:00",
      "score": 0.27537,
      "user_id": "frederik",
      "metadata": {
        "category": "preferences",
        "topic": "programming"
      }
    }
  ]
}

=== STEP 6: Update with Different Metadata ===
Updated text: The user enjoys coding and debugging.
Different metadata: {
  "interest": "software",
  "skill": "debugging"
}
(Should REPLACE old metadata, not merge - old 'category' and 'topic' should be gone)

Result:
{
  "message": "Memory updated successfully!"
}

=== STEP 7: Search to Verify Metadata Replaced ===
Query: coding
(Should have interest='software', skill='debugging')
(Should NOT have category='preferences' or topic='programming' anymore)

Result:
{
  "results": [
    {
      "id": "a7938779-c89d-4ec2-8c2b-353a37f0d8fc",
      "memory": "The user enjoys coding and debugging.",
      "hash": "a10751419de534693c299f1de48df1a2",
      "created_at": "2025-10-22T04:09:41.663371-07:00",
      "updated_at": "2025-10-22T04:09:50.757023-07:00",
      "score": 0.41057885,
      "user_id": "frederik",
      "metadata": {
        "interest": "software",
        "skill": "debugging"
      }
    }
  ]
}

================================================================================
TEST COMPLETE - REVIEW OUTPUT ABOVE
================================================================================

Expected behavior:
- Step 3: No custom metadata (only system fields if any)
- Step 5: Has category='preferences', topic='programming'
- Step 7: Has interest='software', skill='debugging'
         (Old category and topic should be gone - replaced not merged)

@frederikb96 frederikb96 force-pushed the feature/update-memory-metadata branch from a5cecb2 to 1f7268a Compare October 22, 2025 11:36
@parshvadaftari
Copy link
Contributor

Hey @frederikb96 can you resolve merge conflicts as well?

@frederikb96 frederikb96 force-pushed the feature/update-memory-metadata branch from 1f7268a to 73653c7 Compare October 23, 2025 17:39
@frederikb96
Copy link
Contributor Author

@parshvadaftari Yes sure 🙂 Rebased it 👍

Thanks for all the ongoing merging 🙂

Implements metadata update capability for memory operations across
mem0 core library and OpenMemory MCP interface.

mem0 core changes:
- Add optional metadata parameter to update() method (sync and async)
- Modify _update_memory() to preserve existing metadata when metadata=None
- When metadata is provided, it replaces custom metadata fields
- System fields (data, hash, timestamps) always managed automatically
- Session identifiers (user_id, agent_id, run_id) preserved if not in new metadata

MCP update_memory tool:
- Add new MCP tool for updating memory content and metadata
- Implement system field protection (user_id, agent_id, run_id, actor_id, role, source_app, mcp_client)
- System fields from existing metadata are preserved and cannot be overwritten
- User-provided system fields are silently filtered out before merge
- Sync both Qdrant and PostgreSQL databases
- Include_metadata parameter added to search_memory for metadata visibility

Behavior:
- metadata=None: Preserves all existing metadata (default)
- metadata={}: Clears custom fields, preserves system fields
- metadata={...}: Replaces custom fields, preserves system fields

Note: System field preservation depends on metadata being stored in PostgreSQL
during add operation. This functionality will work fully once the add_memories
database storage bug is fixed in a future PR.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@frederikb96 frederikb96 force-pushed the feature/update-memory-metadata branch from 73653c7 to 6f2e02b Compare October 23, 2025 17:43
@NightClover-code
Copy link
Contributor

This PR would unblocks a bunch of band-aid fixes on our side (we had to reach the internal vector to update metadata), any ETA on merge?

@dkoxhorse
Copy link

We really need this merge update and hope the core maintainers can review it as soon as possible

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants