使用说明
持久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/<YOUR_REF>)
步骤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密钥 |
可选(用于电报通知):
| 秘密名称 | 值 |
|---|---|
TELEGRAM_BOT_TOKEN |
Bot令牌来自 @植物学家 |
TELEGRAM_CHAT_ID |
您的聊天ID(从获取 @用户信息机器人) |
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 (<30)
成本估算
| 服务 | 免费等级 | 典型每月费用 |
|---|---|---|
| Supabase | 500MB数据库,500K边缘函数调用 | 大多数用户0美元 |
| OpenRouter | 按次付费 | 3-8美元(5-10次捕获+10-20次搜索/天) |
| 电报 | 无限 | 0美元 |
替代部署选项
默认部署使用Supabase边缘功能+OpenRouter。但是,您可以根据需要在其他配置中运行此服务器。
选项A:本地开发(您的计算机)
在没有任何云服务的情况下,在本地运行服务器进行测试或个人使用。
您需要什么:
- 德诺 已安装(
curl -fsSL https://deno.land/install.sh | sh) - 带pgvector扩展的PostgreSQL
- OpenRouter API密钥(或参见选项C了解本地嵌入)
步骤1:使用pgvector设置本地Postgres
# macOS (Homebrew)
brew install postgresql@16
brew install pgvector
# Ubuntu/Debian
sudo apt install postgresql postgresql-contrib
# Then install pgvector from source:
git clone https://github.com/pgvector/pgvector.git
cd pgvector && make && sudo make install
启动Postgres并创建数据库:
createdb mcp_memory
psql mcp_memory < schema.sql
步骤2:设置环境变量
export SUPABASE_URL="http://localhost:54321" # or your local Postgres connection
export SUPABASE_SERVICE_ROLE_KEY="not-needed-locally"
export OPENROUTER_API_KEY="your-openrouter-key"
步骤3:使Supabase客户端适应本地Postgres
服务器使用Supabase JS客户端。对于纯本地设置,您有两个选择:
选项A1——在本地运行Supabase(最简单):
# Install Supabase CLI
npm install -g supabase
# Initialize and start local Supabase (includes Postgres + pgvector + API)
supabase init
supabase start
这为您提供了一个完整的本地Supabase堆栈 http://localhost:54321。服务器代码按原样工作——只需设置 SUPABASE_URL 到本地URL和 SUPABASE_SERVICE_ROLE_KEY 到由打印的密钥 supabase start.
然后直接运行该函数:
supabase functions serve persistent-mcp --no-verify-jwt --env-file .env
选项A2——将Supabase客户端替换为直接Postgres:
将Supabase客户端替换为直接的Postgres驱动程序,如 postgres (Deno兼容)。这需要更多的代码更改,但完全删除了Supabase依赖关系:
// Replace getSupabase() with:
import postgres from "https://deno.land/x/postgresjs/mod.js";
const sql = postgres({
host: "localhost",
port: 5432,
database: "mcp_memory",
username: "postgres",
password: "your-password",
});
您还需要重写查询构建器调用(.from().select().eq())到原始SQL。这需要更多的工作,但会给你一个零依赖的本地服务器。
第四步:使用Deno跑步
deno run --allow-net --allow-env index.ts
服务器启动于 http://localhost:8000 (或Deno分配的任何端口)。
选项B:云部署(非Supabase)
您可以部署到任何运行Deno或Node.js的云。
Fly.io(为简单起见建议使用):
# Install flyctl
curl -L https://fly.io/install.sh | sh
# Launch
fly launch --name my-mcp-server
fly secrets set OPENROUTER_API_KEY=your-key
fly secrets set DATABASE_URL=your-postgres-connection-string
fly deploy
你需要一个带有pgvector的托管Postgres。选项:
- 霓虹 --免费版,包括pgvector(新技术)
- 铁路 --设置简单,pgvector可用(railway应用程序)
- 渲染 --免费Postgres层(render.com)
- Supabase --仅使用数据库,而不是边缘函数
对于Fly.io/Railway/Render,您需要调整服务器以使用 Deno.serve() 直接(已经在代码中)或将其包装在Node.js HTTP服务器中,稍作更改。
AWS EC2/任何虚拟专用服务器:
# SSH into your server
# Install Deno
curl -fsSL https://deno.land/install.sh | sh
# Install Postgres + pgvector
sudo apt install postgresql postgresql-contrib
git clone https://github.com/pgvector/pgvector.git
cd pgvector && make && sudo make install
# Set up the database
sudo -u postgres createdb mcp_memory
sudo -u postgres psql mcp_memory < schema.sql
# Set environment variables
export SUPABASE_URL="postgresql://postgres:password@localhost:5432/mcp_memory"
export OPENROUTER_API_KEY="your-key"
# Run with PM2 for persistence
npm install -g pm2
pm2 start "deno run --allow-net --allow-env index.ts" --name mcp-server
pm2 save
Docker:
FROM denoland/deno:latest
WORKDIR /app
COPY index.ts .
EXPOSE 8000
CMD ["run", "--allow-net", "--allow-env", "index.ts"]
docker build -t persistent-mcp .
docker run -p 8000:8000 \
-e OPENROUTER_API_KEY=your-key \
-e SUPABASE_URL=your-db-url \
-e SUPABASE_SERVICE_ROLE_KEY=your-key \
persistent-mcp
选项C:替换OpenRouter(本地或替代嵌入)
OpenRouter很方便,但需要花钱并需要互联网接入。以下是替代方案:
与Ollama的本地嵌入:
# Install Ollama
curl -fsSL https://ollama.com/install.sh | sh
# Pull an embedding model
ollama pull nomic-embed-text
然后修改 generateEmbedding() 在 index.ts:
async function generateEmbedding(text: string): Promise<number[]> {
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<string> {
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调用的完全自包含的设置:
- Postgres+pgvector --本地安装(见选项A)
- 奥拉玛 --本地嵌入+本地LLM(见选项C)
- 德诺 --在本地运行服务器
总成本: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) 在模式中 |
许可证
麻省理工学院——见 许可证.
