持久MCP服务器
人工智能代理的语义记忆与自改进检索
A. 模型上下文协议(MCP) 服务器,为AI代理提供跨对话的持久、可搜索的内存。基于Supabase边缘函数和pgvector进行语义搜索。
为什么存在
人工智能助手在对话之间会忘记一切。此服务器修复了此问题。它将想法、决策和观察结果存储为向量嵌入,然后在相关时从语义上检索它们。内置的反馈循环随着时间的推移提高了检索质量。自我评估记分卡对你使用该系统的效率进行评分。
关键能力
- 语义搜索 --通过pgvector嵌入按意义而不是关键字查找记忆
- 自动提取元数据 --LLM在每次拍摄时都会提取主题、人物、行动项目和重要性
- 多代理身份验证 --多个AI客户端使用范围权限(只读、仅捕获、完全访问)连接
- 自我评估记分卡 --7维评分系统使用字母评分(S到F)
- Spark智能引擎 --模式分析、差距检测、跨项目连接、研究简报
- 检索反馈循环 --显式和隐式信号训练系统以获得更好的结果
- 完整审计跟踪 --每个操作都记录了代理标识和时间戳
- 电报通知 --Spark报告的可选推送警报
建筑
┌─────────────────────────────────────────────────┐
│ AI Clients (Claude, ChatGPT, custom agents) │
│ Connect via MCP JSON-RPC over HTTPS │
└──────────────────────┬──────────────────────────┘
│
┌─────────────▼─────────────┐
│ Supabase Edge Function │
│ (Deno runtime) │
│ │
│ ┌─────────────────────┐ │
│ │ Auth + Audit Layer │ │
│ │ (SHA-256 key hash) │ │
│ └────────┬────────────┘ │
│ │ │
│ ┌────────▼────────────┐ │
│ │ 9 MCP Tools │ │
│ │ capture, search, │ │
│ │ browse, stats, │ │
│ │ spark, update, │ │
│ │ delete, scorecard, │ │
│ │ feedback │ │
│ └────────┬────────────┘ │
│ │ │
└───────────┼───────────────┘
│
┌────────────────┼────────────────┐
│ │ │
▼ ▼ ▼
┌────────┐ ┌────────────┐ ┌───────────┐
│Supabase│ │ OpenRouter │ │ Telegram │
│Postgres│ │ (embeddings │ │ (optional │
│+pgvector│ │ + LLM) │ │ alerts) │
└────────┘ └────────────┘ └───────────┘工具参考
| 工具 | 说明 |
|---|---|
capture_thought | 存储具有自动嵌入和元数据提取功能的内存 |
search_thoughts | 通过意义在记忆中进行语义搜索 |
browse_thoughts | 使用项目/日期/类型筛选器列出最近的记忆 |
brain_stats | 系统概述——按项目、活动、代理使用情况统计 |
spark | 智能引擎——分析模式、研究简报、仪表板 |
update_thought | 编辑内存(如果内容发生变化,会自动重新嵌入) |
delete_thought | 带有审计跟踪的软删除(不会永久删除任何内容) |
scorecard | 7个评分维度和字母等级的自我评估 |
feedback | 对检索质量进行评级,以改善未来的搜索结果 |
______________________________________________________________________
部署指南
先决条件
你需要三件事:
- Supabase账户 — 网站 supabase.com (免费版作品)
- 一个OpenRouter API密钥 — openrouter.ai (用于嵌入和LLM调用)
- 已安装Supabase CLI — 安装指南
可选:
- 电报机器人 --用于Spark报告的推送通知
第一步:创建一个Supabase项目
- 首选 suabase.com/dashboard
- 点击 新项目
- 选择一个名称并设置数据库密码
- 选择您附近的地区
- 等待项目完成配置(约2分钟)
- 注意你的 项目参考 (在URL中可见:
supabase.com/dashboard/project/)
步骤2:设置数据库
- 在您的Supabase仪表板中,转到 SQL 编辑器 (左侧边栏)
- 点击 新建查询
- 粘贴以下内容的全部内容
schema.sql来自此repo - 点击 跑
这将创建所有必需的表(thoughts, agents, agent_audit_log, thought_feedback, spark_reports, workflow_scores, build_sessions),功能(match_thoughts, increment_retrieval, project_health, mcp_raw_metrics),以及包括用于语义搜索的pgvector IVFFlat索引在内的索引。
注: 该架构启用 vector 和 pgcrypto 自动扩展。如果你在以下方面遇到错误 vector 不存在,请转到 数据库→ 扩展 在您的仪表板中,手动启用它。
步骤3:创建代理API密钥
每个连接的客户端都需要一个API密钥。在SQL编辑器中运行以下命令:
INSERT INTO agents (name, key_hash, scopes, active)
VALUES (
'my-first-agent',
encode(digest('replace-with-your-secret-key', 'sha256'), 'hex'),
'{"master"}',
true
);替换 replace-with-your-secret-key 用你想要的任何字符串作为你的密钥。 保存此字符串 --数据库只存储SHA-256哈希,因此您以后无法恢复它。
要创建具有受限访问权限的代理,请执行以下操作:
-- Read-only agent (search and browse only)
INSERT INTO agents (name, key_hash, scopes, active)
VALUES (
'readonly-agent',
encode(digest('your-readonly-key', 'sha256'), 'hex'),
'{"read-only"}',
true
);
-- Capture-only agent (store memories and give feedback, no delete)
INSERT INTO agents (name, key_hash, scopes, active)
VALUES (
'capture-agent',
encode(digest('your-capture-key', 'sha256'), 'hex'),
'{"capture-only", "feedback"}',
true
);步骤4:设置环境变量
在您的Supabase仪表板中:
- 首选 边缘函数 (左侧边栏)
- 点击 管理秘密 (您可能需要先在步骤5中部署该功能,然后再回来)
- 添加:
| 秘密名称 | 值 |
|---|---|
OPENROUTER_API_KEY | 您的OpenRouter API密钥 |
可选(用于电报通知):
SUPABASE_URL 和 SUPABASE_SERVICE_ROLE_KEY 在边缘函数中自动可用——您不需要设置它们。
步骤5:部署
git clone https://github.com/YOUR_USERNAME/persistent-mcp-server.git
cd persistent-mcp-server
# Link to your Supabase project
supabase link --project-ref YOUR_PROJECT_REF
# Deploy
supabase functions deploy persistent-mcp --no-verify-jwt这 --no-verify-jwt 标志是必需的,因为身份验证是通过API密钥处理的,而不是通过Suabase JWT令牌处理的。
您的服务器现在位于:
https://YOUR_PROJECT_REF.supabase.co/functions/v1/persistent-mcp步骤6:验证
# Health check
curl https://YOUR_PROJECT_REF.supabase.co/functions/v1/persistent-mcp
# Expected: {"status":"online","version":"1.0.0","protocol":"mcp-jsonrpc"}
# Test a capture
curl -X POST \
"https://YOUR_PROJECT_REF.supabase.co/functions/v1/persistent-mcp?key=your-secret-key" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "capture_thought",
"arguments": { "content": "Test memory — server is working!" }
}
}'
# Test a search
curl -X POST \
"https://YOUR_PROJECT_REF.supabase.co/functions/v1/persistent-mcp?key=your-secret-key" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "search_thoughts",
"arguments": { "query": "test memory" }
}
}'步骤7:连接到AI客户端
克劳德桌面
添加 ~/Library/Application Support/Claude/claude_desktop_config.json (macOS)或 %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"memory": {
"url": "https://YOUR_PROJECT_REF.supabase.co/functions/v1/persistent-mcp?key=your-secret-key"
}
}
}Claude.ai
首选 设置→ 已连接的应用程序 并添加MCP服务器URL。
任何MCP客户端
服务器通过HTTP POST使用标准的MCP JSON-RPC 2.0。任何兼容的客户端都可以使用带有API密钥的URL作为查询参数或通过 x-api-key 头球
______________________________________________________________________
定制指南
更改项目
编辑 PROJECTS 数组输入 index.ts:
const PROJECTS: string[] = [
"default",
"personal",
"work",
"research",
"infrastructure",
// Add your own:
"side-project",
"health",
"finances",
];更改后重新部署:
supabase functions deploy persistent-mcp --no-verify-jwt交换LLM模型
所有LLM电话都会接通 开放路由。在中更改这些常数 index.ts:
const EMBEDDING_MODEL = "openai/text-embedding-3-small"; // Must output 1536-dim vectors
const EXTRACTION_MODEL = "openai/gpt-4o-mini"; // Cheap + fast for metadata
const ANALYSIS_MODEL = "anthropic/claude-haiku-4-5"; // Smarter = better Spark insights
const NARRATIVE_MODEL = "openai/gpt-4o-mini"; // Scorecard personality如果更改嵌入模型 将数据库更新为不同维度(例如,768而不是1536):
ALTER TABLE thoughts ALTER COLUMN embedding TYPE VECTOR(768);
-- Also update the match_thoughts function parameter type代理范围参考
| 范围 | 授予的工具 |
|---|---|
master | 所有工具 |
capture-only | capture_thought, feedback |
capture | capture_thought, feedback |
read-only | search_thoughts, browse_thoughts, brain_stats, scorecard |
read | search_thoughts, browse_thoughts, brain_stats |
search | search_thoughts |
browse | browse_thoughts |
stats | brain_stats, scorecard |
write | update_thought, delete_thought, feedback |
update | update_thought |
delete | delete_thought |
spark-only | spark |
spark | spark, scorecard |
scorecard | scorecard |
feedback | feedback |
自由组合范围:
INSERT INTO agents (name, key_hash, scopes, active)
VALUES (
'custom-agent',
encode(digest('my-key', 'sha256'), 'hex'),
'{"capture", "read", "spark"}',
true
);调整语义搜索
两个参数控制搜索行为:
match_threshold(默认值0.65)--最小余弦相似度。越低捕获越多结果。越高越精确。match_count(默认值10)--返回的最大结果数。
对于大型数据集(10000+个内存),重建IVFFlat索引:
DROP INDEX IF EXISTS idx_thoughts_embedding;
CREATE INDEX idx_thoughts_embedding
ON thoughts USING ivfflat (embedding vector_cosine_ops)
WITH (lists = 300); -- target: sqrt(total_rows)自定义元数据提取
在中编辑提示 extractMetadata() 功能。提取的JSON存储在 metadata JSONB列,因此您可以添加任何字段而无需更改模式:
// Example: adding an "urgency" field
const prompt = `Extract metadata. Return ONLY valid JSON.
{
"type": "observation|decision|action_item|idea",
"topics": ["topic1"],
"urgency": "low|medium|high|critical",
"importance": 5,
"summary": "one-line summary"
}`;添加自定义工具
在 registerTools 功能:
addTool(
"my_tool",
"What this tool does",
{
param1: z.string().describe("Description"),
param2: z.number().optional().describe("Optional param"),
},
async ({ param1, param2 }) => {
// Your logic here
return {
content: [{ type: "text", text: `Result: ${param1}` }],
};
}
);然后将其添加到 toolScopeMap 在 agentCanUseTool:
my_tool: ["write", "my-custom-scope"],禁用Telegram
不要设置 TELEGRAM_BOT_TOKEN 和 TELEGRAM_CHAT_ID 秘密。当Telegram丢失时,该功能会自动跳过。
______________________________________________________________________
记分卡尺寸
| 尺寸 | 重量 | 测量内容 |
|---|---|---|
| 捕捉速度 | 25% | 捕捉记忆的频率和一致性 |
| 检索精度 | 20% | 搜索是否返回有用结果 |
| 项目覆盖率 | 15% | 是否积极跟踪所有项目 |
| 行动完成 | 15% | 行动项是否得到解决 |
| Spark敬业度 | 8% | 是否生成情报报告 |
| 内存新鲜度 | 10% | 内容是最新的还是过时的 |
| 代理多样性 | 7% | 是否有多个客户端为系统供电 |
分数: S (90+) → A. (75+) → B (60+) → C (45+) → D (30+) → F (\ { const res = await fetch("http://localhost:11434/api/embeddings", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ model: "nomic-embed-text", prompt: text, }), }); const data = await res.json(); return data.embedding; }
**重要提示:** `nomic-embed-text` 输出768个维度向量,而不是1536个。更新您的架构:
-- Change vector dimensions ALTER TABLE thoughts ALTER COLUMN embedding TYPE VECTOR(768);
-- Update the match_thoughts function signature -- Change VECTOR(1536) to VECTOR(768) in the function parameter
**用于元数据提取的本地LLM:**
修改 `llmCall()` 点击您当地的Ollama实例:
async function llmCall( prompt: string, model = "llama3.2", systemPrompt?: string ): Promise { const messages = []; if (systemPrompt) messages.push({ role: "system", content: systemPrompt }); messages.push({ role: "user", content: prompt });
const res = await fetch("http://localhost:11434/api/chat", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ model, messages, stream: false }), }); const data = await res.json(); return data.message.content; }
**替代托管提供商(而不是OpenRouter):**
|提供者|嵌入|LLM|注释|
|----------|-----------|-----|-------|
|OpenAI直接| `text-embedding-3-small` | `gpt-4o-mini` |需要OpenAI API密钥,将基本URL更改为 `api.openai.com` |
|直接人类学|--| `claude-haiku-4-5` |没有嵌入API-使用OpenAI或本地用于嵌入,使用Anthropic用于LLM|
|科恩| `embed-english-v3.0` | `command-r` |很好的嵌入免费层|
|Voyage AI| `voyage-3-lite` |--|仅嵌入,高质量|
|谷歌顶点| `text-embedding-004` | `gemini-2.0-flash` |免费套餐可用|
对于任何提供者交换,您只需更改 `generateEmbedding()` 和 `llmCall()` --服务器的其余部分并不关心向量和文本来自哪里。
______________________________________________________________________
### 选项D:完全离线(零云依赖)
对于没有外部API调用的完全自包含的设置:
1. **Postgres+pgvector** --本地安装(见选项A)
1. **奥拉玛** --本地嵌入+本地LLM(见选项C)
1. **德诺** --在本地运行服务器
总成本:0美元。需要具有体面RAM的机器(Ollama型号为8GB+)。离线工作,气隙工作,在飞机上工作。
代价是质量——与OpenAI/Anthropic模型相比,本地嵌入模型和小型LLM产生的元数据提取效果较差,语义搜索精度较低。对于个人使用,这通常是可以的。对于有很多用户的生产来说,托管模型是值得的。
______________________________________________________________________
## 故障排除
|问题|修复|
|---------|-----|
| `Embedding failed: 401` |OpenRouter API密钥丢失或无效-检查边缘功能机密|
| `Invalid or inactive API key` |您的密钥与任何代理都不匹配——请验证SHA-256哈希值|
| `No matches found` |更低 `threshold` 对于更广泛的结果,为0.4|
|记分卡显示零|先捕获一些记忆并运行搜索——需要数据|
|慢速首次请求|边缘功能冷启动(1-2s)——设置定时ping以保暖|
| `vector` 扩展错误|启用pgvector: `CREATE EXTENSION vector;` 或从源代码安装|
|Ollama连接被拒绝|请确保Ollama正在运行: `ollama serve` |
|向量维度错误|嵌入模型维度必须匹配 `VECTOR(N)` 在模式中|
______________________________________________________________________
## 许可证
麻省理工学院——见 [许可证](LICENSE).