# How to Debug Memory Storage Issues Using Health Check Endpoints in MCP Memory Service

> Debug memory storage issues in MCP Memory Service using health check endpoints. Query /api/health/detailed and /api/health/sync-status to quickly pinpoint failures.

- Repository: [Henry/mcp-memory-service](https://github.com/doobidoo/mcp-memory-service)
- Tags: how-to-guide
- Published: 2026-02-28

---

**Query the `/api/health/detailed` endpoint to inspect storage backend statistics, memory counts, and database size, or use `/api/health/sync-status` for hybrid backend synchronization progress to quickly identify the root cause of memory storage failures.**

The **MCP Memory Service** (`doobidoo/mcp-memory-service`) exposes HTTP health check endpoints that surface real-time storage backend statistics and system metrics. By analyzing these JSON payloads, you can debug memory storage issues without accessing the database directly or parsing application logs.

## Health Check Endpoint Overview

Three endpoints in [`src/mcp_memory_service/web/api/health.py`](https://github.com/doobidoo/mcp-memory-service/blob/main/src/mcp_memory_service/web/api/health.py) provide progressive levels of diagnostic detail:

- `/api/health` — Returns minimal liveness status, version, and uptime
- `/api/health/detailed` — Returns full **storage statistics**, system metrics, and performance data
- `/api/health/sync-status` — Returns hybrid backend synchronization progress

Both basic and detailed endpoints delegate storage health checks to `MemoryService.health_check()`, implemented in [`src/mcp_memory_service/services/memory_service.py`](https://github.com/doobidoo/mcp-memory-service/blob/main/src/mcp_memory_service/services/memory_service.py) at lines 690-707, which calls `storage.get_stats()` on the configured backend.

## Decoding the Detailed Health Payload

The `/api/health/detailed` endpoint returns a comprehensive JSON object containing storage metadata critical for debugging.

### Storage Backend Statistics

The `storage` object reveals the backend type and data integrity:

```json
{
  "backend": "sqlite-vec",
  "total_memories": 1247,
  "unique_tags": 87,
  "memories_this_week": 23,
  "database_size_mb": 12.34,
  "embedding_model": "all-MiniLM-L6-v2",
  "embedding_dimension": 384
}

```

- **`backend`**: Identifies the implementation (`sqlite-vec`, `hybrid`, `cloudflare`) from `storage.get_stats()` in [`src/mcp_memory_service/storage/sqlite_vec.py`](https://github.com/doobidoo/mcp-memory-service/blob/main/src/mcp_memory_service/storage/sqlite_vec.py) (lines 2581-2619)
- **`total_memories`**: Count of non-soft-deleted rows via `SELECT COUNT(*) ... WHERE deleted_at IS NULL`
- **`unique_tags`**: Distinct tag count; low values indicate `normalize_tags` issues in [`src/mcp_memory_service/services/memory_service.py`](https://github.com/doobidoo/mcp-memory-service/blob/main/src/mcp_memory_service/services/memory_service.py) (lines 55-75)
- **`database_size_mb`**: Physical SQLite file size from `os.path.getsize(self.db_path)`; sudden drops indicate corruption or resets

### Hybrid Sync Status

For hybrid backends, the `/api/health/sync-status` endpoint (lines 95-110 in [`health.py`](https://github.com/doobidoo/mcp-memory-service/blob/main/health.py)) queries `get_initial_sync_status()` implemented in [`src/mcp_memory_service/storage/hybrid.py`](https://github.com/doobidoo/mcp-memory-service/blob/main/src/mcp_memory_service/storage/hybrid.py) (lines 1516-1525). This reveals whether background synchronization is stuck:

```json
{
  "sync_supported": true,
  "status": {
    "in_progress": true,
    "pending_operations": 42,
    "progress_percentage": 65
  }
}

```

## Step-by-Step Debugging Workflow

Follow this sequence to isolate memory storage issues using the health endpoints:

1. **Verify Service Liveness**

   ```bash
   curl -s http://localhost:8000/api/health | jq .
   ```

   If `status` is not `"healthy"`, check the service logs for startup failures in dependency injection ([`src/mcp_memory_service/web/dependencies.py`](https://github.com/doobidoo/mcp-memory-service/blob/main/src/mcp_memory_service/web/dependencies.py)).

2. **Inspect Storage Metrics**

   ```bash
   curl -s http://localhost:8000/api/health/detailed | jq .storage
   ```

   Confirm the `backend` matches your configuration and `total_memories` reflects expected data volume.

3. **Validate Database Growth**

   Monitor `database_size_mb` across requests. Static values during active writes indicate permission failures or storage backend disconnections.

4. **Check Tag Integrity**

   If `unique_tags` is zero despite memories existing, inspect the `normalize_tags` function in [`memory_service.py`](https://github.com/doobidoo/mcp-memory-service/blob/main/memory_service.py) for comma-handling bugs.

5. **Monitor Hybrid Synchronization**

   ```bash
   curl -s http://localhost:8000/api/health/sync-status | jq .
   ```

   Persistent `"in_progress": true` with high `pending_operations` indicates network latency or authentication failures in the hybrid sync worker.

## Diagnosing Common Storage Issues

Map symptoms to health payload indicators:

**Service Startup Failures**

If `/api/health` returns 500 or missing `status` field, the `MemoryService` failed to initialize. Check that the storage backend implements the `get_stats()` method declared in [`src/mcp_memory_service/storage/base.py`](https://github.com/doobidoo/mcp-memory-service/blob/main/src/mcp_memory_service/storage/base.py) (lines 763-770).

**Silent Write Failures**

When `total_memories` remains unchanged after API writes, verify `database_size_mb` growth. If both are static, the SQLite file lacks write permissions or the `storage.store()` method is failing silently.

**Tag Search Anomalies**

Low `unique_tags` with high `total_memories` signals that the tag normalization logic is stripping delimiters incorrectly. Compare the health output against direct storage queries:

```python
from mcp_memory_service.storage.sqlite_vec import SqliteVectorStore

store = SqliteVectorStore(db_path="data/memory.db")
await store.initialize()
print(await store.get_stats())

```

**Disk Space Exhaustion**

When `system.disk_percent` exceeds 90% or `database_size_mb` approaches `system.disk_free_gb`, the host cannot allocate new pages. Enable database rotation or clean old memory segments.

**Backend Mismatch**

An `backend` value of `"unknown"` indicates the storage class does not properly implement `get_stats()`. Verify your configuration points to a valid `MemoryStorage` implementation.

## Programmatic Health Check Examples

### cURL Diagnostics for Storage Backend

Check storage statistics directly:

```bash
curl -s http://localhost:8000/api/health/detailed | jq .storage

```

### Python Async Health Monitor

```python
import httpx
import asyncio

async def check_storage_health():
    async with httpx.AsyncClient() as client:
        resp = await client.get("http://localhost:8000/api/health/detailed")
        data = resp.json()
        storage = data["storage"]
        
        assert storage["backend"] == "sqlite-vec"
        assert storage["total_memories"] > 0
        assert storage["database_size_mb"] > 0
        
        print(f"Backend: {storage['backend']}")
        print(f"Memories: {storage['total_memories']}")
        print(f"DB Size: {storage['database_size_mb']} MB")

asyncio.run(check_storage_health())

```

### Direct Storage Inspection

Bypass HTTP to verify endpoint accuracy:

```python
import asyncio
from mcp_memory_service.storage.sqlite_vec import SqliteVectorStore

async def verify_stats():
    store = SqliteVectorStore(db_path="data/memory.db")
    await store.initialize()
    stats = await store.get_stats()
    print(f"Direct storage query: {stats}")

asyncio.run(verify_stats())

```

## Summary

- **`/api/health`** confirms service process liveness and basic uptime
- **`/api/health/detailed`** exposes storage backend type, memory counts, database file size, and embedding configuration from [`sqlite_vec.py`](https://github.com/doobidoo/mcp-memory-service/blob/main/sqlite_vec.py) and [`hybrid.py`](https://github.com/doobidoo/mcp-memory-service/blob/main/hybrid.py)
- **`/api/health/sync-status`** tracks background synchronization progress for hybrid deployments
- Correlate `total_memories`, `unique_tags`, and `database_size_mb` against expected values to detect write failures or tag processing errors
- Use `system.disk_percent` and `memory_percent` to identify resource exhaustion causing storage timeouts
- Validate health endpoint output against direct `storage.get_stats()` calls to ensure the HTTP layer accurately reflects backend state

## Frequently Asked Questions

### Why does `/api/health` return healthy but my memories are missing?

The basic endpoint only checks process liveness. Query `/api/health/detailed` and inspect `storage.total_memories`. If the count is zero despite previous writes, the service may be pointing to a different database file path or the storage backend failed to initialize properly. Check `storage.backend` matches your configured implementation.

### How do I detect if the hybrid storage sync is stuck?

Poll `/api/health/sync-status` and examine `status.in_progress`. If this remains `true` for more than 30 minutes with non-zero `pending_operations`, the background sync worker in [`src/mcp_memory_service/storage/hybrid.py`](https://github.com/doobidoo/mcp-memory-service/blob/main/src/mcp_memory_service/storage/hybrid.py) is likely blocked by network latency or authentication failures. Review logs for sync-related exceptions.

### What does a low `unique_tags` count indicate?

When `unique_tags` is unexpectedly low compared to `total_memories`, the `normalize_tags` function in [`src/mcp_memory_service/services/memory_service.py`](https://github.com/doobidoo/mcp-memory-service/blob/main/src/mcp_memory_service/services/memory_service.py) (lines 55-75) may be incorrectly stripping tag delimiters or converting tags to empty strings. Compare the health payload against a direct query of `storage.get_stats()` to confirm the discrepancy.

### Why is `database_size_mb` not increasing when I add memories?

Static database size during write operations indicates the SQLite file is not being modified. Verify file permissions on the database path and ensure the disk has free space (`system.disk_free_gb`). If permissions and space are adequate, check for transaction rollbacks or silent failures in `storage.store()` implementations.