内容
# redis-memory-mcp
> **该仓库已不再开发。** 开发工作已转移至
> [sergesha/claude-essentials](https://github.com/sergesha/claude-essentials/tree/main/redis-memory-mcp)
> 作为独立版本化的软件包,从 v0.5.0 开始(本仓库的最后一个发布版本是 v0.4.0)。`start.sh` 现在只会重定向到那里 —— 现有的安装程序会从 `main` 拉取此文件并自动获取新版本,无需配置更改。新安装程序应直接使用 `sergesha/claude-essentials`;请参阅其 README 以获取安装说明。
> AI 代理的持久跨会话内存 —— 语义搜索 + 键值存储,自动过期
通过 [MCP](https://modelcontextprotocol.io) 为 LLM 代理(Cursor、Claude Code 等)提供长期自我管理的内存。
## 功能
- **语义搜索** (`mem_*`) —— 保存带有向量嵌入的事实,根据含义查找
- **键值存储** (`kv_*`) —— 命名事实的即时 O(1) 查找
- **自动过期** —— 每次读取时重置 TTL;未使用的事实过期,热门事实永久保存
- **多项目** —— `NAMESPACE` 在项目/代理之间隔离数据;`tags` 在一个项目内过滤
- **双范围访问** —— 每个工具调用都带有 `shared: bool`,因此一个客户端可以访问其自己的命名空间区域和始终存在的共享区域,每个调用都可以
- **共享部署** —— `REDIS_MEMORY_MCP_MODE=shared` 允许多个代理重用一个后端,而不是每个代理启动自己的后端
- **自包含** —— Docker 栈:Redis Stack + HuggingFace TEI 嵌入 + MCP 服务器
## 快速开始
```bash
# 1. 克隆
git clone https://github.com/yourname/redis-memory-mcp
cd redis-memory-mcp
# 2. 启动基础设施
docker compose up -d
# 3. 添加到您的 AI 工具的 MCP 配置
```
### Cursor (`~/.cursor/mcp.json`)
```json
{
"mcpServers": {
"redis-memory-mcp": {
"command": "docker",
"args": [
"run", "--rm", "-i",
"-e", "REDIS_URL=redis://host.docker.internal:6379/0",
"-e", "EMBED_URL=http://host.docker.internal:8081",
"-e", "INDEX_NAME=idx:memories",
"redis-memory-mcp"
]
}
}
}
```
### Claude Code
在作为 Claude 插件使用时,通过 `.mcp.json` 在仓库根目录自动工作。
## 工具(共 8 个)
### 键值存储 —— 即时查找
| 工具 | 描述 |
|------|-------------|
| `kv_set(key, value, tags?, ttl_days?, shared?)` | 存储命名事实 |
| `kv_get(key, shared?)` | 通过精确键检索(刷新 TTL) |
| `kv_delete(key, shared?)` | 通过键删除 |
| `kv_list(tag?, pattern?, shared?)` | 列出条目并过滤 |
### 语义内存 —— 向量搜索
| 工具 | 描述 |
|------|-------------|
| `mem_save(text, code?, tags?, ttl_days?, shared?)` | 保存带有嵌入的事实 |
| `mem_search(query, tags?, top_k?, shared?)` | 通过含义查找(刷新命中 TTL) |
| `mem_list(limit?, tag?, shared?)` | 按最近程度浏览 |
| `mem_delete(memory_id, shared?)` | 通过 ID 删除 |
`shared`(默认 `false`,所有工具)—— 请参见下面的 `shared` —— 从一个客户端访问两个区域。
## TTL 与自动过期
| TTL | 用例 |
|-----|----------|
| `ttl_days=90`(默认) | 普通事实 —— 如果 90 天不使用则过期 |
| `ttl_days=0` | 永久 —— API 密钥,关键配置 |
| `ttl_days=7` | 短期上下文 |
- TTL **每次读取时重置** —— 频繁访问的事实永不过期
- Redis `volatile-lru` 在内存压力下驱逐最近最少使用的事实
- 只有带有 TTL 的事实才能被驱逐;永久事实 (`ttl_days=0`) 是安全的
## 架构
```
┌─────────────────┐ ┌────────────────────┐ ┌───────────────────┐
│ Cursor / Claude │────▶│ redis-memory-mcp │────▶│ Redis Stack │
│ (MCP 客户端) │ MCP │ (Python, stdio) │ │ + RediSearch │
└─────────────────┘ └────────┬───────────┘ │ + HNSW 索引 │
│ └───────────────────┘
▼
┌────────────────────┐
│ HuggingFace TEI │
│ (嵌入,CPU) │
└────────────────────┘
```
- **Redis Stack** —— RediSearch 模块,带有 HNSW 向量索引(768 维,余弦)
- **TEI** —— `paraphrase-multilingual-mpnet-base-v2`(多语言,在 CPU 上运行)
- **MCP 服务器** —— Python FastMCP,基于 stdio
## 环境变量
| 变量 | 默认值 | 描述 |
|----------|---------|-------------|
| `REDIS_URL` | `redis://localhost:6379/0` | Redis 连接 URL |
| `EMBED_URL` | `http://localhost:8081` | TEI 嵌入端点 |
| `INDEX_NAME` | `idx:memories`(或 `idx:memories:{NAMESPACE}`,请参见下文) | Redis 搜索索引名称 |
| `NAMESPACE` | 未设置 | 在共享实例上隔离 `kv_*`/`mem_*` 数据 —— 请参见下文 |
| `DEFAULT_TTL` | `7776000`(90 天) | 默认 TTL(秒) |
| `REDIS_MEMORY_MCP_MODE` | `dedicated` | `start.sh` 独有 —— 请参见下面的共享部署 |
### 共享部署(一个后端,多代理)
默认情况下(`start.sh`,模式 `dedicated`),每次调用都会启动自己的 Redis Stack + TEI 容器 —— 对于单个用户在笔记本电脑上使用来说是合适的,但当多个代理共享一台机器时(例如,一fleet Claude Code 代理),每个代理都会启动一个重复且未使用的后端。
`REDIS_MEMORY_MCP_MODE=shared` 跳过了这一点:它要求 `REDIS_URL` 和 `EMBED_URL` 已经指向其他地方启动的后端,并连接到它而不是启动新的后端。典型的布局 —— 一个用户/进程拥有后端(普通的 `docker compose up -d redis embeddings redis-init`,不涉及 `start.sh`),其他用户的 MCP 客户端配置运行 `start.sh`,并设置:
```bash
REDIS_MEMORY_MCP_MODE=shared
REDIS_URL=redis://<后端主机>:6379/0
EMBED_URL=http://<后端主机>:8081
```
共享后端并不意味着共享数据 —— 请参见下面的 `NAMESPACE` 以隔离恰好指向相同 Redis/TEI 的代理。
### NAMESPACE(共享实例上的数据隔离)
`tags`(由 `kv_set`/`mem_save` 接受,由 `mem_search`/`kv_list`/`mem_list` 作为可选筛选器使用)是相同命名空间的筛选器,而不是隔离边界:`kv_get`/`kv_set` 在没有涉及标签的情况下对精确键进行操作,因此两个代理在共享实例上调用 `kv_set('database-url', ...)` 将发生冲突,无论标签如何,并且无标签的 `mem_search` 看到每个代理的内存。
`NAMESPACE` 在键级别解决了这个问题。设置一次每个 MCP 客户端(例如 `-e NAMESPACE=my-project`),它为每个键添加前缀,并选择专用的搜索索引,因此两个命名空间无法看到或覆盖彼此的数据,无论传递什么标签(或不传递标签):
```
NAMESPACE 未设置 → mem:{id} kv:{key} idx:memories (以前的行为,不变)
NAMESPACE=my-project → ns:my-project:mem:{id} ns:my-project:kv:{key} idx:memories:my-project
```
(命名空间键以 `ns:{NAMESPACE}:` 为前缀,而不是 `mem:{NAMESPACE}:` —— 故意不是基前缀 `mem:`/`kv:` 的字符串扩展。RediSearch 的 `FT.CREATE ... PREFIX` 匹配是纯字符串前缀测试,因此命名空间前缀仅扩展基前缀将使基索引也获取每个命名空间的键,一旦两者存在。保持两个前缀集不相交避免了这种情况。)
根据需要与共享部署结合使用:相同后端 + 无 `NAMESPACE` = 每个代理之间的内存共享;相同后端 + 不同 `NAMESPACE` 每个代理 = 共享基础设施,隔离数据。
### `shared` —— 从一个客户端访问两个区域
每个 `kv_*`/`mem_*` 工具还接受一个 `shared: bool = false` 参数,独立于 `NAMESPACE`。这是 **每个调用** 的选择,而不是每个客户端的选择:一个运行带有 `NAMESPACE=my-project` 的 MCP 客户端,可以写入/读取其自己的隔离区域 (`shared=false`,默认值)*和* 始终存在的全局区域 (`shared=true`) —— 无需第二个注册、第二个后端或第二个运行进程。
```
kv_set('db-url', '...') # 自己的区域(NAMESPACE 的,或如果 NAMESPACE 未设置,则为基本区域)
kv_set('db-url', '...', shared=True) # 基本/共享区域,与 NAMESPACE 无关
mem_search('deploy steps') # 仅搜索自己的区域
mem_search('deploy steps', shared=True) # 仅搜索共享区域 —— 永远不会同时搜索两个区域,也不会合并结果
```
两个区域之间没有自动回落:调用只会访问一个区域,调用者通过设置 `shared` 来决定访问哪个区域。读取使用 `shared=True` 保存的内容需要 `shared=True`,否则即使该条目存在于另一个区域,也会报告“未找到”。
## Redis UI
RedisInsight 包含在 **http://localhost:8001** —— 浏览键,运行查询,分析内存使用情况。
## 插件结构
```
redis-memory-mcp/
├── .claude-plugin/marketplace.json # 市场注册表
├── .claude/settings.json # 自动加载配置
├── redis-memory-mcp/ # Claude 插件
│ ├── .claude-plugin/
│ │ ├── plugin.json # 插件元数据
│ │ └── mcp.json # MCP 服务器文档
│ ├── .mcp.json # 运行时 MCP 配置
│ ├── hooks/project-init.json # 会话启动钩子
│ └── skills/persistent-memory/
│ └── SKILL.md # 内存管理技能
├── server/ # MCP 服务器源代码
│ ├── memory_mcp.py
│ ├── Dockerfile
│ └── pyproject.toml
├── docker-compose.yaml # 全栈
└── README.md
```
## 许可证
MIT