Skip to content

feat: expose cube creation in HTTP API + clarify mem_cube_id semantics #1681

@catgodtw

Description

@catgodtw

Summary

MemOS 2.0.14 HTTP API (server mode, Docker) silently accepts requests with arbitrary mem_cube_id values but does not create the cube, leading to data that is stored in Qdrant but not retrievable via /product/search. This is surprising behavior for integrators building multi-tenant / multi-cube systems on top of MemOS server mode.

Reproduction

# 1. Verify cube does NOT exist
curl -s -X POST http://localhost:8004/product/exist_mem_cube_id \
  -H 'Content-Type: application/json' \
  -d '{"user_id":"user_alice","mem_cube_id":"my_custom_cube"}'
# → {"data": {"my_custom_cube": false}}

# 2. Add memory referencing the non-existent cube
curl -s -X POST http://localhost:8004/product/add \
  -H 'Content-Type: application/json' \
  -d '{"messages":[{"role":"user","content":"I like blue"}],"user_id":"user_alice","mem_cube_id":"my_custom_cube"}'
# → 200 OK, response says cube_id="my_custom_cube"

# 3. Search the supposedly-stored memory
curl -s -X POST http://localhost:8004/product/search \
  -H 'Content-Type: application/json' \
  -d '{"query":"color","user_id":"user_alice","mem_cube_id":"my_custom_cube","top_k":5}'
# → 200 OK, but text_mem: [], pref_mem: [], all empty

The data IS in Qdrant (verified via /collections/{name}/points/scroll), but the search engine apparently looks up the cube in a separate registry (Neo4j tree structure) where the cube was never registered.

Root Cause Analysis

Investigation of /app/src/memos/api/handlers/ shows:

  • AddHandler writes to Qdrant + Neo4j tree, but if the cube hasn't been registered, no tree node is created for the new memory
  • SearchHandler traverses the tree first, then resolves to Qdrant. Empty tree → empty search results, even though embeddings exist.

MOSCore.create_cube_for_user(cube_name, owner_id, cube_id) exists in /app/src/memos/mem_os/core.py but is not exposed via any HTTP endpoint. The 24 endpoints listed in /openapi.json include cube lookup (/product/exist_mem_cube_id) but no cube creation:

/product/add
/product/search
/product/get_all
/product/exist_mem_cube_id
/product/delete_memory
... (24 total, none for create_cube)

Impact

For users running MemOS as a remote server (Docker compose pattern documented in the README), there is no way via HTTP to:

  1. Pre-create cubes for multi-tenant isolation
  2. Discover that a mem_cube_id doesn't exist before /add happily accepts it
  3. Get an explicit error when search fails due to missing cube

This silently breaks multi-cube architectures and is hard to debug because:

  • /add returns 200 with the expected cube_id in the response
  • Search returns 200 with empty results (looks like nothing matched)
  • Direct vector search against Qdrant returns the data correctly

Workarounds (what we did)

  1. Drop mem_cube_id in our integration and use user_id as the implicit cube key
  2. For tier-based isolation, suffix the user_id (e.g. user_alice_legal_isolated) — this works because MemOS does internally isolate by user_id

Both work, but neither uses MemOS's intended multi-cube design.

Suggested API Additions

Three options, listed in increasing priority:

Option 1 (Minimum fix): Auto-create on /add

When /product/add receives a mem_cube_id for a cube that doesn't exist, auto-create it. Reuse MOSCore.create_cube_for_user(cube_name=mem_cube_id, owner_id=user_id, cube_id=mem_cube_id).

Option 2 (Explicit): New endpoint POST /product/create_cube

POST /product/create_cube
body: {"cube_id": "...", "owner_id": "...", "cube_name": "..."}
response: {"code": 200, "data": {"cube_id": "..."}}

Option 3 (Robust): Validation + clear error

If mem_cube_id is passed to /add or /search for a non-existent cube, return:

{"code": 404, "message": "Cube 'my_custom_cube' does not exist. Create it via POST /product/create_cube or omit mem_cube_id to use default cube."}

We recommend implementing both Option 2 (explicit creation API) and Option 3 (clear errors).

Reference Integration

We have built a MemoryProvider plugin that integrates MemOS via HTTP in server mode. It currently uses the user_id-suffix workaround for tier-based isolation, with comments referencing this issue.

Once the cube creation API lands, we plan to switch back to using mem_cube_id directly. The plugin includes:

  • Multi-cube routing (planned, currently single-cube via user_id)
  • Tier 3 audit logging
  • Circuit breaker
  • Background prefetch + sync

Happy to contribute the plugin to your apps/ directory as memos-remote-hermes-plugin (complement to the local-SQLite memos-local-hermes-plugin) if useful.

Versions

  • MemOS: 2.0.14 (built from main 2026-05-11)
  • Deployment: Docker compose (memos-server + memos-neo4j 5.26 + memos-qdrant 1.15)
  • Embedding: SiliconFlow BAAI/bge-m3 (1024 dim)
  • LLM: external (via local reverse proxy)
  • Client: multi-machine server-mode deployment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions