Memory MCP Server
SQLite-backed MCP server for persistent memory, full-text retrieval, and graph traversal.
Open source Open in the app JSON README (API)
About
SQLite-backed MCP server for persistent memory, full-text retrieval, and graph traversal.
Details
- Kind
- MCP servers
- Topic
- AI, RAG & memory
- Publisher
- j0hanz
- Origin
- official
- Category
- ferramentas
- Transport
- local
- Version
- 1.7.0
- Last push
- 2026-03-01T18:17:16Z
- Repository state
- ativo
- Language
- TypeScript
- Added
- 2026-08-29 04:00:12
- Updated
- 2026-08-29 04:00:12
- Origin id
io.github.j0hanz/memory-mcp
README
# Memory MCP
<!-- markdownlint-disable MD033 -->
[](https://www.npmjs.com/package/@j0hanz/memory-mcp) [](https://github.com/j0hanz/memory-mcp/blob/master/package.json) [](https://github.com/j0hanz/memory-mcp/blob/master/package.json) [](https://github.com/j0hanz/memory-mcp/blob/master/package.json)
[](https://insiders.vscode.dev/redirect/mcp/install?name=memory-mcp&config=%7B%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40j0hanz%2Fmemory-mcp%40latest%22%5D%7D) [](https://insiders.vscode.dev/redirect/mcp/install?name=memory-mcp&config=%7B%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40j0hanz%2Fmemory-mcp%40latest%22%5D%7D&quality=insiders)
[](https://cursor.com/en/install-mcp?name=memory-mcp&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBqMGhhbnovbWVtb3J5LW1jcEBsYXRlc3QiXX0=)
A SQLite-backed MCP server for persistent memory storage, full-text retrieval, and relationship graph traversal.
## Overview
Memory MCP provides a local, persistent memory layer for MCP-enabled assistants. It stores SHA-256-addressed memory items in SQLite with FTS5-powered full-text search, a directed relationship graph, BFS recall traversal, and token-budget-aware context retrieval — all accessible over stdio transport with no external dependencies.
## Key Features
- **13 MCP tools** for CRUD, batch operations, FTS5 search, BFS graph recall, token-budget context retrieval, relationships, and stats.
- **Full-text search** over content and tags via SQLite FTS5 with importance and type filters.
- **Graph recall** with BFS traversal, bounded frontier, and MCP progress notifications per hop.
- **Token-budget retrieval** (`retrieve_context`) selects memories that fit a caller-specified token budget — no manual pagination needed.
- **Strict Zod input validation** with typed output envelopes and SHA-256 hash addressing.
- **Resource support** with `internal://instructions` (Markdown guide) and `memory://memories/{hash}` URI template with hash auto-completion.
- **stdio transport** with clean shutdown handling (`SIGINT`, `SIGTERM`) and no HTTP endpoints. |
## Requirements
- Node.js `>=24`.
- SQLite with FTS5 support (verified at startup).
- Any MCP client that supports stdio command servers.
## Quick Start
Use the npm package directly with `npx` — no installation required:
```json
{
"mcpServers": {
"memory-mcp": {
"command": "npx",
"args": ["-y", "@j0hanz/memory-mcp@latest"]
}
}
}
```
> [!TIP]
> The server uses stdio transport only; no HTTP endpoint is exposed. Stdout must not be polluted by custom logging.
Or run with Docker:
```bash
docker run --rm -i ghcr.io/j0hanz/memory-mcp:latest
```
## Client Configuration
<details>
<summary><b>Install in VS Code</b></summary>
[](https://insiders.vscode.dev/redirect/mcp/install?name=memory-mcp&config=%7B%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40j0hanz%2Fmemory-mcp%40latest%22%5D%7D)
Workspace file `.vscode/mcp.json`:
```json
{
"servers": {
"memory-mcp": {
"command": "npx",
"args": ["-y", "@j0hanz/memory-mcp@latest"]
}
}
}
```
CLI:
```bash
code --add-mcp '{"name":"memory-mcp","command":"npx","args":["-y","@j0hanz/memory-mcp@latest"]}'
```
</details>
<details>
<summary><b>Install in VS Code Insiders</b></summary>
[](https://insiders.vscode.dev/redirect/mcp/install?name=memory-mcp&config=%7B%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40j0hanz%2Fmemory-mcp%40latest%22%5D%7D&quality=insiders)
CLI:
```bash
code-insiders --add-mcp '{"name":"memory-mcp","command":"npx","args":["-y","@j0hanz/memory-mcp@latest"]}'
```
</details>
<details>
<summary><b>Install in Cursor</b></summary>
[](https://cursor.com/en/install-mcp?name=memory-mcp&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBqMGhhbnovbWVtb3J5LW1jcEBsYXRlc3QiXX0=)
`~/.cursor/mcp.json`:
```json
{
"mcpServers": {
"memory-mcp": {
"command": "npx",
"args": ["-y", "@j0hanz/memory-mcp@latest"]
}
}
}
```
</details>
<details>
<summary><b>Install in Claude Desktop / Claude Code</b></summary>
`claude_desktop_config.json`:
```json
{
"mcpServers": {
"memory-mcp": {
"command": "npx",
"args": ["-y", "@j0hanz/memory-mcp@latest"]
}
}
}
```
CLI:
```bash
claude mcp add memory-mcp -- npx -y @j0hanz/memory-mcp@latest
```
</details>
<details>
<summary><b>Install in Windsurf</b></summary>
MCP config:
```json
{
"mcpServers": {
"memory-mcp": {
"command": "npx",
"args": ["-y", "@j0hanz/memory-mcp@latest"]
}
}
}
```
</details>
<details>
<summary><b>Run with Docker</b></summary>
```bash
# Pull and run (stdio mode)
docker run --rm -i \
-e MEMORY_DB_PATH=/data/memory.db \
-v memory-data:/data \
ghcr.io/j0hanz/memory-mcp:latest
```
MCP client config:
```json
{
"mcpServers": {
"memory-mcp": {
"command": "docker",
"args": [
"run",
"--rm",
"-i",
"-e",
"MEMORY_DB_PATH=/data/memory.db",
"-v",
"memory-data:/data",
"ghcr.io/j0hanz/memory-mcp:latest"
]
}
}
}
```
</details>
## Documentation Maintenance
- **Owner**: maintainers updating MCP behavior in `src/` must update `README.md` and affected `mcp/` mirror pages in the same PR.
- **Link/version policy**: use pinned `https://modelcontextprotocol.io/specification/2025-11-25/...` links for protocol references; avoid `latest` and mixed legacy targets.
- **Drift-check checklist**:
- Re-verify capability declarations in `src/server.ts`.
- Reconcile tool/resource/prompt docs with `src/tools/index.ts`, `src/resources/index.ts`, and `src/prompts/index.ts`.
- Confirm limitations/gotchas in `src/instructions.md` match runtime behavior.
- **Validation commands**: `npm run type-check`, `npm run test:fast`, `npm run build`.
## MCP Surface
### Tools Summary
| Tool | Category | Notes |
| --------------------- | -------- | --------------------------------------- |
| `store_memory` | Write | Idempotent by content+sorted tags hash |
| `store_memories` | Write | Batch (1–50), transaction-wrapped |
| `get_memory` | Read | Hash lookup |
| `update_memory` | Write | Returns `old_hash` + `new_hash` |
| `delete_memory` | Write | Cascades relationship deletion |
| `delete_memories` | Write | Batch (1–50), transaction-wrapped |
| `search_memories` | Read | FTS5 + importance/type filters + cursor |
| `create_relationship` | Write | Idempotent directed edge creation |
| `delete_relationship` | Write | Deletes exact directed edge |
| `get_relationships` | Read | Direction filter + linked memory fields |
| `recall` | Read | FTS5 seed + BFS traversal (depth 0–3) |
| `retrieve_context` | Read | Token-budget-aware context retrieval |
| `memory_stats` | Read | Store aggregates and type breakdown |
---
### `store_memory`
Store a new memory with content, tags, and optional type/importance. Idempotent — storing the same content+tags returns the existing hash with `created: false`.
| Name | Type | Required | Default | Description |
| ------------- | ---------- | -------- | --------- | ---------------------------------------------------------------------------------- |
| `content` | `string` | Yes | — | Memory content (1–100000 chars) |
| `tags` | `string[]` | Yes | — | 1–100 tags, each max 50 chars, no whitespace |
| `memory_type` | enum | No | `general` | `general`, `fact`, `plan`, `decision`, `reflection`, `lesson`, `error`, `gradient` |
| `importance` | `integer` | No | `0` | Priority 0–10 |
Returns: `{ hash, created }`
---
### `store_memories`
Store multiple memories in one transaction (max 50 items).
| Name | Type | Required | Description |
| ------- | ------------------------ | -------- | -------------------------------------------------------------------------------------- |
| `items` | `Array<StoreMemoryItem>` | Yes | 1–50 items, each with `content`, `tags`, optional `memory_type`, optional `importance` |
Returns: `{ items, succeeded, failed }`
---
### `get_memory`
Retrieve one memory by its SHA-256 hash.
| Name | Type | Required | Description |
| ------ | -------- | -------- | ----------------------------- |
| `hash` | `string` | Yes | 64-char lowercase SHA-256 hex |
Returns: `Memory` or `{ ok: false, error }` on `E_NOT_FOUND`.
---
### `update_memory`
Update content and optionally tags for an existing memory. Returns both hashes.
| Name | Type | Required | Default | Description |
| --------- | ---------- | -------- | ------------- | -------------------- |
| `hash` | `string` | Yes | — | Existing memory hash |
| `content` | `string` | Yes | — | Replacement content |
| `tags` | `string[]` | No | Existing tags | Replacement tags |
Returns: `{ old_hash, new_hash }`
---
### `delete_memory`
Delete one memory by hash. Cascades to related relationship rows.
| Name | Type | Required | Description |
| ------ | -------- | -------- | ----------- |
| `hash` | `string` | Yes | Memory hash |
Returns: `{ hash, deleted }`
---
### `delete_memories`
Delete multiple memories by hash in one transaction.
| Name | Type | Required | Description |
| -------- | ---------- | -------- | ------------------ |
| `hashes` | `string[]` | Yes | 1–50 memory hashes |
Returns: `{ items, succeeded, failed }`
---
### `search_memories`
Full-text search over memory content and tags using FTS5. Supports importance and type filters with cursor pagination.
| Name | Type | Required | Default | Description |
| ---------------- | --------- | -------- | ------- | --------------------------------------------------------- |
| `query` | `string` | Yes | — | Search text (1–1000 chars) |
| `limit` | `integer` | No | `20` | Results per page (1–100) |
| `cursor` | `string` | No | — | Pagination cursor from previous response |
| `min_importance` | `integer` | No | — | Only return memories with importance >= this value (0–10) |
| `max_importance` | `integer` | No | — | Only return memories with importance <= this value (0–10) |
| `memory_type` | enum | No | — | Filter by memory type |
Returns: `{ memories, total_returned, nextCursor? }`
---
### `create_relationship`
Create a directed relationship edge between two memories. Idempotent.
Suggested `relation_type` values: `related_to`, `causes`, `depends_on`, `parent_of`, `child_of`, `supersedes`, `contradicts`, `supports`, `references`.
| Name | Type | Required | Description |
| --------------- | -------- | -------- | ------------------------------------------------- |
| `from_hash` | `string` | Yes | Source memory hash |
| `to_hash` | `string` | Yes | Target memory hash |
| `relation_type` | `string` | Yes | Edge label (1–50 chars, no whitespace, free-form) |
Returns: `{ created }`
---
### `delete_relationship`
Delete one directed relationship edge.
| Name | Type | Required | Description |
| --------------- | -------- | -------- | ----------------- |
| `from_hash` | `string` | Yes | Source hash |
| `to_hash` | `string` | Yes | Target hash |
| `relation_type` | `string` | Yes | Relationship type |
Returns: `{ deleted }` or `{ ok: false, error }` on `E_NOT_FOUND`.
---
### `get_relationships`
Retrieve relationships for a memory, with optional direction filter.
| Name | Type | Required | Default | Description |
| ----------- | -------- | -------- | ------- | --------------------------------- |
| `hash` | `string` | Yes | — | Memory hash |
| `direction` | enum | No | `both` | `outgoing`, `incoming`, or `both` |
Returns: `{ relationships, count }`
Each relationship includes `from_hash`, `to_hash`, `relation_type`, `created_at`, `linked_hash`, `linked_content`, and `linked_tags`.
---
### `recall`
Search memories by full-text query, then traverse the relationship graph up to `depth` hops via BFS. Emits MCP progress notifications per hop.
| Name | Type | Required | Default | Description |
| ---------------- | --------- | -------- | ------- | ---------------------------------------------------------- |
| `query` | `string` | Yes | — | Seed search query (1–1000 chars) |
| `depth` | `integer` | No | `1` | BFS hops (0–3) |
| `limit` | `integer` | No | `10` | Seed memory count (1–50) |
| `cursor` | `string` | No | — | Pagination cursor from previous response |
| `min_importance` | `integer` | No | — | Seed filter: only memories with importance >= value (0–10) |
| `max_importance` | `integer` | No | — | Seed filter: only memories with importance <= value (0–10) |
| `memory_type` | enum | No | — | Seed filter: only memories of this type |
Returns: `{ memories, graph, depth_reached, aborted?, nextCursor? }`
Each item in `graph` uses the shape:
```json
{ "from_hash": "...", "to_hash": "...", "relation_type": "..." }
```
> [!NOTE]
> `aborted: true` indicates the traversal hit a safety limit (`RECALL_MAX_FRONTIER_SIZE`, `RECALL_MAX_EDGE_ROWS`, or `RECALL_MAX_VISITED_NODES`). Partial results are still returned.
---
### `retrieve_context`
Search memories and return relevance-ranked results that fit within a caller-specified token budget. Eliminates manual pagination and token counting for context window management.
| Name | Type | Required | Default | Description |
| -------------- | --------- | -------- | ----------- | ------------------------------------------------------------------------------------------ |
| `query` | `string` | Yes | — | Search query (1–1000 chars) |
| `token_budget` | `integer` | No | `4000` | Maximum estimated tokens to return (100–200000) |
| `strategy` | enum | No | `relevance` | Sort order: `relevance` (FTS rank), `importance` (highest first), `recency` (newest first) |
Returns: `{ memories, estimated_tokens, truncated }`
> [!TIP]
> Token estimation is approximate (content length ÷ 4). `truncated: true` means the budget was reached before all candidates were included.
---
### `memory_stats`
Return aggregate memory and relationship stats. Takes no input.
Returns:
```json
{
"memories": {
"total": 0,
"oldest": null,
"newest": null,
"avg_importance": null
},
"relationships": { "total": 0 },
"by_type": {}
}
```
---
### Resources
| URI | MIME | Description |
| -------------------------- | ------------------ | ----------------------------------------------------- |
| `internal://instructions` | `text/markdown` | Markdown usage guide for all tools and workflows |
| `memory://memories/{hash}` | `application/json` | Returns one memory as JSON; hash completion supported |
### Prompts
| Name | Arguments | Purpose |
| ---------- | --------- | --------------------------------------------- |
| `get-help` | none | Returns full usage instructions for all tools |
## Configuration
### Environment Variables
| Variable | Description | Default | Required |
| -------------------------- | ------------------------------------------------------- | --------------------- | -------- |
| `MEMORY_DB_PATH` | SQLite database file path | `memory_db/memory.db` | No |
| `RECALL_MAX_FRONTIER_SIZE` | Max BFS frontier nodes per hop (100–50000) | `1000` | No |
| `RECALL_MAX_EDGE_ROWS` | Max relationship rows fetched per traversal (100–50000) | `5000` | No |
| `RECALL_MAX_VISITED_NODES` | Max visited nodes across entire traversal (100–50000) | `5000` | No |
> [!IMPORTANT]
> If `MEMORY_DB_PATH` is relative (including the default `memory_db/memory.db`), it resolves from the process working directory.
> [!TIP]
> Add `memory_db/` to your `.gitignore` to keep the database out of version control — it contains local session data and should not be shared or committed.
### Limits and Constraints
| Item | Value |
| ------------------------------- | ------------------------------------------------- |
| Content length | 1–100000 chars |
| Tag count | 1–100 per memory |
| Tag length | 1–50 chars, no whitespace |
| Hash format | 64-char lowercase hex SHA-256 |
| Search query length | 1–1000 chars |
| `search_memories.limit` | 1–100 (default 20) |
| `recall.depth` | 0–3 (default 1) |
| `recall.limit` | 1–50 (default 10) |
| `retrieve_context.token_budget` | 100–200000 (default 4000) |
| Batch size | 1–50 items (`store_memories`, `delete_memories`) |
| Recall frontier guard | `RECALL_MAX_FRONTIER_SIZE` (default 1000 per hop) |
| SQLite busy timeout | 5000 ms |
> [!NOTE]
> Cursor values are opaque base64url-encoded tokens. Treat them as opaque and do not parse them.
## Security
- Transport is stdio-only (`StdioServerTransport`) — no HTTP endpoints.
- Fatal process errors are written to `stderr`; stdout must remain clean for the MCP protocol.
- All inputs are validated with strict Zod schemas and bounded field constraints before any database access.
- Hashes are validated against a lowercase 64-char SHA-256 hex regex.
- Search input is tokenized to alphanumeric terms before FTS `MATCH` execution (non-alphanumeric characters act as delimiters, preventing FTS injection).
- SQLite foreign keys are enabled; relationship rows cascade-delete when a memory is removed.
## Development
Install dependencies:
```bash
npm install
```
Core scripts:
| Script | Command | Purpose |
| ------------ | -------------------- | -------------------------------------------------------------------- |
| `build` | `npm run build` | Clean, compile, validate instructions, copy assets, chmod executable |
| `dev` | `npm run dev` | TypeScript watch mode |
| `dev:run` | `npm run dev:run` | Run built server with `.env` and file watch |
| `start` | `npm run start` | Start built server |
| `test` | `npm run test` | Full build + tests via task runner |
| `test:fast` | `npm run test:fast` | Run TS tests directly with Node test runner |
| `lint` | `npm run lint` | ESLint checks |
| `lint:fix` | `npm run lint:fix` | ESLint auto-fix |
| `type-check` | `npm run type-check` | Strict TypeScript checks |
| `format` | `npm run format` | Prettier format |
| `inspector` | `npm run inspector` | Build and open MCP Inspector against stdio server |
Inspect with MCP Inspector:
```bash
npx @modelcontextprotocol/inspector node dist/index.js
```
## Build & Release
GitHub Actions release workflow (`.github/workflows/release.yml`) handles versioning, validation, and publishing via a single `workflow_dispatch` trigger:
```
workflow_dispatch (patch / minor / major / custom)
│
▼
release — bump package.json + server.json → lint → type-check → test → build → tag → GitHub Release
│
├──► publish-npm ──► publish-mcp (npm Trusted Publishing OIDC → MCP Registry)
│
└──► publish-docker (GHCR, linux/amd64 + linux/arm64)
```
Trigger a release:
```bash
gh workflow run release.yml -f bump=patch
```
Or use the GitHub UI: **Actions → Release → Run workflow**.
> [!NOTE]
> npm publishing uses OIDC Trusted Publishing — no `NPM_TOKEN` secret required. MCP Registry uses GitHub OIDC. Docker uses the built-in `GITHUB_TOKEN`.
## Troubleshooting
| Symptom | Cause | Fix |
| ----------------------------- | ---------------------------- | -------------------------------------------------------- |
| Startup fails with FTS5 error | Node.js build without FTS5 | Use Node.js 24+ with SQLite FTS5 support |
| `E_NOT_FOUND` on `get_memory` | Hash doesn't exist | Verify via `search_memories` first |
| `E_INVALID_CURSOR` | Stale or malformed cursor | Retry the request without the `cursor` parameter |
| MCP client can't connect | Custom stdout logging added | Ensure nothing writes to stdout in the server process |
| `aborted: true` in recall | Traversal hit a safety limit | Reduce `depth`, or tune `RECALL_MAX_*` env vars |
| Database locked errors | High concurrent write load | SQLite busy timeout is 5000 ms; reduce concurrent writes |
## License
MIT
<!-- markdownlint-enable MD033 -->