Skip to main content
Glama
bsahane

Memory MCP Server

by bsahane
README.md
# Memory MCP Server

[![Python 3.12+](https://img.shields.io/badge/python-3.12,3.13-blue.svg)](https://www.python.org/downloads/)
[![Tests](https://github.com/redhat-data-and-ai/memory-mcp-server/actions/workflows/test.yml/badge.svg)](https://github.com/redhat-data-and-ai/memory-mcp-server/actions/workflows/test.yml)
[![License: Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)

A Model Context Protocol (MCP) server that gives AI agents persistent memory.
Memories are stored in a local SQLite database (auto-created, zero-config) and
exposed through forty tools following a tools-first architecture. Built on the
[template-mcp-server](https://github.com/redhat-data-and-ai/template-mcp-server)
production scaffold (FastMCP + FastAPI, structured logging, containers,
OpenShift manifests, CI).

## Features

- **40 MCP tools across four domains**: memory, tasks, time (reminders/alerts), dashboard
- **Seven memory tools**: store, get, search, list, update, delete, projects
- **Seven todo tools**: create/search/list/get/update/complete/delete with priority and due dates
- **Eight kanban tools**: boards with configurable columns; card add/move/update/delete
- **Seven tracker tools**: status trackers whose entries roll up into progress metrics
- **SQLite persistence** via aiosqlite — single file, tuned WAL baseline, auto-created
- **Full-text keyword search** — whole-word, porter-stemmed matching over an FTS5 index, ranked by relevance (best match first)
- **Tags & metadata** on memories and todos
- **FastMCP + FastAPI** with multiple transports (HTTP, SSE, streamable-HTTP)
- **Pydantic configuration** via environment variables
- **Structured JSON logging** with structlog
- **OAuth integration** (disabled by default; see `docs/authentication.md`)
- **Container-ready** (Red Hat UBI base image) and OpenShift manifests included

### Search & storage behavior

Search (`memory_search`, `todo_search`) matches **whole-word tokens** over a
porter-stemmed full-text index: partial words never match (querying `check`
will not match `checklist`), punctuation and operators are treated literally,
and results are ranked by bm25 relevance — strongest match first, newest first
on ties. Note that unicode61 tokenization treats a whole CJK sentence as a
single token, so whole-word matching assumes space-delimited scripts.

Databases run in WAL mode with `synchronous=NORMAL`: recently committed
transactions can be lost on an OS crash or power failure (an accepted tradeoff
for notes/tasks — not suitable as a system of record). Steady-state WAL size
is bounded by `wal_autocheckpoint` (~1000 pages ≈ 4 MiB);
`journal_size_limit` (8 MiB) only lets SQLite truncate the WAL file back once
checkpoints free it. Maintenance (`PRAGMA optimize` + a
`wal_checkpoint(TRUNCATE)` pass) runs inline in the reminder poll loop every
tenth tick and may briefly delay a tick; it is bounded by the per-hook
timeout and the database busy timeout.

## Quick Start

```bash
git clone https://github.com/redhat-data-and-ai/memory-mcp-server
cd memory-mcp-server
make install        # creates venv, installs deps + pre-commit hooks
make local          # starts server on localhost:5001
```

Verify in another terminal:

```bash
curl http://localhost:5001/health
```

Manual setup (without Make):

```bash
# Create venv and install
uv venv && source .venv/bin/activate
uv pip install -e ".[dev]"
pre-commit install

# Configure and run
cp .env.example .env
memory-mcp-server

# Verify
curl http://localhost:5001/health
```

## Tools

### Memory

| Tool | Purpose |
|------|---------|
| `memory_store(content, tags?, metadata?)` | Persist a new memory; returns its id |
| `memory_get(id)` | Fetch one memory by id |
| `memory_search(query, limit?, project?, output_format?)` | Whole-word keyword search over content, ranked by relevance |
| `memory_list(limit?, offset?, tag?)` | Browse memories newest-first |
| `memory_update(id, content?/tags?/metadata?)` | Partially update a memory |
| `memory_delete(id)` | Remove a memory by id |

### Todos

| Tool | Purpose |
|------|---------|
| `todo_create(title, description?, priority?, status?, due_date?, tags?)` | Add a structured task |
| `todo_search(query, limit?, offset?)` | Whole-word keyword search over title/description, ranked by relevance |
| `todo_list(status?, tag?, limit?, offset?)` | Browse todos with filters |
| `todo_get(id)` / `todo_update(id, ...)` / `todo_complete(id)` / `todo_delete(id)` | Manage individual todos |

### Kanban

| Tool | Purpose |
|------|---------|
| `board_create(name, description?, columns?)` | New board; defaults to backlog/todo/in_progress/done |
| `board_list()` / `board_delete(board_id)` | Enumerate or tear down boards |
| `board_view(board_id)` | Full board state grouped by column |
| `card_add(board_id, title, ...)` / `card_move(card_id, column, position?)` | Place and reorder work |
| `card_update(card_id, ...)` / `card_delete(card_id)` | Edit or remove cards |

### Reminders & Alerts

| Tool | Purpose |
|------|---------|
| `reminder_create(title, due_at, notes?, repeat?)` | Schedule a future alert (none/hourly/daily/weekly repeats) |
| `reminder_get(id)` / `reminder_update(id, ...)` | Inspect or edit a schedule |
| `reminder_list(status?, limit?, offset?)` | Soonest-due first |
| `reminder_cancel(id)` / `reminder_snooze(id, minutes?)` / `reminder_delete(id)` | Manage schedules |
| `alert_list(acknowledged?, limit?)` / `alert_ack(id)` / `alert_ack_all()` | Review and clear fired alerts |

A background scheduler converts due reminders into alerts server-side;
query them at session start with `alert_list(acknowledged=false)`.

### Dashboard

| Tool | Purpose |
|------|---------|
| `overview()` | Cross-domain counts, unacked alerts, and what's due next |

### Trackers

| Tool | Purpose |
|------|---------|
| `tracker_create(name, description?)` / `tracker_list()` / `tracker_delete(id)` | Manage trackers |
| `entry_add(tracker_id, name, status?)` / `entry_update_status(entry_id, status)` / `entry_remove(entry_id)` | Track items (not_started/in_progress/blocked/done) |
| `tracker_status(tracker_id)` | Progress: total/done/percent plus per-status counts |

All tools return `{status: "success" | "error", ...}` dictionaries and never
raise across the tool boundary.

## Configuration

| Variable | Default | Description |
|----------|---------|-------------|
| `MEMORY_DB_PATH` | `./data/memory.db` | SQLite database file (auto-created, parent dirs included) |
| `REMINDER_POLL_SECONDS` | `30` | Background scheduler interval for firing due reminders |
| `MCP_HOST` | `localhost` | Server bind address |
| `MCP_PORT` | `5001` | Server port (1024-65535) |
| `MCP_TRANSPORT_PROTOCOL` | `http` | Transport protocol (`http`, `sse`, `streamable-http`) |
| `MCP_SSL_KEYFILE` / `MCP_SSL_CERTFILE` | `None` | SSL key/certificate for HTTPS |
| `ENABLE_AUTH` | `False`* | OAuth authentication (see `docs/authentication.md`) |
| `PYTHON_LOG_LEVEL` | `INFO` | Logging level |

\* `ENABLE_AUTH` defaults to `False` in `.env.example`. Always copy `.env.example` to `.env` to start with auth disabled.

## Connecting an MCP Client

Point your MCP client at the server endpoint:

```json
{
  "mcpServers": {
    "memory": {
      "url": "http://localhost:5001/mcp"
    }
  }
}
```

See `examples/fastmcp_client.py` for a working client that stores and searches memories.

## Development

```bash
make lint     # ruff + mypy
make test     # pytest with coverage
make pre-commit  # run all pre-commit hooks
```

## Documentation

| Guide | Description |
|-------|-------------|
| [Architecture](docs/architecture.md) | System diagrams, code structure, key components |
| [Development](docs/development.md) | Setup, running locally, testing, code quality |
| [Deployment](docs/deployment.md) | Podman, OpenShift, container configuration |
| [Authentication](docs/authentication.md) | OAuth setup, auth modes, troubleshooting |

## License

Apache 2.0 — derived from
[redhat-data-and-ai/template-mcp-server](https://github.com/redhat-data-and-ai/template-mcp-server).