Skip to content

bug(mcp): memory_id precision loss for Snowflake IDs in MCP tool layer causes update/delete failures #1098

Description

@lightzt99

Bug Description

update_memory and delete_memory MCP tools silently fail when operating on memories with Snowflake IDs that exceed JavaScript's Number.MAX_SAFE_INTEGER (2^53 − 1 = 9007199254740991). All Snowflake IDs in this system are 64-bit integers (e.g., 725653563933458432), which are approximately 10× larger than this limit.

Root Cause

The bug spans two layers:

1. MCP Tool Schema — int declared as JSON integer

In src/powermem/mcp/server.py, memory_id parameters are typed as Python int:

# Lines 215, 238, 269, 517
async def get_memory_by_id(memory_id: int, ...): ...
async def update_memory(memory_id: int, ...): ...
async def delete_memory(memory_id: int, ...): ...
async def delete_memory_with_profile(memory_id: int, ...): ...

FastMCP auto-generates the JSON Schema from these type hints, emitting "type": "integer" for memory_id. This causes MCP clients (including Claude Code, which runs in a JavaScript/V8 environment) to parse the returned memory_id as a JSON number.

2. MCP Response Serialization — json.dumps() emits bare JSON numbers

The _fmt() helper (line 99–101 of server.py) serializes responses with standard json.dumps():

def _fmt(data) -> str:
    return json.dumps(data, cls=_DateTimeEncoder)

When a Snowflake ID like 725653563933458432 is serialized this way and parsed by a JavaScript client, it becomes 725653563933458400 — losing the last 2 significant digits. The client then passes this rounded value back as the memory_id parameter on a subsequent update_memory or delete_memory call, and the server correctly returns not found.

Contrast: REST API is Already Fixed

The REST API layer (src/server/models/response.py) already handles this correctly via Pydantic field serializers:

# Lines 58–61
@field_serializer('memory_id')
def serialize_memory_id(self, value: int, _info) -> str:
    """Serialize as string to prevent JavaScript precision loss for large Snowflake IDs."""
    return str(value)

REST endpoints also receive memory_id as a URL path string and do int(memory_id) internally — completely avoiding the JSON number precision issue. The MCP layer lacks this protection.

Reproduction Steps

  1. Add any memory via add_memory MCP tool — observe the returned id field (e.g., 725653563933458432)
  2. Call update_memory or delete_memory with that id as memory_id
  3. Observe null / {"success": false} response — the ID was silently rounded to 725653563933458400 before reaching the server

Impact

  • All update_memory, delete_memory, get_memory_by_id, delete_memory_with_profile MCP tools are effectively broken for any memory added in an MCP session (since the returned ID is immediately subject to precision loss on the client side)
  • Users relying on the Claude Code plugin for memory management cannot update or delete specific memories
  • The failure is silent — no error is raised, just a null or success: false result

Proposed Fix

Change the memory_id parameter type from int to str in the affected MCP tool signatures, and convert to int inside the function body. This causes FastMCP to emit "type": "string" in the JSON Schema, ensuring clients treat the ID as an opaque string and preserve all digits.

# Before
async def update_memory(memory_id: int, content: str, ...) -> str:
    result = await memory.update(memory_id=memory_id, ...)

# After
async def update_memory(memory_id: str, content: str, ...) -> str:
    result = await memory.update(memory_id=int(memory_id), ...)

Apply the same change to get_memory_by_id, delete_memory, and delete_memory_with_profile.

Additionally, ensure MCP response serialization converts id/memory_id fields to strings consistently (mirroring the REST layer's @field_serializer), so the round-trip is safe end-to-end.

Environment

  • PowerMem version: 1.1.5
  • Connection mode: MCP (http://[host]:8001/mcp)
  • MCP client: Claude Code (V8/JavaScript runtime)
  • Affected tools: update_memory, delete_memory, get_memory_by_id, delete_memory_with_profile
  • All Snowflake IDs generated by this system are affected (they all exceed 2^53)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status
    In progress

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions