Picard MCP服务器
概述
Picard MCP是一个完整的内存管理系统,建立在 模型上下文协议(MCP) 标准。它由两个主要组件组成:一个提供安全内存存储和检索服务的MCP服务器,以及一个演示如何与MCP服务器集成的Django客户端应用程序。该系统使用户能够存储、检索和管理他们的记忆,同时控制访问权限,并允许基于存储的记忆进行语义搜索和人工智能查询。
MCP合规性
此实现遵循模型上下文协议标准,该标准允许LLM应用程序以标准化的方式与服务器交互。MCP服务器公开:
- 资源:向LLM提供数据(内存内容)的只读端点
- 工具:执行操作(内存创建、更新、查询)的功能端点
- 认证:OAuth 2.0实现,用于安全访问受保护的资源
关键组件
- MCP服务器:基于FastAPI的模型上下文协议实现,提供:
- ✅ OAuth 2.0身份验证和授权,支持PKCE - ✅ 带有PostgreSQL和pgvector扩展的内存存储 - ✅ 基于权限的内存访问控制(私有/公共) - ✅ 基于OpenAI文本嵌入3-small模型的语义搜索向量嵌入 - 🔄 LLM集成用于基于内存的查询(框架就绪,计划提供高级功能)
- Django客户端:演示与MCP服务器集成的web应用程序:
- 用户注册和身份验证 - OAuth 2.0客户端实现 - 内存创建、检索和管理UI - 基于Persona的查询界面
系统架构
总体架构
Picard MCP系统遵循客户端-服务器架构,包含以下组件:
- MCP服务器:处理内存存储、检索和AI操作的核心后端服务
- 内置FastAPI(FastMCP),提供高性能和异步支持 - 使用PostgreSQL和pgvector扩展进行向量存储和语义搜索 - 为用户、内存(带向量嵌入)、OAuth客户端和令牌实现数据模型 - 使用SQLAlchemy ORM和Alembic迁移进行数据库管理 - 实施OAuth 2.0以实现安全身份验证和授权 - 与OpenAI API集成,用于内存嵌入(text-embedding-3-small) - 在可用时使用LangChain进行LLM操作 - 提供有状态和无状态操作模式 - 支持流式HTTP传输,以实现更好的可扩展性
- Django客户端:演示与MCP服务器集成的Web应用程序
- 提供用户注册、身份验证和配置文件管理 - 实现OAuth 2.0客户端,用于与MCP服务器进行安全通信 - 提供用户友好的内存管理和查询界面 - 使用独立于MCP服务器的PostgreSQL数据库
- Docker基础架构:容器化部署,便于设置和扩展
- MCP服务器(端口8001)、Django客户端(端口8000)和PostgreSQL数据库的单独容器 - 已配置用于安全容器间通信的网络 - 用于持久数据存储的卷装载 - 兼容本地Docker部署和Render云部署
身份验证方法
该系统提供两种主要的身份验证方法:
1.与用户上下文令牌流直接连接(推荐)
这种简化的方法允许用户只在Django客户端进行一次身份验证,从而避免了单独的MCP服务器身份验证的需要:
- 客户注册:
- Django客户端使用以下命令向MCP服务器注册 /api/admin/clients/register 端点 - 注册需要管理员身份验证,包括客户端名称、重定向URI和请求的作用域 - MCP服务器发布基于UUID的客户端ID和加密安全的客户端密钥 - 客户端凭据应安全存储,不得在客户端代码中公开
- 用户身份验证流程:
- 用户仅通过Django客户端进行身份验证 - 当用户发起与MCP服务器的连接时,Django客户端向MCP的服务器端发出服务器端请求 /api/user-tokens/user-token 端点 - 该请求包括: - 客户端凭据(Client_id和Client_secret) - 用户信息(用户名和电子邮件) - 如果不存在创建用户的选项 - MCP服务器验证客户端凭据,并找到或创建相应的用户 - MCP服务器为用户发放访问和刷新令牌 - Django客户端安全地存储这些令牌,并将其用于API请求
- API访问:
- 客户端在Authorization标头中包含访问令牌(Authorization: Bearer {token})用于所有API请求 - MCP服务器验证令牌签名、过期和受众声明 - MCP服务器对每个端点强制执行基于范围的权限 - 当访问令牌过期时,客户端使用刷新令牌获取新令牌
- 安全特性:
- 只有机密客户端可以使用此方法,提供服务器到服务器的安全性 - 为每个令牌请求验证客户端凭据 - 令牌在使用后被列入黑名单,以防止重放攻击 - 刷新令牌使用轮换:每次使用都会生成一个新的刷新令牌,并使旧令牌无效
2.使用PKCE的标准OAuth 2.0授权代码流(传统)
该系统还支持符合RFC 6749和RFC 7636标准的标准OAuth 2.0授权码流和PKCE,以增强安全性。这种方法要求用户同时向客户端和MCP服务器进行身份验证:
- 授权流程:
- 用户通过Django客户端发起登录 - 客户端生成加密安全的随机 state CSRF保护参数 - 客户端生成随机的PKCE code_verifier 并推导 code_challenge 使用SHA-256 - 客户端重定向到MCP服务器 /authorize 端点具有: - response_type=code - client_id (UUID格式) - redirect_uri - scope (空格分隔的列表。, memories:read memories:write) - state (用于CSRF保护) - PKCE参数(code_challenge 和 code_challenge_method=S256) - MCP服务器对用户进行身份验证(如果尚未进行身份验证) - MCP服务器验证所有参数,并使用短期授权码重定向回客户端
- 代币交换:
- 客户端验证返回的 state 参数与授权请求中发送的参数匹配 - 客户端通过以下方式交换访问和刷新令牌的授权码 /token 端点 - MCP服务器发出JWT访问令牌、刷新令牌、过期时间和授予的范围
- API访问:
- 与直接连接方法相同
数据库模型
MCP服务器使用SQLAlchemy ORM和以下关键模型:
- 用户模型:
- 使用电子邮件、用户名和哈希密码存储用户信息 - 包括帐户状态的布尔标志(is_active、is_superuser) - 通过一对多关系与记忆相连
- 具有向量存储的内存模型:
- 使用pgvector扩展来存储和查询向量嵌入(1536维) - 支持具有可选加密的文本内容 - 包括权限控制(私有/公共) - 支持限时记忆的过期日期 - 通过外键关系与用户关联
- OAuth模型:
- OAuthClient:存储客户端应用程序详细信息,包括client_id、client_secret、重定向URI和授权范围 - 授权码:使用PKCE支持管理临时授权码 - 代币:存储具有过期跟踪功能的访问和刷新令牌
该系统使用Alembic进行数据库迁移,确保模式版本控制和易于更新。
内存管理系统
Picard MCP的核心功能围绕以下组件的内存管理展开:
- 记忆存储:
- 内存存储为带有相关元数据的文本 - 向量嵌入(使用文本嵌入3-small模型)实现了语义搜索功能 - 权限控制谁可以访问每个内存 - 时间戳跟踪创建、修改和过期 - 内存文本在静止时被加密,而元数据仍然可以搜索 - 所有标识符都使用UUID格式,而不是顺序整数,以实现可扩展性 - 使用OpenAI的嵌入模型将每个内存转换为向量嵌入 - 嵌入支持语义搜索和相似性匹配 - 带有pgvector扩展的PostgreSQL提供了高效的向量存储和检索
- 权限管理:
- 每个内存都有一个权限级别(私有或公共) - 私人记忆仅供所有者访问 - 其他用户可以访问公共记忆进行人物角色查询 - 系统设计为可扩展以适应未来的权限类型(例如,用于统计/聚合用途) - 特定用户或组可以访问共享内存 - 内存所有者可以随时修改权限
- 记忆提取:
- ✅ 用户可以通过过滤和排序选项检索自己的记忆 - ✅ 基于语义的向量嵌入记忆语义搜索 - ✅ 使用pgvector查找相关记忆的向量相似度(余弦) - ✅ 基于查询相关性和可配置相似度阈值的Top-N最相似记忆 - ✅ 权限检查确保用户只能访问授权的内存
- LLM集成:
- 内存可以用作LLM查询的上下文 - 用户可以根据他们的公共记忆创建角色 - 其他用户可以查询这些人物角色,以获得记忆中的响应 - 系统自动处理上下文管理和提示工程
主要特点
MCP服务器功能
- OAuth 2.0身份验证:
- 使用PKCE的授权码流可增强安全性 - 基于范围的权限系统(memories:read, memories:write, memories:admin) - 支持刷新令牌的令牌管理 - 客户注册和管理
- 内存管理:
- 创建、读取、更新和删除记忆 - 语义搜索的向量嵌入 - 基于权限的访问控制 - 批处理操作以实现高效的内存管理
- 用户管理:
- 用户注册和身份验证 - 配置文件管理和设置 - 活动跟踪和分析 - 系统管理的管理员控制
- ✅ 人工智能集成:
- ✅ OpenAI API集成(v1.x),用于使用text-embedding-3-small模型的嵌入 - ✅ 所有存储器的自动矢量嵌入生成(1536维) - ✅ 基于pgvector余弦相似度的语义搜索 - ✅ 具有严格错误处理的异步嵌入生成 - 📋 基于用户记忆的人物角色创建框架(计划中) - 📋 上下文感知查询处理架构(计划中)
Django客户端功能
- 用户界面:
- 桌面和移动设备的简洁、响应式设计 - 直观的内存管理界面 - 高级搜索和筛选选项 - 人物角色创建和查询界面
- OAuth客户端实现:
- 安全的令牌存储和管理 - 自动令牌刷新 - 基于范围的功能可用性 - 错误处理和恢复
- 内存工具:
- 支持富文本的内存创建 - 批量导入和导出 - 权限管理界面 - 标记和分类
MCP接口
MCP资源
- 存储器资源:
memories://{memory_id}
- 通过权限检查返回特定内存的内容 - 参数:memory_id(UUID) - 响应:包含元数据的内存内容
- 用户记忆资源:
users://{user_id}/memories
- 通过权限检查返回特定用户的内存列表 - 参数:user_id(UUID),可选筛选器 - 回复:记忆摘要列表
MCP工具
- 提交内存工具:创建新内存
- 参数:文本(字符串)、权限(字符串) - 返回:使用UUID创建内存详细信息
- 更新内存工具:更新现有内存
- 参数:memory_id(UUID),文本(字符串) - 返回:更新内存详细信息
- 删除内存工具:删除内存
- 参数:memory_id(UUID) - 返回:成功确认
- 查询内存工具:对记忆执行语义搜索
- 参数:查询(字符串)、限制(整数)、相似性阈值(浮点数)、权限过滤器(字符串) - 返回:按相似性得分排序的相关记忆列表 - 用途:OpenAI嵌入和pgvector余弦相似度
- 查询用户:根据记忆查询用户的角色
- 参数:user_id(UUID),查询(字符串) - 返回:基于用户记忆的响应
API终点
OAuth端点
- 客户注册:
/register
- 方法:POST - 说明:注册新的OAuth客户端 - 请求:客户端详细信息(ID、机密、重定向URI、作用域) - 响应:客户端凭据和注册信息
- 授权:
/authorize
- 方法:GET - 说明:启动OAuth授权流 - 参数:response_type、client_id、redirect_uri、作用域、状态、code_challenge、code_chalenge_method - 响应:重定向到具有授权码的客户端
- 代币交换:
/token
- 方法:POST - 说明:代币的交换授权码 - 请求:grant_type、code、redirect_uri、client_id、client_secret、code_verifier - 响应:访问令牌、刷新令牌、过期和范围信息
内存端点
- 获取记忆:
/api/tools(工具:get_memories)
- 方法:POST - 描述:使用可选过滤检索记忆 - 身份验证:承载令牌 - 请求:可选过滤器参数(user_id、权限、过期状态) - 响应:用户可访问的内存列表 - 请求示例:
{
"tool": "get_memories",
"data": {
"user_id": "550e8400-e29b-41d4-a716-446655440000",
"permission": "private"
}
}- 提交内存:
/api/tools(工具:submit_memory)
- 方法:POST - 说明:创建新内存 - 身份验证:承载令牌 - 请求:内存文本、权限级别和到期日期(ISO 8601格式,例如“2025-12-31T23:59:59Z”) - 响应:创建了包括UUID标识符在内的内存详细信息 - 请求示例:
{
"tool": "submit_memory",
"data": {
"text": "This is my memory content",
"permission": "private"
}
}- 找回记忆:
/api/tools(工具:retrieve_memories)
- 方法:POST - 描述:获取经过身份验证的用户的所有内存 - 身份验证:承载令牌 - 响应:具有UUID标识符的内存对象列表 - 请求示例:
{
"tool": "retrieve_memories",
"data": {}
}- 更新存储器:
/api/tools(工具:update_memory)
- 方法:POST - 说明:更新现有内存 - 身份验证:承载令牌 - 请求:内存ID、更新的内容和可选更新的到期日期(ISO 8601格式) - 响应:更新了内存详细信息 - 请求示例:
{
"tool": "update_memory",
"data": {
"memory_id": "550e8400-e29b-41d4-a716-446655440000",
"text": "Updated memory content",
"expiration_date": "2026-01-01T00:00:00Z"
}
}- 修改权限:
/api/tools(工具:modify_permissions)
- 方法:POST - 说明:更新内存权限级别 - 身份验证:承载令牌 - 请求:内存UUID和新的权限级别 - 响应:更新了内存详细信息 - 请求示例:
{
"tool": "modify_permissions",
"data": {
"memory_id": "550e8400-e29b-41d4-a716-446655440000",
"permission": "public"
}
}- 查询内存:
/api/tools(工具:query_memory)
- 方法:POST - 描述:使用向量嵌入对用户的记忆进行语义搜索 - 身份验证:承载令牌 - 请求:在数据字段中搜索查询和可选参数 - 响应:按相似性评分排序的记忆列表 - 请求示例:
{
"tool": "query_memory",
"data": {
"query": "artificial intelligence thoughts",
"limit": 10,
"similarity_threshold": 0.5,
"permission_filter": "private"
}
}- 示例响应:
{
"data": {
"memories": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"text": "I think AI will revolutionize how we work...",
"permission": "private",
"similarity": 0.85,
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z"
}
]
}
}- 查询用户:
/api/tools(工具:query_user)
- 方法:POST - 描述:根据记忆查询用户的角色(对其他用户公开,对自己公开+私有) - 身份验证:承载令牌 - 请求:用户UUID和查询提示 - 响应:JSON包含未过期的内存,无论是所有有效内存还是与查询最相似的前N个内存 - 响应:基于用户记忆的AI生成响应 - 请求示例:
{
"tool": "query_user",
"data": {
"user_id": "550e8400-e29b-41d4-a716-446655440000",
"prompt": "What are your thoughts on artificial intelligence?"
}
}设置和部署
先决条件
- Docker和Docker Compose
- Python 3.10+
- OpenAI API密钥
完整安装指南
- 克隆存储库:
git clone https://github.com/yourusername/picard_mcp.git
cd picard_mcp- 为两个组件创建环境文件:
# For MCP server
cp mcp_server/.env.example mcp_server/.env
# For Django client
cp django_client/.env.example django_client/.env- 编辑环境文件以设置配置:
- 在 mcp_server/.env:设置数据库凭据、OpenAI API密钥和管理员凭据 - 在 django_client/.env:设置数据库凭据和OAuth设置
- 使用Docker Compose启动服务:
docker-compose up -d这将启动以下服务:
- db-mcp:MCP服务器的PostgreSQL数据库 - db-django:Django客户端的PostgreSQL数据库 - mcp_server:MCP服务器正在运行http://localhost:8001 - django_client:正在运行的Django客户端http://localhost:8000
- 为MCP服务器创建管理员用户:
docker-compose exec mcp_server python scripts/create_admin_user.py这将使用环境变量中指定的凭据创建管理员用户。
- 在MCP服务器上注册Django客户端:
docker-compose exec django_client python register_oauth_client.py这将在MCP服务器上注册Django客户端,并更新Django客户端的 .env 包含客户端凭据的文件。
- 访问应用程序:
- MCP服务器:http://localhost:8001 - Django客户端:http://localhost:8000
- 在Django客户端创建一个用户帐户并开始使用该应用程序。
初始测试
要验证您的设置是否正常工作,请运行以下测试:
- MCP服务器测试:
docker-compose exec mcp_server python -m pytest这将运行MCP服务器的所有单元测试,包括OAuth端点、管理功能和内存管理。
- Django客户端测试:
docker-compose exec django_client python manage.py test这将测试Django客户端与MCP服务器的集成。
- 手动测试:
- 在Django客户端中创建一个用户帐户http://localhost:8000/register - 登录并通过OAuth连接到MCP服务器 - 创建、检索和管理记忆 - 测试语义搜索功能
安全考虑
数据保护
- 内存文本内容在静止时使用Python的Fernet对称加密(CBC模式下的AES-128,带有PKCS7填充)进行加密,而元数据仍然可以搜索
- 个人身份信息(PII)通过文本字段加密进行保护
- 访问令牌的过期时间为1小时,以限制暴露
- 刷新令牌寿命长,但使用轮换:每次使用都会生成一个新的刷新令牌,并使旧令牌无效
- OAuth令牌安全地存储在Django客户端的PostgreSQL数据库中
UUID使用情况
系统中的所有标识符都使用UUID v4格式,而不是顺序整数,原因有几个:
- 安全:UUID不公开系统信息或记录计数
- 可扩展性:UUID可以在没有数据库协调的情况下生成,从而支持分布式系统
- 不可猜测:UUID几乎无法猜测,从而防止枚举攻击
- 一致性:在整个系统中使用UUID简化了与其他服务的集成
API中的所有id(user_id、memory_id、client_id等)都必须是UUID格式。
OAuth最佳实践
- 所有OAuth通信必须在生产环境中使用HTTPS
- 授权码是一次性使用的,期限短(最多5分钟)
- 所有客户端,甚至是机密客户端,都需要PKCE进行深度防御
- 刷新令牌是长期有效的,但可以由用户或管理员撤销
- 系统为已撤销的令牌维护一个令牌黑名单
文档
API文档
MCP服务器包括所有端点的Swagger/OpenAPI文档:
- 访问Swagger用户界面
/docs服务器运行时 - OpenAPI规范可在
/openapi.json - 所有API端点都有完整的请求/响应模式和示例文档
附加文档文件
- 测试.md:测试应用程序的综合指南
- 描述所有已实施的测试及其目的 - 本地和CI/CD中运行测试的说明 - 记录测试范围并确定需要额外测试的区域
- DEBUGGING.md:跟踪问题及其解决方案
- 记录尚未修复的已知错误 - 以前解决的错误及其解决方案的文档 - 为常见问题提供故障排除指导
- 规划.md:跟踪实施网站所需任务的细分
- 列出实施站点所需的任务和子任务 - 使用复选框记录任务是否已完成
部署
该项目包括 docker-compose.yml 促进地方发展和 render.yaml 部署到Render的蓝图。相同的代码库既可以在Docker容器中本地运行,也可以在部署到Render云服务时运行。
MCP服务器部署
- Docker部署 (推荐用于生产):
docker-compose up -dDocker Compose配置包括:
- 集装箱间通信的网络配置 - 用于持久数据存储的卷装载 - 从.env文件配置环境变量 - 端口映射(Django客户端8000,MCP服务器8001) - 服务依赖关系的健康检查
- 渲染云部署:
使用随附的 render.yaml 要部署到Render的蓝图。
许可证
麻省理工学院
